If an user belongs to more than one group
Typically, users are members of more groups that may have different or even conflicting permissions. In such case, the resulting permissions are merged together, respecting the groups priority. Therefore, if conflicting permissions exists in two groups, the winning one is in the group with the higher priority.
On the following screenshot, the group Administrators has a higher priority of the group HR, that in turn has a higher priority of the group Everyone.
The groups priority is a per-user setting.
How the object access permissions are computed
Objects access permissions are computed differently depending on the type of the object. In other words, the definition of an object type in the schema defines also how the access permissions on object of that type are computed.
The base of the computation are the attributes of type ‘access’ – that are used both directly (on the objects containing them) and indirectly (on the objects referencing objects containing them). The access permission to an object are computed combining the value of its own ‘access’ attribute (if any) with that of all the other objects referenced by the main one that have an ‘access’ attribute themselves. E.g. the following schema fragment:
<xs:element name="folder">
<xs:complexType>
<xs:complexContent>
<xs:extension base="object">
<xs:sequence>
<xs:element
name="access"
type="access"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType name="folderObject">
<xs:complexContent>
<xs:extension base="object">
<xs:sequence>
<xs:element
name="folderRef"
type="reference"
gs:refer="folder"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="document">
<xs:complexContent>
<xs:extension base="object">
<xs:sequence>
<xs:element
name="folderRef"
type="reference"
gs:refer="folder"/>
<xs:element
name="access"
type="access"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
access permissions on the objects of type ‘folder’ will depend only on the value of their ‘access’ attribute; access permissions on the objects of type ‘folderObject’ will depend only on the value of the ‘access’ attribute of the referenced ‘folder’ object; access permissions on the objects of type ‘document’ will depend on the combination of the value of their ‘access’ attribute with that of the referenced ‘folder’ object.
Note 1: what matters is the type of the attributes, not their name – i.e. as long as an attribute is of type ‘access’ it will be used to compute access permissions, even if its name is something else. It is customary though to call ‘access’ attributes of type ‘access’.
Note 2: only the single reference are used for access permissions computations, multiple references are ignored (e.g. if ‘document’ above had a multiple reference to ‘folder’ the ‘access’ attribute of the folders would NOT have been used to compute access permissions).
Each access attribute specifies an unordered list of user and/or group objects, and for each of them a list of permissions that are either granted or denied to that particular user or group.
To compute the access permissions of a user on an object:
1.the access attribute values of all the referenced object are merged together;
2.the result is merged with the access attribute value of the object itself – converting RefXXXX permissions into XXXX (e.g. converting RefReadNormal into RefNormal);
3.the access permissions that are associated with the user and with the user’s groups in the resulting access list are combined – yielding the final result.
Merging two access values means taking the users and groups listed in either one, and if a user/group is listed in both take the permissions granted/denied in both user/group. If there are conflicting values – i.e. if the same user/group is listed in both access values with the same permission granted in one and denied in the other, in the step (1) the permission is granted and in the step (2) the permission is considered not specified (neither granted nor denied). For example the merged value in step 1 of:
<securityAccess objectType="group">
<keyVal>Administrators</keyVal>
<grant>all</grant>
</securityAccess>
<securityAccess objectType="user">
<keyVal>Guest</keyVal>
<grant>ReadNormal</grant>
<grant>ReadContent</grant>
</securityAccess>
and:
<securityAccess objectType="group">
<keyVal>Everyone</keyVal>
<grant>ReadNormal</grant>
<grant>WriteNormal</grant>
</securityAccess>
<securityAccess objectType="user">
<keyVal>Guest</keyVal>
<grant>ReadSpecial</grant>
<deny>ReadContent</deny>
<deny>Delete</deny>
</securityAccess>
is:
<securityAccess objectType="group">
<keyVal>Administrators</keyVal>
<grant>all</grant>
</securityAccess>
<securityAccess objectType="group">
<keyVal>Everyone</keyVal>
<grant>ReadNormal</grant>
<grant>WriteNormal</grant>
</securityAccess>
<securityAccess objectType="user">
<keyVal>Guest</keyVal>
<grant>ReadSpecial</grant>
<grant>ReadNormal</grant>
<grant>ReadContent</grant>
<deny>Delete</deny>
</securityAccess>
(note how the two conflicting values for ReadContent for user Guest resulted in the permission being granted).
When merging in step 2 there is also the conversion of RefXXXX into XXXX to consider, so for example if the result of the merging of the referenced accesses is:
<securityAccess objectType="group">
<keyVal>Everyone</keyVal>
<grant>RefReadNormal</grant>
<grant>RefWriteNormal</grant>
<grant>RefDelete</grant>
</securityAccess>
<securityAccess objectType="user">
<keyVal>Guest</keyVal>
<grant>RefReadSpecial</grant>
</securityAccess>
and the object own access is:
<securityAccess objectType="group">
<keyVal>Everyone</keyVal>
<grant>ReadNormal</grant>
<grant>ReadProtected</grant>
<grant>ReadSpecial</grant>
<grant>ReadContent</grant>
<deny>Delete</deny>
</securityAccess>
the merged value after step 2 will be:
<securityAccess objectType="group">
<keyVal>Everyone</keyVal>
<grant>ReadNormal</grant>
<grant>ReadProtected</grant>
<grant>ReadSpecial</grant>
<grant>ReadContent</grant>
<grant>WriteNormal</grant>
</securityAccess>
<securityAccess objectType="user">
<keyVal>Guest</keyVal>
<grant>ReadSpecial</grant>
</securityAccess>
(note how the conflicting RefDelete and Delete values for group Everyone cancelled each other out in this case).
The step (3) considers only the permissions in the computed access value for the logged-in user and for his groups – and combines them in order of priority. So if the final merged access value is:
<securityAccess objectType="group">
<keyVal>Administrators</keyVal>
<grant>all</grant>
</securityAccess>
<securityAccess objectType="group">
<keyVal>Everyone</keyVal>
<grant>ReadNormal</grant>
</securityAccess>
<securityAccess objectType="group">
<keyVal>Group1</keyVal>
<grant>ReadSpecial</grant>
<deny>ReadNormal</deny>
</securityAccess>
<securityAccess objectType="user">
<keyVal>Admin1</keyVal>
<grant>ReadSpecial</grant>
<deny>Delete</grant>
</securityAccess>
The user Admin1 belonging to the groups Administrators and Everyone will have all permissions on the object except ‘Delete’ (because the deny of Delete for the user has higher priority that the group granting of it).
The user Alice belonging only to the group Everyone will have only the ReadNormal permission on the object.
The user Bob belonging to the groups Group1 and Everyone (in this order) will have only the ReadSpecial permission (granted via Group1, ReadNormal is denied in Group1 that has precedence over Everyone).
If a particular permission is neither granted nor denied it is given to the user if he has the DefaultPermission system privilege.