1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00

feat: allows creation of PATs for other users (#2718)

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 <gaston@getunleash.ai>
This commit is contained in:
Nuno Góis 2023-01-02 10:49:57 +00:00 committed by GitHub
parent aab809cac3
commit 88004a6138
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 51 additions and 8 deletions

View File

@ -36,3 +36,6 @@ export const DELETE_SEGMENT = 'DELETE_SEGMENT';
export const APPLY_CHANGE_REQUEST = 'APPLY_CHANGE_REQUEST'; export const APPLY_CHANGE_REQUEST = 'APPLY_CHANGE_REQUEST';
export const APPROVE_CHANGE_REQUEST = 'APPROVE_CHANGE_REQUEST'; export const APPROVE_CHANGE_REQUEST = 'APPROVE_CHANGE_REQUEST';
export const SKIP_CHANGE_REQUEST = 'SKIP_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';

View File

@ -80,6 +80,7 @@ exports[`should create default config 1`] = `
"networkView": false, "networkView": false,
"proxyReturnAllToggles": false, "proxyReturnAllToggles": false,
"responseTimeWithAppName": false, "responseTimeWithAppName": false,
"serviceAccounts": false,
"variantsPerEnvironment": false, "variantsPerEnvironment": false,
}, },
}, },
@ -98,6 +99,7 @@ exports[`should create default config 1`] = `
"networkView": false, "networkView": false,
"proxyReturnAllToggles": false, "proxyReturnAllToggles": false,
"responseTimeWithAppName": false, "responseTimeWithAppName": false,
"serviceAccounts": false,
"variantsPerEnvironment": false, "variantsPerEnvironment": false,
}, },
"externalResolver": { "externalResolver": {

View File

@ -78,7 +78,11 @@ export default class PatController extends Controller {
async createPat(req: IAuthRequest, res: Response): Promise<void> { async createPat(req: IAuthRequest, res: Response): Promise<void> {
const pat = req.body; 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( this.openApiService.respondWithValidation(
201, 201,
res, res,
@ -88,7 +92,7 @@ export default class PatController extends Controller {
} }
async getPats(req: IAuthRequest, res: Response<PatSchema>): Promise<void> { async getPats(req: IAuthRequest, res: Response<PatSchema>): Promise<void> {
const pats = await this.patService.getAll(req.user); const pats = await this.patService.getAll(req.user.id);
this.openApiService.respondWithValidation(200, res, patsSchema.$id, { this.openApiService.respondWithValidation(200, res, patsSchema.$id, {
pats: serializeDates(pats), pats: serializeDates(pats),
}); });

View File

@ -33,24 +33,28 @@ export default class PatService {
this.eventStore = eventStore; this.eventStore = eventStore;
} }
async createPat(pat: IPat, user: User): Promise<IPat> { async createPat(
await this.validatePat(pat, user.id); pat: IPat,
forUserId: number,
creator: User,
): Promise<IPat> {
await this.validatePat(pat, forUserId);
pat.secret = this.generateSecretKey(); pat.secret = this.generateSecretKey();
pat.userId = user.id; pat.userId = forUserId;
const newPat = await this.patStore.create(pat); const newPat = await this.patStore.create(pat);
pat.secret = '***'; pat.secret = '***';
await this.eventStore.store({ await this.eventStore.store({
type: PAT_CREATED, type: PAT_CREATED,
createdBy: user.email || user.username, createdBy: creator.email || creator.username,
data: pat, data: pat,
}); });
return newPat; return newPat;
} }
async getAll(user: User): Promise<IPat[]> { async getAll(userId: number): Promise<IPat[]> {
return this.patStore.getAllByUser(user.id); return this.patStore.getAllByUser(userId);
} }
async deletePat(id: number, userId: number): Promise<void> { async deletePat(id: number, userId: number): Promise<void> {

View File

@ -51,6 +51,10 @@ const flags = {
process.env.UNLEASH_EXPERIMENTAL_MESSAGE_BANNER, process.env.UNLEASH_EXPERIMENTAL_MESSAGE_BANNER,
false, false,
), ),
serviceAccounts: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_SERVICE_ACCOUNTS,
false,
),
}; };
export const defaultExperimentalOptions: IExperimentalOptions = { export const defaultExperimentalOptions: IExperimentalOptions = {

View File

@ -42,3 +42,6 @@ export const DELETE_SEGMENT = 'DELETE_SEGMENT';
export const APPROVE_CHANGE_REQUEST = 'APPROVE_CHANGE_REQUEST'; export const APPROVE_CHANGE_REQUEST = 'APPROVE_CHANGE_REQUEST';
export const APPLY_CHANGE_REQUEST = 'APPLY_CHANGE_REQUEST'; export const APPLY_CHANGE_REQUEST = 'APPLY_CHANGE_REQUEST';
export const SKIP_CHANGE_REQUEST = 'SKIP_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';

View File

@ -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,
);
};

View File

@ -41,6 +41,8 @@ process.nextTick(async () => {
responseTimeWithAppName: true, responseTimeWithAppName: true,
changeRequests: true, changeRequests: true,
variantsPerEnvironment: true, variantsPerEnvironment: true,
maintenance: false,
serviceAccounts: true,
}, },
}, },
authentication: { authentication: {