|
|
|
@ -10,6 +10,7 @@ import {
|
|
|
|
|
Post,
|
|
|
|
|
Put,
|
|
|
|
|
Query,
|
|
|
|
|
UnauthorizedException,
|
|
|
|
|
UploadedFile,
|
|
|
|
|
UseGuards,
|
|
|
|
|
UseInterceptors,
|
|
|
|
@ -36,6 +37,7 @@ import { TokenService } from 'src/modules/utility/services/token.service';
|
|
|
|
|
import { PageOptions } from 'src/types/pagination.interfaces';
|
|
|
|
|
import { AdminService } from './admin.service';
|
|
|
|
|
import { OAuth2TokenService } from 'src/modules/objects/oauth2-token/oauth2-token.service';
|
|
|
|
|
import { Throttle } from '@nestjs/throttler';
|
|
|
|
|
|
|
|
|
|
const RELATIONS = ['urls', 'picture', 'owner'];
|
|
|
|
|
const SET_CLIENT_FIELDS = [
|
|
|
|
@ -66,22 +68,47 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Get('scopes')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async oauth2Scopes() {
|
|
|
|
|
return this._oaClient.availableScopes;
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async oauth2Scopes(@CurrentUser() user: User) {
|
|
|
|
|
const reducedPermissions = !this._service.userHasPrivilege(
|
|
|
|
|
user,
|
|
|
|
|
'admin:oauth2',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return reducedPermissions
|
|
|
|
|
? this._service.removeUnprivileged(this._oaClient.availableScopes)
|
|
|
|
|
: this._oaClient.availableScopes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Get('grants')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async oauth2Grants() {
|
|
|
|
|
return this._oaClient.availableGrantTypes;
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async oauth2Grants(@CurrentUser() user: User) {
|
|
|
|
|
const reducedPermissions = !this._service.userHasPrivilege(
|
|
|
|
|
user,
|
|
|
|
|
'admin:oauth2',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return reducedPermissions
|
|
|
|
|
? this._service.removeUnprivileged(this._oaClient.availableGrantTypes)
|
|
|
|
|
: this._oaClient.availableGrantTypes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Get('clients')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async oauth2ClientList(@Query() options: { q?: string } & PageOptions) {
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async oauth2ClientList(
|
|
|
|
|
@Query() options: { q?: string } & PageOptions,
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
if (!this._service.userHasPrivilege(user, 'admin:oauth2')) {
|
|
|
|
|
const unprivileged = await this._oaClient.getClientsByOwner(
|
|
|
|
|
user,
|
|
|
|
|
RELATIONS,
|
|
|
|
|
);
|
|
|
|
|
return unprivileged.map((item) => this._service.stripClientInfo(item));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const search = options.q ? decodeURIComponent(options.q) : null;
|
|
|
|
|
const resultCount = await this._oaClient.searchClientsCount(
|
|
|
|
|
search,
|
|
|
|
@ -108,29 +135,49 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Get('clients/:id')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async oauth2Client(@Param('id') id: string) {
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async oauth2Client(@Param('id') id: string, @CurrentUser() user: User) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), RELATIONS);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to view this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this._service.stripClientInfo(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Patch('clients/:id')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async updateOauth2Client(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Body() setter: Partial<OAuth2Client>,
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const allowedFieldsOnly = this._form.pluckObject(setter, SET_CLIENT_FIELDS);
|
|
|
|
|
|
|
|
|
|
// Non-admin cannot change the activated or verified status
|
|
|
|
|
if (!this._service.userHasPrivilege(user, 'admin:oauth2')) {
|
|
|
|
|
delete allowedFieldsOnly.activated;
|
|
|
|
|
delete allowedFieldsOnly.verified;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Object.keys(allowedFieldsOnly).length) {
|
|
|
|
|
return this._service.stripClientInfo(client);
|
|
|
|
|
}
|
|
|
|
@ -143,13 +190,19 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Post('clients/:id/new-secret')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async createNewSecret(@Param('id') id: string) {
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async createNewSecret(@Param('id') id: string, @CurrentUser() user: User) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client.client_secret = this._token.generateSecret();
|
|
|
|
|
await this._oaClient.updateClient(client);
|
|
|
|
|
|
|
|
|
@ -162,13 +215,22 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Delete('clients/:id/authorizations')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async deleteClientAuthorizations(@Param('id') id: string) {
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async deleteClientAuthorizations(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this._oaClient.wipeClientAuthorizations(client);
|
|
|
|
|
|
|
|
|
|
return this._service.stripClientInfo(client);
|
|
|
|
@ -176,21 +238,29 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Get('clients/:id/urls')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async oauth2ClientURLs(@Param('id') id: string) {
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async oauth2ClientURLs(@Param('id') id: string, @CurrentUser() user: User) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return client.urls;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Delete('clients/:id/urls/:url')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async deleteOAuth2ClientURL(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Param('url') urlId: string,
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
|
|
|
|
const parsedURLId = parseInt(urlId, 10);
|
|
|
|
@ -199,6 +269,12 @@ export class OAuth2AdminController {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!parsedURLId) {
|
|
|
|
|
throw new BadRequestException('Invalid URL ID');
|
|
|
|
|
}
|
|
|
|
@ -215,11 +291,12 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Put('clients/:id/urls/:url')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async updateOAuth2ClientURL(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Param('url') urlId: string,
|
|
|
|
|
@Body() setter: { url: string; type: string },
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
|
|
|
|
const plucked = this._form.pluckObject(setter, ['url', 'type']);
|
|
|
|
@ -229,6 +306,12 @@ export class OAuth2AdminController {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!parsedURLId) {
|
|
|
|
|
throw new BadRequestException('Invalid URL ID');
|
|
|
|
|
}
|
|
|
|
@ -249,16 +332,23 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Post('clients/:id/urls')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async createOAuth2ClientURL(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Body() setter: { url: string; type: string },
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!setter.url || !setter.type || !URL_TYPES.includes(setter.type)) {
|
|
|
|
|
throw new NotFoundException('Missing or invalid fields');
|
|
|
|
|
}
|
|
|
|
@ -272,9 +362,10 @@ export class OAuth2AdminController {
|
|
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Throttle(3, 60)
|
|
|
|
|
@Post('clients/:id/picture')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
@UseInterceptors(FileInterceptor('file'))
|
|
|
|
|
async uploadClientPictureFile(
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
@ -288,6 +379,12 @@ export class OAuth2AdminController {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
|
throw new BadRequestException('Picture upload failed');
|
|
|
|
|
}
|
|
|
|
@ -315,13 +412,22 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
@Delete('clients/:id/picture')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
async deleteClientPictureFile(@Param('id') id: string) {
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async deleteClientPictureFile(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const client = await this._oaClient.getById(parseInt(id, 10), ['picture']);
|
|
|
|
|
if (!client) {
|
|
|
|
|
throw new NotFoundException('Client not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._service.userCanEditClient(user, client)) {
|
|
|
|
|
throw new UnauthorizedException(
|
|
|
|
|
'You do not have permission to edit this client',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._oaClient.deletePicture(client);
|
|
|
|
|
client.picture = null;
|
|
|
|
|
await this._oaClient.updateClient(client);
|
|
|
|
@ -332,12 +438,16 @@ export class OAuth2AdminController {
|
|
|
|
|
// New client
|
|
|
|
|
@Post('/clients')
|
|
|
|
|
@Scopes('management')
|
|
|
|
|
@Privileges('admin', 'admin:oauth2')
|
|
|
|
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
|
|
|
|
async createNewClient(
|
|
|
|
|
@Body() setter: Partial<OAuth2Client>,
|
|
|
|
|
@CurrentUser() user: User,
|
|
|
|
|
) {
|
|
|
|
|
const allowedFieldsOnly = this._form.pluckObject(setter, SET_CLIENT_FIELDS);
|
|
|
|
|
const reducedPermissions = !this._service.userHasPrivilege(
|
|
|
|
|
user,
|
|
|
|
|
'admin:oauth2',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!Object.keys(allowedFieldsOnly).length) {
|
|
|
|
|
throw new BadRequestException('Required fields are missing');
|
|
|
|
@ -349,20 +459,21 @@ export class OAuth2AdminController {
|
|
|
|
|
|
|
|
|
|
const splitGrants = allowedFieldsOnly.grants.split(' ');
|
|
|
|
|
const splitScopes = allowedFieldsOnly.scope.split(' ');
|
|
|
|
|
let availableGrantTypes = this._oaClient.availableGrantTypes;
|
|
|
|
|
let availableScopes = this._oaClient.availableScopes;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!splitGrants.every((grant) =>
|
|
|
|
|
this._oaClient.availableGrantTypes.includes(grant),
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
if (reducedPermissions) {
|
|
|
|
|
availableGrantTypes =
|
|
|
|
|
this._service.removeUnprivileged(availableGrantTypes);
|
|
|
|
|
availableScopes = this._service.removeUnprivileged(availableScopes);
|
|
|
|
|
allowedFieldsOnly.activated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!splitGrants.every((grant) => availableGrantTypes.includes(grant))) {
|
|
|
|
|
throw new BadRequestException('Bad grant types');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!splitScopes.every((scope) =>
|
|
|
|
|
this._oaClient.availableScopes.includes(scope),
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
if (!splitScopes.every((scope) => availableScopes.includes(scope))) {
|
|
|
|
|
throw new BadRequestException('Bad scopes');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|