auth defined test required
This commit is contained in:
@@ -18,7 +18,6 @@ class PasswordHandlers {
|
||||
|
||||
create_hashed_password(uuid: string, password: string): string {
|
||||
const data = `${uuid}:${password}`;
|
||||
console.log(crypto.createHash('sha256').update(data).digest('hex'));
|
||||
return crypto.createHash('sha256').update(data).digest('hex');
|
||||
}
|
||||
|
||||
@@ -33,7 +32,6 @@ class PasswordHandlers {
|
||||
hashed_password: string,
|
||||
): boolean {
|
||||
const created_hashed_password = this.create_hashed_password(uuid, password);
|
||||
console.log('created_hashed_password', created_hashed_password);
|
||||
return created_hashed_password === hashed_password;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,17 @@ import {
|
||||
import { CacheService } from '@/src/cache.service';
|
||||
import { users } from '@prisma/client';
|
||||
import { PasswordHandlers } from './login_handler';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, ForbiddenException } from '@nestjs/common';
|
||||
|
||||
interface LoginFromRedis {
|
||||
key: string;
|
||||
value: AuthToken;
|
||||
}
|
||||
|
||||
interface SelectFromRedis {
|
||||
key: string;
|
||||
value: TokenDictInterface;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RedisHandlers {
|
||||
@@ -17,6 +27,50 @@ export class RedisHandlers {
|
||||
private readonly passwordService: PasswordHandlers,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates that a Redis key follows the expected format
|
||||
* Format: AUTH_TOKEN:token:token:UUID or AUTH_TOKEN:token:token:*:*
|
||||
this.AUTH_TOKEN:token:token:UUID:UUID
|
||||
*/
|
||||
private validateRedisKey(redisKey: string): boolean {
|
||||
if (!redisKey.startsWith(this.AUTH_TOKEN + ':')) {
|
||||
throw new ForbiddenException(
|
||||
`Invalid Redis key format. Must start with ${this.AUTH_TOKEN}:`,
|
||||
);
|
||||
}
|
||||
const colonCount = (redisKey.match(/:/g) || []).length;
|
||||
if (colonCount !== 4) {
|
||||
throw new ForbiddenException(
|
||||
`Invalid Redis key format. Must have exactly 5 colons. Found: ${colonCount}`,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public mergeLoginKey(req: Request): string {
|
||||
const acsToken = req.headers['acs'];
|
||||
if (!acsToken) {
|
||||
throw new ForbiddenException('Access token header is missing');
|
||||
}
|
||||
const mergedRedisKey = `${this.AUTH_TOKEN}:${acsToken}:${acsToken}:*:*`;
|
||||
this.validateRedisKey(mergedRedisKey);
|
||||
return mergedRedisKey;
|
||||
}
|
||||
|
||||
public mergeSelectKey(req: Request): string {
|
||||
const acsToken = req.headers['acs'];
|
||||
if (!acsToken) {
|
||||
throw new ForbiddenException('Access token header is missing');
|
||||
}
|
||||
const slcToken = req.headers['slc'];
|
||||
if (!slcToken) {
|
||||
throw new ForbiddenException('Select token header is missing');
|
||||
}
|
||||
const mergedRedisKey = `${this.AUTH_TOKEN}:${acsToken}:${slcToken}:*:*`;
|
||||
this.validateRedisKey(mergedRedisKey);
|
||||
return mergedRedisKey;
|
||||
}
|
||||
|
||||
generateSelectToken(accessToken: string, userUUID: string) {
|
||||
return this.passwordService.createSelectToken(accessToken, userUUID);
|
||||
}
|
||||
@@ -25,22 +79,97 @@ export class RedisHandlers {
|
||||
return this.passwordService.generateAccessToken();
|
||||
}
|
||||
|
||||
async getLoginFromRedis(redisKey: string): Promise<AuthToken> {
|
||||
return this.cacheService.get(redisKey);
|
||||
private async scanKeys(pattern: string): Promise<string[]> {
|
||||
this.validateRedisKey(pattern);
|
||||
const client = (this.cacheService as any).client;
|
||||
if (!client) throw new Error('Redis client not available');
|
||||
|
||||
const keys: string[] = [];
|
||||
let cursor = '0';
|
||||
|
||||
do {
|
||||
const [nextCursor, matchedKeys] = await client.scan(
|
||||
cursor,
|
||||
'MATCH',
|
||||
pattern,
|
||||
);
|
||||
cursor = nextCursor;
|
||||
keys.push(...matchedKeys);
|
||||
} while (cursor !== '0');
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
async getSelectFromRedis(redisKey: string): Promise<TokenDictInterface> {
|
||||
return this.cacheService.get(redisKey);
|
||||
async getLoginFromRedis(req: Request): Promise<LoginFromRedis | null> {
|
||||
const mergedKey = this.mergeLoginKey(req);
|
||||
if (mergedKey.includes('*')) {
|
||||
const keys = await this.scanKeys(mergedKey);
|
||||
if (keys.length === 0) {
|
||||
throw new ForbiddenException('Authorization failed - No matching keys');
|
||||
}
|
||||
for (const key of keys) {
|
||||
const parts = key.split(':');
|
||||
if (parts.length >= 3) {
|
||||
if (parts[1] === parts[2]) {
|
||||
const value = await this.cacheService.get(key);
|
||||
if (value) {
|
||||
return { key, value };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ForbiddenException('Authorization failed - No valid keys');
|
||||
}
|
||||
|
||||
const value = await this.cacheService.get(mergedKey);
|
||||
return value ? { key: mergedKey, value } : null;
|
||||
}
|
||||
|
||||
async renewTtlLoginFromRedis(redisKey: string): Promise<any> {
|
||||
const token = await this.getLoginFromRedis(redisKey);
|
||||
return this.cacheService.set_with_ttl(redisKey, token, 60 * 30);
|
||||
async getSelectFromRedis(req: Request): Promise<SelectFromRedis | null> {
|
||||
const mergedKey = this.mergeSelectKey(req);
|
||||
if (mergedKey.includes('*')) {
|
||||
const keys = await this.scanKeys(mergedKey);
|
||||
|
||||
if (keys.length === 0) {
|
||||
throw new ForbiddenException(
|
||||
'Authorization failed - No matching select keys',
|
||||
);
|
||||
}
|
||||
for (const key of keys) {
|
||||
const value = await this.cacheService.get(key);
|
||||
if (value) {
|
||||
return { key, value };
|
||||
}
|
||||
}
|
||||
throw new ForbiddenException(
|
||||
'Authorization failed - No valid select keys',
|
||||
);
|
||||
}
|
||||
|
||||
const value = await this.cacheService.get(mergedKey);
|
||||
return value ? { key: mergedKey, value } : null;
|
||||
}
|
||||
|
||||
async renewTtlSelectFromRedis(redisKey: string): Promise<any> {
|
||||
const token = await this.getSelectFromRedis(redisKey);
|
||||
return this.cacheService.set_with_ttl(redisKey, token, 60 * 30);
|
||||
async deleteLoginFromRedis(req: Request): Promise<any> {
|
||||
const mergedKey = this.mergeLoginKey(req);
|
||||
return this.cacheService.delete(mergedKey);
|
||||
}
|
||||
|
||||
async deleteSelectFromRedis(req: Request): Promise<any> {
|
||||
const mergedKey = this.mergeSelectKey(req);
|
||||
return this.cacheService.delete(mergedKey);
|
||||
}
|
||||
|
||||
async renewTtlLoginFromRedis(req: Request): Promise<any> {
|
||||
const mergedKey = this.mergeLoginKey(req);
|
||||
const value = await this.cacheService.get(mergedKey);
|
||||
return this.cacheService.set_with_ttl(mergedKey, value, 86400);
|
||||
}
|
||||
|
||||
async renewTtlSelectFromRedis(req: Request): Promise<any> {
|
||||
const mergedKey = this.mergeSelectKey(req);
|
||||
const value = await this.cacheService.get(mergedKey);
|
||||
return this.cacheService.set_with_ttl(mergedKey, value, 60 * 30);
|
||||
}
|
||||
|
||||
async setLoginToRedis(token: AuthToken, userUUID: string): Promise<any> {
|
||||
|
||||
Reference in New Issue
Block a user