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

Refactored system configuration service for improved structure and asynchronicity #346

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
44 changes: 43 additions & 1 deletion app/exceptions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,43 @@
super(`Unable to create software, property ${propertyName} is not allowed`, options);
}
}


class SystemConfigurationNotFound extends CustomError {
constructor(options) {
super(`System configuration not found`, options);
}

Check warning on line 111 in app/exceptions/index.js

View check run for this annotation

Codecov / codecov/patch

app/exceptions/index.js#L110-L111

Added lines #L110 - L111 were not covered by tests
}

class OrganizationIdentityNotSetError extends CustomError {
constructor(options) {
super(`Organization identity not set`, options);
}
}

class DefaultMarkingDefinitionsNotFoundError extends CustomError {
constructor(options) {
super(`Default marking definitions not found`, options);
}

Check warning on line 123 in app/exceptions/index.js

View check run for this annotation

Codecov / codecov/patch

app/exceptions/index.js#L122-L123

Added lines #L122 - L123 were not covered by tests
}

class OrganizationIdentityNotFoundError extends CustomError {
constructor(identityRef, options) {
super(`Identity with id ${ identityRef } not found`, options);
}

Check warning on line 129 in app/exceptions/index.js

View check run for this annotation

Codecov / codecov/patch

app/exceptions/index.js#L128-L129

Added lines #L128 - L129 were not covered by tests
}

class AnonymousUserAccountNotSetError extends CustomError {
constructor(options) {
super(`Anonymous user account not set`, options);
}
}

class AnonymousUserAccountNotFoundError extends CustomError {
constructor(userAccountid, options) {
super(`Anonymous user account ${ userAccountid } not found`, options);
}

Check warning on line 141 in app/exceptions/index.js

View check run for this annotation

Codecov / codecov/patch

app/exceptions/index.js#L140-L141

Added lines #L140 - L141 were not covered by tests
}

class InvalidTypeError extends CustomError {
constructor(options) {
super('Invalid stix.type', options);
Expand Down Expand Up @@ -135,6 +171,12 @@
TechniquesServiceError,
TacticsServiceError,
PropertyNotAllowedError,
SystemConfigurationNotFound,
DefaultMarkingDefinitionsNotFoundError,
OrganizationIdentityNotSetError,
OrganizationIdentityNotFoundError,
AnonymousUserAccountNotSetError,
AnonymousUserAccountNotFoundError,

InvalidTypeError,
};
11 changes: 6 additions & 5 deletions app/lib/database-configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
const AttackObject = require('../models/attack-object-model');
const CollectionIndex = require('../models/collection-index-model');
const MarkingDefinition = require('../models/marking-definition-model');
const { OrganizationIdentityNotFoundError, OrganizationIdentityNotSetError, AnonymousUserAccountNotFoundError, AnonymousUserAccountNotSetError } = require('../exceptions');

async function createPlaceholderOrganizationIdentity() {
// Create placeholder identity object
Expand Down Expand Up @@ -74,11 +75,11 @@
logger.info(`Success: Organization identity is set to ${ identity.stix.name }`);
}
catch(err) {
if (err.message === systemConfigurationService.errors.organizationIdentityNotFound) {
logger.warn(`Organization identity with id ${ err.organizationIdentityRef } not found, setting to placeholder identity`);
if (err instanceof OrganizationIdentityNotFoundError) {
logger.warn(`Organization identity with id ${ err } not found, setting to placeholder identity`);

Check warning on line 79 in app/lib/database-configuration.js

View check run for this annotation

Codecov / codecov/patch

app/lib/database-configuration.js#L79

Added line #L79 was not covered by tests
await createPlaceholderOrganizationIdentity();
}
else if (err.message === systemConfigurationService.errors.organizationIdentityNotSet) {
else if (err instanceof OrganizationIdentityNotSetError) {
logger.warn(`Organization identity not set, setting to placeholder identity`);
await createPlaceholderOrganizationIdentity();
}
Expand All @@ -100,11 +101,11 @@
logger.info(`Success: Anonymous user account is set to ${ anonymousUserAccount.id }`);
}
catch (err) {
if (err.message === systemConfigurationService.errors.anonymousUserAccountNotFound) {
if (err instanceof AnonymousUserAccountNotFoundError) {
logger.warn(`Anonymous user account with id ${ err.anonymousUserAccountId } not found, creating new anonymous user account`);
await createAnonymousUserAccount();
}
else if (err.message === systemConfigurationService.errors.anonymousUserAccountNotSet) {
else if (err instanceof AnonymousUserAccountNotSetError) {
logger.warn(`Anonymous user account not set, creating new anonymous user account`);
await createAnonymousUserAccount();
}
Expand Down
34 changes: 34 additions & 0 deletions app/repository/system-configurations-repository.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

const SystemConfiguration = require('../models/system-configuration-model');
const { DatabaseError } = require('../exceptions');

class SystemConfigurationsRepository {
constructor(model) {
this.model = model;
}

createNewDocument(data) {
return new this.model(data);
}

static async saveDocument(document) {
try {
return await document.save();
}
catch(err) {
throw new DatabaseError(err);
}

Check warning on line 20 in app/repository/system-configurations-repository.js

View check run for this annotation

Codecov / codecov/patch

app/repository/system-configurations-repository.js#L19-L20

Added lines #L19 - L20 were not covered by tests
}

async retrieveOne(options) {
options = options ?? {};
if (options.lean) {
return await this.model.findOne().lean();
}
else {
return await this.model.findOne();
}
}
}

module.exports = new SystemConfigurationsRepository(SystemConfiguration);
109 changes: 52 additions & 57 deletions app/services/system-configuration-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,19 @@

const fs = require('fs');
const config = require('../config/config');

const SystemConfiguration = require('../models/system-configuration-model');
const Identity = require('../models/identity-model');
const UserAccount = require('../models/user-account-model');
const MarkingDefinition = require('../models/marking-definition-model');
const systemConfigurationRepository = require('../repository/system-configurations-repository');
const userAccountsService = require('./user-accounts-service');
const {
SystemConfigurationNotFound,
OrganizationIdentityNotSetError,
OrganizationIdentityNotFoundError,
DefaultMarkingDefinitionsNotFoundError,
AnonymousUserAccountNotSetError,
AnonymousUserAccountNotFoundError } = require('../exceptions');

let allowedValues;

const errors = {
systemConfigurationDocumentNotFound: 'System configuration document not found',
organizationIdentityNotFound: 'Organization identity not found',
organizationIdentityNotSet: 'Organization identity not set',
anonymousUserAccountNotFound: 'Anonymous user account not found',
anonymousUserAccountNotSet: 'Anonymous user account not set',
defaultMarkingDefinitionNotFound: 'Default marking definition not found',
systemConfigurationNotFound: 'System configuration not found'
};
exports.errors = errors;
let markingDefinitionsService;
let identitiesService;

// NOTE: Some parts of the system configuration are stored in the systemconfiguration collection in the database
// (the systemconfiguration collection should have exactly one document)
Expand Down Expand Up @@ -70,60 +65,64 @@

exports.retrieveOrganizationIdentityRef = async function() {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne();

if (systemConfig && systemConfig.organization_identity_ref) {
return systemConfig.organization_identity_ref;
}
else {
throw new Error(errors.organizationIdentityNotSet);
throw new OrganizationIdentityNotSetError;

Check warning on line 74 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L74

Added line #L74 was not covered by tests
}
}

exports.retrieveOrganizationIdentity = async function() {
if (!identitiesService) {
identitiesService = require('./identities-service');
}
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne({ lean: true });

if (systemConfig && systemConfig.organization_identity_ref) {
const identity = await Identity.findOne({ 'stix.id': systemConfig.organization_identity_ref }).lean();
if (identity) {
return identity;
const identities = await identitiesService.retrieveById(systemConfig.organization_identity_ref, { versions: 'latest' });
if (identities.length === 1) {
return identities[0];
}
else {
const error = new Error(errors.organizationIdentityNotFound)
error.organizationIdentityRef = systemConfig.organization_identity_ref;
throw error;
throw new OrganizationIdentityNotFoundError(systemConfig.organization_identity_ref);

Check warning on line 91 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L91

Added line #L91 was not covered by tests
}
}
else {
throw new Error(errors.organizationIdentityNotSet);
throw new OrganizationIdentityNotSetError;
}
}

exports.setOrganizationIdentity = async function(stixId) {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne();

if (systemConfig) {
// The document exists already. Set the identity reference.
systemConfig.organization_identity_ref = stixId;
await systemConfig.save();
await systemConfigurationRepository.constructor.saveDocument(systemConfig);
}
else {
// The document doesn't exist yet. Create a new one.
const systemConfigData = {
organization_identity_ref: stixId
};
const systemConfig = new SystemConfiguration(systemConfigData);
await systemConfig.save();
const systemConfig = systemConfigurationRepository.createNewDocument(systemConfigData);
await systemConfigurationRepository.constructor.saveDocument(systemConfig);
}
}

exports.retrieveDefaultMarkingDefinitions = async function(options) {
if (!markingDefinitionsService) {
markingDefinitionsService = require('./marking-definitions-service');
}
options = options ?? {};

// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne().lean();
const systemConfig = await systemConfigurationRepository.retrieveOne({ lean: true });

if (systemConfig) {
if (systemConfig.default_marking_definitions) {
Expand All @@ -134,16 +133,14 @@
const defaultMarkingDefinitions = [];
for (const stixId of systemConfig.default_marking_definitions) {
// eslint-disable-next-line no-await-in-loop
const markingDefinition = await MarkingDefinition.findOne({ 'stix.id': stixId }).lean();
if (markingDefinition) {
defaultMarkingDefinitions.push(markingDefinition);
} else {
const error = new Error(errors.defaultMarkingDefinitionNotFound);
error.markingDefinitionRef = stixId;
throw error;
const markingDefinition = await markingDefinitionsService.retrieveById(stixId);
if (markingDefinition.length === 1) {
defaultMarkingDefinitions.push(markingDefinition[0]);
}
else {
throw new DefaultMarkingDefinitionsNotFoundError;

Check warning on line 141 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L141

Added line #L141 was not covered by tests
}
}

return defaultMarkingDefinitions;
}
}
Expand All @@ -160,54 +157,52 @@

exports.setDefaultMarkingDefinitions = async function(stixIds) {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne();

if (systemConfig) {
// The document exists already. Set the default marking definitions.
systemConfig.default_marking_definitions = stixIds;
await systemConfig.save();
await systemConfigurationRepository.constructor.saveDocument(systemConfig);
}
else {
// The document doesn't exist yet. Create a new one.
const systemConfigData = {
default_marking_definitions: stixIds
};
const systemConfig = new SystemConfiguration(systemConfigData);
await systemConfig.save();
const systemConfig = systemConfigurationRepository.createNewDocument(systemConfigData);
await systemConfigurationRepository.constructor.saveDocument(systemConfig);

Check warning on line 173 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L172-L173

Added lines #L172 - L173 were not covered by tests
}
}

exports.retrieveAnonymousUserAccount = async function() {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne({ lean: true });

if (systemConfig && systemConfig.anonymous_user_account_id) {
const userAccount = await UserAccount.findOne({ 'id': systemConfig.anonymous_user_account_id });
const userAccount = await userAccountsService.retrieveById(systemConfig.anonymous_user_account_id, {});
if (userAccount) {
return userAccount;
}
else {
const error = new Error(errors.anonymousUserAccountNotFound)
error.anonymousUserAccountId = systemConfig.anonymous_user_account_id;
throw error;
throw new AnonymousUserAccountNotFoundError(systemConfig.anonymous_user_account_id);

Check warning on line 187 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L187

Added line #L187 was not covered by tests
}
}
else {
throw new Error(errors.anonymousUserAccountNotSet);
throw new AnonymousUserAccountNotSetError;
}
}

exports.setAnonymousUserAccountId = async function(userAccountId) {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne();

if (systemConfig) {
// The document exists already. Set the anonymous user account id.
systemConfig.anonymous_user_account_id = userAccountId;
await systemConfig.save();
await systemConfigurationRepository.constructor.saveDocument(systemConfig);
}
else {
throw new Error(errors.systemConfigurationDocumentNotFound);
throw new SystemConfigurationNotFound;

Check warning on line 205 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L205

Added line #L205 was not covered by tests
}
}

Expand All @@ -224,25 +219,25 @@

exports.retrieveOrganizationNamespace = async function() {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne({ lean: true });

if (systemConfig) {
return systemConfig.organization_namespace;
}
else {
throw new Error(errors.systemConfigurationDocumentNotFound);
throw new SystemConfigurationNotFound;

Check warning on line 228 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L228

Added line #L228 was not covered by tests
}
}

exports.setOrganizationNamespace = async function(namespace) {
// There should be exactly one system configuration document
const systemConfig = await SystemConfiguration.findOne();
const systemConfig = await systemConfigurationRepository.retrieveOne();

if (systemConfig) {
systemConfig.organization_namespace = namespace;
await systemConfig.save();
await systemConfigurationRepository.constructor.saveDocument(systemConfig);
}
else {
throw new Error(errors.systemConfigurationDocumentNotFound);
throw new SystemConfigurationNotFound;

Check warning on line 241 in app/services/system-configuration-service.js

View check run for this annotation

Codecov / codecov/patch

app/services/system-configuration-service.js#L241

Added line #L241 was not covered by tests
}
}
Loading