3 * Copyright (C) 2012 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.api
.application
;
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
;
18 import java
.util
.UUID
;
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
;
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
;
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.
60 * <li>User 'admin' and role 'ROLE_ADMIN'</li>
61 * <li>cdm metadata</li>
64 * The <code>runAsAuthenticationProvider</code> must be set in a security application context, eg:
66 <bean id="firstDataInserter" class="eu.etaxonomy.cdm.api.application.FirstDataInserter">
67 <property name="runAsAuthenticationProvider" ref="runAsAuthenticationProvider"/>
73 * @author a.kohlbecker
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
> {
80 public static final Logger logger
= Logger
.getLogger(FirstDataInserter
.class);
83 * must match the key in eu/etaxonomy/cdm/services_security.xml
85 private static final String RUN_AS_KEY
= "TtlCx3pgKC4l";
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]",
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(),
104 private ICommonService commonService
;
107 private IUserService userService
;
110 private IGroupService groupService
;
114 private IGrantedAuthorityService grantedAuthorityService
;
116 // not to be autowired, since the FirstdataInserter must be usable without security
117 private AuthenticationProvider runAsAuthenticationProvider
= null;
119 protected PlatformTransactionManager transactionManager
;
121 protected DefaultTransactionDefinition txDefinition
= new DefaultTransactionDefinition();
123 private IProgressMonitor progressMonitor
= null;
125 private boolean firstDataInserted
= false;
127 private Authentication authentication
;
129 private ApplicationContext applicationContext
;
132 public void setTransactionManager(PlatformTransactionManager transactionManager
) {
133 this.transactionManager
= transactionManager
;
136 public FirstDataInserter() {
137 txDefinition
.setName("FirstDataInserter.insertFirstData()");
138 txDefinition
.setPropagationBehavior(TransactionDefinition
.PROPAGATION_REQUIRED
);
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
148 * see http://dev.e-taxonomy.eu/trac/ticket/3140 (generic way to obtain work ticks of application startup for monitoring)
152 progressMonitor
= new NullProgressMonitor();
154 applicationContext
= event
.getApplicationContext();
160 private void insertFirstData() {
162 // this ApplicationListener may be called multiple times in nested
163 // application contexts like in web applications
164 if(!firstDataInserted
){
166 runAsAuthentication();
168 TransactionStatus txStatus
= transactionManager
.getTransaction(txDefinition
);
170 logger
.info("inserting first data");
172 checkDefaultGroups();
174 firstDataInserted
= true;
176 transactionManager
.commit(txStatus
);
178 restoreAuthentication();
181 logger
.debug("insertFirstData() already executed before, skipping this time");
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
189 private void restoreAuthentication() {
190 if(runAsAuthenticationProvider
== null){
191 logger
.debug("no RunAsAuthenticationProvider set, thus nothing to restore");
193 SecurityContext securityContext
= SecurityContextHolder
.getContext();
194 securityContext
.setAuthentication(authentication
);
195 logger
.debug("last authentication restored: " + (authentication
!= null ? authentication
: "NULL"));
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
203 private void runAsAuthentication() {
204 if(runAsAuthenticationProvider
== null){
205 logger
.debug("no RunAsAuthenticationProvider set, skipping run-as authentication");
209 SecurityContext securityContext
= SecurityContextHolder
.getContext();
210 authentication
= securityContext
.getAuthentication();
213 Collection
<GrantedAuthority
> rules
= new ArrayList
<GrantedAuthority
>();
214 rules
.add(Role
.ROLE_ADMIN
);
215 RunAsUserToken adminToken
= new RunAsUserToken(
220 (authentication
!= null ? authentication
.getClass() : AnonymousAuthenticationToken
.class));
222 Authentication runAsAuthentication
= runAsAuthenticationProvider
.authenticate(adminToken
);
223 SecurityContextHolder
.getContext().setAuthentication(runAsAuthentication
);
225 logger
.debug("switched to run-as authentication: " + runAsAuthentication
);
229 private void checkMetadata() {
230 int metaDataCount
= commonService
.getCdmMetaData().size();
231 if (metaDataCount
== 0){
232 progressMonitor
.subTask("Creating Meta Data");
237 private void checkAdminUser() {
238 User admin
= findFirstUser();
241 progressMonitor
.subTask("Creating Admin User");
242 admin
= createAdminUser();
244 logger
.info("Assuming first user '" + admin
+ "' is admin.");
247 checkAdminRole(admin
);
248 progressMonitor
.worked(1);
251 private void checkDefaultGroups(){
253 progressMonitor
.subTask("Checking default groups");
254 checkGroup(Group
.groupEditorUuid
, "Editor", editorGroupAuthorities
);
255 checkGroup(Group
.groupProjectManagerUuid
, "ProjectManager", projectManagerGroupAuthorities
);
256 progressMonitor
.worked(1);
262 * @param requiredAuthorities
264 private void checkGroup(UUID groupUuid
, String groupName
, String
[] requiredAuthorities
) {
265 Group group
= groupService
.load(groupUuid
);
267 group
= Group
.NewInstance();
268 group
.setUuid(groupUuid
);
269 logger
.info("New Group '" + groupName
+ "' created");
271 group
.setName(groupName
); // force name
273 Set
<GrantedAuthority
> grantedAuthorities
= group
.getGrantedAuthorities();
275 for(String a
: requiredAuthorities
){
276 boolean isMissing
= true;
277 for(GrantedAuthority ga
: grantedAuthorities
){
278 if(a
.equals(ga
.getAuthority())){
284 GrantedAuthorityImpl newGa
= GrantedAuthorityImpl
.NewInstance();
285 newGa
.setAuthority(a
);
286 group
.addGrantedAuthority(newGa
);
287 logger
.info("New GrantedAuthority '" + a
+ "' added to '" + groupName
+ "'");
290 groupService
.saveOrUpdate(group
);
291 logger
.info("Check of group '" + groupName
+ "' done");
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);
306 private User
createAdminUser(){
308 User admin
= User
.NewInstance(Configuration
.adminLogin
, Configuration
.adminPassword
);
309 userService
.save(admin
);
310 logger
.info("user '" + Configuration
.adminLogin
+ "' created.");
314 private void checkAdminRole(User admin
) {
315 Set
<GrantedAuthority
> authorities
= new HashSet
<GrantedAuthority
>();
318 authorities
= (Set
<GrantedAuthority
>) admin
.getAuthorities();
320 boolean hasRoleAdmin
= false;
321 for(GrantedAuthority grau
: authorities
){
322 if(grau
.getAuthority().contentEquals(Role
.ROLE_ADMIN
.getAuthority())){
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");
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();
348 private void createMetadata(){
349 List
<CdmMetaData
> metaData
= CdmMetaData
.defaultMetaData();
350 commonService
.saveAllMetaData(metaData
);
351 logger
.info("Metadata created.");
355 * @return the runAsAuthenticationProvider
357 public AuthenticationProvider
getRunAsAuthenticationProvider() {
358 return runAsAuthenticationProvider
;
362 * @param runAsAuthenticationProvider the runAsAuthenticationProvider to set
364 public void setRunAsAuthenticationProvider(AuthenticationProvider runAsAuthenticationProvider
) {
365 this.runAsAuthenticationProvider
= runAsAuthenticationProvider
;