#3948 first step, if a taxon is used in a polytomous key, the taxonNode will be delet...
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / application / FirstDataInserter.java
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
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.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;
48
49 /**
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.
54 * <p>
55 * responsibilities:
56 * <ul>
57 * <li>User 'admin' and role 'ROLE_ADMIN'</li>
58 * <li>cdm metadata</li>
59 * <ul>
60 * <p>
61 * The <code>runAsAuthenticationProvider</code> must be set in a security application context, eg:
62 * {@code
63 <bean id="firstDataInserter" class="eu.etaxonomy.cdm.api.application.FirstDataInserter">
64 <property name="runAsAuthenticationProvider" ref="runAsAuthenticationProvider"/>
65 </bean>
66 }
67 *
68 *
69 *
70 * @author a.kohlbecker
71 * @date Oct 12, 2012
72 *
73 */
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> {
76
77 public static final Logger logger = Logger.getLogger(FirstDataInserter.class);
78
79 private static final long serialVersionUID = -4738245032655597608L;
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 @Autowired
87 private ICommonService commonService;
88
89 @Autowired
90 private IUserService userService;
91
92 @Autowired
93 private IGrantedAuthorityService grantedAuthorityService;
94
95 // not to be autowired, since the FirstdataInserter must be usable without security
96 private AuthenticationProvider runAsAuthenticationProvider = null;
97
98 protected PlatformTransactionManager transactionManager;
99
100 protected DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
101
102 private IProgressMonitor progressMonitor = null;
103
104 private boolean firstDataInserted = false;
105
106 private Authentication authentication;
107
108 private ApplicationContext applicationContext;
109
110 @Autowired
111 public void setTransactionManager(PlatformTransactionManager transactionManager) {
112 this.transactionManager = transactionManager;
113 }
114
115 public FirstDataInserter() {
116 txDefinition.setName("FirstDataInserter.insertFirstData()");
117 txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
118 }
119
120 @Override
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
126 * additional ticks.
127 * see http://dev.e-taxonomy.eu/trac/ticket/3140 (generic way to obtain work ticks of application startup for monitoring)
128 *
129 */
130 } else {
131 progressMonitor = new NullProgressMonitor();
132 }
133 applicationContext = event.getApplicationContext();
134
135 insertFirstData();
136 }
137
138
139 private void insertFirstData() {
140
141 // this ApplicationListener may be called multiple times in nested
142 // application contexts like in web applications
143 if(!firstDataInserted){
144
145 runAsAuthentication();
146
147 TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
148
149 logger.info("inserting first data");
150 checkAdminUser();
151 checkMetadata();
152 firstDataInserted = true;
153
154 transactionManager.commit(txStatus);
155
156 restoreAuthentication();
157
158 } else {
159 logger.debug("insertFirstData() already executed before, skipping this time");
160 }
161 }
162
163 /**
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
166 */
167 private void restoreAuthentication() {
168 if(runAsAuthenticationProvider == null){
169 logger.debug("no RunAsAuthenticationProvider set, thus nothing to restore");
170 }
171 SecurityContext securityContext = SecurityContextHolder.getContext();
172 securityContext.setAuthentication(authentication);
173 logger.debug("last authentication restored: " + (authentication != null ? authentication : "NULL"));
174 }
175
176 /**
177 *
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
180 */
181 private void runAsAuthentication() {
182 if(runAsAuthenticationProvider == null){
183 logger.debug("no RunAsAuthenticationProvider set, skipping run-as authentication");
184 return;
185 }
186
187 SecurityContext securityContext = SecurityContextHolder.getContext();
188 authentication = securityContext.getAuthentication();
189
190
191 Collection<GrantedAuthority> rules = new ArrayList<GrantedAuthority>();
192 rules.add(Role.ROLE_ADMIN);
193 RunAsUserToken adminToken = new RunAsUserToken(
194 RUN_AS_KEY,
195 "system-admin",
196 null,
197 rules,
198 (authentication != null ? authentication.getClass() : AnonymousAuthenticationToken.class));
199
200 Authentication runAsAuthentication = runAsAuthenticationProvider.authenticate(adminToken);
201 SecurityContextHolder.getContext().setAuthentication(runAsAuthentication);
202
203 logger.debug("switched to run-as authentication: " + runAsAuthentication);
204 }
205
206
207 private void checkMetadata() {
208 int metaDataCount = commonService.getCdmMetaData().size();
209 if (metaDataCount == 0){
210 progressMonitor.subTask("Creating Meta Data");
211 createMetadata();
212 }
213 }
214
215 private void checkAdminUser() {
216 User admin = findFirstUser();
217
218 if (admin == null){
219 progressMonitor.subTask("Creating Admin User");
220 admin = createAdminUser();
221 } else {
222 logger.info("Assuming first user '" + admin + "' is admin.");
223 }
224
225 checkAdminRole(admin);
226 progressMonitor.worked(1);
227 }
228
229 /**
230 * @return
231 */
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);
237 }
238 return firstUser;
239 }
240
241 private User createAdminUser(){
242
243 User admin = User.NewInstance(Configuration.adminLogin, Configuration.adminPassword);
244 userService.save(admin);
245 logger.info("user '" + Configuration.adminLogin + "' created.");
246 return admin;
247 }
248
249 private void checkAdminRole(User admin) {
250 Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
251
252
253 authorities = (Set<GrantedAuthority>) admin.getAuthorities();
254
255 boolean hasRoleAdmin = false;
256 for(GrantedAuthority grau : authorities){
257 if(grau.getAuthority().contentEquals(Role.ROLE_ADMIN.getAuthority())){
258 hasRoleAdmin = true;
259 break;
260 }
261 }
262
263 if(!hasRoleAdmin){
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");
269 }
270 }
271
272 /**
273 * @return
274 */
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();
279 }
280 return role_admin;
281 }
282
283 private void createMetadata(){
284 List<CdmMetaData> metaData = CdmMetaData.defaultMetaData();
285 commonService.saveAllMetaData(metaData);
286 logger.info("Metadata created.");
287 }
288
289 /**
290 * @return the runAsAuthenticationProvider
291 */
292 public AuthenticationProvider getRunAsAuthenticationProvider() {
293 return runAsAuthenticationProvider;
294 }
295
296 /**
297 * @param runAsAuthenticationProvider the runAsAuthenticationProvider to set
298 */
299 public void setRunAsAuthenticationProvider(AuthenticationProvider runAsAuthenticationProvider) {
300 this.runAsAuthenticationProvider = runAsAuthenticationProvider;
301 }
302
303
304 }