Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Configuring OIDC for User Authentication #284

Merged
merged 4 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,48 @@ Sample configuration file setting the server port and database url:
}
```


##### Step 4. Run the app

```
node ./bin/www
```

### Configuring Workbench to Enable OIDC Authentication for Users

Workbench supports OIDC authentication for users, allowing you to integrate Workbench with your organization's authentication system.

#### Registering with the OIDC Server

In order to use OIDC authentication, your Workbench instance must be registered with your organization's OIDC authentication server.
The details depend on your authentication server, but the following values should cover most of what you need:

* Workbench uses the *Authorization Code Flow* for authenticating users
* Claims:

| claim | required | description |
|------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| **email** | yes | Identifies the user account associated with an authenticated user |
| **preferred_username** | no | If present, the `preferred_username` claim is used to set the `name` property of the user account when the user initially registers with Workbench |
| **name** | no | If present, the `name` claim is used to set the `displayName` property of the user account when the user initially registers with Workbench |

* Grant Types: *Client Credentials*, *Authorization Code* and *Refresh Token*
* Redirect URL: `<host_url>/api/authn/oidc/callback`

After registering with the OIDC authentication system, you will need the `client_id` and `client_secret` assigned as part of that process.
You will also need the Issuer URL for the OIDC Identity Server.

#### Workbench Configuration

Configuring Workbench to use OIDC can be done using environment variables or the corresponding properties in a configuration file.

| environment variable | required | description | configuration file property name |
|--------------------------------|----------|-------------------------------------------------------------------------------------------------------|----------------------------------|
| **AUTHN_MECHANISM** | yes | Must be set to `oidc` | userAuthn.mechanism |
| **AUTHN_OIDC_CLIENT_ID** | yes | Client ID assigned to the Workbench instance when registering with the OIDC authentication system | userAuthn.oidc.clientId |
| **AUTHN_OIDC_CLIENT_SECRET** | yes | Client secret assigned to the Workbench instance when registering with the OIDC authentication system | userAuthn.oidc.clientSecret |
| **AUTHN_OIDC_ISSUER_URL** | yes | Issuer URL for the Identity Server | userAuthn.oidc.issuerUrl |
| **AUTHN_OIDC_REDIRECT_ORIGIN** | yes | URL for the Workbench host | userAuthn.oidc.redirectOrigin |

## Scripts

`package.json` contains a number of scripts that can be used to perform recurring tasks.
Expand Down
8 changes: 3 additions & 5 deletions app/services/recent-activity-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
const Relationship = require('../models/relationship-model');
const identitiesService = require('./identities-service');

const logger = require('../lib/logger');

const { lastUpdatedByQueryHelper } = require('../lib/request-parameter-helper');

const errors = {
Expand All @@ -19,111 +17,111 @@
exports.errors = errors;

exports.retrieveAll = async function(options) {
// Build the query
const query = {};
if (!options.includeRevoked) {
query['stix.revoked'] = { $in: [null, false] };
}
if (!options.includeDeprecated) {
query['stix.x_mitre_deprecated'] = { $in: [null, false] };
}
if (typeof options.lastUpdatedBy !== 'undefined') {
query['workspace.workflow.created_by_user_account'] = lastUpdatedByQueryHelper(options.lastUpdatedBy);
}

// Filter out objects without modified dates (incl. Marking Definitions & Identities)
query['stix.modified'] = { $exists: true };

// Build the aggregation
const aggregation = [];

// Sort objects by last modified
aggregation.push({ $sort: { 'stix.modified': -1 } });

// Limit documents to prevent memory issues
const limit = options.limit ?? 0;
if (limit) {
aggregation.push({ $limit: limit });
}

// Then apply query, skip and limit options
aggregation.push({ $match: query });

// Retrieve the documents
let objectDocuments = await AttackObject.aggregate(aggregation);
const objectDocuments = await AttackObject.aggregate(aggregation);

// Lookup source/target refs for relationships
aggregation.push({
$lookup: {
from: 'attackObjects',
localField: 'stix.source_ref',
foreignField: 'stix.id',
as: 'source_objects'
}
});
aggregation.push({
$lookup: {
from: 'attackObjects',
localField: 'stix.target_ref',
foreignField: 'stix.id',
as: 'target_objects'
}
});
let relationshipDocuments = await Relationship.aggregate(aggregation);
let documents = objectDocuments.concat(relationshipDocuments);
const relationshipDocuments = await Relationship.aggregate(aggregation);
const documents = objectDocuments.concat(relationshipDocuments);

// Sort by most recent
documents.sort((a, b) => b.stix.modified - a.stix.modified);

// Move latest source and target objects to a non-array property, then remove array of source and target objects
for (const document of documents) {
if (Array.isArray(document.source_objects)) {
if (document.source_objects.length === 0) {
document.source_objects = undefined;
}
else {
document.source_object = document.source_objects[0];
document.source_objects = undefined;
}
}

if (Array.isArray(document.target_objects)) {
if (document.target_objects.length === 0) {
document.target_objects = undefined;
}
else {
document.target_object = document.target_objects[0];
document.target_objects = undefined;
}
}
}

// Apply pagination
const offset = options.offset ?? 0;
let paginatedDocuments;
if (limit > 0) {
paginatedDocuments = documents.slice(offset, offset + limit);
}
else {
paginatedDocuments = documents.slice(offset);
}

// Add identities
await identitiesService.addCreatedByAndModifiedByIdentitiesToAll(paginatedDocuments);

// Prepare the return value
if (options.includePagination) {
const returnValue = {
pagination: {
total: documents.length,
offset: options.offset,
limit: options.limit
},
data: paginatedDocuments
};
return returnValue;
}
else {
return paginatedDocuments;
}

Check warning on line 126 in app/services/recent-activity-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/recent-activity-service.js#L20-L126

Added lines #L20 - L126 were not covered by tests
};
166 changes: 62 additions & 104 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"migrate-mongo": "^9.0.0",
"mongoose": "^6.5.1",
"mongoose": "^6.12.0",
"morgan": "^1.10.0",
"nanoid": "^3.3.6",
"node-cache": "^5.1.2",
Expand Down
Loading