CDM authorisation and access control

It is evident that the cdm library needs authorisation and access control. There are library methods which need to protected from unauthorised execution and there is also the data which is exposed by the library. Not all data should be visible to every user so a row level access control is needed.

Further information can be found in:

This page only is a brief note for now, compiled by a lot of copy paste from the java doc!

Roles and CdmAuthorities


Roles are defined in two places:

  1. eu.etaxonomy.cdm.persistence.hibernate.permission.Role
    • ROLE_PROJECT_MANAGER (permission to modify server-side preferences, terms and vocabularies, ... this Role explicitly has NO overlap with ROLE_USER_MANAGER or ROLE_PUBLISH)
    • ROLE_USER_MANAGER (permission to modify User, Group, GrantedAuthority)
    • ROLE_PUBLISH (permission to modify the publish flag of Taxon, ...)
    • ROLE_REMOTING (permission to use the /remoting/** webservice, that is to connect the TaxEditor to a cdm instance, see #7972)
  2. eu.etaxonomy.cdm.remote.config.MultiWebSecurityConfiguration


A CdmAuthority consists basically of two parts which are separated by a dot character '.'.

  • permissionClass: an CdmPermissionClass instance with represents a cdm type or a part of the cdm type hierarchy. The className is always represented as an upper case string.
  • property: The CdmAuthority only applies to instances which satisfy the specified property. Interpretation is up to type specific voters.
  • operation: a string which specifies a Operation on that set of cdm types
  • targetUuid: The operation may be restricted to a specific cdm entity by adding the entity uuid to the operation. The uuid string is enclosed in curly brackets '{' , '}' and appended to the end of the operation.

Examples for permissionStrings:


in contrast to CdmAuthorities there are more general role like the following listed below, all these roles are having the role prefix 'ROLE_' which is defined in the String Security RoleVoter class:


Permission Groups

Permission groups can usually created as needed. The group name should reflect semantics the nature of the permission group.

The cdm provides default permission groups which are created by the FirstDataInserter in case they do not yet exist. See #4082 (implement default permission groups) for implementation details. Currently the following default permission groups exist:


  • Default group, added by the FirstDataInserter

Grant users with the ability to work on all taxa in all classifications.
You may want to compine this group with Editor as far as no restriction on the taxa are mandatory.



  • Default group, added by the FirstDataInserter

Contains all Authorities which are required to be granted for general editing, except the Authority for a specific taxonomical group. In order to actually enable an Editor to edit something it must be associated with a TAXONNODE Authority, e.g: @TAXONNODE.[CREATE,UPDATE,DELETE,READ]{...@}. (===> Allow_for_all_taxa) Users which belong to this permission group are prohibited from toggling the publish-flag and from changing or deleting references and authors, but are able to create new ones. Once a 'Editor' has created a new Reference or Author the new entity is considered being still in the process of being created for some time even if the entity has already been saved to the database. See #4305 (newly created entities must stay editable even if a user only has the permission to create them) for implementation details on this feature.



  • Default group, added by the FirstDataInserter

A permission group which enables a User to edit and delete entities which are of central importance for a whole project. Users which belong to this permission group are granted to edit and delete taxa, names, references, authors, terms and can toggle the publish-flag on taxa, specimens and occurrences.



  • Custom group, needs to be added manually

Enables a User to work with structured descriptions in the "Matrix editor".
Does not need to be combined with any other group like it is needed for Editor as long as the users only it to be working on specimen descriptions.



Authorisation control

Different approaches of authorisation control need to be used in order to implement all kinds of CRUD permissions:

  • service layer: method @PreAuthorize annotations with Spring EL, e.g. UserService:
@PreAuthorize("#username == or hasRole('ROLE_ADMIN')")
public void changePasswordForUser(String username, String newPassword) {
  • UPDATE and CREATE of cdm instances is protected by a Hibernate interceptor, the CdmSecurityHibernateInterceptor@. This interceptor implements the @onSave() and onFlushDirty methods and thus can control creation and updating of cdm objects. Hibernate calls these methods for any entity which needs to be persisted even if a save or update operations causes a cascading save of objects connected to the object graph which is being saved. TODO implement DELETE (#3079)
  • READ permissions can not be implemented using the CdmSecurityHibernateInterceptor@. Reasons: @onLoad() is called just before an object is initialized, so the object has no uuid or anything else with would be needed to decide on principals authorisation. A row based access control mechanism is required, see HibernateSpringAndRowlevelSecurity

Authorisation evaluation

One of the central classes for the evaluation of Roles and CdmAuthorities is the CdmPermissionEvluator with implements the The method public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) will always return true if the authentication has the role ROLE_ADMIN otherwise decision making is delegated to a AccessDecisionManager which should be UnanimousBasedUnrevocable. This AccessDecisionManager polls all configured AccessDecisionVoters grants access if only grant (or abstain) votes were received. The AccessDecisionManager then asks the set of plugged in AccessDecisionVoters*. The cdm specific voters are found in the package eu.etaxonomy.cdm.persistence.hibernate.permission.voter. For actual configuration details please see the *persistence_security.xml , here is an example:

<bean id="accessDecisionManager" class="">
        <property name="decisionVoters">
                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.GrantAlwaysVoter" />
                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonNodeVoter" />
                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonBaseVoter" />
                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.DescriptionBaseVoter" />
                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.DescriptionElementVoter" />

    CdmPermissionEvaluator.hasPermissions() evaluates the CdmPermissions like TAXONNODE.[UPDATE]{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}
<bean id="cdmPermissionEvaluator" class="eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator">
    <property name="accessDecisionManager" ref="accessDecisionManager" />


As briefly described above, the AccessDecisionVoters are crucial for finding a decision on whether a user is allowed to perform an operation on a specific cdm object or class of cdm objects. The operations are the so called CRUD operations. This acronym refers to Create, Read, Update, Delete.

A user can have one or multiple granted authorities, for more information on granted authorities please refer to the paragraph CdmAuthority above. All voters will cast votes on all granted authorities a user has.

In the following we will just use the term voter as a short form for AccessDecisionVoter.

Voters can cast three different vote:

  • Allow
  • Deny
  • Abstain

In order to make a access decision the AccessDecisionVoters are asked sequentially to cast their vote. If at least one voter casts Deny the the whole operation will be denied. This behaviour is guaranteed by the GrantAlwaysVoter which will always cast an Allow.

All AccessDecisionVoters are responsible for a specific class of data entities and thus it will only cast an Allow or Deny vote when an operation on an object of this class is to be performed. If a voter is not responsible it will Abstain. If a voter "feels" not responsible it casts an Abstain. As a consequence of this concept everything is allowed unless a voter is responsible for a specific object class. Only in this case there is the potential that an operation will be denied.

Generally each of the voters will check if the user has a granted authority which matches the the operation and object. Voters can also perform additional checks (see TaxonNodeVoter, DescriptionElementVoter) which can take related objects into account (parent taxon nodes) or specific properties of of Objects (feature of description element).

List of existing AccessDecisionVoters in the order of their execution:

  1. TaxonNodeVoter:
    • responsible for TaxonNode
    • allows if voter is responsible for the object class in question and
    • if the user has a granted authority for that specific object or for all objects of this class which matches the operation to be performed
    • or if the user has a matching granted authority for any of the parent taxon nodes in the classification (e.g.: TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7})
    • denies otherwise
  2. TaxonBaseVoter:
    • responsible for Taxon, Synonym
    • allows if voter is responsible for the object class in question and
    • if the user has a granted authority for that specific object or for all objects of this class which matches the operation to be performed (e.g.: TAXONBASE.UPDATE)
    • denies otherwise
  3. DescriptionBaseVoter:
    • responsible for TaxonDescription, SpecimenDescription
    • allows if voter is responsible for the object class in question and
    • if the user has a granted authority for that specific object or for all objects of this class which matches the operation to be performed (e.g.: DESCRIPTIONBASE.UPDATE)
    • denies otherwise
  4. DescriptionElementVoter:
    • responsible for all types of DescriptionElements
    • allows if voter is responsible for the object class in question and
    • if the user has a granted authority for that specific object or for all objects of this class which matches the operation to be performed
    • if the the operation to be preformed is specific to the Feature of the Description Element and if the Property of one of the users granted authorities matches this Feature. (e.g.: DESCRIPTIONELEMENTBASE(Ecology).[UPDATE])
    • denies otherwise

Cdm Entity access control

Type bases access control

This is defined through the general CdmAuthotities which are not referring to a specific entity uuid.

Per entity access control

This is defined through the entity specific CdmAuthotities which referring to a specific entity by the uuid.

Extended create permission problem

for e general discussion see #4305 (newly created entities must stay editable even if a user only has the permission to create them) for details regarding the current implementation see #6867 (explicitely assign and revoke UPDATE & DELETE permission per enitity in the registration workflow). Problems with the current implementation: #7147, ...

Web service access control


All web service end points which require authentication support http basic.

There are two sources of users:

CDM users:

The users stored in the cdm database

Global managing users:

Users stored in the Managing users properties file ($HOME/.cdmLibrary/ This is a java properties file to populate the InMemoryUserDetailsManager in any of the cdm-remote instances with special global management users which are granted to access special web services. Among these are the /manage/ web services and those triggering long running tasks. Global management users have the role ROLE_MANAGE_CLIENT and will be available in each of the cdm-remote instances. Changes made to this file are applied after restarting a cdm instance. For implementation details please refer to #6248.

Web service endpoints accessible to global managing users:

  • /manage/**
  • /**description/accumulateDistributions


  1. Classical spring security authorization as described above
  2. OAuth2: see #6118 evaluate spring-security-auth2 and spring-cloud-security as a framework for OAuth2

Use cases

A. a specific classification sub tree is publicly visible that is it is published
B. a specific classification sub tree must not be publicly visible in the data portal and thus must be also hidden in the web service responses
C. a specific classification sub tree is only visible for users which have a specific role but the user is not granted to edit anything in/below it
D. A user is only granted to edit Descriptions
E. A user is only granted to edit DescriptionElements of a specific Feature
F. A user is only granted to edit structured Descriptions
G. Combinations of B, C and D, E must be possible
H. Only users with the roles Admin or Usermanager or the user in question it self (if currently authenticated) are allowed to execute change password
I. Only users with the roles Admin or Usermanager are allowed to create or edit new users

<code class="rst">
Tabular summary of above use cases translated into roles:

===========  =========================  ======================================  ===============  =============================
\                                                                               what to protect
-----------  -------------------------  --------------------------------------  ----------------------------------------------
Usecase      Role                       authority string                        Entity            ServiceMethod
===========  =========================  ======================================  ===============  =============================
A.           Anonymous                  TaxonNode.[READ]{uuid}                     ...
B.           TaxGroupX_User             TaxonNode.[READ]{uuid}                     ...
C.           TaxGroupX_Editor           TaxonNode.[UPDATE]{uuid}                   ...
D.           DescriptionEditor      Description.[UPDATE]             ...                    
E.           DescriptionEditor      DescriptioElement(Ecology).[UPDATE]        ...                  
H.           Admin, Usermanager                                                                  UserService.changePassword()
===========  =========================  ======================================  ===============  =============================

*Anonymous* means not authenticated  

Special cases:

  • TaxonNames can potentially be shared between different taxa, thus a situation may occur where a user has grants to edit taxon A but not for taxon B, but both taxa are sharing the same name. How will we handle this situation, should the name be cloned when the user starts editing taxon A, so that taxon A has another name entity than taxon B after the user saved the latest changes?
  • The same problem as described above for TaxonNames also accounts for References, but in this case the problem is more severe since references are very often part of multiple taxon names.

Updated by Katja Luther over 1 year ago · 70 revisions