Project

General

Profile

Download (13 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2012 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.api.application;
10

    
11
import java.util.ArrayList;
12
import java.util.Arrays;
13
import java.util.Collection;
14
import java.util.HashSet;
15
import java.util.List;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import org.apache.log4j.Logger;
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.context.ApplicationContext;
22
import org.springframework.context.ApplicationListener;
23
import org.springframework.context.event.ContextRefreshedEvent;
24
import org.springframework.context.event.ContextStartedEvent;
25
import org.springframework.security.access.intercept.RunAsUserToken;
26
import org.springframework.security.authentication.AnonymousAuthenticationToken;
27
import org.springframework.security.authentication.AuthenticationProvider;
28
import org.springframework.security.core.Authentication;
29
import org.springframework.security.core.GrantedAuthority;
30
import org.springframework.security.core.context.SecurityContext;
31
import org.springframework.security.core.context.SecurityContextHolder;
32
import org.springframework.transaction.PlatformTransactionManager;
33
import org.springframework.transaction.TransactionDefinition;
34
import org.springframework.transaction.TransactionStatus;
35
import org.springframework.transaction.support.DefaultTransactionDefinition;
36

    
37
import eu.etaxonomy.cdm.api.service.ICommonService;
38
import eu.etaxonomy.cdm.api.service.IGrantedAuthorityService;
39
import eu.etaxonomy.cdm.api.service.IGroupService;
40
import eu.etaxonomy.cdm.api.service.IUserService;
41
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
42
import eu.etaxonomy.cdm.common.monitor.NullProgressMonitor;
43
import eu.etaxonomy.cdm.config.Configuration;
44
import eu.etaxonomy.cdm.model.common.GrantedAuthorityImpl;
45
import eu.etaxonomy.cdm.model.common.Group;
46
import eu.etaxonomy.cdm.model.common.User;
47
import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
48
import eu.etaxonomy.cdm.persistence.hibernate.permission.Role;
49
import eu.etaxonomy.cdm.persistence.query.OrderHint;
50

    
51
/**
52
 * The <code>FirstDataInserter</code> is responsible for equipping a new and empty database with
53
 * the initial set of data need by the cdmlib. It operates not only on empty databases,
54
 * its methods are executed everytime the ApplicationContext has been started up, that is listens
55
 * for {@link ContextStartedEvent}s.
56
 * <p>
57
 * responsibilities:
58
 * <ul>
59
 * <li>User 'admin' and role 'ROLE_ADMIN'</li>
60
 * <li>cdm metadata</li>
61
 * <ul>
62
 * <p>
63
 * The <code>runAsAuthenticationProvider</code> must be set in a security application context, eg:
64
 * {@code
65
    <bean id="firstDataInserter" class="eu.etaxonomy.cdm.api.application.FirstDataInserter">
66
        <property name="runAsAuthenticationProvider" ref="runAsAuthenticationProvider"/>
67
    </bean>
68
    }
69
 *
70
 *
71
 *
72
 * @author a.kohlbecker
73
 * @date Oct 12, 2012
74
 *
75
 */
76
//@RunAs("ROLE_ADMIN") // seems to be broken in spring see: https://jira.springsource.org/browse/SEC-1671
77
public class FirstDataInserter implements ApplicationListener<ContextRefreshedEvent> {
78

    
79
    public static final Logger logger = Logger.getLogger(FirstDataInserter.class);
80

    
81
    /**
82
     * must match the key in eu/etaxonomy/cdm/services_security.xml
83
     */
84
    private static final String RUN_AS_KEY = "TtlCx3pgKC4l";
85

    
86
    public static final String[] editorGroupAuthorities = new String[]{
87
            "REFERENCE.[CREATE,READ]",
88
            "TAXONNAMEBASE.[CREATE,READ,UPDATE]",
89
            "TEAMORPERSONBASE.[CREATE,READ]",
90
            "TAXONBASE.[CREATE,UPDATE,DELETE,READ]",
91
            "DESCRIPTIONBASE.[CREATE,UPDATE,DELETE,READ]",
92
            "DESCRIPTIONELEMENTBASE.[CREATE,UPDATE,DELETE,READ]",
93
    };
94

    
95
    public static final String[] projectManagerGroupAuthorities = new String[]{
96
            "REFERENCE.[UPDATE,DELETE]",
97
            "TAXONNAMEBASE.[DELETE]",
98
            "TEAMORPERSONBASE.[UPDATE,DELETE]",
99
            Role.ROLE_PROJECT_MANAGER.toString(),
100
    };
101

    
102
    @Autowired
103
    private ICommonService commonService;
104

    
105
    @Autowired
106
    private IUserService userService;
107

    
108
    @Autowired
109
    private IGroupService groupService;
110

    
111

    
112
    @Autowired
113
    private IGrantedAuthorityService grantedAuthorityService;
114

    
115
    // not to be autowired, since the FirstdataInserter must be usable without security
116
    private AuthenticationProvider runAsAuthenticationProvider = null;
117

    
118
    protected PlatformTransactionManager transactionManager;
119

    
120
    protected DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
121

    
122
    private IProgressMonitor progressMonitor = null;
123

    
124
    private boolean firstDataInserted = false;
125

    
126
    private Authentication authentication;
127

    
128
    private ApplicationContext applicationContext;
129

    
130
    @Autowired
131
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
132
        this.transactionManager = transactionManager;
133
    }
134

    
135
    public FirstDataInserter() {
136
        txDefinition.setName("FirstDataInserter.insertFirstData()");
137
        txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
138
    }
139

    
140
    @Override
141
    public void onApplicationEvent(ContextRefreshedEvent event) {
142
        if(event.getApplicationContext() instanceof MonitoredGenericApplicationContext){
143
            progressMonitor = ((MonitoredGenericApplicationContext)event.getApplicationContext()).getCurrentMonitor();
144
            /* TODO set up work amount, currently the amount of work ticks is hard coded
145
             *      in {@link CdmApplicationControllersetNewDataSource}, but we need some means to register
146
             *      additional ticks.
147
             *      see http://dev.e-taxonomy.eu/trac/ticket/3140 (generic way to obtain work ticks of application startup for monitoring)
148
             *
149
             */
150
        } else {
151
            progressMonitor = new NullProgressMonitor();
152
        }
153
        applicationContext = event.getApplicationContext();
154

    
155
        insertFirstData();
156
    }
157

    
158

    
159
    private void insertFirstData() {
160

    
161
        // this ApplicationListener may be called multiple times in nested
162
        // application contexts like in web applications
163
        if(!firstDataInserted){
164

    
165
            runAsAuthentication();
166

    
167
            TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
168

    
169
            logger.info("inserting first data");
170
            checkAdminUser();
171
            checkDefaultGroups();
172
            checkMetadata();
173
            firstDataInserted = true;
174

    
175
            transactionManager.commit(txStatus);
176

    
177
            restoreAuthentication();
178

    
179
        } else {
180
            logger.debug("insertFirstData() already executed before, skipping this time");
181
        }
182
    }
183

    
184
    /**
185
     * needed to work around the broken @RunAs("ROLE_ADMIN") which
186
     * seems to be broken in spring see: https://jira.springsource.org/browse/SEC-1671
187
     */
188
    private void restoreAuthentication() {
189
        if(runAsAuthenticationProvider == null){
190
            logger.debug("no RunAsAuthenticationProvider set, thus nothing to restore");
191
        }
192
        SecurityContext securityContext = SecurityContextHolder.getContext();
193
        securityContext.setAuthentication(authentication);
194
        logger.debug("last authentication restored: " + (authentication != null ? authentication : "NULL"));
195
    }
196

    
197
    /**
198
     *
199
     * needed to work around the broken @RunAs("ROLE_ADMIN") which seems to be
200
     * broken in spring see: https://jira.springsource.org/browse/SEC-1671
201
     */
202
    private void runAsAuthentication() {
203
        if(runAsAuthenticationProvider == null){
204
            logger.debug("no RunAsAuthenticationProvider set, skipping run-as authentication");
205
            return;
206
        }
207

    
208
        SecurityContext securityContext = SecurityContextHolder.getContext();
209
        authentication = securityContext.getAuthentication();
210

    
211

    
212
        Collection<GrantedAuthority> rules = new ArrayList<GrantedAuthority>();
213
        rules.add(Role.ROLE_ADMIN);
214
        RunAsUserToken adminToken = new RunAsUserToken(
215
                RUN_AS_KEY,
216
                "system-admin",
217
                null,
218
                rules,
219
                (authentication != null ? authentication.getClass() : AnonymousAuthenticationToken.class));
220

    
221
        Authentication runAsAuthentication = runAsAuthenticationProvider.authenticate(adminToken);
222
        SecurityContextHolder.getContext().setAuthentication(runAsAuthentication);
223

    
224
        logger.debug("switched to run-as authentication: " + runAsAuthentication);
225
    }
226

    
227

    
228
    private void checkMetadata() {
229
        int metaDataCount = commonService.getCdmMetaData().size();
230
        if (metaDataCount == 0){
231
            progressMonitor.subTask("Creating Meta Data");
232
            createMetadata();
233
        }
234
    }
235

    
236
    private void checkAdminUser() {
237
        User admin = findFirstUser();
238

    
239
        if (admin == null){
240
            progressMonitor.subTask("Creating Admin User");
241
            admin = createAdminUser();
242
        } else {
243
            logger.info("Assuming first user '" + admin + "' is admin.");
244
        }
245

    
246
        checkAdminRole(admin);
247
        progressMonitor.worked(1);
248
    }
249

    
250
    private void checkDefaultGroups(){
251

    
252
        progressMonitor.subTask("Checking default groups");
253
        checkGroup(Group.groupEditorUuid, "Editor", editorGroupAuthorities);
254
        checkGroup(Group.groupProjectManagerUuid, "ProjectManager", projectManagerGroupAuthorities);
255
        progressMonitor.worked(1);
256
    }
257

    
258
    /**
259
     * @param newGroups
260
     * @param groupName
261
     * @param requiredAuthorities
262
     */
263
    private void checkGroup(UUID groupUuid, String groupName, String[] requiredAuthorities) {
264
        Group group = groupService.load(groupUuid);
265
        if(group == null){
266
            group = Group.NewInstance();
267
            group.setUuid(groupUuid);
268
            logger.info("New Group '" + groupName + "' created");
269
        }
270
        group.setName(groupName); // force name
271

    
272
        Set<GrantedAuthority> grantedAuthorities = group.getGrantedAuthorities();
273

    
274
        for(String a : requiredAuthorities){
275
            boolean isMissing = true;
276
            for(GrantedAuthority ga : grantedAuthorities){
277
                if(a.equals(ga.getAuthority())){
278
                    isMissing = false;
279
                    break;
280
                }
281
            }
282
            if(isMissing){
283
                GrantedAuthorityImpl newGa = GrantedAuthorityImpl.NewInstance();
284
                newGa.setAuthority(a);
285
                group.addGrantedAuthority(newGa);
286
                logger.info("New GrantedAuthority '" + a + "' added  to '" + groupName + "'");
287
            }
288
        }
289
        groupService.saveOrUpdate(group);
290
        logger.info("Check of group  '" + groupName + "' done");
291
    }
292

    
293
    /**
294
     * @return
295
     */
296
    private User findFirstUser() {
297
        User firstUser = null;
298
        List<User> users = userService.list(null, 1, null, Arrays.asList(new OrderHint[]{new OrderHint("id", OrderHint.SortOrder.ASCENDING)}), null);
299
        if(users.size() > 0){
300
            firstUser = users.get(0);
301
        }
302
        return firstUser;
303
    }
304

    
305
    private User createAdminUser(){
306

    
307
        User admin = User.NewInstance(Configuration.adminLogin, Configuration.adminPassword);
308
        userService.save(admin);
309
        logger.info("user '" + Configuration.adminLogin + "' created.");
310
        return admin;
311
    }
312

    
313
    private void checkAdminRole(User admin) {
314
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
315

    
316

    
317
        authorities = (Set<GrantedAuthority>) admin.getAuthorities();
318

    
319
        boolean hasRoleAdmin = false;
320
        for(GrantedAuthority grau : authorities){
321
            if(grau.getAuthority().contentEquals(Role.ROLE_ADMIN.getAuthority())){
322
                hasRoleAdmin = true;
323
                break;
324
            }
325
        }
326

    
327
        if(!hasRoleAdmin){
328
            authorities.add(getRoleAdmin());
329
            admin.setGrantedAuthorities(authorities);
330
            progressMonitor.subTask("Creating Admins Role");
331
            userService.saveOrUpdate(admin);
332
            logger.info("Role " + Role.ROLE_ADMIN.getAuthority() + " for user '" + Configuration.adminLogin + "' created and added");
333
        }
334
    }
335

    
336
    /**
337
     * @return
338
     */
339
    private GrantedAuthorityImpl getRoleAdmin() {
340
        GrantedAuthorityImpl role_admin = grantedAuthorityService.find(Role.ROLE_ADMIN.getUuid());
341
        if(role_admin == null){
342
            role_admin = Role.ROLE_ADMIN.asNewGrantedAuthority();
343
        }
344
        return role_admin;
345
    }
346

    
347
    private void createMetadata(){
348
        List<CdmMetaData> metaData = CdmMetaData.defaultMetaData();
349
        commonService.saveAllMetaData(metaData);
350
        logger.info("Metadata created.");
351
    }
352

    
353
    /**
354
     * @return the runAsAuthenticationProvider
355
     */
356
    public AuthenticationProvider getRunAsAuthenticationProvider() {
357
        return runAsAuthenticationProvider;
358
    }
359

    
360
    /**
361
     * @param runAsAuthenticationProvider the runAsAuthenticationProvider to set
362
     */
363
    public void setRunAsAuthenticationProvider(AuthenticationProvider runAsAuthenticationProvider) {
364
        this.runAsAuthenticationProvider = runAsAuthenticationProvider;
365
    }
366

    
367

    
368
}
(3-3/6)