Browse Source

Minor improvements

pull/4458/head
Thomas Kaul 3 weeks ago
parent
commit
a44897a539
  1. 8
      apps/api/src/app/auth/auth.service.ts
  2. 22
      apps/api/src/app/user/user.controller.ts
  3. 49
      apps/api/src/app/user/user.service.ts
  4. 18
      apps/client/src/app/components/admin-users/admin-users.component.ts
  5. 2
      apps/client/src/app/components/admin-users/admin-users.html
  6. 4
      apps/client/src/app/services/data.service.ts

8
apps/api/src/app/auth/auth.service.ts

@ -20,10 +20,10 @@ export class AuthService {
public async validateAnonymousLogin(accessToken: string): Promise<string> {
return new Promise(async (resolve, reject) => {
try {
const hashedAccessToken = this.userService.createAccessToken(
accessToken,
this.configurationService.get('ACCESS_TOKEN_SALT')
);
const hashedAccessToken = this.userService.createAccessToken({
password: accessToken,
salt: this.configurationService.get('ACCESS_TOKEN_SALT')
});
const [user] = await this.userService.users({
where: { accessToken: hashedAccessToken }

22
apps/api/src/app/user/user.controller.ts

@ -1,6 +1,7 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import {
AccessTokenResponse,
@ -40,6 +41,7 @@ export class UserController {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly jwtService: JwtService,
private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService,
@Inject(REQUEST) private readonly request: RequestWithUser,
private readonly userService: UserService
@ -51,10 +53,10 @@ export class UserController {
public async deleteOwnUser(
@Body() data: DeleteOwnUserDto
): Promise<UserModel> {
const hashedAccessToken = this.userService.createAccessToken(
data.accessToken,
this.configurationService.get('ACCESS_TOKEN_SALT')
);
const hashedAccessToken = this.userService.createAccessToken({
password: data.accessToken,
salt: this.configurationService.get('ACCESS_TOKEN_SALT')
});
const [user] = await this.userService.users({
where: { accessToken: hashedAccessToken, id: this.request.user.id }
@ -89,14 +91,20 @@ export class UserController {
});
}
@Post(':id/access-token')
@HasPermission(permissions.accessAdminControl)
@Post(':id/access-token')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async generateAccessToken(
@Param('id') id: string
): Promise<AccessTokenResponse> {
const { accessToken } = await this.userService.generateAccessToken({
userId: id
const { accessToken, hashedAccessToken } =
this.userService.generateAccessToken({
userId: id
});
await this.prismaService.user.update({
data: { accessToken: hashedAccessToken },
where: { id }
});
return { accessToken };

49
apps/api/src/app/user/user.service.ts

@ -67,13 +67,33 @@ export class UserService {
return this.prismaService.user.count(args);
}
public createAccessToken(password: string, salt: string): string {
public createAccessToken({
password,
salt
}: {
password: string;
salt: string;
}): string {
const hash = createHmac('sha512', salt);
hash.update(password);
return hash.digest('hex');
}
public generateAccessToken({ userId }: { userId: string }) {
const accessToken = this.createAccessToken({
password: userId,
salt: getRandomString(10)
});
const hashedAccessToken = this.createAccessToken({
password: accessToken,
salt: this.configurationService.get('ACCESS_TOKEN_SALT')
});
return { accessToken, hashedAccessToken };
}
public async getUser(
{ Account, id, permissions, Settings, subscription }: UserWithSettings,
aLocale = locale
@ -464,10 +484,15 @@ export class UserService {
}
if (data.provider === 'ANONYMOUS') {
const { accessToken } = await this.generateAccessToken({
const { accessToken, hashedAccessToken } = this.generateAccessToken({
userId: user.id
});
await this.prismaService.user.update({
data: { accessToken: hashedAccessToken },
where: { id: user.id }
});
return { ...user, accessToken };
}
@ -573,24 +598,4 @@ export class UserService {
return settings;
}
public async generateAccessToken({
userId
}: {
userId: string;
}): Promise<{ accessToken: string }> {
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 };
}
}

18
apps/client/src/app/components/admin-users/admin-users.component.ts

@ -1,3 +1,4 @@
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
import { getDateFormatString, getEmojiFlag } from '@ghostfolio/common/helper';
import { AdminUsers, InfoItem, User } from '@ghostfolio/common/interfaces';
@ -29,9 +30,9 @@ import { UserService } from '../../services/user/user.service';
@Component({
selector: 'gf-admin-users',
standalone: false,
styleUrls: ['./admin-users.scss'],
templateUrl: './admin-users.html',
standalone: false
templateUrl: './admin-users.html'
})
export class AdminUsersComponent implements OnDestroy, OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
@ -56,6 +57,7 @@ export class AdminUsersComponent implements OnDestroy, OnInit {
private dataService: DataService,
private impersonationStorageService: ImpersonationStorageService,
private notificationService: NotificationService,
private tokenStorageService: TokenStorageService,
private userService: UserService
) {
this.info = this.dataService.fetchInfo();
@ -141,14 +143,22 @@ export class AdminUsersComponent implements OnDestroy, OnInit {
});
}
public onGenerateAccessToken(aId: string) {
public onGenerateAccessToken(aUserId: string) {
this.notificationService.confirm({
confirmFn: () => {
this.dataService
.generateAccessToken(aId)
.generateAccessToken(aUserId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ accessToken }) => {
this.notificationService.alert({
discardFn: () => {
if (aUserId === this.user.id) {
this.tokenStorageService.signOut();
this.userService.remove();
document.location.href = `/${document.documentElement.lang}`;
}
},
message: accessToken,
title: $localize`Security token`
});

2
apps/client/src/app/components/admin-users/admin-users.html

@ -239,7 +239,6 @@
<span i18n>Impersonate User</span>
</span>
</button>
<hr class="m-0" />
}
<button
mat-menu-item
@ -250,6 +249,7 @@
<span i18n>Generate Security Token</span>
</span>
</button>
<hr class="m-0" />
<button
mat-menu-item
[disabled]="element.id === user?.id"

4
apps/client/src/app/services/data.service.ts

@ -692,9 +692,9 @@ export class DataService {
return this.http.get<Tag[]>('/api/v1/tags');
}
public generateAccessToken(aId: string) {
public generateAccessToken(aUserId: string) {
return this.http.post<AccessTokenResponse>(
`/api/v1/user/${aId}/access-token`,
`/api/v1/user/${aUserId}/access-token`,
{}
);
}

Loading…
Cancel
Save