mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-27 08:14:54 +00:00
SPRINT-1 related tasks done
This commit is contained in:
@ -7,22 +7,22 @@ import { JwtStrategy } from './strategies/jwt.strategy';
|
||||
import { UserSessionRepository } from '../modules/session/repositories/session.repository';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { UserRepository } from '../modules/user/repositories';
|
||||
import { RefreshTokenStrategy } from './strategies/refresh-token.strategy';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
PassportModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
secret: configService.get('JWT_SECRET'),
|
||||
signOptions: { expiresIn: configService.get('JWT_EXPIRE_TIME') },
|
||||
}),
|
||||
}),
|
||||
JwtModule.register({}),
|
||||
HelperModule,
|
||||
],
|
||||
providers: [JwtStrategy, UserSessionRepository, AuthService, UserRepository],
|
||||
providers: [
|
||||
JwtStrategy,
|
||||
RefreshTokenStrategy,
|
||||
UserSessionRepository,
|
||||
AuthService,
|
||||
UserRepository,
|
||||
],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import * as argon2 from 'argon2';
|
||||
import { HelperHashService } from '../../helper/services';
|
||||
import { UserRepository } from '../../../../common/src/modules/user/repositories';
|
||||
import { UserSessionRepository } from '../../../../common/src/modules/session/repositories/session.repository';
|
||||
import { UserSessionEntity } from '../../../../common/src/modules/session/entities';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
@ -12,6 +14,7 @@ export class AuthService {
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly sessionRepository: UserSessionRepository,
|
||||
private readonly helperHashService: HelperHashService,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async validateUser(email: string, pass: string): Promise<any> {
|
||||
@ -40,6 +43,24 @@ export class AuthService {
|
||||
return await this.sessionRepository.save(data);
|
||||
}
|
||||
|
||||
async getTokens(payload) {
|
||||
const [accessToken, refreshToken] = await Promise.all([
|
||||
this.jwtService.signAsync(payload, {
|
||||
secret: this.configService.get<string>('JWT_SECRET'),
|
||||
expiresIn: '24h',
|
||||
}),
|
||||
this.jwtService.signAsync(payload, {
|
||||
secret: this.configService.get<string>('JWT_SECRET'),
|
||||
expiresIn: '7d',
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
};
|
||||
}
|
||||
|
||||
async login(user: any) {
|
||||
const payload = {
|
||||
email: user.email,
|
||||
@ -48,8 +69,22 @@ export class AuthService {
|
||||
type: user.type,
|
||||
sessionId: user.sessionId,
|
||||
};
|
||||
return {
|
||||
access_token: this.jwtService.sign(payload),
|
||||
};
|
||||
const tokens = await this.getTokens(payload);
|
||||
await this.updateRefreshToken(user.uuid, tokens.refreshToken);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
async updateRefreshToken(userId: string, refreshToken: string) {
|
||||
const hashedRefreshToken = await this.hashData(refreshToken);
|
||||
await this.userRepository.update(
|
||||
{ uuid: userId },
|
||||
{
|
||||
refreshToken: hashedRefreshToken,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
hashData(data: string) {
|
||||
return argon2.hash(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { UserSessionRepository } from '../../../src/modules/session/repositories
|
||||
import { AuthInterface } from '../interfaces/auth.interface';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor(
|
||||
private readonly sessionRepository: UserSessionRepository,
|
||||
private readonly configService: ConfigService,
|
||||
|
||||
42
libs/common/src/auth/strategies/refresh-token.strategy.ts
Normal file
42
libs/common/src/auth/strategies/refresh-token.strategy.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { UserSessionRepository } from '../../../src/modules/session/repositories/session.repository';
|
||||
import { AuthInterface } from '../interfaces/auth.interface';
|
||||
|
||||
@Injectable()
|
||||
export class RefreshTokenStrategy extends PassportStrategy(
|
||||
Strategy,
|
||||
'jwt-refresh',
|
||||
) {
|
||||
constructor(
|
||||
private readonly sessionRepository: UserSessionRepository,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get('JWT_SECRET'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: AuthInterface) {
|
||||
const validateUser = await this.sessionRepository.findOne({
|
||||
where: {
|
||||
uuid: payload.sessionId,
|
||||
isLoggedOut: false,
|
||||
},
|
||||
});
|
||||
if (validateUser) {
|
||||
return {
|
||||
email: payload.email,
|
||||
userId: payload.id,
|
||||
uuid: payload.uuid,
|
||||
sessionId: payload.sessionId,
|
||||
};
|
||||
} else {
|
||||
throw new BadRequestException('Unauthorized');
|
||||
}
|
||||
}
|
||||
}
|
||||
4
libs/common/src/constants/permission-type.enum.ts
Normal file
4
libs/common/src/constants/permission-type.enum.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum PermissionType {
|
||||
READ = 'READ',
|
||||
CONTROLLABLE = 'CONTROLLABLE',
|
||||
}
|
||||
@ -7,6 +7,8 @@ import { UserSessionEntity } from '../modules/session/entities/session.entity';
|
||||
import { UserOtpEntity } from '../modules/user-otp/entities';
|
||||
import { HomeEntity } from '../modules/home/entities';
|
||||
import { ProductEntity } from '../modules/product/entities';
|
||||
import { DeviceEntity, DeviceUserPermissionEntity } from '../modules/device/entities';
|
||||
import { PermissionTypeEntity } from '../modules/permission/entities';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -27,6 +29,9 @@ import { ProductEntity } from '../modules/product/entities';
|
||||
UserOtpEntity,
|
||||
HomeEntity,
|
||||
ProductEntity,
|
||||
DeviceUserPermissionEntity,
|
||||
DeviceEntity,
|
||||
PermissionTypeEntity
|
||||
],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))),
|
||||
|
||||
11
libs/common/src/guards/jwt-refresh.auth.guard.ts
Normal file
11
libs/common/src/guards/jwt-refresh.auth.guard.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
export class RefreshTokenGuard extends AuthGuard('jwt-refresh') {
|
||||
handleRequest(err, user) {
|
||||
if (err || !user) {
|
||||
throw err || new UnauthorizedException();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
13
libs/common/src/modules/device/device.repository.module.ts
Normal file
13
libs/common/src/modules/device/device.repository.module.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { DeviceEntity, DeviceUserPermissionEntity } from './entities';
|
||||
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([DeviceEntity, DeviceUserPermissionEntity]),
|
||||
],
|
||||
})
|
||||
export class DeviceRepositoryModule {}
|
||||
19
libs/common/src/modules/device/dtos/device-user-type.dto.ts
Normal file
19
libs/common/src/modules/device/dtos/device-user-type.dto.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class DeviceUserTypeDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public uuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public userUuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public deviceUuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public permissionTypeUuid: string;
|
||||
}
|
||||
20
libs/common/src/modules/device/dtos/device.dto.ts
Normal file
20
libs/common/src/modules/device/dtos/device.dto.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
export class DeviceDto{
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public uuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
spaceUuid:string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
deviceTuyaUuid:string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
productUuid:string;
|
||||
}
|
||||
2
libs/common/src/modules/device/dtos/index.ts
Normal file
2
libs/common/src/modules/device/dtos/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './device.dto';
|
||||
export * from './device-user-type.dto';
|
||||
@ -0,0 +1,26 @@
|
||||
import { Column, Entity } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { DeviceUserTypeDto } from '../dtos/device-user-type.dto';
|
||||
|
||||
@Entity({ name: 'device-user-permission' })
|
||||
export class DeviceUserPermissionEntity extends AbstractEntity<DeviceUserTypeDto> {
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public userUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
deviceUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public permissionTypeUuid: string;
|
||||
|
||||
constructor(partial: Partial<DeviceUserPermissionEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
||||
32
libs/common/src/modules/device/entities/device.entity.ts
Normal file
32
libs/common/src/modules/device/entities/device.entity.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Column, Entity } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { DeviceDto } from '../dtos/device.dto';
|
||||
|
||||
@Entity({ name: 'device' })
|
||||
export class DeviceEntity extends AbstractEntity<DeviceDto> {
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public spaceUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
deviceTuyaUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public productUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
default: true,
|
||||
})
|
||||
isActive: true;
|
||||
|
||||
constructor(partial: Partial<DeviceEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
||||
2
libs/common/src/modules/device/entities/index.ts
Normal file
2
libs/common/src/modules/device/entities/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './device.entity';
|
||||
export * from './device-user-type.entity';
|
||||
1
libs/common/src/modules/device/index.ts
Normal file
1
libs/common/src/modules/device/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './device.repository.module';
|
||||
@ -0,0 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DeviceUserPermissionEntity } from '../entities';
|
||||
|
||||
@Injectable()
|
||||
export class DeviceUserTypeRepository extends Repository<DeviceUserPermissionEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(DeviceUserPermissionEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DeviceEntity } from '../entities';
|
||||
|
||||
@Injectable()
|
||||
export class DeviceRepository extends Repository<DeviceEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(DeviceEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
||||
2
libs/common/src/modules/device/repositories/index.ts
Normal file
2
libs/common/src/modules/device/repositories/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './device.repository';
|
||||
export * from './device-user-type.repository';
|
||||
1
libs/common/src/modules/permission/dtos/index.ts
Normal file
1
libs/common/src/modules/permission/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './permission.dto'
|
||||
11
libs/common/src/modules/permission/dtos/permission.dto.ts
Normal file
11
libs/common/src/modules/permission/dtos/permission.dto.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class PermissionTypeDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public uuid: string;
|
||||
|
||||
@IsEnum(PermissionType)
|
||||
public type: PermissionType;
|
||||
}
|
||||
1
libs/common/src/modules/permission/entities/index.ts
Normal file
1
libs/common/src/modules/permission/entities/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './permission.entity'
|
||||
@ -0,0 +1,18 @@
|
||||
import { Column, Entity } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { PermissionTypeDto } from '../dtos/permission.dto';
|
||||
|
||||
@Entity({ name: 'permission-type' })
|
||||
export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> {
|
||||
@Column({
|
||||
nullable: false,
|
||||
enum: Object.values(PermissionType),
|
||||
})
|
||||
type: string;
|
||||
|
||||
constructor(partial: Partial<PermissionTypeEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { PermissionTypeEntity } from './entities/permission.entity';
|
||||
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [TypeOrmModule.forFeature([PermissionTypeEntity])],
|
||||
})
|
||||
export class PermissionTypeRepositoryModule {}
|
||||
1
libs/common/src/modules/permission/repositories/index.ts
Normal file
1
libs/common/src/modules/permission/repositories/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './permission.repository'
|
||||
@ -0,0 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PermissionTypeEntity } from '../entities/permission.entity';
|
||||
|
||||
@Injectable()
|
||||
export class PermissionTypeRepository extends Repository<PermissionTypeEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(PermissionTypeEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
||||
@ -30,12 +30,23 @@ export class UserEntity extends AbstractEntity<UserDto> {
|
||||
})
|
||||
public lastName: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
public refreshToken: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
default: false,
|
||||
})
|
||||
public isUserVerified: boolean;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
default: true,
|
||||
})
|
||||
public isActive: boolean;
|
||||
|
||||
constructor(partial: Partial<UserEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
|
||||
Reference in New Issue
Block a user