From b82e395a98ca690d4b948ce8499617d732cd389b Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Mon, 17 Mar 2025 17:51:27 +0100 Subject: [PATCH] Generate security token for user --- apps/api/src/app/user/user.controller.ts | 15 +++++++- apps/api/src/app/user/user.service.ts | 34 ++++++++++++------- .../admin-users/admin-users.component.ts | 32 +++++++++++++---- .../components/admin-users/admin-users.html | 9 +++++ apps/client/src/app/services/data.service.ts | 8 +++++ .../src/app/services/user/user.service.ts | 4 +-- .../lib/interfaces/access-token.interface.ts | 3 ++ libs/common/src/lib/interfaces/index.ts | 2 ++ 8 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 libs/common/src/lib/interfaces/access-token.interface.ts diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index 149e06285..f7984074f 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -2,7 +2,7 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; -import { User, UserSettings } from '@ghostfolio/common/interfaces'; +import { AccessToken, User, UserSettings } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -123,6 +123,19 @@ export class UserController { }; } + @Post(':id/security-token') + @HasPermission(permissions.accessAdminControl) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async generateSecurityToken( + @Param('id') id: string + ): Promise { + const accessToken = await this.userService.generateAccessToken({ + userId: id + }); + + return { accessToken }; + } + @Put('setting') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async updateUserSetting(@Body() data: UpdateUserSettingDto) { diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 40bc1b2b5..09d9b2184 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -433,7 +433,7 @@ export class UserService { data.provider = 'ANONYMOUS'; } - let user = await this.prismaService.user.create({ + const user = await this.prismaService.user.create({ data: { ...data, Account: { @@ -464,17 +464,7 @@ export class UserService { } if (data.provider === 'ANONYMOUS') { - const accessToken = this.createAccessToken(user.id, getRandomString(10)); - - const hashedAccessToken = this.createAccessToken( - accessToken, - this.configurationService.get('ACCESS_TOKEN_SALT') - ); - - user = await this.prismaService.user.update({ - data: { accessToken: hashedAccessToken }, - where: { id: user.id } - }); + const accessToken = await this.generateAccessToken({ userId: user.id }); return { ...user, accessToken }; } @@ -581,4 +571,24 @@ export class UserService { return settings; } + + public async generateAccessToken({ + userId + }: { + userId: string; + }): Promise { + const accessToken = this.createAccessToken(userId, getRandomString(10)); + + const hashedAccessToken = this.createAccessToken( + accessToken, + this.configurationService.get('ACCESS_TOKEN_SALT') + ); + + await this.prismaService.user.update({ + data: { accessToken: hashedAccessToken }, + where: { id: userId } + }); + + return accessToken; + } } diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index 50b8cb5f3..ac8f7dca0 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -1,9 +1,3 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; -import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; -import { AdminService } from '@ghostfolio/client/services/admin.service'; -import { DataService } from '@ghostfolio/client/services/data.service'; -import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; -import { UserService } from '@ghostfolio/client/services/user/user.service'; import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { getDateFormatString, getEmojiFlag } from '@ghostfolio/common/helper'; import { AdminUsers, InfoItem, User } from '@ghostfolio/common/interfaces'; @@ -26,6 +20,13 @@ import { import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { ConfirmationDialogType } from '../../core/notification/confirmation-dialog/confirmation-dialog.type'; +import { NotificationService } from '../../core/notification/notification.service'; +import { AdminService } from '../../services/admin.service'; +import { DataService } from '../../services/data.service'; +import { ImpersonationStorageService } from '../../services/impersonation-storage.service'; +import { UserService } from '../../services/user/user.service'; + @Component({ selector: 'gf-admin-users', styleUrls: ['./admin-users.scss'], @@ -140,6 +141,25 @@ export class AdminUsersComponent implements OnDestroy, OnInit { }); } + public onGenerateSecurityToken(aId: string) { + this.notificationService.confirm({ + confirmFn: () => { + this.dataService + .generateSecurityToken(aId) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ accessToken }) => { + this.notificationService.prompt({ + confirmFn: () => {}, + defaultValue: accessToken, + title: $localize`Security token` + }); + }); + }, + confirmType: ConfirmationDialogType.Warn, + title: $localize`Do you really want to generate a new security token for this user?` + }); + } + public onImpersonateUser(aId: string) { if (aId) { this.impersonationStorageService.setId(aId); diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index ca8ef0558..3424463e7 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -241,6 +241,15 @@
} +