Skip to main content

Demo/Lab: Working with connector list files

In this lab, we are going to look at the connector list files. The connector list files are stored in SQLite and it is often useful to be able to examine these list files during list operations to both validate data and troubleshoot listing issues.

The Python API provides callbacks to write to the connect list files. Even though you are not responsible for writing directly to the connector list files, to some extent, you are responsible for using the Python callbacks correctly. For example, it is important to understand the data you are consuming and how it is represented in list files.

Overview of primary keys and attributes

The primary keys of a connector are:

  • Stableid: the least immutable identifier that is ideally able to remain consistent across rename and move context operations.

  • Longid: the primary identifier used as the main actors on all operations: reset, unlock, enable, and so on.

  • Shortid: user friendly identifier used to logon and often used to associate to the user profile.

The stableid, longid and shortid all must uniquely identify an object. In other words, there cannot be duplicates across multiple objects per target.

Simplistic connectors

In many connectors the stableid, longid, and shortid can represent the same identifier. For example, when targeting UNIX, an account identifier, such as the UNIX "root" account, is the same for the stableid, longid and shortid. This does make these target types difficult to track context on rename operations, but this is generally not a limitation. Where this becomes important is when we are talking about cross targeting, as is the case with multitargeting Active Directory and Active Directory forested infrastructures.

Complex connectors

In the more complicated connectors, such as Active Directory (and sometimes ldap when using a more fully functional schema), the stableid represents a unique identifier (called an Object GUID) which never changes, even if an object is moved or renamed. In other words, the identifier is immutable. When an object is created a new unique Object GUID is issued and remains unchanged for the lifetime of the object. Active Directory also has the ability to delete an object which can then be revived/undeleted from a tombstone in which case, their Object GUID still remains unchanged.

The longid is used to act on objects during an operation. In the Active Directory scenario, the longid represents the Distinguished Name. This identifier can change, either by:

  • Arename operation by changing the object’s common name or,

  • By the move context operation where the object is moved to a different relative DN.

It is for this reason, the rename and move-context operations return the longid and shortid, to indicate to Bravura Security Fabric that the values for these keys have changed. This in turn updates the values within the product database. In all other operations, the longid remains unchanged.

Finally, the shortid represents a friendly name that the user can type quickly and is easy to remember. This identifier is also used during association with the profile user id. In Active Directory this object is represented by the SAM Account Name (otherwise simply known as the logon name.)

Connector performance during listing

Selective attribute listing and incremental listing are another layer of complexity in writing a connector. Generally, these are used to enhance performance. These can be exposed in your custom connector but generally, they are only warranted if your connector’s performance is a concern. Contact Bravura Security support for an explanation if this is an improving connector performance.

Connector list file schema

The connector list file contains the following tables:

  • Discmeta - Stores meta information about the list operation in general

  • Discobj - Stores objects that were discovered

  • Discobjattr - Stores attribute information on the objects

  • Discobjrel - Stored relationship information on between the objects

Demo/Lab start

Prerequisites

Use the target configured in Demo/Lab: Targeting with SQLite example .

Steps

  1. Open the Windows start menu and find the DB Browser (SQLite) program.

  2. Right click on the DB Browser (SQLite) program and select More > Run as administrator.

  3. Click Yes when prompted by the "Do you want to allow this app...changes to your device" warning.

  4. Click Open Database in the top toolbar.

  5. Navigate to the <instance>\psconfig directory.

  6. Select the MYSQLITEDEMO.db file. This was created during the previous lab when iddiscover was executed.

  7. Click Open.

  8. Open the discmeta table. Click the Browse Data tab and in the Table field select the "discmeta" table.

    34808.png

    As you can see, the discmeta table shows a quick summary of the operations run, the output KVG of the run and the return value from the list operation execution. This is useful to know if the list operation failed and why it failed.

  9. Open the discobj table. Click the Browse Data tab and in the Table field select the "discobj" table

    34809.png

    The information stored in the discobj table contains all the primary keys. As you can see, this connector is somewhat simple where the stableid and longid are using the same identifier and the shortid (generally the login identifier) is different. Note the type field and how it distinguishes between object types and also note that both accounts and groups are stored in the same table. If you listed computers, they would also coexist here.

  10. Open agtsqlitedemo.py and navigate to the listusers Python definition.

        groups = []
        attrs = {GAttrFullName: account.fullname}
        if wantAttributes:
          account.listAttributes(attrs)
        agent.addAccount(
            
          account.id,
          account.id,
          account.shortid,
          attrs,
          groups)

    When execution of the agent.addAccount is done, very much like the connector list file, you can see that the stableid and longid pass in the account.id and the shortid passes in account.shortid. Notice that we check the wantAttributes flag, which is passed in by the product by the "List account attributes" checkbox on the target configuration page. In other words, the attributes only get added to the agent.addAccount callback if attributes are desired.

    Also notice that we pass in the global attribute GAttrFullName with account.fullname. This is the displayid in the connector list file above. But it’s also the "@fulName" global attribute in the discobjattr table that we will discuss next.

  11. Open the discobjattr table. Click the Browse Data tab and in the Table field select the "discobjattr" table

    34810.png

    This shows you the attributes for all the objects. The id, shortid, expiredpw, enabled, etc are all attributes we pass into a dictionary. Looking at the listAttributes Python definition, in the agtsqlitedemo.py, we see the following code:

     def listAttributes(self, attrs):
        # list attributes
        attrs.update({"id": self.id})
        attrs.update({"shortid": self.shortid})
        attrs.update({"fullname": self.fullname})
        attrs.update({"expiredpw": self.expiredpw})
        attrs.update({"enabled": self.enabled})
        attrs.update({"locked": self.locked})
        attrs.update({"expiredacct": self.expiredacct})
        if self.department is not None:
          attrs.update({"email": self.email})
        if self.department is not None:
          attrs.update({"department": self.department})
        # list pseudo attributes
        attrs.update({GAttrShortID: self.shortid})
        attrs.update({GAttrFullName: self.fullname})
        attrs.update({GAttrAccountEnabled: self.enabled})
        if self.email is not None:
          attrs.update({GAttrEmail: self.email})
        return ACSuccess

    Global attributes are prefixed with GAttr and provide consistent attributes names for the product to utilize. For example, the GAttrEmail expands to "@email" in the connector list file and this can be used by the expiry service in conjunction with the "@passwordExpiration" (not listed here.)

  12. Open the discobjrel table. Click the Browse Data tab and in the Table field select the "discobjrel" table

    34811.png

    The discobjrel tables shows the relationships between objects. We need the parent object stableid and its type with its corresponding relationship type with the child stableid and its type.

    There are two types of relationships: members and owners. This fits within the same paradigm with Active Directory where owners can generally manage properties of groups.

    Take a look at the listgroups definition in agtsqldemo.py to see how the agent.addGroup callback function works.

       groupid = group.id
             
        shortid = group.shortid
        attrs = {
          GAttrDescription: group.desc,
          GAttrSecurity: 'true',
          'grpattr0': group.grpattr0}
        owners = {}
        members = {}
        for manager in group.managers:
             
          account = Account(manager)
          if account.get():
            return ACUnknownError
          owners.update({account.id: {}})
        if wantMembers:
          for member in group.members:
            account = Account(member)
            if account.get():
              return ACUnknownError
            members.update({account.id: {}})
        agent.addGroup(groupid, shortid, shortid, attrs, owners, members)

    Like the agent.addAccount callback, the agent.addGroup assigns the stableid, longid and shortid, as well as, attributes, but there is also an owners and members dictionary that assigned the relationships. And memberships are only ever assigned if the wantMembers flag is true. This gets passed in by the product if groups are to be managed. This is exposed in the "Automatically managed groups" configuration option on the target configuration page.

Useful SQL Queries

The following are some useful SQL Queries when working with connector list db files:

  • Query objects by shortid:

    SELECT * FROM discobj WHERE shortid = ‘<shortid>’
  • Query attributes for object based on stableid:

    SELECT * FROM discobjattr WHERE type = '<ACCT|GRP>’ and stableid = '<stableid>';
  • Query relationships for object based on stableid:

    SELECT * FROM discobjrel WHERE type = '<ACCT|GRP>’ and stableid = '<stableid>';