Class SimpleACIWorld

java.lang.Object
com.isode.dsapi.aci.SimpleACIWorld

public class SimpleACIWorld extends Object

Representation of all the ACI for the whole DIT using a much simplified model compared to ACIWorld. The simplifications are made by imposing certain restrictions. By making these restrictions it is possible to combine different rules without having one rule clash with another rule unexpectedly.

ACI rule information is split up differently compared to how it is handled in ACIWorld or in the BER. It is split into "roles" (which contain auth-level and user info), "rules" (which contain grant/deny, permission bits, protected items, objectclass refinements and so on) and "areas" (which contain lists of region specifications based on base/min/max/chops info). An ACI "item" combines a role, a rule and an area with a precedence.

These are the restrictions imposed to permit the simplifications:

  • SAC (Simple Access Control) areas are used by default. With SAC, it isn't necessary to worry about ACIAs or entryACI. Everything is controlled by the prescriptive ACI on the ACSA, and the complete set of rules can be visualised by the user in any GUI built on top of SimpleACIWorld. For SAC, subentry ACI is completely independent from prescriptive ACI. However for BAC with ACIA admin points, subentry ACI on the ACIA is affected by superior prescriptive ACI. So for simplicity, BAC regions and ACIAs are not directly supported by this class. However, if the user chooses to switch the ACSA to BAC, the top-level prescriptive ACI and subentry ACI is handled as for SAC, and any ACI encoded in entry ACI or ACIA prescriptive ACI is left alone. Since entry ACI and prescriptive ACI on ACIAs is invisible to this class, and may override the rules defined here, warnings are given if ACIAs or BAC regions are detected.
  • Deny rules must always apply to all users and for all bind levels. This simplifies the way that different preset rules work together, allowing different rules to be combined without unexpected consequences. Note that grant rules just add up, so don't need differentiation by precedence. Precedence is only necessary when deny rules are introduced. So we get the user to order the grant rules using the precedences, and then insert the deny rules at a precedence level that gives the effect that they are looking for.
  • Deny rules can never have the same precedence as grant rules. This is because more-specific grant rules will override a deny at the same precedence, which makes the effect non-obvious to the user when the precise rules are hidden away behind descriptive titles. It is suggested that grants be given even precedences (e.g. 10,20,30), and denies be given odd precedences (e.g. 15,25,35) to avoid problems.
  • EntryACI on the ACSA or subentries is not accepted, causing the region to be treated as 'local' (i.e. not part of the 'global' ACI controlled by this class), even if entry ACI may be tolerated otherwise when in BAC mode. This is because it will be overwritten when temporary entryACI is written as part of the process of saving the ACI rules.
  • A rule that specifies 'user_entry' (i.e. that the rule only applies to entries whose DN matches the user's bound DN) can only be used in combination with a role that specifies 'user_all'. This is due to the way that the low-level ACI represents this scenario as a user filter rather than an entry filter.
  • There are also restrictions from ACIWorld, for example: all deny rules must be separate from grant rules, and entry-protection rule tuples must be separate from attribute-protection rule tuples. All the other limitations and simplifications documented in ACITuple/ACIWorld also apply.

This is how the simple ACI objects are linked up: SimpleACIWorld -> SimpleACIItem -> { SimpleACIRole, SimpleACIArea[.Region], SimpleACIRule[.Tuple] }.

Role and rule name hashes are stored in the low-level ACI tuple names on the directory to help them be recovered on reloading from a live DIT. The full specification of the roles and rules is stored in a compressed blob of XML data in a subentry below each ACSA in order to allow the original role and rule names to be recovered, along with any roles or rules which were defined but unused. In addition, a blob of subentry ACI and subtree specifications is stored for subentry ACI rules to ensure that it is always possible to recover the original area information configured by the user.

The fields of this class may be accessed directly. There are also methods available to simplify access to some of the fields.

Since:
15.0
Author:
jp
  • Field Details

    • ds

      public final IsodeDirectorySession ds
      Directory session to use, or null if this is not connected to a directory.
    • acsas

      public final SimpleACIWorld.ACSAList acsas
      List of all ACSAs loaded, including global, local and removed ones.
    • roles

      public final List<SimpleACIRole> roles
      List of roles.
    • rules

      public final List<SimpleACIRule> rules
      List of rules.
    • items

      public final List<SimpleACIItem> items
      List of active items combining roles and rules.
    • root_proxy_acsa

      public static final DN root_proxy_acsa
      DN of ACSA that acts as a proxy for the root ACSA on D3.
    • gac_true_av

      public static final AttributeValue gac_true_av
      Special subentryACI value that has no significant effect but is used to mark admin points that are part of the scope of SimpleACIWorld. The rule name is "GlobalAccessControl: true".
    • gac_false_av

      public static final AttributeValue gac_false_av
      Special subentryACI value that has no significant effect but is used to mark admin points that should be treated as 'local', i.e. outside of the scope of SimpleACIWorld. The rule name is "GlobalAccessControl: false".
  • Constructor Details

    • SimpleACIWorld

      public SimpleACIWorld(IsodeDirectorySession ds, DN base) throws BadAttributeTypeException
      Construct a SimpleACIWorld for the given directory session and base-DN.
      Parameters:
      ds - IsodeDirectorySession to use to access and modify directory, or null if this is an offline SimpleACIWorld
      base - Base DN of subtree that will be searched for ACSAs: normally the root DN
      Throws:
      BadAttributeTypeException - in the case of attribute types being missing from schema.
  • Method Details

    • setACSAIgnoreList

      public void setACSAIgnoreList(List<DN> acsa_ignore)
      Set the list of DNs that should be specifically ignored and treated as 'local'.
    • setACSAFilter

      public void setACSAFilter(SimpleACIWorld.ACSAFilter filter)
      Set the ACSA filter, which selects which ACSAs to load and may modify the base DN. See SimpleACIWorld.ACSAFilter.
      Parameters:
      filter - ACSAFilter, or null if no filter is required
    • filterToACSAAboveAndPathTo

      public void filterToACSAAboveAndPathTo(DN target_dn)
      Set the ACSA filter to look for the nearest enclosing ACSA for the given DN, and then to filter the ACSAs to just that ACSA and the ACSA at the DN itself, if it exists. This is a suitable set of ACSAs to add/remove the ACSA flags on the given DN.
      Parameters:
      target_dn - Starting-point DN
    • getBase

      public DN getBase()
      Return the base-DN used by the most recent load().
      Returns:
      Base DN
    • relocate

      public void relocate(DN... reloc_arr)
      Relocate all the areas of the DIT according to the list of source/target DNs provided. The logical process for relocating a given DN is to go through the 'reloc_arr' and find the first source/target pair where the DN is within the source subtree. The DN is then relocated according to that pair. If the DN does not match any source/target pair, then it is not relocated.

      Both the item-areas and role-DNs are relocated according to 'reloc_arr'. Where subtrees are moved from one location to another, the old area reverts to whatever the parent-defined behaviour is (for a miscellaneous subtree at that level), rather than being excluded and chopped out entirely.

      Parameters:
      reloc_arr - Array containing an even number of DNs organized into pairs of DNs: a source DN and a target DN. Each source/target pair specify a subtree relocation, and they are searched in the order given. The first match found defines the relocation for any given DN.
    • load

      public void load() throws OperationFailedException
      Load up all the ACI information from the directory, clearing any currently loaded data. This does directory operations, so must be called from a BackgroundTask. Warnings may be generated, which can be fetched with getWarnings().
      Throws:
      OperationFailedException - in case of any errors. The list of errors can be fetched with getErrors().
    • loadBERHash

      public int loadBERHash() throws OperationFailedException
      Load up the ACI information from the directory and calculate a hash from it. The data is not decoded, so the SimpleACIWorld remains empty. This call is very much faster than the normal load() call. This does directory operations, so must be called from a BackgroundTask. Warnings may be generated, which can be fetched with getWarnings().
      Returns:
      Hash code of data loaded, which is the same value that can be fetched using getLastBERHash()
      Throws:
      OperationFailedException - in case of any errors. The list of errors can be fetched with getErrors().
    • getLastBERHash

      public int getLastBERHash()
      Get the hash value calculated by the last successful load(), loadBERHash() or save() operation. This hash value incorporates data from all the GLOBAL ACI BER saved to the DSA and also the ACSA-related flags from all areas on the DIT. It is only sensitive to changes in the higher-level roles and rules where they change the tuple BER, or where there are role/rule name changes (as the name hashes are included in the tuple name). Addition or deletion of unused roles or rules will not cause a change in this hash value.

      The intention is that the caller should get an identical hash value for loading data back from the DSA as they got for saving it. This may be used to check for changes on the DSA.

    • writeACSASummary

      public void writeACSASummary(String fnam) throws IOException
      Write a summary of the current ACSA settings to the given file.
      Parameters:
      fnam - File to write settings to.
      Throws:
      IOException - In case of I/O problem
    • writeXML

      public void writeXML(String filename) throws IOException
      Write the items, roles and rules to the given XML file. This dump is intended to record the logical global ACI view, without worrying about the details of the ACSAs beneath it, so no ACSA information is included in the XML. However ACSA information is dumped as comments to aid debugging remote configurations.
      Parameters:
      filename - Path of file to write
      Throws:
      IOException - In case of I/O problem
    • loadXML

      public static SimpleACIWorld loadXML(String filename)
      Load up a SimpleACIWorld from the given file. In the case of loading errors, dumps messages to the Isode logs as warnings and returns null.
    • addACIItem

      public void addACIItem(int precedence, SimpleACIRole role, SimpleACIRule rule, SimpleACIArea area)
      Create a new ACI item, and add it to the list of items. This is a convenience method, as the same can be achieved by adding to items directly.
      Parameters:
      precedence - Precedence of item, from 0 (lowest) to 255 (highest)
      role - SimpleACIRole, or null for deny
      rule - SimpleACIRule
      area - SimpleACIArea
    • save

      public void save() throws OperationFailedException
      Write back all the ACI to the directory, in all the appropriate places. All the ACSAs that are marked as 'global' have their ACI written. Those marked as 'local' are ignored. The original rule and role definitions are saved as a blob as an extra subentry under each ACSA. That allows the full names and default precedences, etc to be recovered.

      This method performs directory operations and must be called from a BackgroundTask. All the relevant entries are rewritten whether they need it or not.

      Throws:
      OperationFailedException - in case of any errors. The list of errors can be fetched with getErrors().
    • generateBootstrapACI

      public static List<AttributeValue> generateBootstrapACI(DN user_dn)
      Generate bootstrap ACI which can be used for entryACI or subentryACI attributes. Uses default rule names.
      Parameters:
      user_dn - DN of user who will be given permissions
      Returns:
      List
    • generateBootstrapACI

      public static List<AttributeValue> generateBootstrapACI(DN user_dn, String name1, String name2)
      Generate bootstrap ACI which can be used for entryACI or subentryACI attributes.
      Parameters:
      user_dn - DN of user who will be given permissions
      name1 - Name to use for entry ACI rule, or null for default
      name2 - Name to use for attribute ACI rule, or null for default
      Returns:
      List
    • addACSA

      public SimpleACIWorld.ACSA addACSA(DN dn, boolean isBAC, boolean isAAP)
      Add a new ACSA or AAP, using SAC or BAC according to the isBAC argument. The SimpleACIWorld code will make the given entry into an admin point (if it isn't already) and add ACSA, optional AAP and SAC/BAC flags when .save() is called. This call makes no changes to the ACI items or rules that are already present in the SimpleACIWorld. The new ACSA will automatically pick up whatever ACI was already applying to the region according to the current set of items.

      This call may also be used to turn a local ACSA into a global one. The previously local ACSA will get the ACI rules that have been set to apply to the area it covers, ignoring whatever ACI rules it currently has. Note that the user should check that this is what they want, as the loading code has to guess what is most likely to be the intention of the rules for a local ACSA area, since it can't load up any ACI from it.

      Parameters:
      dn - ACSA base DN
      isBAC - 'true' for BAC, 'false' for SAC (preferred)
      isAAP - 'true' for AAP, 'false' for plain ACSA
      Returns:
      ACSA that was added or updated
    • addACSA

      public SimpleACIWorld.ACSA addACSA(DN dn, boolean isBAC)
      Add a new ACSA, using SAC or BAC according to the isBAC argument. The SimpleACIWorld code will make the given entry into an admin point (if it isn't already) and add ACSA and SAC/BAC flags when .save() is called. This call makes no changes to the ACI items or rules that are already present in the SimpleACIWorld. The new ACSA will automatically pick up whatever ACI was already applying to the region according to the current set of items.

      This call may also be used to turn a local ACSA into a global one. The previously local ACSA will get the ACI rules that have been set to apply to the area it covers, ignoring whatever ACI rules it currently has. Note that the user should check that this is what they want, as the loading code has to guess what is most likely to be the intention of the rules for a local ACSA area, since it can't load up any ACI from it.

      Parameters:
      dn - ACSA base DN
      isBAC - 'true' for BAC, 'false' for SAC (preferred)
      Returns:
      ACSA that was added or updated
    • addACSA

      public SimpleACIWorld.ACSA addACSA(DN dn)
      Add a new ACSA, using SAC (Simple Access Control). The SimpleACIWorld code will make the given entry into an admin point (if it isn't already) and add ACSA and SAC flags when .save() is called. This call makes no changes to the ACI items or rules that are already present in the SimpleACIWorld. The new ACSA will automatically pick up whatever ACI was already applying to the region according to the current set of items.

      This call may also be used to turn a local ACSA into a global one. The previously local ACSA will get the ACI rules that have been set to apply to the area it covers, ignoring whatever ACI rules it currently has. Note that the user should check that this is what they want, as the loading code has to guess what is most likely to be the intention of the rules for a local ACSA area, since it can't load up any ACI from it.

      Parameters:
      dn - ACSA base DN
      Returns:
      ACSA that was added or updated
    • removeACSA

      public void removeACSA(DN dn)
      Remove the ACSA from the directory completely. On .save(), all ACI-related attributes and roles will be cleared from the admin point and its subentries. If there are no roles left in the admin point, then the admin point is also removed, leaving a normal entry. The ACI that would have been installed in that ACSA will now be installed into the superior ACSA instead. See also localACSA(com.isode.dsapi.DN).
      Parameters:
      dn - ACSA base DN
    • localACSA

      public void localACSA(DN dn)
      Clear all prescriptive ACSA information from the admin point on next .save(), but leave it with bootstrap ACI and flagged as a local ACSA (i.e. with GAC flag of 'false'). See also removeACSA(com.isode.dsapi.DN).
      Parameters:
      dn - ACSA base DN
    • ignoreACSA

      public void ignoreACSA(DN dn)
      Tell the SimpleACIWorld to treat the ACSA at the given DN as 'local', which means that it will ignore it and not update it when save() is called.
      Parameters:
      dn - ACSA base DN
    • getACSA

      public SimpleACIWorld.ACSA getACSA(DN dn)
      Return the ACSA object corresponding to the given DN, or null if it is not found.
      Parameters:
      dn - DN of ACSA object to search for
      Returns:
      ACSA, or null if not found
    • correctLists

      public void correctLists()
      Scan the 'items' list, and pick up any new roles or rules which aren't already in the 'roles' or 'rules' lists, and add them (using addRole(com.isode.dsapi.aci.SimpleACIRole) or addRule(com.isode.dsapi.aci.SimpleACIRule) to ensure that identical copies are merged). This makes sure that everything is sane, even if the caller hasn't been updating the 'roles' and 'rules' lists. This also sorts the items, roles and rules into order, and renames roles or rules that have the same name. This is called automatically on saving.
    • loadAndLogErrors

      public boolean loadAndLogErrors()
      Load up ACI data from the directory, using load(). If there are any errors, log them as warnings using Logger, and return false. Otherwise return true.
      Returns:
      false: warnings, true: success
    • saveAndLogErrors

      public boolean saveAndLogErrors()
      Write back all the ACI to the directory, using save(). If there are any errors, log them as warnings with Logger and return false. Otherwise return true.
      Returns:
      false: errors, true: success
    • dumpWarningsToLog

      public void dumpWarningsToLog()
      Dump out all errors and warnings using logger.warning().
    • getErrors

      public List<SimpleACIWorld.DNMessage> getErrors()
      Returns the list of errors reported by the previous save() or load() call. Each is a DNMessage with message text, and possibly an attached exception as a cause.
      Returns:
      List
    • getWarnings

      public List<SimpleACIWorld.DNMessage> getWarnings()
      Returns the list of warnings reported by the previous save() or load() call, plus warnings generated by checks on the current state of the loaded data (which are regenerated each call). Each is a DNMessage with message text, and possibly an attached exception as a cause.
      Returns:
      List
    • replaceRole

      public void replaceRole(SimpleACIRole old_role, SimpleACIRole new_role)
      Replace one role with another role. Updates all the SimpleACIItem instances that refer to the old role to refer to the new one. Makes the new role name unique.
    • replaceRule

      public void replaceRule(SimpleACIRule old_rule, SimpleACIRule new_rule)
      Replace one rule with another rule. Updates all the SimpleACIItem instances that refer to the old rule to refer to the new one. Makes the new rule name unique.
    • getDupRole

      public SimpleACIRole getDupRole(SimpleACIRole excl, String name)
      Look for a role that has the given name but isn't the one passed. This is to check for duplicates.
      Parameters:
      excl - Role to exclude from search
      name - Name to look for
      Returns:
      SimpleACIRole if found, else null
    • getDupRule

      public SimpleACIRule getDupRule(SimpleACIRule excl, String name)
      Look for a rule that has the given name but isn't the one passed. This is to check for duplicates.
      Parameters:
      excl - Rule to exclude from search
      name - Name to look for
      Returns:
      SimpleACIRule if found, else null
    • makeRoleNameUnique

      public void makeRoleNameUnique(SimpleACIRole role)
      Check that the given role's name is unique, and if it isn't, add a number to the name to make it unique.
      Parameters:
      role - SimpleACIRole to check
    • makeRuleNameUnique

      public void makeRuleNameUnique(SimpleACIRule rule)
      Check that the given rule's name is unique, and if it isn't, add a number to the name to make it unique.
      Parameters:
      rule - SimpleACIRule to check
    • addRole

      public SimpleACIRole addRole(SimpleACIRole role)
      Add a new role to the SimpleACIWorld, checking to see if it is already present with the same name (identical role), or if the name is already taken for another role (non-identical). If the role is already present, then a reference to the existing copy is returned. If the name is already taken, then a new name is generated for the added role. Otherwise the role is simply added.
    • addRule

      public SimpleACIRule addRule(SimpleACIRule rule)
      Add a new rule to the SimpleACIWorld, checking to see if it is already present with the same name (identical rule), or if the name is already taken for another rule (non-identical). If the rule is already present, then a reference to the existing copy is returned. If the name is already taken, then a new name is generated for the added rule. Otherwise the rule is simply added.
    • getUnusedRoles

      public List<SimpleACIRole> getUnusedRoles()
      Return a list of unused roles, that is those not referenced by any item.
      Returns:
      List
    • getUnusedRules

      public List<SimpleACIRule> getUnusedRules()
      Return a list of unused rules, that is those not referenced by any item.
      Returns:
      List
    • removeItemsWithPrecedence

      public void removeItemsWithPrecedence(int prec)
      Remove all the items with the given precedence number. This is used to support deselecting rule-items in the DSASetup.
      Parameters:
      prec - Precedence
    • removeUsersAndGroups

      public void removeUsersAndGroups(List<DN> dnlist)
      Remove all mentions of the given user and/or group DNs from the roles. Where a role is left empty, it is deleted, along with any items that depend on it. This is used to strip unwanted groups out of a GAC config by DSASetup.
      Parameters:
      dnlist - List of DNs to delete (not null)
    • sort

      public void sort()
      Sort roles, rules, items and ACSAs according to their sort orders. Roles and rules are sorted according to their label, and items are sorted by precedence, grant/deny, role and rule. ACSAs are sorted by DN.
    • equals

      public boolean equals(Object oo)
      Test whether the contents of two SimpleACIWorld instances are the same. This forces all internal lists in all the objects to be sorted.
      Overrides:
      equals in class Object
      Parameters:
      oo - Object to compare to
      Returns:
      Test result
    • hashCode

      public int hashCode()
      Hash code. Forces everything to be sorted.
      Overrides:
      hashCode in class Object
      Returns:
      Hash code
    • dump

      public String dump()
      Return a debug dump of the entire ACI world data structures.
      Returns:
      Debug dump string
    • toString

      public String toString()
      Debugging dump.
      Overrides:
      toString in class Object
      Returns:
      Debugging dump