////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////

[[acl-domainClasses]]
=== Domain Classes

The plugin uses domain classes to manage database state. Ordinarily the database structure isn't all that important, but to be compatible with the traditional JDBC-based Spring Security code, the domain classes are configured to generate the table and column names that are used there.

The plugin classes related to persistence use these classes, so they're included in the plugin but can be overridden by running the <<acl-s2-create-acl-domains>> script.

As you can see, the database structure is highly normalized.

==== AclClass

The `AclClass` domain class contains entries for the names of each application domain class that has associated permissions:
[source,groovy]
----
package grails.plugin.springsecurity.acl

class AclClass {

	String className

	@Override
	String toString() {
		"AclClass id $id, className $className"
	}

	static mapping = {
		className column: 'class'
		version false
	}

	static constraints = {
		className unique: true, blank: false
	}
}
----

==== AclSid

The `AclSid` domain class contains entries for the names of grant recipients (a principal or authority - SID is an acronym for "`security identity`"). These are typically usernames (where `principal` is `true`) but can also be a `GrantedAuthority` (role name, where `principal` is `false`). When granting permissions to a role, any user with that role receives that permission:

[source,groovy]
----
package grails.plugin.springsecurity.acl

class AclSid {

	String sid
	boolean principal

	@Override
	String toString() {
		"AclSid id $id, sid $sid, principal $principal"
	}

	static mapping = {
		version false
	}

	static constraints = {
		principal unique: 'sid'
		sid blank: false, size: 1..255
	}
}
----

==== AclObjectIdentity

The `AclObjectIdentity` domain class contains entries representing individual domain class instances (OIDs). It has a field for the instance id (`objectId`) and domain class (`aclClass`) that uniquely identify the instance. In addition there are optional nullable fields for the parent OID (`parent`) and owner (`owner`). There's also a flag (`entriesInheriting`) to indicate whether ACL entries can inherit from a parent ACL.

[source,groovy]
----
package grails.plugin.springsecurity.acl

class AclObjectIdentity extends AbstractAclObjectIdentity {

    Long objectId

    @Override
    String toString() {
        "AclObjectIdentity id $id, aclClass $aclClass.className, " +
        "objectId $objectId, entriesInheriting $entriesInheriting"
    }

    static mapping = {
        version false
        aclClass column: 'object_id_class'
        owner column: 'owner_sid'
        parent column: 'parent_object'
        objectId column: 'object_id_identity'
    }

    static constraints = {
        objectId unique: 'aclClass'
    }
}
----

`AclObjectIdentity` actually extends a base class, `AbstractAclObjectIdentity`:

[source,groovy]
----
abstract class AbstractAclObjectIdentity {

   AclClass aclClass
   AclObjectIdentity parent
   AclSid owner
   boolean entriesInheriting

   static constraints = {
      parent nullable: true
      owner nullable: true
   }
}
----

By default it's assumed that domain classes have a numeric primary key, but that's not required. So the default implementation has a `Long` `objectId` field, but if you want to support other types of ids you can change that field and retain the other standard functionality from the base class.

==== AclEntry

Finally, the `AclEntry` domain class contains entries representing grants (or denials) of a permission on an object instance to a recipient. The `aclObjectIdentity` field references the domain class instance (since an instance can have many granted permissions). The `sid` field references the recipient. The `granting` field determines whether the entry grants the permission (`true`) or denies it (`false`). The `aceOrder` field specifies the position of the entry, which is important because the entries are evaluated in order and the first matching entry determines whether access is allowed. `auditSuccess` and `auditFailure` determine whether to log success and/or failure events (these both default to `false`).

The `mask` field holds the permission. This can be a source of confusion because the name (and the Spring Security documentation) indicates that it's a bit mask. A value of 1 indicates permission A, a value of 2 indicates permission B, a value of 4 indicates permission C, a value of 8 indicates permission D, etc. So you would think that a value of 5 would indicate a grant of both permission A and C. Unfortunately this is not the case. There is a https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/acls/domain/CumulativePermission.html[CumulativePermission] class that supports this, but the standard classes don't support it (`AclImpl.isGranted()` checks for == rather than using | (bitwise or) so a combined entry would never match). So rather than grouping all permissions for one recipient on one instances into a bit mask, you must create individual records for each.

[source,groovy]
----
package grails.plugin.springsecurity.acl

class AclEntry {

	AclObjectIdentity aclObjectIdentity
	int aceOrder
	AclSid sid
	int mask
	boolean granting
	boolean auditSuccess
	boolean auditFailure

	@Override
	String toString() {
		"AclEntry id $id, aceOrder $aceOrder, mask $mask, granting $granting, " +
		"aclObjectIdentity $aclObjectIdentity"
	}

	static mapping = {
		version false
		sid column: 'sid'
		aclObjectIdentity column: 'acl_object_identity'
	}

	static constraints = {
		aceOrder unique: 'aclObjectIdentity'
	}
}
----
