Reference user accounts

User accounts are uniquely identified in Itential Platform by a database ID, or by a username and provenance pair where provenance is the name of the AAA adapter from which the account originated. This page covers best practices for user account representation, including how to reference accounts in documents, return account data from APIs, and query and sort documents with referenced accounts.

Best practices

Account references in database documents

Reference user accounts via database ID. In MongoDB, accounts are keyed with ObjectId, and references should use that type as well. This allows the application to use $lookup in the MongoDB aggregation pipeline for sorting, filtering, and efficient querying when account data needs to be retrieved.

Accounts in the UI

Do not present users with a list of account IDs. Display user lists and references using the username and provenance fields. Username alone is no longer sufficient — users may need to distinguish between similarly named accounts in different systems.

Account references in APIs

Based on how existing APIs and documents are structured, APIs should accept and return both account IDs and username/provenance pairs. Where account IDs are exposed, they should appear as strings in JSON. APIs must take on the burden of validating IDs, converting them to ObjectId where appropriate, and testing for equality using the appropriate methods.

Query, filter, and sort account documents

Example account document

The examples below use the following account document:

1{
2 "_id": ObjectId("5aebd2fae2c5b5614927362b"),
3 "provenance": "Local AAA",
4 "username": "admin@pronghorn",
5 "firstname": "admin",
6 "memberOf": [...],
7 "assignedRoles": [...],
8 "lastLogin": ISODate("2018-05-30T16:29:39.944Z")
9}

Query documents with referenced accounts

Denormalizing user account data — for example, storing a copy of the username alongside the account ID — creates a maintenance problem when the account data changes but the copy does not. Instead, load account data at query time to include it in the API response.

Example workflow document

1{
2 "_id": "b6517ac9-a8fc-4621-902b-174458005c90",
3 "name": "Example Workflow",
4 "tasks": {},
5 "transitions": {},
6 "created": "5/18/2018, 9:32:50 AM",
7 "created_by": {
8 "id": ObjectId("5aebd2fae2c5b5614927362b")
9 },
10 "last_updated": "5/18/2018, 9:33:31 AM",
11 "last_updated_by": {
12 "id": ObjectId("5aebd2fae2c5b5614927362b")
13 },
14 "groups": [
15 ObjectId("5aebd2ffe2c5b5614927362d")
16 ]
17}

Example API response with inline account data

This response includes the human-readable username and provenance fields alongside the account ID:

1[
2 {
3 "id": "b6517ac9-a8fc-4621-902b-174458005c90",
4 "name": "Example Workflow",
5 "created": "5/18/2018, 9:32:50 AM",
6 "created_by": {
7 "id": "5aebd2fae2c5b5614927362b",
8 "username": "admin@pronghorn",
9 "provenance": "Local AAA"
10 },
11 "last_updated": "5/18/2018, 9:33:31 AM",
12 "last_updated_by": {
13 "id": "5aebd2fae2c5b5614927362b",
14 "username": "admin@pronghorn",
15 "provenance": "Local AAA"
16 }
17 }
18]

Aggregation pipeline query

This query produces the desired data set in a single request to MongoDB:

1const workflowListWithInlineAccountsPipeline = [
2 {
3 $lookup: {
4 from: 'accounts',
5 localField: 'created_by.id',
6 foreignField: '_id',
7 as: 'created_by_accounts'
8 }
9 },
10 {
11 $lookup: {
12 from: 'accounts',
13 localField: 'last_updated_by.id',
14 foreignField: '_id',
15 as: 'last_updated_by_accounts'
16 }
17 },
18 {
19 $project: {
20 name: 1,
21 created: 1,
22 created_by_account: { $arrayElemAt: ['$created_by_accounts', 0] },
23 last_updated: 1,
24 last_updated_by_account: { $arrayElemAt: ['$created_by_accounts', 0] }
25 }
26 }
27];
28
29const workflowList = await workflowsCollection.aggregate(workflowListWithInlineAccountsPipeline).toArray();

Filter and sort documents by referenced account fields

You can extend the pipeline above to filter and sort by account fields:

1const filteredAndSortedByCreatorPipeline = [
2 { $match: { name: /^Exa/ } },
3 ...workflowListWithInlineAccountsPipeline,
4 { $sort: { 'created_by_account.username': 1 } },
5];
6
7const workflowList = await workflowsCollection.aggregate(filteredAndSortedByCreatorPipeline).toArray();