Skip to content
This repository has been archived by the owner on Feb 22, 2019. It is now read-only.

cdo.security

Pavel Vlasov edited this page May 19, 2015 · 7 revisions

CDO Security

Model and helper classes for protecting access to repository objects at the application level.

CDO Security Class Diagram

Protection domain

Root application classes shall implement this interface. E.g. in Examples SystemOfRecords implements ProtectionDomain<LoginPasswordCredentials>. Protection domain contains actions, but doesn't have to directly contain users and groups. There might be different types of users stored in different parts of the model. E.g. the Examples there are two types of users - Guest and Customer. Groups can be organized in application-specific manner, e.g. nested or contained in model elements other than the element implementing protection domain (for example Organization might be the root element/protection domain and groups might be contained in Organization Units).

  • Operations
    • authenticate() - Authenticates credentials and returns an instance of user or null if authentication is successful.
    • clearPermissions() - Removes permissions associated with a particular object from the model. Call this method to avoid dangling references.
    • getAllUsers() - Returns all users in the protection domain.
  • References
    • unauthenticatedPrincipal - All security checks are performed against a principal. If a system user hasn't authenticated yet then unauthenticatedPrincipal is used for security checks. In Examples Guest is unauthenticated principal - it has its home page with sign-in and sign-up functionality and a list of bank products.
    • superUsersGroup - Members of this group have all permissions in the system.
    • everyoneGroup - Every authenticated principal is an implicit member of this group.

Principal

Principal is an entity which can be authorized by the system to perform actions in the system, in particular on repository elements.

User

User is a principal which can be authenticated by the system using credentials.

Group

Groups allow to assign permissions to group members in one operation.

Action

Action can be granted (allowed) or denied to a principal through a permission.

Actions can be scoped to particular classes (and their subclasses) and namespaces with targetClass and targetNamespaceURI attributes. For EObjects targetClass is class name and targetNamespaceURI is the targetNamespaceURI of the containing package. For other objects targetClass is the part of the class name after the last dot and targetNamespaceURI is concatenation of java:// and the part of class name before the last dot. E.g. for java.lang.String targetNamespaceURI is java://java.lang and targetClass is String. For classes in default package targetNamespaceURI is java://default. Actions with blank targetNamespaceURI and blank targetClass are global.

Actions can be associated with each other by implies/impliedBy relationship, e.g. read action may be implied by update action. An action can be grantable or not. Only grantable actions shall be associated with permissions. In combination with implies/impledBy relationship it allows to have a fine-grained set of actions for authorizing access at code level (e.g. invoke on a particular operation) and group such actions together into coarse-grained grantable actions (roles). This approach makes the code independent of higher level security concerns. I.e. there is no isUserInRole() method and actions may be moved between roles without having to change code, e.g. in one deployment of the system action sendMessageToEveryOne may be implied by Administrator role (grantable action) and in another by Power User role.

Actions can be defined in a scope of one class, but govern access to objects contained by instances of this class. E.g. an action defined for Customer may govern access to customer's accounts. It is done by using pathPatterns. During permission checks AuthorizationHelper traverses target objects containment and builds a path. E.g. if Account is contained in Customer.accounts reference then action path checked at Account level would be /, and at the Customer level it would be /accounts. Action path is matched against pathPatterns. If pathPatterns collection is empty it defaults to a singleton collection containing /, i.e. it applies to its permission target.

Actions can have a condition and properties used in condition evaluation. Condition is a JavaScript code. Action matches if its condition evaluates to true (or if it is empty). Condition code accesses the action properties through actionProperties variable, the target object through target variable. It also has access to environment variables passed to WebContext.authorize() method.

An example of use of conditional action - Action transfer on an account can be made conditional depending on on the transfer amount and transfer actions with different thresholds can be granted to different principals.

Action can contain other actions. Contained actions are implied by the containing action. They also inherit action attributes except the qualifier attribute. It allows to build action structures paralleling class structures, e.g. define an action for a class (say, Account), then a sub-action for operation invocation inheriting parent action's targetClass and targetNamespace (say, transfer(Account other, BigDecimal amount)), and then sub-actions with different conditions or properties and qualifiers (say, amount<$5000, amount<$10000, no limit) and grant those sub-actions to different roles/groups (say, Clerk, Supervisor, General Manager).

Action name can have class: prefix to indicate that it is an action on class, not instance. For example if the application needs to check whether the principal is allows to instantiate mortgage accounts it can do something like this:

if (context.authorize(MortgagePackage.eINSTANCE.getMortgageAccount(), "create", null, null)) {
...
}

It will result in checking for a permission to execute an action with name class:create.

* as an action name is a wildcard which matches any name.

Permission

Permission is owned by a principal and is associated with an action and optionally with a repository object. Permission can allow or deny its action. Permission can have effective dates. Association of a permission to its action is done not by a reference but by a lookup of an action with the same ActionKey attributes. It allows to store actions is security policies and use different security policies in different application deployments.

Permissions without target apply to all instances of permission's class.

Sub-classes inherit super-classes actions/permissions. During authorization permissions are ordered by distance from the target's class. Permission defined on a subclass takes precedence over a permission defined on a superclass. For example, transfer permission can be allowed to clerks at Account class but denied at MortgateAccount class, which is a subclass of Account.

Annotation for implying permissions

Dealing with fine-grained permissions can be cumbersome. Model classes can be annotated with org.nasdanika.cdo.security:permissions annotation to specify permissions imply relationship. Detail keys correspond to implying permissions in format <action>/<qualifier>, and detail values contain implied permissions <action>/<path/qualifier pattern>- one permission per line. Implied action name * matches any action. Lines starting with # are considered comment lines.

Implying of permissions works only in the ALLOW direction - allowing the implying permission allows the implied, but denying the implying permission does not deny implied permissions because of the one-to-many relationship between the implying and the implied.

Example

Execution of landing user story on a class XYZ requires loading of the generated JavaScript module, read access to home and summary rows, loading of applications modules, and invocation of getSummaryRow getter. The permissions annotation defines a details entry with story/landing key and the value shown below.

extension/js
read/home
read/summary
read/applications/getSummaryRow
extension/applications/js    

Principals are granted story/landing "business" permission and it implies "technical" permissions:

guest.getPermissions().add(createPermission("story", hub, "/landing", true));				

LoginPasswordProtectionDomain

This class is a binding of ProtectionDomain to LoginPasswordCredentials.

LoginPasswordHashUser

User which uses login and password for authentication.

AuthorizationHelper

Helper class for Principal implementations. It should be create with the principal implementation as a constructor argument and the principal's authorize() method shall delegate to the helper:

private AuthorizationHelper authorizationHelper = new AuthorizationHelper(this);
	
public AccessDecision authorize(
		SecurityPolicy securityPolicy, 
		Context context, 
		Object target, 
		String action, 
		String qualifier, 
		Map<String, Object> environment) {
	return authorizationHelper.authorize(securityPolicy, context, target, action, qualifier, environment);
}

Security Policy

SecurityPolicy allows to look-up an action for a permission. Security policies can be provided by extensions and services. ProtectionDomain can also implement SecurityPolicy.

SecurityPolicyContainer

SecurityPolicyContainer is a EClass which implements SecurityPolicy and ActionContainer. It can also import other action containers, including other security policy containers. SecurityPolicyContainer definition can be edited with Nasdanika CDO Security Editor

Nasdanika CDO Security Editor

SecurityPolicyContaner file can be added to the system through policy_resource extension:

Policy Resource