2 * Copyright (C) 2012 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.api
.application
;
11 import java
.util
.Arrays
;
12 import java
.util
.EnumSet
;
13 import java
.util
.List
;
15 import java
.util
.UUID
;
17 import org
.apache
.logging
.log4j
.LogManager
;
18 import org
.apache
.logging
.log4j
.Logger
;
19 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
20 import org
.springframework
.context
.event
.ContextRefreshedEvent
;
21 import org
.springframework
.context
.event
.ContextStartedEvent
;
22 import org
.springframework
.security
.core
.GrantedAuthority
;
23 import org
.springframework
.transaction
.PlatformTransactionManager
;
24 import org
.springframework
.transaction
.TransactionDefinition
;
25 import org
.springframework
.transaction
.TransactionStatus
;
26 import org
.springframework
.transaction
.support
.DefaultTransactionDefinition
;
28 import eu
.etaxonomy
.cdm
.api
.service
.ICommonService
;
29 import eu
.etaxonomy
.cdm
.api
.service
.IGrantedAuthorityService
;
30 import eu
.etaxonomy
.cdm
.api
.service
.IGroupService
;
31 import eu
.etaxonomy
.cdm
.api
.service
.IUserService
;
32 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
33 import eu
.etaxonomy
.cdm
.common
.monitor
.NullProgressMonitor
;
34 import eu
.etaxonomy
.cdm
.config
.Configuration
;
35 import eu
.etaxonomy
.cdm
.model
.metadata
.CdmMetaData
;
36 import eu
.etaxonomy
.cdm
.model
.permission
.CRUD
;
37 import eu
.etaxonomy
.cdm
.model
.permission
.GrantedAuthorityImpl
;
38 import eu
.etaxonomy
.cdm
.model
.permission
.Group
;
39 import eu
.etaxonomy
.cdm
.model
.permission
.PermissionClass
;
40 import eu
.etaxonomy
.cdm
.model
.permission
.User
;
41 import eu
.etaxonomy
.cdm
.persistence
.permission
.CdmAuthority
;
42 import eu
.etaxonomy
.cdm
.persistence
.permission
.Role
;
43 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
46 * The <code>FirstDataInserter</code> is responsible for equipping a new and empty database with
47 * the initial set of data need by the cdmlib. It operates not only on empty databases,
48 * its methods are executed everytime the ApplicationContext has been started up, that is listens
49 * for {@link ContextStartedEvent}s.
53 * <li>User 'admin' and role 'ROLE_ADMIN'</li>
54 * <li>cdm metadata</li>
57 * The <code>runAsAuthenticationProvider</code> must be set in a security application context, eg:
59 <bean id="firstDataInserter" class="eu.etaxonomy.cdm.api.application.FirstDataInserter">
60 <property name="runAsAuthenticationProvider" ref="runAsAuthenticationProvider"/>
64 * @author a.kohlbecker
67 //@RunAs("ROLE_ADMIN") // seems to be broken in spring see: https://jira.springsource.org/browse/SEC-1671
68 public class FirstDataInserter
extends AbstractDataInserter
{
70 private static final Logger logger
= LogManager
.getLogger();
72 private static final EnumSet
<CRUD
> CREATE_READ
= EnumSet
.of(CRUD
.CREATE
, CRUD
.READ
);
73 private static final EnumSet
<CRUD
> UPDATE_DELETE
= EnumSet
.of(CRUD
.UPDATE
, CRUD
.DELETE
);
74 private static final EnumSet
<CRUD
> CREATE_READ_UPDATE
= EnumSet
.of(CRUD
.CREATE
, CRUD
.READ
, CRUD
.UPDATE
);
75 private static final EnumSet
<CRUD
> CREATE_READ_UPDATE_DELETE
= EnumSet
.of(CRUD
.CREATE
, CRUD
.READ
, CRUD
.UPDATE
, CRUD
.DELETE
);
77 public static final GrantedAuthority
[] EDITOR_GROUP_AUTHORITIES
= new GrantedAuthority
[]{
78 new CdmAuthority(PermissionClass
.REFERENCE
, CREATE_READ
),
79 new CdmAuthority(PermissionClass
.TAXONNAME
, CREATE_READ_UPDATE
),
80 new CdmAuthority(PermissionClass
.TEAMORPERSONBASE
, CREATE_READ
),
81 new CdmAuthority(PermissionClass
.TAXONBASE
, CREATE_READ_UPDATE_DELETE
),
82 new CdmAuthority(PermissionClass
.DESCRIPTIONBASE
, CREATE_READ_UPDATE_DELETE
),
83 new CdmAuthority(PermissionClass
.DESCRIPTIONELEMENTBASE
, CREATE_READ_UPDATE_DELETE
),
84 new CdmAuthority(PermissionClass
.SPECIMENOROBSERVATIONBASE
, CREATE_READ_UPDATE_DELETE
),
85 new CdmAuthority(PermissionClass
.COLLECTION
, CREATE_READ_UPDATE_DELETE
),
89 * This group will in future replace the group Editor, see issue #7150
91 public static final CdmAuthority
[] EDITOR_GROUP_EXTENDED_CREATE_GROUP_AUTHORITIES
= new CdmAuthority
[]{
92 new CdmAuthority(PermissionClass
.REFERENCE
, CREATE_READ
),
93 new CdmAuthority(PermissionClass
.TAXONNAME
, CREATE_READ
),
94 new CdmAuthority(PermissionClass
.TEAMORPERSONBASE
, CREATE_READ
),
95 new CdmAuthority(PermissionClass
.TAXONBASE
, CREATE_READ
),
96 new CdmAuthority(PermissionClass
.DESCRIPTIONBASE
, CREATE_READ
),
97 new CdmAuthority(PermissionClass
.DESCRIPTIONELEMENTBASE
, CREATE_READ
),
98 new CdmAuthority(PermissionClass
.SPECIMENOROBSERVATIONBASE
, CREATE_READ
),
99 new CdmAuthority(PermissionClass
.COLLECTION
, CREATE_READ
),
102 public static final GrantedAuthority
[] PROJECT_MANAGER_GROUP_AUTHORITIES
= new GrantedAuthority
[]{
103 new CdmAuthority(PermissionClass
.REFERENCE
, UPDATE_DELETE
),
104 new CdmAuthority(PermissionClass
.TAXONNAME
, EnumSet
.of(CRUD
.DELETE
)),
105 new CdmAuthority(PermissionClass
.TEAMORPERSONBASE
, UPDATE_DELETE
),
106 Role
.ROLE_PROJECT_MANAGER
,
109 public static final CdmAuthority
[] EDITOR_REFERENCE_GROUP_AUTHORITIES
= new CdmAuthority
[]{
110 new CdmAuthority(PermissionClass
.REFERENCE
, UPDATE_DELETE
),
111 new CdmAuthority(PermissionClass
.TEAMORPERSONBASE
, UPDATE_DELETE
)
114 public static final Role
[] PUBLISH_GROUP_ROLES
= new Role
[]{
118 public static final CdmAuthority
[] EDIT_ALL_TAXA_GROUP_AUTHORITIES
= new CdmAuthority
[]{
119 new CdmAuthority(PermissionClass
.TAXONNODE
, CREATE_READ_UPDATE_DELETE
)
122 public static final Role
[] ADMIN_GROUP_ROLES
= new Role
[]{
126 public static final Role
[] USER_MANAGER_ROLES
= new Role
[]{
127 Role
.ROLE_USER_MANAGER
131 private ICommonService commonService
;
134 private IUserService userService
;
137 private IGroupService groupService
;
140 private IGrantedAuthorityService grantedAuthorityService
;
142 // not to be autowired, since the FirstdataInserter must be usable without security
143 // private AuthenticationProvider runAsAuthenticationProvider = null;
145 protected PlatformTransactionManager transactionManager
;
147 protected DefaultTransactionDefinition txDefinition
= new DefaultTransactionDefinition();
149 private IProgressMonitor progressMonitor
= null;
151 private boolean firstDataInserted
= false;
154 public void setTransactionManager(PlatformTransactionManager transactionManager
) {
155 this.transactionManager
= transactionManager
;
158 public FirstDataInserter() {
159 txDefinition
.setName("FirstDataInserter.insertFirstData()");
160 txDefinition
.setPropagationBehavior(TransactionDefinition
.PROPAGATION_REQUIRED
);
164 public void onApplicationEvent(ContextRefreshedEvent event
) {
165 if(event
.getApplicationContext() instanceof MonitoredGenericApplicationContext
){
166 progressMonitor
= ((MonitoredGenericApplicationContext
)event
.getApplicationContext()).getCurrentMonitor();
167 /* TODO set up work amount, currently the amount of work ticks is hard coded
168 * in {@link CdmApplicationControllersetNewDataSource}, but we need some means to register
170 * see https://dev.e-taxonomy.eu/redmine/issues/3140 (generic way to obtain work ticks of application startup for monitoring)
174 progressMonitor
= new NullProgressMonitor();
180 private void insertFirstData() {
182 // this ApplicationListener may be called multiple times in nested
183 // application contexts like in web applications
184 if(!firstDataInserted
){
186 runAsAuthentication(Role
.ROLE_ADMIN
);
187 TransactionStatus txStatus
= transactionManager
.getTransaction(txDefinition
);
189 logger
.info("inserting first data");
191 checkDefaultGroups();
192 assureRole_REMOTING_forEditors();
194 firstDataInserted
= true;
196 transactionManager
.commit(txStatus
);
197 restoreAuthentication();
199 logger
.debug("insertFirstData() already executed before, skipping this time");
203 private void checkMetadata() {
204 int metaDataCount
= commonService
.getCdmMetaData().size();
205 if (metaDataCount
== 0){
206 progressMonitor
.subTask("Creating Meta Data");
211 private void checkAdminUser() {
212 User admin
= findFirstUser();
215 progressMonitor
.subTask("Creating Admin User");
216 admin
= createAdminUser();
218 logger
.info("Assuming first user '" + admin
+ "' is admin.");
221 checkAdminRole(admin
);
222 progressMonitor
.worked(1);
225 private void checkDefaultGroups(){
227 progressMonitor
.subTask("Checking default groups");
228 checkGroup(Group
.GROUP_EDITOR_UUID
, Group
.GROUP_EDITOR_NAME
, EDITOR_GROUP_AUTHORITIES
);
229 checkGroup(Group
.GROUP_EDITOR_EXTENDED_CREATE_UUID
, Group
.GROUP_EDITOR_EXTENDED_CREATE_NAME
, EDITOR_GROUP_EXTENDED_CREATE_GROUP_AUTHORITIES
);
230 checkGroup(Group
.GROUP_PROJECT_MANAGER_UUID
, Group
.GROUP_PROJECT_MANAGER_NAME
, PROJECT_MANAGER_GROUP_AUTHORITIES
);
231 checkGroup(Group
.GROUP_ADMIN_UUID
, Group
.GROUP_ADMIN_NAME
, ADMIN_GROUP_ROLES
);
232 checkGroup(Group
.GROUP_EDITOR_REFERENCE_UUID
, Group
.GROUP_EDITOR_REFERENCE_NAME
, EDITOR_REFERENCE_GROUP_AUTHORITIES
);
233 checkGroup(Group
.GROUP_ALLOW_ALL_TAXA_UUID
, Group
.GROUP_ALLOW_ALL_TAXA_NAME
, EDIT_ALL_TAXA_GROUP_AUTHORITIES
);
234 checkGroup(Group
.GROUP_PUBLISH_UUID
, Group
.GROUP_PUBLISH_NAME
, PUBLISH_GROUP_ROLES
);
235 checkGroup(Group
.GROUP_USER_MANAGER_UUID
, Group
.GROUP_USER_MANAGER_NAME
, USER_MANAGER_ROLES
);
236 progressMonitor
.worked(1);
239 private void checkGroup(UUID groupUuid
, String groupName
, GrantedAuthority
[] requiredAuthorities
) {
240 Group group
= groupService
.load(groupUuid
);
242 group
= Group
.NewInstance();
243 group
.setUuid(groupUuid
);
244 logger
.info("New Group '" + groupName
+ "' created");
246 group
.setName(groupName
); // force default name
248 Set
<GrantedAuthority
> grantedAuthorities
= group
.getGrantedAuthorities();
250 for(GrantedAuthority requiredAuthority
: requiredAuthorities
){
251 boolean isMissing
= true;
252 for(GrantedAuthority ga
: grantedAuthorities
){
253 if(requiredAuthority
.getAuthority().equals(ga
.getAuthority())){
259 addMissingAuthority(groupName
, group
, requiredAuthority
);
263 groupService
.saveOrUpdate(group
);
264 logger
.info("Check of group '" + groupName
+ "' done");
267 private void addMissingAuthority(String groupName
, Group group
, GrantedAuthority requiredAuthority
) {
268 //NOTE: we still have to do this by string until UUIDs are fixed
269 GrantedAuthorityImpl newGa
= grantedAuthorityService
.findAuthorityString(requiredAuthority
.getAuthority());
272 newGa
= GrantedAuthorityImpl
.NewInstance(requiredAuthority
.toString());
273 if (requiredAuthority
instanceof Role
){
274 newGa
.setUuid(((Role
)requiredAuthority
).getUuid());
278 group
.addGrantedAuthority(newGa
);
279 logger
.info("New GrantedAuthority '" + requiredAuthority
+ "' added to '" + groupName
+ "'");
282 private User
findFirstUser() {
283 User firstUser
= null;
284 List
<User
> users
= userService
.list(null, 1, null, Arrays
.asList(new OrderHint
[]{new OrderHint("id", OrderHint
.SortOrder
.ASCENDING
)}), null);
285 if(users
.size() > 0){
286 firstUser
= users
.get(0);
291 private User
createAdminUser(){
293 User admin
= User
.NewInstance(Configuration
.adminLogin
, Configuration
.adminPassword
);
294 userService
.save(admin
);
295 logger
.info("user '" + Configuration
.adminLogin
+ "' created.");
300 * Assures the {@link Role#ROLE_REMOTING} exists.
302 * If the role is missing in the db it will be created and added to the Groups <code>Editor</code> and <code>EditorExtendedCreate</code>.
304 * The role will however not be added to the editor groups in case the role exist but is missing from one of these groups. This allows removal
305 * of the role from the editor groups to withdraw the remote editing permission from editors in general for a project.
307 * see https://dev.e-taxonomy.eu/redmine/issues/7972
309 private void assureRole_REMOTING_forEditors(){
311 if(!roleExists(Role
.ROLE_REMOTING
)){
312 GrantedAuthorityImpl roleRemoting
= assureRole(Role
.ROLE_REMOTING
);
313 Group groupEditor
= groupService
.load(Group
.GROUP_EDITOR_UUID
);
314 groupEditor
.addGrantedAuthority(roleRemoting
);
315 groupService
.saveOrUpdate(groupEditor
);
316 Group groupEditorExtendedCreate
= groupService
.load(Group
.GROUP_EDITOR_EXTENDED_CREATE_UUID
);
317 groupEditorExtendedCreate
.addGrantedAuthority(roleRemoting
);
318 groupService
.saveOrUpdate(groupEditorExtendedCreate
);
322 private void checkAdminRole(User admin
) {
324 Set
<GrantedAuthority
> authorities
= (Set
<GrantedAuthority
>) admin
.getAuthorities();
326 boolean hasRoleAdmin
= false;
327 for(GrantedAuthority grau
: authorities
){
328 if(grau
.getAuthority().contentEquals(Role
.ROLE_ADMIN
.getAuthority())){
335 authorities
.add(assureRole(Role
.ROLE_ADMIN
));
336 admin
.setGrantedAuthorities(authorities
);
337 progressMonitor
.subTask("Creating Admins Role");
338 userService
.saveOrUpdate(admin
);
339 logger
.info("Role " + Role
.ROLE_ADMIN
.getAuthority() + " for user '" + Configuration
.adminLogin
+ "' created and added");
343 private GrantedAuthorityImpl
assureRole(Role role
) {
344 GrantedAuthorityImpl roleLoaded
= grantedAuthorityService
.find(role
.getUuid());
345 if(roleLoaded
== null){
346 roleLoaded
= grantedAuthorityService
.save(role
.asNewGrantedAuthority());
351 private boolean roleExists(Role role
) {
352 GrantedAuthorityImpl roleLoaded
= grantedAuthorityService
.find(role
.getUuid());
353 return roleLoaded
!= null;
356 private void createMetadata(){
357 List
<CdmMetaData
> metaData
= CdmMetaData
.defaultMetaData();
358 commonService
.saveAllMetaData(metaData
);
359 logger
.info("Metadata created.");