separate role for user oauth2 clients
This commit is contained in:
parent
e884ce80fc
commit
7a06c882ac
@ -4,5 +4,5 @@ import { SetMetadata } from '@nestjs/common';
|
|||||||
* Restrict this route to only these privileges. AND logic!
|
* Restrict this route to only these privileges. AND logic!
|
||||||
* @param privileges List of privileges for this route
|
* @param privileges List of privileges for this route
|
||||||
*/
|
*/
|
||||||
export const Privileges = (...privileges: string[]) =>
|
export const Privileges = (...privileges: (string | string[])[]) =>
|
||||||
SetMetadata('privileges', privileges);
|
SetMetadata('privileges', privileges);
|
||||||
|
@ -9,7 +9,7 @@ export class PrivilegesGuard implements CanActivate {
|
|||||||
constructor(private reflector: Reflector) {}
|
constructor(private reflector: Reflector) {}
|
||||||
|
|
||||||
canActivate(context: ExecutionContext): boolean {
|
canActivate(context: ExecutionContext): boolean {
|
||||||
const privileges = this.reflector.get<string[]>(
|
const privileges = this.reflector.get<(string | string[])[]>(
|
||||||
'privileges',
|
'privileges',
|
||||||
context.getHandler(),
|
context.getHandler(),
|
||||||
);
|
);
|
||||||
@ -18,6 +18,20 @@ export class PrivilegesGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
const user = request.user;
|
const user = request.user;
|
||||||
|
|
||||||
|
const withOrLogic = privileges.some((entry) => Array.isArray(entry));
|
||||||
|
if (withOrLogic) {
|
||||||
|
return privileges.some((entry) => {
|
||||||
|
if (Array.isArray(entry)) {
|
||||||
|
return entry.every((item) =>
|
||||||
|
user.privileges.find(({ name }) => name === item),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return user.privileges.find(({ name }) => name === entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return privileges.every((item) =>
|
return privileges.every((item) =>
|
||||||
user.privileges.find(({ name }) => name === item),
|
user.privileges.find(({ name }) => name === item),
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Client } from 'connect-redis';
|
||||||
import { OAuth2Client } from 'src/modules/objects/oauth2-client/oauth2-client.entity';
|
import { OAuth2Client } from 'src/modules/objects/oauth2-client/oauth2-client.entity';
|
||||||
|
import { User } from 'src/modules/objects/user/user.entity';
|
||||||
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
|
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
|
||||||
|
|
||||||
|
const UNPRIVILEGED_STRIP = ['openid', 'id_token', 'management', 'implicit'];
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
constructor(private _form: FormUtilityService) {}
|
constructor(private _form: FormUtilityService) {}
|
||||||
@ -14,4 +18,26 @@ export class AdminService {
|
|||||||
: null,
|
: null,
|
||||||
} as Partial<OAuth2Client>;
|
} as Partial<OAuth2Client>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public userHasPrivilege(user: User, privilege: string): boolean {
|
||||||
|
return user.privileges.some(({ name }) => name === privilege);
|
||||||
|
}
|
||||||
|
|
||||||
|
public userCanEditClient(user: User, client: Client): boolean {
|
||||||
|
if (this.userHasPrivilege(user, 'admin:oauth2')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.owner?.id === user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeUnprivileged(input: string[]): string[] {
|
||||||
|
return input.reduce((list, current) => {
|
||||||
|
if (UNPRIVILEGED_STRIP.includes(current)) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...list, current];
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
|
UnauthorizedException,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
@ -36,6 +37,7 @@ import { TokenService } from 'src/modules/utility/services/token.service';
|
|||||||
import { PageOptions } from 'src/types/pagination.interfaces';
|
import { PageOptions } from 'src/types/pagination.interfaces';
|
||||||
import { AdminService } from './admin.service';
|
import { AdminService } from './admin.service';
|
||||||
import { OAuth2TokenService } from 'src/modules/objects/oauth2-token/oauth2-token.service';
|
import { OAuth2TokenService } from 'src/modules/objects/oauth2-token/oauth2-token.service';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
const RELATIONS = ['urls', 'picture', 'owner'];
|
const RELATIONS = ['urls', 'picture', 'owner'];
|
||||||
const SET_CLIENT_FIELDS = [
|
const SET_CLIENT_FIELDS = [
|
||||||
@ -66,22 +68,47 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Get('scopes')
|
@Get('scopes')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async oauth2Scopes() {
|
async oauth2Scopes(@CurrentUser() user: User) {
|
||||||
return this._oaClient.availableScopes;
|
const reducedPermissions = !this._service.userHasPrivilege(
|
||||||
|
user,
|
||||||
|
'admin:oauth2',
|
||||||
|
);
|
||||||
|
|
||||||
|
return reducedPermissions
|
||||||
|
? this._service.removeUnprivileged(this._oaClient.availableScopes)
|
||||||
|
: this._oaClient.availableScopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('grants')
|
@Get('grants')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async oauth2Grants() {
|
async oauth2Grants(@CurrentUser() user: User) {
|
||||||
return this._oaClient.availableGrantTypes;
|
const reducedPermissions = !this._service.userHasPrivilege(
|
||||||
|
user,
|
||||||
|
'admin:oauth2',
|
||||||
|
);
|
||||||
|
|
||||||
|
return reducedPermissions
|
||||||
|
? this._service.removeUnprivileged(this._oaClient.availableGrantTypes)
|
||||||
|
: this._oaClient.availableGrantTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('clients')
|
@Get('clients')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async oauth2ClientList(@Query() options: { q?: string } & PageOptions) {
|
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 search = options.q ? decodeURIComponent(options.q) : null;
|
||||||
const resultCount = await this._oaClient.searchClientsCount(
|
const resultCount = await this._oaClient.searchClientsCount(
|
||||||
search,
|
search,
|
||||||
@ -108,29 +135,49 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Get('clients/:id')
|
@Get('clients/:id')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async oauth2Client(@Param('id') id: string) {
|
async oauth2Client(@Param('id') id: string, @CurrentUser() user: User) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), RELATIONS);
|
const client = await this._oaClient.getById(parseInt(id, 10), RELATIONS);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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);
|
return this._service.stripClientInfo(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('clients/:id')
|
@Patch('clients/:id')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async updateOauth2Client(
|
async updateOauth2Client(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Body() setter: Partial<OAuth2Client>,
|
@Body() setter: Partial<OAuth2Client>,
|
||||||
|
@CurrentUser() user: User,
|
||||||
) {
|
) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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);
|
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) {
|
if (!Object.keys(allowedFieldsOnly).length) {
|
||||||
return this._service.stripClientInfo(client);
|
return this._service.stripClientInfo(client);
|
||||||
}
|
}
|
||||||
@ -143,13 +190,19 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Post('clients/:id/new-secret')
|
@Post('clients/:id/new-secret')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async createNewSecret(@Param('id') id: string) {
|
async createNewSecret(@Param('id') id: string, @CurrentUser() user: User) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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();
|
client.client_secret = this._token.generateSecret();
|
||||||
await this._oaClient.updateClient(client);
|
await this._oaClient.updateClient(client);
|
||||||
|
|
||||||
@ -162,13 +215,22 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Delete('clients/:id/authorizations')
|
@Delete('clients/:id/authorizations')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async deleteClientAuthorizations(@Param('id') id: string) {
|
async deleteClientAuthorizations(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user: User,
|
||||||
|
) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
const client = await this._oaClient.getById(parseInt(id, 10), []);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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);
|
await this._oaClient.wipeClientAuthorizations(client);
|
||||||
|
|
||||||
return this._service.stripClientInfo(client);
|
return this._service.stripClientInfo(client);
|
||||||
@ -176,21 +238,29 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Get('clients/:id/urls')
|
@Get('clients/:id/urls')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async oauth2ClientURLs(@Param('id') id: string) {
|
async oauth2ClientURLs(@Param('id') id: string, @CurrentUser() user: User) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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;
|
return client.urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete('clients/:id/urls/:url')
|
@Delete('clients/:id/urls/:url')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async deleteOAuth2ClientURL(
|
async deleteOAuth2ClientURL(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Param('url') urlId: string,
|
@Param('url') urlId: string,
|
||||||
|
@CurrentUser() user: User,
|
||||||
) {
|
) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
||||||
const parsedURLId = parseInt(urlId, 10);
|
const parsedURLId = parseInt(urlId, 10);
|
||||||
@ -199,6 +269,12 @@ export class OAuth2AdminController {
|
|||||||
throw new NotFoundException('Client not found');
|
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) {
|
if (!parsedURLId) {
|
||||||
throw new BadRequestException('Invalid URL ID');
|
throw new BadRequestException('Invalid URL ID');
|
||||||
}
|
}
|
||||||
@ -215,11 +291,12 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Put('clients/:id/urls/:url')
|
@Put('clients/:id/urls/:url')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async updateOAuth2ClientURL(
|
async updateOAuth2ClientURL(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Param('url') urlId: string,
|
@Param('url') urlId: string,
|
||||||
@Body() setter: { url: string; type: string },
|
@Body() setter: { url: string; type: string },
|
||||||
|
@CurrentUser() user: User,
|
||||||
) {
|
) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
||||||
const plucked = this._form.pluckObject(setter, ['url', 'type']);
|
const plucked = this._form.pluckObject(setter, ['url', 'type']);
|
||||||
@ -229,6 +306,12 @@ export class OAuth2AdminController {
|
|||||||
throw new NotFoundException('Client not found');
|
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) {
|
if (!parsedURLId) {
|
||||||
throw new BadRequestException('Invalid URL ID');
|
throw new BadRequestException('Invalid URL ID');
|
||||||
}
|
}
|
||||||
@ -249,16 +332,23 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Post('clients/:id/urls')
|
@Post('clients/:id/urls')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async createOAuth2ClientURL(
|
async createOAuth2ClientURL(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Body() setter: { url: string; type: string },
|
@Body() setter: { url: string; type: string },
|
||||||
|
@CurrentUser() user: User,
|
||||||
) {
|
) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
const client = await this._oaClient.getById(parseInt(id, 10), ['urls']);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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)) {
|
if (!setter.url || !setter.type || !URL_TYPES.includes(setter.type)) {
|
||||||
throw new NotFoundException('Missing or invalid fields');
|
throw new NotFoundException('Missing or invalid fields');
|
||||||
}
|
}
|
||||||
@ -272,9 +362,10 @@ export class OAuth2AdminController {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throttle(3, 60)
|
||||||
@Post('clients/:id/picture')
|
@Post('clients/:id/picture')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
@UseInterceptors(FileInterceptor('file'))
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
async uploadClientPictureFile(
|
async uploadClientPictureFile(
|
||||||
@CurrentUser() user: User,
|
@CurrentUser() user: User,
|
||||||
@ -288,6 +379,12 @@ export class OAuth2AdminController {
|
|||||||
throw new NotFoundException('Client not found');
|
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) {
|
if (!file) {
|
||||||
throw new BadRequestException('Picture upload failed');
|
throw new BadRequestException('Picture upload failed');
|
||||||
}
|
}
|
||||||
@ -315,13 +412,22 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
@Delete('clients/:id/picture')
|
@Delete('clients/:id/picture')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async deleteClientPictureFile(@Param('id') id: string) {
|
async deleteClientPictureFile(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user: User,
|
||||||
|
) {
|
||||||
const client = await this._oaClient.getById(parseInt(id, 10), ['picture']);
|
const client = await this._oaClient.getById(parseInt(id, 10), ['picture']);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new NotFoundException('Client not found');
|
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);
|
this._oaClient.deletePicture(client);
|
||||||
client.picture = null;
|
client.picture = null;
|
||||||
await this._oaClient.updateClient(client);
|
await this._oaClient.updateClient(client);
|
||||||
@ -332,12 +438,16 @@ export class OAuth2AdminController {
|
|||||||
// New client
|
// New client
|
||||||
@Post('/clients')
|
@Post('/clients')
|
||||||
@Scopes('management')
|
@Scopes('management')
|
||||||
@Privileges('admin', 'admin:oauth2')
|
@Privileges(['admin', 'admin:oauth2'], 'self:oauth2')
|
||||||
async createNewClient(
|
async createNewClient(
|
||||||
@Body() setter: Partial<OAuth2Client>,
|
@Body() setter: Partial<OAuth2Client>,
|
||||||
@CurrentUser() user: User,
|
@CurrentUser() user: User,
|
||||||
) {
|
) {
|
||||||
const allowedFieldsOnly = this._form.pluckObject(setter, SET_CLIENT_FIELDS);
|
const allowedFieldsOnly = this._form.pluckObject(setter, SET_CLIENT_FIELDS);
|
||||||
|
const reducedPermissions = !this._service.userHasPrivilege(
|
||||||
|
user,
|
||||||
|
'admin:oauth2',
|
||||||
|
);
|
||||||
|
|
||||||
if (!Object.keys(allowedFieldsOnly).length) {
|
if (!Object.keys(allowedFieldsOnly).length) {
|
||||||
throw new BadRequestException('Required fields are missing');
|
throw new BadRequestException('Required fields are missing');
|
||||||
@ -349,20 +459,21 @@ export class OAuth2AdminController {
|
|||||||
|
|
||||||
const splitGrants = allowedFieldsOnly.grants.split(' ');
|
const splitGrants = allowedFieldsOnly.grants.split(' ');
|
||||||
const splitScopes = allowedFieldsOnly.scope.split(' ');
|
const splitScopes = allowedFieldsOnly.scope.split(' ');
|
||||||
|
let availableGrantTypes = this._oaClient.availableGrantTypes;
|
||||||
|
let availableScopes = this._oaClient.availableScopes;
|
||||||
|
|
||||||
if (
|
if (reducedPermissions) {
|
||||||
!splitGrants.every((grant) =>
|
availableGrantTypes =
|
||||||
this._oaClient.availableGrantTypes.includes(grant),
|
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');
|
throw new BadRequestException('Bad grant types');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!splitScopes.every((scope) => availableScopes.includes(scope))) {
|
||||||
!splitScopes.every((scope) =>
|
|
||||||
this._oaClient.availableScopes.includes(scope),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new BadRequestException('Bad scopes');
|
throw new BadRequestException('Bad scopes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +159,9 @@ export class OAuth2ClientService {
|
|||||||
{
|
{
|
||||||
title: ILike(`%${search}%`),
|
title: ILike(`%${search}%`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
client_id: search,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
: undefined,
|
: undefined,
|
||||||
skip: offset,
|
skip: offset,
|
||||||
@ -177,12 +180,25 @@ export class OAuth2ClientService {
|
|||||||
{
|
{
|
||||||
title: ILike(`%${search}%`),
|
title: ILike(`%${search}%`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
client_id: search,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
: undefined,
|
: undefined,
|
||||||
relations,
|
relations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getClientsByOwner(
|
||||||
|
owner: User,
|
||||||
|
relations?: string[],
|
||||||
|
): Promise<OAuth2Client[]> {
|
||||||
|
return this.clientRepository.find({
|
||||||
|
where: { owner: { id: owner.id } },
|
||||||
|
relations,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getClientURLs(
|
public async getClientURLs(
|
||||||
id: string,
|
id: string,
|
||||||
type?: OAuth2ClientURLType,
|
type?: OAuth2ClientURLType,
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { FileInterceptor } from '@nestjs/platform-express';
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { unlink } from 'fs/promises';
|
import { unlink } from 'fs/promises';
|
||||||
import { OAuth2ClientService } from 'src/modules/objects/oauth2-client/oauth2-client.service';
|
import { OAuth2ClientService } from 'src/modules/objects/oauth2-client/oauth2-client.service';
|
||||||
@ -83,6 +84,7 @@ export class SettingsController {
|
|||||||
res.redirect('/account/general');
|
res.redirect('/account/general');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throttle(3, 60)
|
||||||
@Post('avatar')
|
@Post('avatar')
|
||||||
@UseInterceptors(FileInterceptor('file'))
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
async uploadAvatarFile(
|
async uploadAvatarFile(
|
||||||
|
Reference in New Issue
Block a user