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
;
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
;
37 import eu
.etaxonomy
.cdm
.api
.service
.ICommonService
;
38 import eu
.etaxonomy
.cdm
.api
.service
.IGrantedAuthorityService
;
39 import eu
.etaxonomy
.cdm
.api
.service
.IUserService
;
40 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
41 import eu
.etaxonomy
.cdm
.common
.monitor
.NullProgressMonitor
;
42 import eu
.etaxonomy
.cdm
.config
.Configuration
;
43 import eu
.etaxonomy
.cdm
.model
.common
.GrantedAuthorityImpl
;
44 import eu
.etaxonomy
.cdm
.model
.common
.User
;
45 import eu
.etaxonomy
.cdm
.model
.metadata
.CdmMetaData
;
46 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.Role
;
47 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
50 * The <code>FirstDataInserter</code> is responsible for equipping a new and empty database with
51 * the initial set of data need by the cdmlib. It operates not only on empty databases,
52 * its methods are executed everytime the ApplicationContext has been started up, that is listens
53 * for {@link ContextStartedEvent}s.
57 * <li>User 'admin' and role 'ROLE_ADMIN'</li>
58 * <li>cdm metadata</li>
61 * The <code>runAsAuthenticationProvider</code> must be set in a security application context, eg:
63 <bean id="firstDataInserter" class="eu.etaxonomy.cdm.api.application.FirstDataInserter">
64 <property name="runAsAuthenticationProvider" ref="runAsAuthenticationProvider"/>
70 * @author a.kohlbecker
74 //@RunAs("ROLE_ADMIN") // seems to be broken in spring see: https://jira.springsource.org/browse/SEC-1671
75 public class FirstDataInserter
implements ApplicationListener
<ContextRefreshedEvent
> {
77 public static final Logger logger
= Logger
.getLogger(FirstDataInserter
.class);
79 private static final long serialVersionUID
= -4738245032655597608L;
82 * must match the key in eu/etaxonomy/cdm/services_security.xml
84 private static final String RUN_AS_KEY
= "TtlCx3pgKC4l";
87 private ICommonService commonService
;
90 private IUserService userService
;
93 private IGrantedAuthorityService grantedAuthorityService
;
95 // not to be autowired, since the FirstdataInserter must be usable without security
96 private AuthenticationProvider runAsAuthenticationProvider
= null;
98 protected PlatformTransactionManager transactionManager
;
100 protected DefaultTransactionDefinition txDefinition
= new DefaultTransactionDefinition();
102 private IProgressMonitor progressMonitor
= null;
104 private boolean firstDataInserted
= false;
106 private Authentication authentication
;
108 private ApplicationContext applicationContext
;
111 public void setTransactionManager(PlatformTransactionManager transactionManager
) {
112 this.transactionManager
= transactionManager
;
115 public FirstDataInserter() {
116 txDefinition
.setName("FirstDataInserter.insertFirstData()");
117 txDefinition
.setPropagationBehavior(TransactionDefinition
.PROPAGATION_REQUIRED
);
121 public void onApplicationEvent(ContextRefreshedEvent event
) {
122 if(event
.getApplicationContext() instanceof MonitoredGenericApplicationContext
){
123 progressMonitor
= ((MonitoredGenericApplicationContext
)event
.getApplicationContext()).getCurrentMonitor();
124 /* TODO set up work amount, currently the amount of work ticks is hard coded
125 * in {@link CdmApplicationControllersetNewDataSource}, but we need some means to register
127 * see http://dev.e-taxonomy.eu/trac/ticket/3140 (generic way to obtain work ticks of application startup for monitoring)
131 progressMonitor
= new NullProgressMonitor();
133 applicationContext
= event
.getApplicationContext();
139 private void insertFirstData() {
141 // this ApplicationListener may be called multiple times in nested
142 // application contexts like in web applications
143 if(!firstDataInserted
){
145 runAsAuthentication();
147 TransactionStatus txStatus
= transactionManager
.getTransaction(txDefinition
);
149 logger
.info("inserting first data");
152 firstDataInserted
= true;
154 transactionManager
.commit(txStatus
);
156 restoreAuthentication();
159 logger
.debug("insertFirstData() already executed before, skipping this time");
164 * needed to work around the broken @RunAs("ROLE_ADMIN") which
165 * seems to be broken in spring see: https://jira.springsource.org/browse/SEC-1671
167 private void restoreAuthentication() {
168 if(runAsAuthenticationProvider
== null){
169 logger
.debug("no RunAsAuthenticationProvider set, thus nothing to restore");
171 SecurityContext securityContext
= SecurityContextHolder
.getContext();
172 securityContext
.setAuthentication(authentication
);
173 logger
.debug("last authentication restored: " + (authentication
!= null ? authentication
: "NULL"));
178 * needed to work around the broken @RunAs("ROLE_ADMIN") which seems to be
179 * broken in spring see: https://jira.springsource.org/browse/SEC-1671
181 private void runAsAuthentication() {
182 if(runAsAuthenticationProvider
== null){
183 logger
.debug("no RunAsAuthenticationProvider set, skipping run-as authentication");
187 SecurityContext securityContext
= SecurityContextHolder
.getContext();
188 authentication
= securityContext
.getAuthentication();
191 Collection
<GrantedAuthority
> rules
= new ArrayList
<GrantedAuthority
>();
192 rules
.add(Role
.ROLE_ADMIN
);
193 RunAsUserToken adminToken
= new RunAsUserToken(
198 (authentication
!= null ? authentication
.getClass() : AnonymousAuthenticationToken
.class));
200 Authentication runAsAuthentication
= runAsAuthenticationProvider
.authenticate(adminToken
);
201 SecurityContextHolder
.getContext().setAuthentication(runAsAuthentication
);
203 logger
.debug("switched to run-as authentication: " + runAsAuthentication
);
207 private void checkMetadata() {
208 int metaDataCount
= commonService
.getCdmMetaData().size();
209 if (metaDataCount
== 0){
210 progressMonitor
.subTask("Creating Meta Data");
215 private void checkAdminUser() {
216 User admin
= findFirstUser();
219 progressMonitor
.subTask("Creating Admin User");
220 admin
= createAdminUser();
222 logger
.info("Assuming first user '" + admin
+ "' is admin.");
225 checkAdminRole(admin
);
226 progressMonitor
.worked(1);
232 private User
findFirstUser() {
233 User firstUser
= null;
234 List
<User
> users
= userService
.list(null, 1, null, Arrays
.asList(new OrderHint
[]{new OrderHint("id", OrderHint
.SortOrder
.ASCENDING
)}), null);
235 if(users
.size() > 0){
236 firstUser
= users
.get(0);
241 private User
createAdminUser(){
243 User admin
= User
.NewInstance(Configuration
.adminLogin
, Configuration
.adminPassword
);
244 userService
.save(admin
);
245 logger
.info("user '" + Configuration
.adminLogin
+ "' created.");
249 private void checkAdminRole(User admin
) {
250 Set
<GrantedAuthority
> authorities
= new HashSet
<GrantedAuthority
>();
253 authorities
= (Set
<GrantedAuthority
>) admin
.getAuthorities();
255 boolean hasRoleAdmin
= false;
256 for(GrantedAuthority grau
: authorities
){
257 if(grau
.getAuthority().contentEquals(Role
.ROLE_ADMIN
.getAuthority())){
264 authorities
.add(getRoleAdmin());
265 admin
.setGrantedAuthorities(authorities
);
266 progressMonitor
.subTask("Creating Admins Role");
267 userService
.saveOrUpdate(admin
);
268 logger
.info("Role " + Role
.ROLE_ADMIN
.getAuthority() + " for user '" + Configuration
.adminLogin
+ "' created and added");
275 private GrantedAuthorityImpl
getRoleAdmin() {
276 GrantedAuthorityImpl role_admin
= grantedAuthorityService
.find(Role
.ROLE_ADMIN
.getUuid());
277 if(role_admin
== null){
278 role_admin
= Role
.ROLE_ADMIN
.asNewGrantedAuthority();
283 private void createMetadata(){
284 List
<CdmMetaData
> metaData
= CdmMetaData
.defaultMetaData();
285 commonService
.saveAllMetaData(metaData
);
286 logger
.info("Metadata created.");
290 * @return the runAsAuthenticationProvider
292 public AuthenticationProvider
getRunAsAuthenticationProvider() {
293 return runAsAuthenticationProvider
;
297 * @param runAsAuthenticationProvider the runAsAuthenticationProvider to set
299 public void setRunAsAuthenticationProvider(AuthenticationProvider runAsAuthenticationProvider
) {
300 this.runAsAuthenticationProvider
= runAsAuthenticationProvider
;