Skip to content

Commit

Permalink
Merge pull request #259 from adhocteam/main
Browse files Browse the repository at this point in the history
Bootstrap account admin
  • Loading branch information
rahearn authored Jan 20, 2021
2 parents 8d0702c + 782498a commit 4d8bf0a
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 9 deletions.
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"scripts": {
"axe:ci": "axe --exit --load-delay 1000 `cat axe-urls` --dir ./reports/ --chromedriver-path=\"node_modules/chromedriver/bin/chromedriver\"",
"build": "./node_modules/.bin/babel src -d ./build/server && ./node_modules/.bin/babel config -d build/config",
"build": "./node_modules/.bin/babel src -d ./build/server && ./node_modules/.bin/babel config -d build/config",
"deps:local": "yarn install && yarn --cwd frontend install",
"deps": "yarn install --frozen-lockfile && yarn --cwd frontend install --frozen-lockfile",
"start:local": "concurrently \"yarn server\" \"yarn client\"",
Expand All @@ -18,7 +18,7 @@
"server:debug": "nodemon src/index.js --exec babel-node --inspect",
"client": "yarn --cwd frontend start",
"test": "jest src",
"test:ci": "cross-env JEST_JUNIT_OUTPUT_DIR=reports JEST_JUNIT_OUTPUT_NAME=unit.xml POSTGRES_USERNAME=postgres POSTGRES_DB=ttasmarthub CURRENT_USER_ID=5 CI=true jest src tools --coverage --reporters=default --reporters=jest-junit",
"test:ci": "cross-env JEST_JUNIT_OUTPUT_DIR=reports JEST_JUNIT_OUTPUT_NAME=unit.xml POSTGRES_USERNAME=postgres POSTGRES_DB=ttasmarthub CURRENT_USER_ID=5 CI=true jest src --coverage --reporters=default --reporters=jest-junit",
"test:all": "yarn test:ci && yarn --cwd frontend test:ci",
"lint": "eslint src",
"lint:ci": "eslint -f eslint-formatter-multiple src",
Expand All @@ -27,6 +27,8 @@
"docs:serve": "npx redoc-cli serve -p 5000 docs/openapi/index.yaml",
"cucumber": "./node_modules/.bin/cucumber-js --publish ./cucumber/features/*.feature -f json:./reports/cucumber_report.json && node ./cucumber/index.js",
"cucumber:ci": "cross-env TTA_SMART_HUB_URI=http://localhost:3000 yarn cucumber",
"db:bootstrap:admin:local": "./node_modules/.bin/babel-node ./src/tools/bootstrapAdminCLI.js",
"db:bootstrap:admin": "node ./build/server/tools/bootstrapAdminCLI.js",
"db:migrate": "node_modules/.bin/sequelize db:migrate",
"db:migrate:ci": "cross-env POSTGRES_USERNAME=postgres POSTGRES_DB=ttasmarthub node_modules/.bin/sequelize db:migrate",
"db:migrate:prod": "node_modules/.bin/sequelize db:migrate --options-path .production.sequelizerc",
Expand All @@ -49,7 +51,8 @@
"docker:db:migrate:undo": "docker-compose run --rm backend node_modules/.bin/sequelize db:migrate:undo",
"docker:db:seed": "docker-compose run --rm backend yarn db:seed",
"docker:db:seed:undo": "docker-compose run --rm backend yarn db:seed:undo",
"import:goals": "./node_modules/.bin/babel-node ./tools/importTTAPlanGoals.js"
"import:goals:local": "./node_modules/.bin/babel-node ./src/tools/importTTAPlanGoals.js",
"import:goals": "node ./build/server/tools/importTTAPlanGoals.js"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -86,7 +89,7 @@
],
"coverageThreshold": {
"global": {
"branches": 70
"branches": 75
}
}
},
Expand Down
28 changes: 28 additions & 0 deletions src/tools/bootstrapAdmin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { User, Permission } from '../models';
import logger from '../logger';
import SCOPES from '../middleware/scopeConstants';

const { ADMIN } = SCOPES;

export const ADMIN_EMAIL = 'ryan.ahearn@gsa.gov';

const bootstrapAdmin = async () => {
const user = await User.findOne({ where: { email: ADMIN_EMAIL } });
if (user === null) {
throw new Error(`User ${ADMIN_EMAIL} could not be found to bootstrap admin`);
}

const [permission, created] = await Permission.findOrCreate({
where: {
userId: user.id,
scopeId: ADMIN,
regionId: 14,
},
});
if (created) {
logger.warn(`SECURITY ALERT: Setting ${ADMIN_EMAIL} as an ADMIN`);
}
return permission;
};

export default bootstrapAdmin;
40 changes: 40 additions & 0 deletions src/tools/bootstrapAdmin.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import bootstrapAdmin, { ADMIN_EMAIL } from './bootstrapAdmin';
import db, { User } from '../models';

describe('Bootstrap the first Admin user', () => {
afterAll(() => {
db.sequelize.close();
});

describe('when user already exists', () => {
beforeAll(async () => {
await User.findOrCreate({ where: { email: ADMIN_EMAIL } });
});
afterAll(async () => {
await User.destroy({ where: { email: ADMIN_EMAIL } });
});

it('should create an admin permission for the user', async () => {
const user = await User.findOne({ where: { email: ADMIN_EMAIL } });
expect(user).toBeDefined();
expect((await user.getPermissions()).length).toBe(0);
const newPermission = await bootstrapAdmin();
expect(newPermission).toBeDefined();
expect((await user.getPermissions()).length).toBe(1);
await newPermission.destroy();
});

it('is idempotent', async () => {
const permission = await bootstrapAdmin();
const secondRun = await bootstrapAdmin();
expect(secondRun.id).toBe(permission.id);
await permission.destroy();
});
});

describe('when user does not exist', () => {
it('should loudly exit', async () => {
await expect(bootstrapAdmin()).rejects.toThrow(`User ${ADMIN_EMAIL} could not be found to bootstrap admin`);
});
});
});
16 changes: 16 additions & 0 deletions src/tools/bootstrapAdminCLI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import bootstrapAdmin from './bootstrapAdmin';

/**
* bootstrapAdminCLI is responsible for setting the first ADMIN for TTA Smart Hub.
* It should be run via `cf run-task tta-smarthub-prod "yarn db:bootstrap:admin"`
*
* All other admins and permissions should be set via the admin UI.
*
* To change the initial admin (for instance, after the previous one has left the project)
* Open a new issue and PR to update the ADMIN_EMAIL constant within bootstrapAdmin.js
*/
bootstrapAdmin().catch((e) => {
// eslint-disable-next-line no-console
console.log(e);
return process.exit(1);
});
7 changes: 4 additions & 3 deletions tools/importPlanGoals.js → src/tools/importPlanGoals.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
import { readFileSync } from 'fs';
import parse from 'csv-parse/lib/sync';
import {
Role, Topic, RoleTopic, Goal, TopicGoal, Grantee, Grant, GrantGoal,
} from '../src/models';
import { exit } from 'process';
Role, Topic, RoleTopic, Goal, TopicGoal, Grant, GrantGoal,
} from '../models';

const hubRoles = [
{ name: 'RPM', fullName: 'Regional Program Manager' },
Expand Down Expand Up @@ -161,6 +160,7 @@ export default async function importGoals(file, region) {
const fullGrant = { number: grant.trim(), regionId };
const dbGrant = await Grant.findOne({ where: { ...fullGrant }, attributes: ['id', 'granteeId'] });
if (!dbGrant) {
// eslint-disable-next-line no-console
console.log(`Couldn't find grant: ${fullGrant.number}. Exiting...`);
process.exit(1);
}
Expand Down Expand Up @@ -190,6 +190,7 @@ export default async function importGoals(file, region) {
ignoreDuplicates: true,
});
} catch (err) {
// eslint-disable-next-line no-console
console.log(err);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Op } from 'sequelize';
import importGoals from './importPlanGoals';
import { processFiles } from '../src/lib/updateGrantsGrantees';
import { processFiles } from '../lib/updateGrantsGrantees';
import db, {
Role, Topic, RoleTopic, Goal, Grantee, Grant,
} from '../src/models';
} from '../models';

describe('Import TTA plan goals', () => {
afterAll(() => {
Expand Down
File renamed without changes.

0 comments on commit 4d8bf0a

Please sign in to comment.