This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.

Security

Note

This is mostly a direct copy from an old package-info.java file from the ch.tocco.nice2.security.api package. Feel free to rewrite or expand this documentation.

The Policy

The policy can be thought of as a table of security rules. Each row denotes one rule. An example:

Selector

Action

Permission

Subject

Condition

entityManager(MyEntity)

grant

create

someGroup, anotherGroup

entityManager(MyEntity)

deny

create

group3

entity(MyEntity)

grant

access(read|write, *)

principal.key == owner

entity(MyEntity)

deny

access(write, *)

anonymous

For each secured action, this table will be processed top-down, resulting in a conclusion: deny or grant. It first assumes deny, then changes to the specified action for each rule where all conditions are met. A rule may be declared final, in which case processing stops if that rule applies, further rules will be ignored.

The selector defines the general type of the object affected by the action. The permission the exact permission, that’s asked for. The currently logged in user is the principal, and finally, there may be some generic condition.

Table Reduction

Because the full table for all possible principals and all possible objects may get very big for a full-scale application, this table will be reduced whenever possible. There are two points, where some important facts get known that allow to filter out rules that won’t apply anyway:

  • After login, the exact principal is known. At this point, a new policy will be generated for that user that doesn’t contain any rules anymore that don’t affect that principal.

  • When a guard is needed, the exact object is known. At this point, all rules that don’t affect this object will be filtered out using the selector.

Therefore, in practice, the policy for a concrete object will finally be relatively compact.

The Access Control Language (ACL)

The define the policy, a language called ACL is introduced. The top-level structural element of this language is the section. A section defines the selector for the following rules, until a new section is started. The section header looks like this:

entityManager(MyEntity):
    // the rules

So, each rule starts with the keyword grant or deny, optionally followed by a list of permissions, optionally followed by the keyword to and a list of subjects (role name or '&' + principal name), optionally followed by the keyword if and a condition (or unless and a condition, which is a shortcut for if not (...)). It is finally terminated by a semicolon.

Optionally, a rule may end with and stop, which declares a rule final. If a final rule applies, processing of rules stops at this point.

This is the table above written as ACL:

entityManager(myEntity):
    grant create
        to someGroup, anotherGroup;
    deny create
        to group3;

entity(myEntity):
    grant access(read|write, *)
        if principal.key == owner;
    deny access(write, *)
        to anonymous;

Keywords

The following words are ACL keywords: grant, deny, include, to, if, unless, principal, null, true, false, or, and, not, role. Also, the keyword permission is currently reserved, but unused.

Selectors and Permissions

Selectors and permissions are written very similarily: identifier [(arguments)].

The identifier is the one as contributed to the security system:

@Bean
public SecurityDomainContribution infoboxSecurityDomainContribution(MyDomain domain) {
    SecurityDomainContribution contribution = new SecurityDomainContribution();
    contribution.setName("myDomain");
    contribution.setImplementation(domain);
    return contribution;
}

@Bean
public PermissionContribution infoboxDisplayPermission() {
    PermissionContribution contribution = new PermissionContribution();
    contribution.setDomain("myDomain");
    contribution.setName("myPermission");
    contribution.setId("myPermission");
    contribution.setPermissionClass(my.pkg.MyPermission.class);
    return contribution;
}

This defines the domain myDomain as provided by the service MyDomain, and assigns the permission myPermission imlemented by the class my.pkg.MyPermission to this domain.

The argument list will be mapped to method signatures for domains and constructors for permissions. See the documentation of SecurityDomain#getSelectorFactory() and permissions. See the documentation of Permission for more information.

Method and Constructor Matching

  • Primitive Types

    Numeric types are matched straight-forward. All Java rules apply for Integers and Doubles. Supported primitive types are: Integer, Long, Short, Byte, Double and Boolean, all in their primitive and wrapped version.

  • Strings

    Strings may be written in three ways: myString, 'myString' and "myString". The two quoted variants support all Java escapes, the unquoted variant must follow Java’s rules for identifiers. If a string matches a keyword, it must be quoted.

  • Enumerations

    Enumeration types are written just like strings, however, these Strings are converted: All letters to upper case, ‘-’ will be replaced by ‘_’, i.e 'my-enum-value' refers to the enumeration value MY_ENUM_VALUE.

  • Arrays of Enumerations

    Arrays of enumerations can be written by separating the enum values with ‘|’. A typical example is read/write access: read|write.

  • Varargs

    If the last argument of a method or constructor is an array, it will be interpreted as Varargs. If no varargs or the wildcard are passed, the method will be called with null for the array. Enumeration types are never treated as varargs. For all other types, arrays are only allowed as the last argument.

  • Wildcards

    Wildcards are written as ‘*’ and result in null being passed to the method. They’re allowed both for arrays of enumerations or varargs.

Conditions

Conditions are simple boolean critera and logical operations (and, or, not, you know that stuff). Supported operators are ==, !=, <, <=, >, >= and ~=. Identifiers and paths thereof (a.b.c is a path) will be resolved depending on the object being checked. For example, in entities, relations and fields will be used.

The special identifier principal may only occur as the first path element and refers to the principal of the current security context. The second path element will be resolved using the Principal’s attributes, except name (the Principal’s username) and key (the Principal’s primary key). All further path elements will be resolved using the bean introspection.

Date Conditions

The keyword now specifies the current date/time. It’s fields are actually operators which operate on that object. These can be combined freely, e.g. now.yesterday.date means yesterday at 0:00.

The following “path elements” (operators) are available:

  • date

    Keep the date as-is, set the time to 0:00.

  • time

    Keep the time as-is, set the date to January 1st, 1970

  • tomorrow

    Plus one day.

  • yesterday

    Minus one day.

Includes

ACL files may include other ACL files. The syntax for this is:

include 'another-resource.acl';

Important: An include statement will end the current section! This is an error:

entity(*):
grant access(*, *);

include 'another-resource.acl';

deny access(write) to anonymous; // ERROR: No section header