Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Personal access tokens backend (#2064)
* First version ready * Final * Refactor * Update pat store * Website revert * Website revert * Update * Revert website * Revert docs to main * Revert docs to main * Fix eslint * Test * Fix table name
- Loading branch information
Showing
26 changed files
with
664 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
CHANGELOG.md | ||
CHANGELOG.md | ||
website/docs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { Knex } from 'knex'; | ||
import { Logger, LogProvider } from '../logger'; | ||
import { IPatStore } from '../types/stores/pat-store'; | ||
import Pat, { IPat } from '../types/models/pat'; | ||
import NotFoundError from '../error/notfound-error'; | ||
|
||
const TABLE = 'personal_access_tokens'; | ||
|
||
const PAT_COLUMNS = [ | ||
'secret', | ||
'user_id', | ||
'expires_at', | ||
'created_at', | ||
'seen_at', | ||
]; | ||
|
||
const fromRow = (row) => { | ||
if (!row) { | ||
throw new NotFoundError('No PAT found'); | ||
} | ||
return new Pat({ | ||
secret: row.secret, | ||
userId: row.user_id, | ||
createdAt: row.created_at, | ||
seenAt: row.seen_at, | ||
expiresAt: row.expires_at, | ||
}); | ||
}; | ||
|
||
const toRow = (user: IPat) => ({ | ||
secret: user.secret, | ||
user_id: user.userId, | ||
expires_at: user.expiresAt, | ||
}); | ||
|
||
export default class PatStore implements IPatStore { | ||
private db: Knex; | ||
|
||
private logger: Logger; | ||
|
||
constructor(db: Knex, getLogger: LogProvider) { | ||
this.db = db; | ||
this.logger = getLogger('pat-store.ts'); | ||
} | ||
|
||
async create(token: IPat): Promise<IPat> { | ||
const row = await this.db(TABLE).insert(toRow(token)).returning('*'); | ||
return fromRow(row[0]); | ||
} | ||
|
||
async delete(secret: string): Promise<void> { | ||
return this.db(TABLE).where({ secret: secret }).del(); | ||
} | ||
|
||
async deleteAll(): Promise<void> { | ||
await this.db(TABLE).del(); | ||
} | ||
|
||
destroy(): void {} | ||
|
||
async exists(secret: string): Promise<boolean> { | ||
const result = await this.db.raw( | ||
`SELECT EXISTS(SELECT 1 FROM ${TABLE} WHERE secret = ?) AS present`, | ||
[secret], | ||
); | ||
const { present } = result.rows[0]; | ||
return present; | ||
} | ||
|
||
async get(secret: string): Promise<Pat> { | ||
const row = await this.db(TABLE).where({ secret }).first(); | ||
return fromRow(row); | ||
} | ||
|
||
async getAll(): Promise<Pat[]> { | ||
const groups = await this.db.select(PAT_COLUMNS).from(TABLE); | ||
return groups.map(fromRow); | ||
} | ||
|
||
async getAllByUser(userId: number): Promise<Pat[]> { | ||
const groups = await this.db | ||
.select(PAT_COLUMNS) | ||
.from(TABLE) | ||
.where('user_id', userId); | ||
return groups.map(fromRow); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { FromSchema } from 'json-schema-to-ts'; | ||
|
||
export const patSchema = { | ||
$id: '#/components/schemas/patSchema', | ||
type: 'object', | ||
properties: { | ||
secret: { | ||
type: 'string', | ||
}, | ||
expiresAt: { | ||
type: 'string', | ||
format: 'date-time', | ||
nullable: true, | ||
}, | ||
createdAt: { | ||
type: 'string', | ||
format: 'date-time', | ||
nullable: true, | ||
}, | ||
seenAt: { | ||
type: 'string', | ||
format: 'date-time', | ||
nullable: true, | ||
}, | ||
}, | ||
components: { | ||
schemas: {}, | ||
}, | ||
} as const; | ||
|
||
export type PatSchema = FromSchema<typeof patSchema>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { FromSchema } from 'json-schema-to-ts'; | ||
import { patSchema } from './pat-schema'; | ||
|
||
export const patsSchema = { | ||
$id: '#/components/schemas/patsSchema', | ||
type: 'object', | ||
properties: { | ||
pats: { | ||
type: 'array', | ||
items: { | ||
$ref: '#/components/schemas/patSchema', | ||
}, | ||
}, | ||
}, | ||
components: { | ||
schemas: { | ||
patSchema, | ||
}, | ||
}, | ||
} as const; | ||
|
||
export type PatsSchema = FromSchema<typeof patsSchema>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { Response } from 'express'; | ||
import Controller from '../../controller'; | ||
import { Logger } from '../../../logger'; | ||
import { IUnleashConfig, IUnleashServices } from '../../../types'; | ||
import { createRequestSchema } from '../../../openapi/util/create-request-schema'; | ||
import { createResponseSchema } from '../../../openapi/util/create-response-schema'; | ||
import { OpenApiService } from '../../../services/openapi-service'; | ||
import { emptyResponse } from '../../../openapi/util/standard-responses'; | ||
|
||
import PatService from '../../../services/pat-service'; | ||
import { NONE } from '../../../types/permissions'; | ||
import { IAuthRequest } from '../../unleash-types'; | ||
import { serializeDates } from '../../../types/serialize-dates'; | ||
import { PatSchema, patSchema } from '../../../openapi/spec/pat-schema'; | ||
import { patsSchema } from '../../../openapi/spec/pats-schema'; | ||
|
||
export default class PatController extends Controller { | ||
private patService: PatService; | ||
|
||
private openApiService: OpenApiService; | ||
|
||
private logger: Logger; | ||
|
||
constructor( | ||
config: IUnleashConfig, | ||
{ | ||
openApiService, | ||
patService, | ||
}: Pick<IUnleashServices, 'openApiService' | 'patService'>, | ||
) { | ||
super(config); | ||
this.logger = config.getLogger('lib/routes/auth/pat-controller.ts'); | ||
this.openApiService = openApiService; | ||
this.patService = patService; | ||
this.route({ | ||
method: 'get', | ||
path: '', | ||
handler: this.getPats, | ||
permission: NONE, | ||
middleware: [ | ||
openApiService.validPath({ | ||
tags: ['admin'], | ||
operationId: 'getPats', | ||
responses: { 200: createResponseSchema('patsSchema') }, | ||
}), | ||
], | ||
}); | ||
this.route({ | ||
method: 'post', | ||
path: '', | ||
handler: this.createPat, | ||
permission: NONE, | ||
middleware: [ | ||
openApiService.validPath({ | ||
tags: ['admin'], | ||
operationId: 'createPat', | ||
requestBody: createRequestSchema('patSchema'), | ||
responses: { 200: createResponseSchema('patSchema') }, | ||
}), | ||
], | ||
}); | ||
|
||
this.route({ | ||
method: 'delete', | ||
path: '/:secret', | ||
acceptAnyContentType: true, | ||
handler: this.deletePat, | ||
permission: NONE, | ||
middleware: [ | ||
openApiService.validPath({ | ||
tags: ['admin'], | ||
operationId: 'deletePat', | ||
responses: { 200: emptyResponse }, | ||
}), | ||
], | ||
}); | ||
} | ||
|
||
async createPat(req: IAuthRequest, res: Response): Promise<void> { | ||
const pat = req.body; | ||
const createdPat = await this.patService.createPat(pat, req.user); | ||
this.openApiService.respondWithValidation( | ||
201, | ||
res, | ||
patSchema.$id, | ||
serializeDates(createdPat), | ||
); | ||
} | ||
|
||
async getPats(req: IAuthRequest, res: Response<PatSchema>): Promise<void> { | ||
const pats = await this.patService.getAll(req.user); | ||
this.openApiService.respondWithValidation(200, res, patsSchema.$id, { | ||
pats: serializeDates(pats), | ||
}); | ||
} | ||
|
||
async deletePat( | ||
req: IAuthRequest<{ secret: string }>, | ||
res: Response, | ||
): Promise<void> { | ||
const { secret } = req.params; | ||
await this.patService.deletePat(secret); | ||
res.status(200).end(); | ||
} | ||
} |
10 changes: 5 additions & 5 deletions
10
src/lib/routes/admin-api/user.test.ts → src/lib/routes/admin-api/user/user.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 17 additions & 17 deletions
34
src/lib/routes/admin-api/user.ts → src/lib/routes/admin-api/user/user.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.