DN name = opContext.getDn();
// Access the principal requesting the operation, and bypass checks if it is the admin
ClonedServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
LdapPrincipal principal = opContext.getSession().getEffectivePrincipal();
DN principalDn = principal.getClonedName();
// bypass authz code if we are disabled
if ( !opContext.getSession().getDirectoryService().isAccessControlEnabled() )
{
next.modify( opContext );
return;
}
List<Modification> mods = opContext.getModItems();
// bypass authz code but manage caches if operation is performed by the admin
if ( isPrincipalAnAdministrator( principalDn ) )
{
next.modify( opContext );
/**
* @TODO: A virtual entry can be created here for not hitting the backend again.
*/
ServerEntry modifiedEntry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
tupleCache.subentryModified( name, mods, modifiedEntry );
groupCache.groupModified( name, mods, entry, schemaManager );
return;
}
Set<DN> userGroups = groupCache.getGroups( principalDn.getNormName() );
Collection<ACITuple> tuples = new HashSet<ACITuple>();
addPerscriptiveAciTuples( opContext, tuples, name, entry.getOriginalEntry() );
addEntryAciTuples( tuples, entry );
addSubentryAciTuples( opContext, tuples, name, entry );
engine.checkPermission( schemaManager, opContext, userGroups, principalDn,
principal.getAuthenticationLevel(), name, null, null,
Collections.singleton( MicroOperation.MODIFY ), tuples, entry, null );
Collection<MicroOperation> perms = null;
ServerEntry entryView = ( ServerEntry ) entry.clone();
for ( Modification mod : mods )
{
ServerAttribute attr = (ServerAttribute)mod.getAttribute();
switch ( mod.getOperation() )
{
case ADD_ATTRIBUTE :
perms = ADD_PERMS;
// If the attribute is being created with an initial value ...
if ( entry.get( attr.getId() ) == null )
{
// ... we also need to check if adding the attribute is permitted
engine.checkPermission( schemaManager, opContext, userGroups, principalDn, principal.getAuthenticationLevel(), name,
attr.getId(), null, perms, tuples, entry, null );
}
break;
case REMOVE_ATTRIBUTE :
perms = REMOVE_PERMS;
EntryAttribute entryAttr = entry.get( attr.getId() );
if ( entryAttr != null )
{
// If there is only one value remaining in the attribute ...
if ( entryAttr.size() == 1 )
{
// ... we also need to check if removing the attribute at all is permitted
engine.checkPermission( schemaManager, opContext, userGroups, principalDn,
principal.getAuthenticationLevel(), name, attr.getId(),
null, perms, tuples, entry, null );
}
}
break;
case REPLACE_ATTRIBUTE :
perms = REPLACE_PERMS;
break;
}
/**
* Update the entry view as the current modification is applied to the original entry.
* This is especially required for handling the MaxValueCount protected item. Number of
* values for an attribute after a modification should be known in advance in order to
* check permissions for MaxValueCount protected item. So during addition of the first
* value of an attribute it can be rejected if the permission denied due the the
* MaxValueCount protected item. This is not the perfect implementation as required by
* the specification because the system should reject the addition exactly on the right
* value of the attribute. However as we do not have that much granularity in our
* implementation (we consider an Attribute Addition itself a Micro Operation,
* not the individual Value Additions) we just handle this when the first value of an
* attribute is being checked for relevant permissions below.
*/
entryView = ServerEntryUtils.getTargetEntry( mod, entryView, schemaManager );
for ( Value<?> value:attr )
{
engine.checkPermission( schemaManager, opContext, userGroups, principalDn,
principal.getAuthenticationLevel(), name, attr.getId(), value,
perms, tuples, entry, entryView );
}
}