Python3 FastAPI based REST API for storing and vending group memberships. Groups can have many members. Members are simply records with their type annoted in a 'type' column'. Every record, group or member, has an entry in a records table (types detailed below). The API is self documenting with OpenAPI (swagger) metadata.
The intention behind operations is to allow for eventual garbage collection of member records. These are units of work that operate on records within the database but shouldn't lock tables. Efforts will be made to perform partial updates and soft-delete memberships when external sytems notify of memberships being removed, the issue is that delta synchronization systems carry no guarantees, sometimes the system just doesn't tell you when someone was removed. That shorcoming can be worked around by triggering a member clean-up operation after a full-update, any membership whos last_operation_id value is less than the most recent completed full-update can be assumed no longer a member and soft-deleted.
Postgresql is the database target with SQLAlchemy as the ORM.
| Name | Description |
|---|---|
| id | uuid |
| type | int |
| subtype | int |
| deleted | bool - soft-delete |
| typename | varchar |
| description | varchar |
| displayname | varchar |
| identifier | varchar - may be used for access control: email address, or samaccountname |
| sourceSystem | varchar - Entra Tenant ID, Active Directory domain name, Workday tenant, etc. |
| created | datetime |
| modified | datetime |
| deleted | datetime |
| notes | text |
| tags | varchar[] |
| Name | Description |
|---|---|
| id | ulong |
| source | ulong |
| group_id | foreign key |
| member_id | foreign key |
| member_name | varchar - human readable name |
| removed | bool - soft deleted membership record |
| contributing | bool - determines whether or not an imported group membership contributes to a unified group membership, more below |
| last_operation_id | ulong |
| created_timestamp | datetime |
| removed_timestamp | datetime - timestamp identifying when a membership was soft-deleted |
These records track operations that maintain group memberships. These are usually scripted operations that pull group membership records from directory services: LDAP, Entra, AWS Cogneto, etc.
| Name | Description |
|---|---|
| id | ulong |
| type | int |
| description | varchar - full-update, partial-update, delete, reconcile |
| status | varchar - running, pending, complete, failed |
| created | datetime |
| modified | datetime - tracks the last time status was updated |
| completed | datetime - tracks when the operation reached the complete or failed status |
This idea came from a project at work where syncing group memberships across systems is a requirement but mapping a given user to its counterpart in another system has nuances. They utilize separation of duties for accounts, meaning a given person may have multiple user accounts in a single directory. No identity management systems that I know of handle this case; I've expressly asked Okta, Ping, ForgeRock, and Microsoft, nada. While less of an issue for group syncing, because account naming conventions can usually serve for mapping cross directory memberships, managing an individual's identity requires associating all accounts related to all duties of the individual: standard, privileged, training, testing, etc.
The refactored group membership changes function is working with great performance now but has identified an issue in how associated accounts are identified. In instances where a user has multiple standard member accounts, cloud native or otherwise in a tenant, only one of those accounts is selected for membership eligibility. This has the effect of erroneously identifying a guest in another tenant, associated to their other member account as needing removal from a group.
| User | Synced Group (Home) | Synced Group (Foo) | Synced Group (Bar) |
|---|---|---|---|
| Sean 1 (Home) | Member | Member | Member |
| Sean 2 (Home) | Not a member | Not a member | Not a member |
In the case where we're evaluating who needs to be removed from group memberships across a tenant, if Sean 2 is mistaken for Sean 1, Sean 1 will be flagged as needing removal from the Foo and Bar tenants. The way identity correlation is handled currently, all associated users are related through a "prime" user reference, think Identity vs Persona.
graph TD;
P1(Prime) <--- S1(Sean 1 - Home);
P1 <--- S2(Sean 2 - Home);
P1 <--- S1a(Sean 1 - Bar);
P1 <--- S2a(Sean 2 - Bar);
P1 <--- S1b(Sean 1 - Foo);
P1 <--- S2b(Sean 2 - Foo);
Figure 1 Single tier of association.
All users across tenants are associated through a single prime record. When performing the crosswalk, there's no disambiguation between Sean 1 and Sean 2 across the tenant boundary. Record types: prime, native, guest, privileged are the only distinctions.
graph TD;
P0(Prime) -..-> WD(Workday Sean)
P0 <--- P1
P0 <--- P2
P0 <--- P3
subgraph Sean 1 Cohort
P1(Prime Sean 1) <--- S1(Native - Home);
P1 <--- S1a(Guest - Bar);
P1 <--- S1b(Guest - Foo);
end
subgraph Sean 2 Cohort
P2(Prime Sean 2) <--- S2(Native - Home);
P2 <--- S2b(Guest Foo);
P2 <--- S2a(Guest Bar);
end
subgraph Sean PA Cohort
P3(Prime Sean) <--- SP2(Privileged - Home);
P3 <--- SPa(Guest Foo);
P3 <--- SPb(Guest Bar);
end
Figure 2 Multi-tier association, allowing for propagating changes based on degree of separation.
A multi-tiered association where a cohort can be joined to associated cohorts by linking prime references. Resolving the root prime reference can be done recursively until a prime account is found with a globalId of null. Once the root reference is resolved, all associated accounts can be derived for performing actions that are global (disabling from HR employment status change) versus actions local to a cohort (updating display name changes).