From 88004a6138b82a85bc725c2e8fa20c83a281f528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Mon, 2 Jan 2023 10:49:57 +0000 Subject: [PATCH] feat: allows creation of PATs for other users (#2718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://linear.app/unleash/issue/2-530/api-allow-creation-of-pats-for-other-users ![image](https://user-images.githubusercontent.com/14320932/208720680-5d5ccee7-1972-4f5b-8024-3a69d50a571f.png) Adds and takes into account the following permissions: - **READ_USER_PAT**; - **CREATE_USER_PAT**; - **DELETE_USER_PAT**; API only, will make some exploration on UI soon. Co-authored-by: Gastón Fournier --- .../providers/AccessProvider/permissions.ts | 3 +++ .../__snapshots__/create-config.test.ts.snap | 2 ++ src/lib/routes/admin-api/user/pat.ts | 8 +++++-- src/lib/services/pat-service.ts | 16 ++++++++------ src/lib/types/experimental.ts | 4 ++++ src/lib/types/permissions.ts | 3 +++ .../20221220160345-user-pat-permissions.js | 21 +++++++++++++++++++ src/server-dev.ts | 2 ++ 8 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/migrations/20221220160345-user-pat-permissions.js diff --git a/frontend/src/component/providers/AccessProvider/permissions.ts b/frontend/src/component/providers/AccessProvider/permissions.ts index 1e7ee2c0c4..1382b050ab 100644 --- a/frontend/src/component/providers/AccessProvider/permissions.ts +++ b/frontend/src/component/providers/AccessProvider/permissions.ts @@ -36,3 +36,6 @@ export const DELETE_SEGMENT = 'DELETE_SEGMENT'; export const APPLY_CHANGE_REQUEST = 'APPLY_CHANGE_REQUEST'; export const APPROVE_CHANGE_REQUEST = 'APPROVE_CHANGE_REQUEST'; export const SKIP_CHANGE_REQUEST = 'SKIP_CHANGE_REQUEST'; +export const READ_USER_PAT = 'READ_USER_PAT'; +export const CREATE_USER_PAT = 'CREATE_USER_PAT'; +export const DELETE_USER_PAT = 'DELETE_USER_PAT'; diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index a70140e103..14685cbbad 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -80,6 +80,7 @@ exports[`should create default config 1`] = ` "networkView": false, "proxyReturnAllToggles": false, "responseTimeWithAppName": false, + "serviceAccounts": false, "variantsPerEnvironment": false, }, }, @@ -98,6 +99,7 @@ exports[`should create default config 1`] = ` "networkView": false, "proxyReturnAllToggles": false, "responseTimeWithAppName": false, + "serviceAccounts": false, "variantsPerEnvironment": false, }, "externalResolver": { diff --git a/src/lib/routes/admin-api/user/pat.ts b/src/lib/routes/admin-api/user/pat.ts index aebd1d18b2..8d4bb8606b 100644 --- a/src/lib/routes/admin-api/user/pat.ts +++ b/src/lib/routes/admin-api/user/pat.ts @@ -78,7 +78,11 @@ export default class PatController extends Controller { async createPat(req: IAuthRequest, res: Response): Promise { const pat = req.body; - const createdPat = await this.patService.createPat(pat, req.user); + const createdPat = await this.patService.createPat( + pat, + req.user.id, + req.user, + ); this.openApiService.respondWithValidation( 201, res, @@ -88,7 +92,7 @@ export default class PatController extends Controller { } async getPats(req: IAuthRequest, res: Response): Promise { - const pats = await this.patService.getAll(req.user); + const pats = await this.patService.getAll(req.user.id); this.openApiService.respondWithValidation(200, res, patsSchema.$id, { pats: serializeDates(pats), }); diff --git a/src/lib/services/pat-service.ts b/src/lib/services/pat-service.ts index a26af6d317..5c0e70ac86 100644 --- a/src/lib/services/pat-service.ts +++ b/src/lib/services/pat-service.ts @@ -33,24 +33,28 @@ export default class PatService { this.eventStore = eventStore; } - async createPat(pat: IPat, user: User): Promise { - await this.validatePat(pat, user.id); + async createPat( + pat: IPat, + forUserId: number, + creator: User, + ): Promise { + await this.validatePat(pat, forUserId); pat.secret = this.generateSecretKey(); - pat.userId = user.id; + pat.userId = forUserId; const newPat = await this.patStore.create(pat); pat.secret = '***'; await this.eventStore.store({ type: PAT_CREATED, - createdBy: user.email || user.username, + createdBy: creator.email || creator.username, data: pat, }); return newPat; } - async getAll(user: User): Promise { - return this.patStore.getAllByUser(user.id); + async getAll(userId: number): Promise { + return this.patStore.getAllByUser(userId); } async deletePat(id: number, userId: number): Promise { diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 6189feafc1..daad973746 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -51,6 +51,10 @@ const flags = { process.env.UNLEASH_EXPERIMENTAL_MESSAGE_BANNER, false, ), + serviceAccounts: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_SERVICE_ACCOUNTS, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { diff --git a/src/lib/types/permissions.ts b/src/lib/types/permissions.ts index 00b3a98b6c..7c37d41134 100644 --- a/src/lib/types/permissions.ts +++ b/src/lib/types/permissions.ts @@ -42,3 +42,6 @@ export const DELETE_SEGMENT = 'DELETE_SEGMENT'; export const APPROVE_CHANGE_REQUEST = 'APPROVE_CHANGE_REQUEST'; export const APPLY_CHANGE_REQUEST = 'APPLY_CHANGE_REQUEST'; export const SKIP_CHANGE_REQUEST = 'SKIP_CHANGE_REQUEST'; +export const READ_USER_PAT = 'READ_USER_PAT'; +export const CREATE_USER_PAT = 'CREATE_USER_PAT'; +export const DELETE_USER_PAT = 'DELETE_USER_PAT'; diff --git a/src/migrations/20221220160345-user-pat-permissions.js b/src/migrations/20221220160345-user-pat-permissions.js new file mode 100644 index 0000000000..25cf51756b --- /dev/null +++ b/src/migrations/20221220160345-user-pat-permissions.js @@ -0,0 +1,21 @@ +exports.up = function (db, cb) { + db.runSql( + ` + INSERT INTO permissions (permission, display_name, type) VALUES ('READ_USER_PAT', 'Read PATs for a specific user', 'root'); + INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_USER_PAT', 'Create a PAT for a specific user', 'root'); + INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_USER_PAT', 'Delete a PAT for a specific user', 'root'); + `, + cb, + ); +}; + +exports.down = function (db, cb) { + db.runSql( + ` + DELETE FROM permissions WHERE permission = 'READ_USER_PAT'; + DELETE FROM permissions WHERE permission = 'CREATE_USER_PAT'; + DELETE FROM permissions WHERE permission = 'DELETE_USER_PAT'; + `, + cb, + ); +}; diff --git a/src/server-dev.ts b/src/server-dev.ts index 24881f79d7..f43af98678 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -41,6 +41,8 @@ process.nextTick(async () => { responseTimeWithAppName: true, changeRequests: true, variantsPerEnvironment: true, + maintenance: false, + serviceAccounts: true, }, }, authentication: {