Project

General

Profile

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

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

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

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

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

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

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

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

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

    
103
    @Autowired
104
    private ICommonService commonService;
105

    
106
    @Autowired
107
    private IUserService userService;
108

    
109
    @Autowired
110
    private IGroupService groupService;
111

    
112

    
113
    @Autowired
114
    private IGrantedAuthorityService grantedAuthorityService;
115

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

    
119
    protected PlatformTransactionManager transactionManager;
120

    
121
    protected DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
122

    
123
    private IProgressMonitor progressMonitor = null;
124

    
125
    private boolean firstDataInserted = false;
126

    
127
    private Authentication authentication;
128

    
129
    private ApplicationContext applicationContext;
130

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

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

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

    
156
        insertFirstData();
157
    }
158

    
159

    
160
    private void insertFirstData() {
161

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

    
166
            runAsAuthentication();
167

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

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

    
176
            transactionManager.commit(txStatus);
177

    
178
            restoreAuthentication();
179

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

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

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

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

    
212

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

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

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

    
228

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

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

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

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

    
251
    private void checkDefaultGroups(){
252

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

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

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

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

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

    
306
    private User createAdminUser(){
307

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

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

    
317

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

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

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

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

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

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

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

    
368

    
369
}
(3-3/6)