mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 11:16:31 -05:00
auth: Add tests for AuthService
Move AuthTokens to auth folder Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
c9751404f7
commit
508ad26771
30 changed files with 329 additions and 186 deletions
|
@ -5,12 +5,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { UsersModule } from '../../users/users.module';
|
|
||||||
import { TokensController } from './tokens/tokens.controller';
|
import { TokensController } from './tokens/tokens.controller';
|
||||||
import { LoggerModule } from '../../logger/logger.module';
|
import { LoggerModule } from '../../logger/logger.module';
|
||||||
|
import { UsersModule } from '../../users/users.module';
|
||||||
|
import { AuthModule } from '../../auth/auth.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UsersModule, LoggerModule],
|
imports: [LoggerModule, UsersModule, AuthModule],
|
||||||
controllers: [TokensController],
|
controllers: [TokensController],
|
||||||
})
|
})
|
||||||
export class PrivateApiModule {}
|
export class PrivateApiModule {}
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { TokensController } from './tokens.controller';
|
import { TokensController } from './tokens.controller';
|
||||||
import { LoggerModule } from '../../../logger/logger.module';
|
import { LoggerModule } from '../../../logger/logger.module';
|
||||||
import { UsersModule } from '../../../users/users.module';
|
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { Identity } from '../../../users/identity.entity';
|
import { Identity } from '../../../users/identity.entity';
|
||||||
import { User } from '../../../users/user.entity';
|
import { User } from '../../../users/user.entity';
|
||||||
import { AuthToken } from '../../../users/auth-token.entity';
|
import { AuthToken } from '../../../auth/auth-token.entity';
|
||||||
|
import { AuthModule } from '../../../auth/auth.module';
|
||||||
|
|
||||||
describe('TokensController', () => {
|
describe('TokensController', () => {
|
||||||
let controller: TokensController;
|
let controller: TokensController;
|
||||||
|
@ -19,7 +19,7 @@ describe('TokensController', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
controllers: [TokensController],
|
controllers: [TokensController],
|
||||||
imports: [LoggerModule, UsersModule],
|
imports: [LoggerModule, AuthModule],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(User))
|
.overrideProvider(getRepositoryToken(User))
|
||||||
.useValue({})
|
.useValue({})
|
||||||
|
|
|
@ -14,15 +14,15 @@ import {
|
||||||
Post,
|
Post,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { UsersService } from '../../../users/users.service';
|
import { AuthTokenDto } from '../../../auth/auth-token.dto';
|
||||||
import { AuthTokenDto } from '../../../users/auth-token.dto';
|
import { AuthTokenWithSecretDto } from '../../../auth/auth-token-with-secret.dto';
|
||||||
import { AuthTokenWithSecretDto } from '../../../users/auth-token-with-secret.dto';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
|
|
||||||
@Controller('tokens')
|
@Controller('tokens')
|
||||||
export class TokensController {
|
export class TokensController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: ConsoleLoggerService,
|
private readonly logger: ConsoleLoggerService,
|
||||||
private usersService: UsersService,
|
private authService: AuthService,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(TokensController.name);
|
this.logger.setContext(TokensController.name);
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ export class TokensController {
|
||||||
async getUserTokens(): Promise<AuthTokenDto[]> {
|
async getUserTokens(): Promise<AuthTokenDto[]> {
|
||||||
// ToDo: Get real userName
|
// ToDo: Get real userName
|
||||||
return (
|
return (
|
||||||
await this.usersService.getTokensByUsername('hardcoded')
|
await this.authService.getTokensByUsername('hardcoded')
|
||||||
).map((token) => this.usersService.toAuthTokenDto(token));
|
).map((token) => this.authService.toAuthTokenDto(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@ -41,18 +41,13 @@ export class TokensController {
|
||||||
@Body('until') until: number,
|
@Body('until') until: number,
|
||||||
): Promise<AuthTokenWithSecretDto> {
|
): Promise<AuthTokenWithSecretDto> {
|
||||||
// ToDo: Get real userName
|
// ToDo: Get real userName
|
||||||
const authToken = await this.usersService.createTokenForUser(
|
return this.authService.createTokenForUser('hardcoded', label, until);
|
||||||
'hardcoded',
|
|
||||||
label,
|
|
||||||
until,
|
|
||||||
);
|
|
||||||
return this.usersService.toAuthTokenWithSecretDto(authToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete('/:keyId')
|
@Delete('/:keyId')
|
||||||
@HttpCode(204)
|
@HttpCode(204)
|
||||||
async deleteToken(@Param('keyId') keyId: string) {
|
async deleteToken(@Param('keyId') keyId: string) {
|
||||||
// ToDo: Get real userName
|
// ToDo: Get real userName
|
||||||
return this.usersService.removeToken('hardcoded', keyId);
|
return this.authService.removeToken('hardcoded', keyId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { NotesModule } from '../../../notes/notes.module';
|
||||||
import { Tag } from '../../../notes/tag.entity';
|
import { Tag } from '../../../notes/tag.entity';
|
||||||
import { Authorship } from '../../../revisions/authorship.entity';
|
import { Authorship } from '../../../revisions/authorship.entity';
|
||||||
import { Revision } from '../../../revisions/revision.entity';
|
import { Revision } from '../../../revisions/revision.entity';
|
||||||
import { AuthToken } from '../../../users/auth-token.entity';
|
import { AuthToken } from '../../../auth/auth-token.entity';
|
||||||
import { Identity } from '../../../users/identity.entity';
|
import { Identity } from '../../../users/identity.entity';
|
||||||
import { User } from '../../../users/user.entity';
|
import { User } from '../../../users/user.entity';
|
||||||
import { UsersModule } from '../../../users/users.module';
|
import { UsersModule } from '../../../users/users.module';
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { NotesModule } from '../../../notes/notes.module';
|
||||||
import { Tag } from '../../../notes/tag.entity';
|
import { Tag } from '../../../notes/tag.entity';
|
||||||
import { Authorship } from '../../../revisions/authorship.entity';
|
import { Authorship } from '../../../revisions/authorship.entity';
|
||||||
import { Revision } from '../../../revisions/revision.entity';
|
import { Revision } from '../../../revisions/revision.entity';
|
||||||
import { AuthToken } from '../../../users/auth-token.entity';
|
import { AuthToken } from '../../../auth/auth-token.entity';
|
||||||
import { Identity } from '../../../users/identity.entity';
|
import { Identity } from '../../../users/identity.entity';
|
||||||
import { User } from '../../../users/user.entity';
|
import { User } from '../../../users/user.entity';
|
||||||
import { MediaController } from './media.controller';
|
import { MediaController } from './media.controller';
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { Tag } from '../../../notes/tag.entity';
|
||||||
import { Authorship } from '../../../revisions/authorship.entity';
|
import { Authorship } from '../../../revisions/authorship.entity';
|
||||||
import { Revision } from '../../../revisions/revision.entity';
|
import { Revision } from '../../../revisions/revision.entity';
|
||||||
import { RevisionsModule } from '../../../revisions/revisions.module';
|
import { RevisionsModule } from '../../../revisions/revisions.module';
|
||||||
import { AuthToken } from '../../../users/auth-token.entity';
|
import { AuthToken } from '../../../auth/auth-token.entity';
|
||||||
import { Identity } from '../../../users/identity.entity';
|
import { Identity } from '../../../users/identity.entity';
|
||||||
import { User } from '../../../users/user.entity';
|
import { User } from '../../../users/user.entity';
|
||||||
import { UsersModule } from '../../../users/users.module';
|
import { UsersModule } from '../../../users/users.module';
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { IsNumber, IsString } from 'class-validator';
|
||||||
export class AuthTokenDto {
|
export class AuthTokenDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
label: string;
|
label: string;
|
||||||
|
@IsString()
|
||||||
|
keyId: string;
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
created: number;
|
created: number;
|
||||||
@IsNumber()
|
@IsNumber()
|
|
@ -11,7 +11,7 @@ import {
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { User } from './user.entity';
|
import { User } from '../users/user.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class AuthToken {
|
export class AuthToken {
|
||||||
|
@ -31,7 +31,7 @@ export class AuthToken {
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
@Column({ unique: true })
|
@Column({ unique: true })
|
||||||
accessToken: string;
|
accessTokenHash: string;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -49,12 +49,12 @@ export class AuthToken {
|
||||||
keyId: string,
|
keyId: string,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
validUntil?: number,
|
validUntil?: number,
|
||||||
): Pick<AuthToken, 'user' | 'accessToken'> {
|
): Pick<AuthToken, 'user' | 'accessTokenHash'> {
|
||||||
const newToken = new AuthToken();
|
const newToken = new AuthToken();
|
||||||
newToken.user = user;
|
newToken.user = user;
|
||||||
newToken.identifier = identifier;
|
newToken.identifier = identifier;
|
||||||
newToken.keyId = keyId;
|
newToken.keyId = keyId;
|
||||||
newToken.accessToken = accessToken;
|
newToken.accessTokenHash = accessToken;
|
||||||
newToken.createdAt = new Date();
|
newToken.createdAt = new Date();
|
||||||
if (validUntil !== undefined) {
|
if (validUntil !== undefined) {
|
||||||
newToken.validUntil = validUntil;
|
newToken.validUntil = validUntil;
|
|
@ -1,11 +1,26 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { UsersModule } from '../users/users.module';
|
import { UsersModule } from '../users/users.module';
|
||||||
import { PassportModule } from '@nestjs/passport';
|
import { PassportModule } from '@nestjs/passport';
|
||||||
import { TokenStrategy } from './token.strategy';
|
import { TokenStrategy } from './token.strategy';
|
||||||
|
import { LoggerModule } from '../logger/logger.module';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { AuthToken } from './auth-token.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UsersModule, PassportModule],
|
imports: [
|
||||||
|
UsersModule,
|
||||||
|
PassportModule,
|
||||||
|
LoggerModule,
|
||||||
|
TypeOrmModule.forFeature([AuthToken]),
|
||||||
|
],
|
||||||
providers: [AuthService, TokenStrategy],
|
providers: [AuthService, TokenStrategy],
|
||||||
|
exports: [AuthService],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|
|
@ -1,26 +1,91 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { PassportModule } from '@nestjs/passport';
|
import { PassportModule } from '@nestjs/passport';
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { AuthToken } from '../users/auth-token.entity';
|
import { AuthToken } from './auth-token.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
import { UsersModule } from '../users/users.module';
|
import { UsersModule } from '../users/users.module';
|
||||||
import { Identity } from '../users/identity.entity';
|
import { Identity } from '../users/identity.entity';
|
||||||
|
import { LoggerModule } from '../logger/logger.module';
|
||||||
|
|
||||||
describe('AuthService', () => {
|
describe('AuthService', () => {
|
||||||
let service: AuthService;
|
let service: AuthService;
|
||||||
|
let user: User;
|
||||||
|
let authToken: AuthToken;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
user = {
|
||||||
|
authTokens: [],
|
||||||
|
createdAt: new Date(),
|
||||||
|
displayName: 'hardcoded',
|
||||||
|
id: '1',
|
||||||
|
identities: [],
|
||||||
|
ownedNotes: [],
|
||||||
|
updatedAt: new Date(),
|
||||||
|
userName: 'Testy',
|
||||||
|
};
|
||||||
|
|
||||||
|
authToken = {
|
||||||
|
accessTokenHash: '',
|
||||||
|
createdAt: new Date(),
|
||||||
|
id: 1,
|
||||||
|
identifier: 'testIdentifier',
|
||||||
|
keyId: 'abc',
|
||||||
|
lastUsed: null,
|
||||||
|
user: null,
|
||||||
|
validUntil: null,
|
||||||
|
};
|
||||||
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [AuthService],
|
providers: [
|
||||||
imports: [PassportModule, UsersModule],
|
AuthService,
|
||||||
|
{
|
||||||
|
provide: getRepositoryToken(AuthToken),
|
||||||
|
useValue: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
imports: [PassportModule, UsersModule, LoggerModule],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(AuthToken))
|
.overrideProvider(getRepositoryToken(AuthToken))
|
||||||
.useValue({})
|
.useValue({
|
||||||
|
findOne: (): AuthToken => {
|
||||||
|
return {
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
save: async (entity: AuthToken) => {
|
||||||
|
if (entity.lastUsed === undefined) {
|
||||||
|
expect(entity.lastUsed).toBeUndefined();
|
||||||
|
} else {
|
||||||
|
expect(entity.lastUsed).toBeLessThanOrEqual(new Date().getTime());
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
},
|
||||||
|
remove: async (entity: AuthToken) => {
|
||||||
|
expect(entity).toEqual({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
.overrideProvider(getRepositoryToken(Identity))
|
.overrideProvider(getRepositoryToken(Identity))
|
||||||
.useValue({})
|
.useValue({})
|
||||||
.overrideProvider(getRepositoryToken(User))
|
.overrideProvider(getRepositoryToken(User))
|
||||||
.useValue({})
|
.useValue({
|
||||||
|
findOne: (): User => {
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
authTokens: [authToken],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
})
|
||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
service = module.get<AuthService>(AuthService);
|
service = module.get<AuthService>(AuthService);
|
||||||
|
@ -29,4 +94,64 @@ describe('AuthService', () => {
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('checkPassword', async () => {
|
||||||
|
const testPassword = 'thisIsATestPassword';
|
||||||
|
const hash = await service.hashPassword(testPassword);
|
||||||
|
service
|
||||||
|
.checkPassword(testPassword, hash)
|
||||||
|
.then((result) => expect(result).toBeTruthy());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTokensByUsername', async () => {
|
||||||
|
const tokens = await service.getTokensByUsername(user.userName);
|
||||||
|
expect(tokens).toHaveLength(1);
|
||||||
|
expect(tokens).toEqual([authToken]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAuthToken', async () => {
|
||||||
|
const token = 'testToken';
|
||||||
|
authToken.accessTokenHash = await service.hashPassword(token);
|
||||||
|
const authTokenFromCall = await service.getAuthToken(
|
||||||
|
authToken.keyId,
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
expect(authTokenFromCall).toEqual({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setLastUsedToken', async () => {
|
||||||
|
await service.setLastUsedToken(authToken.keyId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validateToken', async () => {
|
||||||
|
const token = 'testToken';
|
||||||
|
authToken.accessTokenHash = await service.hashPassword(token);
|
||||||
|
const userByToken = await service.validateToken(
|
||||||
|
`${authToken.keyId}.${token}`,
|
||||||
|
);
|
||||||
|
expect(userByToken).toEqual({
|
||||||
|
...user,
|
||||||
|
authTokens: [authToken],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removeToken', async () => {
|
||||||
|
await service.removeToken(user.userName, authToken.keyId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('createTokenForUser', async () => {
|
||||||
|
const identifier = 'identifier2';
|
||||||
|
const token = await service.createTokenForUser(
|
||||||
|
user.userName,
|
||||||
|
identifier,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
expect(token.label).toEqual(identifier);
|
||||||
|
expect(token.validUntil).toBeUndefined();
|
||||||
|
expect(token.lastUsed).toBeUndefined();
|
||||||
|
expect(token.secret.startsWith(token.keyId)).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,158 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UsersService } from '../users/users.service';
|
import { UsersService } from '../users/users.service';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
|
import { AuthToken } from './auth-token.entity';
|
||||||
|
import { AuthTokenDto } from './auth-token.dto';
|
||||||
|
import { AuthTokenWithSecretDto } from './auth-token-with-secret.dto';
|
||||||
|
import { compare, hash } from 'bcrypt';
|
||||||
|
import { NotInDBError, TokenNotValidError } from '../errors/errors';
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
constructor(private usersService: UsersService) {}
|
constructor(
|
||||||
|
private readonly logger: ConsoleLoggerService,
|
||||||
|
private usersService: UsersService,
|
||||||
|
@InjectRepository(AuthToken)
|
||||||
|
private authTokenRepository: Repository<AuthToken>,
|
||||||
|
) {
|
||||||
|
this.logger.setContext(AuthService.name);
|
||||||
|
}
|
||||||
|
|
||||||
async validateToken(token: string): Promise<User> {
|
async validateToken(token: string): Promise<User> {
|
||||||
const parts = token.split('.');
|
const parts = token.split('.');
|
||||||
const user = await this.usersService.getUserByAuthToken(parts[0], parts[1]);
|
const accessToken = await this.getAuthToken(parts[0], parts[1]);
|
||||||
|
const user = await this.usersService.getUserByUsername(
|
||||||
|
accessToken.user.userName,
|
||||||
|
);
|
||||||
if (user) {
|
if (user) {
|
||||||
await this.usersService.setLastUsedToken(parts[0])
|
await this.setLastUsedToken(parts[0]);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async hashPassword(cleartext: string): Promise<string> {
|
||||||
|
// hash the password with bcrypt and 2^16 iterations
|
||||||
|
return hash(cleartext, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkPassword(cleartext: string, password: string): Promise<boolean> {
|
||||||
|
// hash the password with bcrypt and 2^16 iterations
|
||||||
|
return compare(cleartext, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
randomBase64UrlString(length = 64): string {
|
||||||
|
// This is necessary as the is no base64url encoding in the toString method
|
||||||
|
// but as can be seen on https://tools.ietf.org/html/rfc4648#page-7
|
||||||
|
// base64url is quite easy buildable from base64
|
||||||
|
return randomBytes(length)
|
||||||
|
.toString('base64')
|
||||||
|
.replace('+', '-')
|
||||||
|
.replace('/', '_')
|
||||||
|
.replace(/=+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTokenForUser(
|
||||||
|
userName: string,
|
||||||
|
identifier: string,
|
||||||
|
until: number,
|
||||||
|
): Promise<AuthTokenWithSecretDto> {
|
||||||
|
const user = await this.usersService.getUserByUsername(userName);
|
||||||
|
const secret = this.randomBase64UrlString();
|
||||||
|
const keyId = this.randomBase64UrlString(8);
|
||||||
|
const accessToken = await this.hashPassword(secret);
|
||||||
|
let token;
|
||||||
|
if (until === 0) {
|
||||||
|
token = AuthToken.create(user, identifier, keyId, accessToken);
|
||||||
|
} else {
|
||||||
|
token = AuthToken.create(user, identifier, keyId, accessToken, until);
|
||||||
|
}
|
||||||
|
const createdToken = await this.authTokenRepository.save(token);
|
||||||
|
return this.toAuthTokenWithSecretDto(createdToken, `${keyId}.${secret}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setLastUsedToken(keyId: string) {
|
||||||
|
const accessToken = await this.authTokenRepository.findOne({
|
||||||
|
where: { keyId: keyId },
|
||||||
|
});
|
||||||
|
accessToken.lastUsed = new Date().getTime();
|
||||||
|
await this.authTokenRepository.save(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAuthToken(keyId: string, token: string): Promise<AuthToken> {
|
||||||
|
const accessToken = await this.authTokenRepository.findOne({
|
||||||
|
where: { keyId: keyId },
|
||||||
|
relations: ['user'],
|
||||||
|
});
|
||||||
|
if (accessToken === undefined) {
|
||||||
|
throw new NotInDBError(`AuthToken '${token}' not found`);
|
||||||
|
}
|
||||||
|
if (!(await this.checkPassword(token, accessToken.accessTokenHash))) {
|
||||||
|
// hashes are not the same
|
||||||
|
throw new TokenNotValidError(`AuthToken '${token}' is not valid.`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
accessToken.validUntil &&
|
||||||
|
accessToken.validUntil < new Date().getTime()
|
||||||
|
) {
|
||||||
|
// tokens validUntil Date lies in the past
|
||||||
|
throw new TokenNotValidError(
|
||||||
|
`AuthToken '${token}' is not valid since ${new Date(
|
||||||
|
accessToken.validUntil,
|
||||||
|
)}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTokensByUsername(userName: string): Promise<AuthToken[]> {
|
||||||
|
const user = await this.usersService.getUserByUsername(userName, true);
|
||||||
|
if (user.authTokens === undefined) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return user.authTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeToken(userName: string, keyId: string) {
|
||||||
|
const user = await this.usersService.getUserByUsername(userName);
|
||||||
|
const token = await this.authTokenRepository.findOne({
|
||||||
|
where: { keyId: keyId, user: user },
|
||||||
|
});
|
||||||
|
await this.authTokenRepository.remove(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
toAuthTokenDto(authToken: AuthToken | null | undefined): AuthTokenDto | null {
|
||||||
|
if (!authToken) {
|
||||||
|
this.logger.warn(`Recieved ${authToken} argument!`, 'toAuthTokenDto');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: authToken.identifier,
|
||||||
|
keyId: authToken.keyId,
|
||||||
|
created: authToken.createdAt.getTime(),
|
||||||
|
validUntil: authToken.validUntil,
|
||||||
|
lastUsed: authToken.lastUsed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toAuthTokenWithSecretDto(
|
||||||
|
authToken: AuthToken | null | undefined,
|
||||||
|
secret: string,
|
||||||
|
): AuthTokenWithSecretDto | null {
|
||||||
|
const tokeDto = this.toAuthTokenDto(authToken);
|
||||||
|
return {
|
||||||
|
...tokeDto,
|
||||||
|
secret: secret,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,6 @@ export class PermissionError extends Error {
|
||||||
name = 'PermissionError';
|
name = 'PermissionError';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TokenNotValid extends Error {
|
export class TokenNotValidError extends Error {
|
||||||
name = 'TokenNotValid';
|
name = 'TokenNotValidError';
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm/index';
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Group {
|
export class Group {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { NotesModule } from '../notes/notes.module';
|
||||||
import { Tag } from '../notes/tag.entity';
|
import { Tag } from '../notes/tag.entity';
|
||||||
import { Authorship } from '../revisions/authorship.entity';
|
import { Authorship } from '../revisions/authorship.entity';
|
||||||
import { Revision } from '../revisions/revision.entity';
|
import { Revision } from '../revisions/revision.entity';
|
||||||
import { AuthToken } from '../users/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Identity } from '../users/identity.entity';
|
import { Identity } from '../users/identity.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
import { UsersModule } from '../users/users.module';
|
import { UsersModule } from '../users/users.module';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Column, Entity, ManyToOne } from 'typeorm/index';
|
import { Column, Entity, ManyToOne } from 'typeorm';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
import { Note } from './note.entity';
|
import { Note } from './note.entity';
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { LoggerModule } from '../logger/logger.module';
|
||||||
import { Authorship } from '../revisions/authorship.entity';
|
import { Authorship } from '../revisions/authorship.entity';
|
||||||
import { Revision } from '../revisions/revision.entity';
|
import { Revision } from '../revisions/revision.entity';
|
||||||
import { RevisionsModule } from '../revisions/revisions.module';
|
import { RevisionsModule } from '../revisions/revisions.module';
|
||||||
import { AuthToken } from '../users/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Identity } from '../users/identity.entity';
|
import { Identity } from '../users/identity.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
import { UsersModule } from '../users/users.module';
|
import { UsersModule } from '../users/users.module';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Column, Entity, ManyToOne } from 'typeorm/index';
|
import { Column, Entity, ManyToOne } from 'typeorm';
|
||||||
import { Group } from '../groups/group.entity';
|
import { Group } from '../groups/group.entity';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Column, Entity, ManyToOne } from 'typeorm/index';
|
import { Column, Entity, ManyToOne } from 'typeorm';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm/index';
|
} from 'typeorm';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
import { Revision } from './revision.entity';
|
import { Revision } from './revision.entity';
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IsDate, IsNumber, IsString } from 'class-validator';
|
import { IsDate, IsNumber } from 'class-validator';
|
||||||
import { Revision } from './revision.entity';
|
import { Revision } from './revision.entity';
|
||||||
|
|
||||||
export class RevisionMetadataDto {
|
export class RevisionMetadataDto {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { JoinTable, ManyToMany } from 'typeorm/index';
|
import { JoinTable, ManyToMany } from 'typeorm';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { Authorship } from './authorship.entity';
|
import { Authorship } from './authorship.entity';
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { LoggerModule } from '../logger/logger.module';
|
||||||
import { AuthorColor } from '../notes/author-color.entity';
|
import { AuthorColor } from '../notes/author-color.entity';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { NotesModule } from '../notes/notes.module';
|
import { NotesModule } from '../notes/notes.module';
|
||||||
import { AuthToken } from '../users/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Identity } from '../users/identity.entity';
|
import { Identity } from '../users/identity.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
import { Authorship } from './authorship.entity';
|
import { Authorship } from './authorship.entity';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm/index';
|
} from 'typeorm';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ISession } from 'connect-typeorm';
|
import { ISession } from 'connect-typeorm';
|
||||||
import { Column, Entity, Index, PrimaryColumn } from 'typeorm/index';
|
import { Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Session implements ISession {
|
export class Session implements ISession {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { Column, OneToMany } from 'typeorm';
|
import { Column, OneToMany } from 'typeorm';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { AuthToken } from './auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Identity } from './identity.entity';
|
import { Identity } from './identity.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
|
|
@ -7,16 +7,12 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { LoggerModule } from '../logger/logger.module';
|
import { LoggerModule } from '../logger/logger.module';
|
||||||
import { AuthToken } from './auth-token.entity';
|
|
||||||
import { Identity } from './identity.entity';
|
import { Identity } from './identity.entity';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
import { UsersService } from './users.service';
|
import { UsersService } from './users.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [TypeOrmModule.forFeature([User, Identity]), LoggerModule],
|
||||||
TypeOrmModule.forFeature([User, AuthToken, Identity]),
|
|
||||||
LoggerModule,
|
|
||||||
],
|
|
||||||
providers: [UsersService],
|
providers: [UsersService],
|
||||||
exports: [UsersService],
|
exports: [UsersService],
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { LoggerModule } from '../logger/logger.module';
|
import { LoggerModule } from '../logger/logger.module';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
import { UsersService } from './users.service';
|
import { UsersService } from './users.service';
|
||||||
import { AuthToken } from './auth-token.entity';
|
|
||||||
|
|
||||||
describe('UsersService', () => {
|
describe('UsersService', () => {
|
||||||
let service: UsersService;
|
let service: UsersService;
|
||||||
|
@ -22,17 +21,11 @@ describe('UsersService', () => {
|
||||||
provide: getRepositoryToken(User),
|
provide: getRepositoryToken(User),
|
||||||
useValue: {},
|
useValue: {},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: getRepositoryToken(AuthToken),
|
|
||||||
useValue: {},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
imports: [LoggerModule],
|
imports: [LoggerModule],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(User))
|
.overrideProvider(getRepositoryToken(User))
|
||||||
.useValue({})
|
.useValue({})
|
||||||
.overrideProvider(getRepositoryToken(AuthToken))
|
|
||||||
.useValue({})
|
|
||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
service = module.get<UsersService>(UsersService);
|
service = module.get<UsersService>(UsersService);
|
||||||
|
|
|
@ -7,23 +7,16 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { NotInDBError, TokenNotValid } from '../errors/errors';
|
import { NotInDBError } from '../errors/errors';
|
||||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
import { UserInfoDto } from './user-info.dto';
|
import { UserInfoDto } from './user-info.dto';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
import { AuthToken } from './auth-token.entity';
|
|
||||||
import { hash, compare } from 'bcrypt';
|
|
||||||
import { randomBytes } from 'crypto';
|
|
||||||
import { AuthTokenDto } from './auth-token.dto';
|
|
||||||
import { AuthTokenWithSecretDto } from './auth-token-with-secret.dto';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: ConsoleLoggerService,
|
private readonly logger: ConsoleLoggerService,
|
||||||
@InjectRepository(User) private userRepository: Repository<User>,
|
@InjectRepository(User) private userRepository: Repository<User>,
|
||||||
@InjectRepository(AuthToken)
|
|
||||||
private authTokenRepository: Repository<AuthToken>,
|
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(UsersService.name);
|
this.logger.setContext(UsersService.name);
|
||||||
}
|
}
|
||||||
|
@ -33,39 +26,6 @@ export class UsersService {
|
||||||
return this.userRepository.save(user);
|
return this.userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
randomBase64UrlString(): string {
|
|
||||||
// This is necessary as the is no base64url encoding in the toString method
|
|
||||||
// but as can be seen on https://tools.ietf.org/html/rfc4648#page-7
|
|
||||||
// base64url is quite easy buildable from base64
|
|
||||||
return randomBytes(64)
|
|
||||||
.toString('base64')
|
|
||||||
.replace('+', '-')
|
|
||||||
.replace('/', '_')
|
|
||||||
.replace(/=+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
async createTokenForUser(
|
|
||||||
userName: string,
|
|
||||||
identifier: string,
|
|
||||||
until: number,
|
|
||||||
): Promise<AuthToken> {
|
|
||||||
const user = await this.getUserByUsername(userName);
|
|
||||||
const secret = this.randomBase64UrlString();
|
|
||||||
const keyId = this.randomBase64UrlString();
|
|
||||||
const accessToken = await this.hashPassword(secret);
|
|
||||||
let token;
|
|
||||||
if (until === 0) {
|
|
||||||
token = AuthToken.create(user, identifier, keyId, accessToken);
|
|
||||||
} else {
|
|
||||||
token = AuthToken.create(user, identifier, keyId, accessToken, until);
|
|
||||||
}
|
|
||||||
const createdToken = await this.authTokenRepository.save(token);
|
|
||||||
return {
|
|
||||||
...createdToken,
|
|
||||||
accessToken: `${keyId}.${secret}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteUser(userName: string) {
|
async deleteUser(userName: string) {
|
||||||
// TODO: Handle owned notes and edits
|
// TODO: Handle owned notes and edits
|
||||||
const user = await this.userRepository.findOne({
|
const user = await this.userRepository.findOne({
|
||||||
|
@ -85,50 +45,6 @@ export class UsersService {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hashPassword(cleartext: string): Promise<string> {
|
|
||||||
// hash the password with bcrypt and 2^16 iterations
|
|
||||||
return hash(cleartext, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkPassword(cleartext: string, password: string): Promise<boolean> {
|
|
||||||
// hash the password with bcrypt and 2^16 iterations
|
|
||||||
return compare(cleartext, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setLastUsedToken(keyId: string) {
|
|
||||||
const accessToken = await this.authTokenRepository.findOne({
|
|
||||||
where: { keyId: keyId },
|
|
||||||
});
|
|
||||||
accessToken.lastUsed = new Date().getTime();
|
|
||||||
await this.authTokenRepository.save(accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUserByAuthToken(keyId: string, token: string): Promise<User> {
|
|
||||||
const accessToken = await this.authTokenRepository.findOne({
|
|
||||||
where: { keyId: keyId },
|
|
||||||
relations: ['user'],
|
|
||||||
});
|
|
||||||
if (accessToken === undefined) {
|
|
||||||
throw new NotInDBError(`AuthToken '${token}' not found`);
|
|
||||||
}
|
|
||||||
if (!(await this.checkPassword(token, accessToken.accessToken))) {
|
|
||||||
// hashes are not the same
|
|
||||||
throw new TokenNotValid(`AuthToken '${token}' is not valid.`);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
accessToken.validUntil &&
|
|
||||||
accessToken.validUntil < new Date().getTime()
|
|
||||||
) {
|
|
||||||
// tokens validUntil Date lies in the past
|
|
||||||
throw new TokenNotValid(
|
|
||||||
`AuthToken '${token}' is not valid since ${new Date(
|
|
||||||
accessToken.validUntil,
|
|
||||||
)}.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.getUserByUsername(accessToken.user.userName);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPhotoUrl(user: User): string {
|
getPhotoUrl(user: User): string {
|
||||||
if (user.photo) {
|
if (user.photo) {
|
||||||
return user.photo;
|
return user.photo;
|
||||||
|
@ -138,45 +54,6 @@ export class UsersService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTokensByUsername(userName: string): Promise<AuthToken[]> {
|
|
||||||
const user = await this.getUserByUsername(userName, true);
|
|
||||||
if (user.authTokens === undefined) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return user.authTokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeToken(userName: string, keyId: string) {
|
|
||||||
const user = await this.getUserByUsername(userName);
|
|
||||||
const token = await this.authTokenRepository.findOne({
|
|
||||||
where: { keyId: keyId, user: user },
|
|
||||||
});
|
|
||||||
await this.authTokenRepository.remove(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
toAuthTokenDto(authToken: AuthToken | null | undefined): AuthTokenDto | null {
|
|
||||||
if (!authToken) {
|
|
||||||
this.logger.warn(`Recieved ${authToken} argument!`, 'toAuthTokenDto');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
label: authToken.identifier,
|
|
||||||
created: authToken.createdAt.getTime(),
|
|
||||||
validUntil: authToken.validUntil,
|
|
||||||
lastUsed: authToken.lastUsed,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toAuthTokenWithSecretDto(
|
|
||||||
authToken: AuthToken | null | undefined,
|
|
||||||
): AuthTokenWithSecretDto | null {
|
|
||||||
const tokeDto = this.toAuthTokenDto(authToken)
|
|
||||||
return {
|
|
||||||
...tokeDto,
|
|
||||||
secret: authToken.accessToken,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toUserDto(user: User | null | undefined): UserInfoDto | null {
|
toUserDto(user: User | null | undefined): UserInfoDto | null {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
this.logger.warn(`Recieved ${user} argument!`, 'toUserDto');
|
this.logger.warn(`Recieved ${user} argument!`, 'toUserDto');
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { INestApplication } from '@nestjs/common';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
import { AppModule } from '../../src/app.module';
|
import { AppModule } from '../../src/app.module';
|
||||||
//import { UsersService } from '../../src/users/users.service';
|
|
||||||
import { UserInfoDto } from '../../src/users/user-info.dto';
|
import { UserInfoDto } from '../../src/users/user-info.dto';
|
||||||
import { HistoryService } from '../../src/history/history.service';
|
import { HistoryService } from '../../src/history/history.service';
|
||||||
import { NotesService } from '../../src/notes/notes.service';
|
import { NotesService } from '../../src/notes/notes.service';
|
||||||
|
|
Loading…
Reference in a new issue