mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-29 16:24:22 -05:00
auth: Add cron to clean old tokens
Rename AuthToken.identifier to label Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
cc2fcac532
commit
0a3247492a
6 changed files with 27 additions and 17 deletions
|
@ -30,7 +30,7 @@ entity "auth_token"{
|
||||||
*userId : uuid
|
*userId : uuid
|
||||||
*keyId: text
|
*keyId: text
|
||||||
*accessToken : text
|
*accessToken : text
|
||||||
*identifier: text
|
*label: text
|
||||||
*createdAt: date
|
*createdAt: date
|
||||||
lastUsed: number
|
lastUsed: number
|
||||||
validUntil: number
|
validUntil: number
|
||||||
|
|
|
@ -30,9 +30,11 @@
|
||||||
"@nestjs/passport": "^7.1.5",
|
"@nestjs/passport": "^7.1.5",
|
||||||
"@nestjs/platform-express": "7.6.5",
|
"@nestjs/platform-express": "7.6.5",
|
||||||
"@nestjs/swagger": "4.7.12",
|
"@nestjs/swagger": "4.7.12",
|
||||||
|
"@nestjs/schedule": "^0.4.2",
|
||||||
"@nestjs/typeorm": "7.1.5",
|
"@nestjs/typeorm": "7.1.5",
|
||||||
"@types/passport-http-bearer": "^1.0.36",
|
|
||||||
"@types/bcrypt": "^3.0.0",
|
"@types/bcrypt": "^3.0.0",
|
||||||
|
"@types/cron": "^1.7.2",
|
||||||
|
"@types/passport-http-bearer": "^1.0.36",
|
||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"class-transformer": "0.3.2",
|
"class-transformer": "0.3.2",
|
||||||
"class-validator": "0.13.1",
|
"class-validator": "0.13.1",
|
||||||
|
|
|
@ -26,6 +26,7 @@ import cspConfig from './config/csp.config';
|
||||||
import databaseConfig from './config/database.config';
|
import databaseConfig from './config/database.config';
|
||||||
import authConfig from './config/auth.config';
|
import authConfig from './config/auth.config';
|
||||||
import { PrivateApiModule } from './api/private/private-api.module';
|
import { PrivateApiModule } from './api/private/private-api.module';
|
||||||
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -46,6 +47,7 @@ import { PrivateApiModule } from './api/private/private-api.module';
|
||||||
],
|
],
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
}),
|
}),
|
||||||
|
ScheduleModule.forRoot(),
|
||||||
NotesModule,
|
NotesModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
RevisionsModule,
|
RevisionsModule,
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class AuthToken {
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
identifier: string;
|
label: string;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
@ -51,16 +51,11 @@ export class AuthToken {
|
||||||
validUntil?: Date,
|
validUntil?: Date,
|
||||||
): Pick<
|
): Pick<
|
||||||
AuthToken,
|
AuthToken,
|
||||||
| 'user'
|
'user' | 'label' | 'keyId' | 'accessTokenHash' | 'createdAt' | 'validUntil'
|
||||||
| 'identifier'
|
|
||||||
| 'keyId'
|
|
||||||
| 'accessTokenHash'
|
|
||||||
| 'createdAt'
|
|
||||||
| 'validUntil'
|
|
||||||
> {
|
> {
|
||||||
const newToken = new AuthToken();
|
const newToken = new AuthToken();
|
||||||
newToken.user = user;
|
newToken.user = user;
|
||||||
newToken.identifier = identifier;
|
newToken.label = identifier;
|
||||||
newToken.keyId = keyId;
|
newToken.keyId = keyId;
|
||||||
newToken.accessTokenHash = accessToken;
|
newToken.accessTokenHash = accessToken;
|
||||||
newToken.createdAt = new Date();
|
newToken.createdAt = new Date();
|
||||||
|
|
|
@ -35,7 +35,7 @@ describe('AuthService', () => {
|
||||||
accessTokenHash: '',
|
accessTokenHash: '',
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
id: 1,
|
id: 1,
|
||||||
identifier: 'testIdentifier',
|
label: 'testIdentifier',
|
||||||
keyId: 'abc',
|
keyId: 'abc',
|
||||||
lastUsed: null,
|
lastUsed: null,
|
||||||
user: null,
|
user: null,
|
||||||
|
@ -186,7 +186,7 @@ describe('AuthService', () => {
|
||||||
const tokenDto = await service.toAuthTokenDto(authToken);
|
const tokenDto = await service.toAuthTokenDto(authToken);
|
||||||
expect(tokenDto.keyId).toEqual(authToken.keyId);
|
expect(tokenDto.keyId).toEqual(authToken.keyId);
|
||||||
expect(tokenDto.lastUsed).toBeNull();
|
expect(tokenDto.lastUsed).toBeNull();
|
||||||
expect(tokenDto.label).toEqual(authToken.identifier);
|
expect(tokenDto.label).toEqual(authToken.label);
|
||||||
expect(tokenDto.validUntil).toBeNull();
|
expect(tokenDto.validUntil).toBeNull();
|
||||||
expect(tokenDto.createdAt.getTime()).toEqual(
|
expect(tokenDto.createdAt.getTime()).toEqual(
|
||||||
authToken.createdAt.getTime(),
|
authToken.createdAt.getTime(),
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
import { TimestampMillis } from '../utils/timestamp';
|
import { TimestampMillis } from '../utils/timestamp';
|
||||||
|
import { Cron } from '@nestjs/schedule';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
@ -32,11 +33,11 @@ export class AuthService {
|
||||||
async validateToken(token: string): Promise<User> {
|
async validateToken(token: string): Promise<User> {
|
||||||
const [keyId, secret] = token.split('.');
|
const [keyId, secret] = token.split('.');
|
||||||
const accessToken = await this.getAuthTokenAndValidate(keyId, secret);
|
const accessToken = await this.getAuthTokenAndValidate(keyId, secret);
|
||||||
|
await this.setLastUsedToken(keyId);
|
||||||
const user = await this.usersService.getUserByUsername(
|
const user = await this.usersService.getUserByUsername(
|
||||||
accessToken.user.userName,
|
accessToken.user.userName,
|
||||||
);
|
);
|
||||||
if (user) {
|
if (user) {
|
||||||
await this.setLastUsedToken(keyId);
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -125,9 +126,7 @@ export class AuthService {
|
||||||
) {
|
) {
|
||||||
// tokens validUntil Date lies in the past
|
// tokens validUntil Date lies in the past
|
||||||
throw new TokenNotValidError(
|
throw new TokenNotValidError(
|
||||||
`AuthToken '${token}' is not valid since ${new Date(
|
`AuthToken '${token}' is not valid since ${accessToken.validUntil}.`,
|
||||||
accessToken.validUntil,
|
|
||||||
)}.`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return accessToken;
|
return accessToken;
|
||||||
|
@ -156,7 +155,7 @@ export class AuthService {
|
||||||
const tokenDto: AuthTokenDto = {
|
const tokenDto: AuthTokenDto = {
|
||||||
lastUsed: null,
|
lastUsed: null,
|
||||||
validUntil: null,
|
validUntil: null,
|
||||||
label: authToken.identifier,
|
label: authToken.label,
|
||||||
keyId: authToken.keyId,
|
keyId: authToken.keyId,
|
||||||
createdAt: authToken.createdAt,
|
createdAt: authToken.createdAt,
|
||||||
};
|
};
|
||||||
|
@ -182,4 +181,16 @@ export class AuthService {
|
||||||
secret: secret,
|
secret: secret,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete all non valid tokens every sunday on 3:00 AM
|
||||||
|
@Cron('0 0 3 * * 0')
|
||||||
|
async handleCron() {
|
||||||
|
const currentTime = new Date().getTime();
|
||||||
|
const tokens: AuthToken[] = await this.authTokenRepository.find();
|
||||||
|
for (const token of tokens) {
|
||||||
|
if (token.validUntil && token.validUntil.getTime() <= currentTime) {
|
||||||
|
await this.authTokenRepository.remove(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue