Impersination backend
This commit is contained in:
@@ -22,7 +22,7 @@ import { LogModule } from './modules/log/log.module';
|
|||||||
envFilePath: ['.env'],
|
envFilePath: ['.env'],
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
}),
|
}),
|
||||||
CacheModule.register({ ttl: 1000, isGlobal: true }),
|
// CacheModule.register({ ttl: 1000, isGlobal: true }),
|
||||||
DatabaseModule,
|
DatabaseModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
UserModule,
|
UserModule,
|
||||||
@@ -38,10 +38,10 @@ import { LogModule } from './modules/log/log.module';
|
|||||||
providers: [
|
providers: [
|
||||||
AppService,
|
AppService,
|
||||||
AuthGuard,
|
AuthGuard,
|
||||||
{
|
// {
|
||||||
provide: APP_INTERCEPTOR,
|
// provide: APP_INTERCEPTOR,
|
||||||
useClass: CacheInterceptor,
|
// useClass: CacheInterceptor,
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export class AuthGuard implements CanActivate {
|
|||||||
if (payload.type != 'access') {
|
if (payload.type != 'access') {
|
||||||
throw new UnauthorizedException('wrong token');
|
throw new UnauthorizedException('wrong token');
|
||||||
}
|
}
|
||||||
const user = await this.authService.getUserById(payload.id);
|
const user = await this.authService.getUserById(payload.id, true);
|
||||||
if (!user.isActive) {
|
if (!user.isActive) {
|
||||||
throw new HttpException('not active', HttpStatus.FORBIDDEN);
|
throw new HttpException('not active', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|||||||
22
api/src/model/entitites/impersination.entity.ts
Normal file
22
api/src/model/entitites/impersination.entity.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn, Column } from "typeorm";
|
||||||
|
import { User } from "./user/user.entity";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Impersonation {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, { nullable: false, eager: true })
|
||||||
|
@JoinColumn({ name: 'fromUserId' })
|
||||||
|
fromUser: User;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, { nullable: false, eager: true })
|
||||||
|
@JoinColumn({ name: 'toUserId' })
|
||||||
|
toUser: User;
|
||||||
|
|
||||||
|
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
|
||||||
|
startedAt: Date;
|
||||||
|
|
||||||
|
@Column({ type: 'datetime', nullable: true })
|
||||||
|
endedAt?: Date;
|
||||||
|
}
|
||||||
11
api/src/model/repositories/impersination.repository.ts
Normal file
11
api/src/model/repositories/impersination.repository.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Repository, DataSource } from 'typeorm';
|
||||||
|
import { Impersonation } from '../entitites/impersination.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ImpersonationRepository extends Repository<Impersonation> {
|
||||||
|
constructor(dataSource: DataSource) {
|
||||||
|
super(Impersonation, dataSource.createEntityManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import { AuthService } from './auth.service';
|
|||||||
import { AuthCodeDto } from 'src/model/dto';
|
import { AuthCodeDto } from 'src/model/dto';
|
||||||
import { User } from 'src/model/entitites';
|
import { User } from 'src/model/entitites';
|
||||||
import { AuthGuard } from 'src/core/guards/auth.guard';
|
import { AuthGuard } from 'src/core/guards/auth.guard';
|
||||||
|
import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface';
|
||||||
|
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
@@ -30,7 +31,7 @@ export class AuthController {
|
|||||||
|
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@Get('me')
|
@Get('me')
|
||||||
getMe(@Req() req: any) {
|
getMe(@Req() req: AuthenticatedRequest) {
|
||||||
return req.user;
|
return req.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ import { JwtService } from '@nestjs/jwt';
|
|||||||
import { IExternalAccessPayload, IPayload } from 'src/model/interface';
|
import { IExternalAccessPayload, IPayload } from 'src/model/interface';
|
||||||
import { User } from 'src/model/entitites';
|
import { User } from 'src/model/entitites';
|
||||||
import { LogService, LogType } from '../log/log.service';
|
import { LogService, LogType } from '../log/log.service';
|
||||||
|
import { ImpersonationRepository } from 'src/model/repositories/impersination.repository';
|
||||||
|
import { IsNull } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
constructor(
|
constructor(
|
||||||
private userRepo: UserRepository,
|
private userRepo: UserRepository,
|
||||||
|
private impersinationRepo: ImpersonationRepository,
|
||||||
private readonly http: HttpService,
|
private readonly http: HttpService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private jwt: JwtService,
|
private jwt: JwtService,
|
||||||
@@ -119,9 +122,21 @@ export class AuthService {
|
|||||||
return bodyFormData;
|
return bodyFormData;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserById(id: string): Promise<User> {
|
async getUserById(id: string, withImpersination = false): Promise<User> {
|
||||||
|
|
||||||
this.log.log(LogType.Auth, null);
|
this.log.log(LogType.Auth, null);
|
||||||
return this.userRepo.findById(id);
|
let user = await this.userRepo.findById(id);
|
||||||
|
if (withImpersination) {
|
||||||
|
const impersination = await this.impersinationRepo.findOne({
|
||||||
|
where: { fromUser: { id: user.id }, endedAt: IsNull() },
|
||||||
|
relations: ['toUser']
|
||||||
|
});
|
||||||
|
if (impersination) {
|
||||||
|
return this.userRepo.findById(impersination.toUser.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNewToken(refresh: string) {
|
async getNewToken(refresh: string) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from 'src/model/entitites';
|
} from 'src/model/entitites';
|
||||||
import { EmailLog } from 'src/model/entitites/log';
|
import { EmailLog } from 'src/model/entitites/log';
|
||||||
import { KeySystem } from 'src/model/entitites/system.entity';
|
import { KeySystem } from 'src/model/entitites/system.entity';
|
||||||
|
import { Impersonation } from 'src/model/entitites/impersination.entity';
|
||||||
import { UserSettings } from 'src/model/entitites/user/user.settings.entity';
|
import { UserSettings } from 'src/model/entitites/user/user.settings.entity';
|
||||||
import {
|
import {
|
||||||
ActivityRepository,
|
ActivityRepository,
|
||||||
@@ -26,6 +27,7 @@ import {
|
|||||||
} from 'src/model/repositories';
|
} from 'src/model/repositories';
|
||||||
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
|
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
|
||||||
import { EmailLogRepository } from 'src/model/repositories/log';
|
import { EmailLogRepository } from 'src/model/repositories/log';
|
||||||
|
import { ImpersonationRepository } from 'src/model/repositories/impersination.repository';
|
||||||
|
|
||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
User,
|
User,
|
||||||
@@ -39,6 +41,7 @@ const ENTITIES = [
|
|||||||
Activity,
|
Activity,
|
||||||
EmailLog,
|
EmailLog,
|
||||||
UserSettings,
|
UserSettings,
|
||||||
|
Impersonation
|
||||||
];
|
];
|
||||||
const REPOSITORIES = [
|
const REPOSITORIES = [
|
||||||
UserRepository,
|
UserRepository,
|
||||||
@@ -51,7 +54,8 @@ const REPOSITORIES = [
|
|||||||
KeyHandoutRepository,
|
KeyHandoutRepository,
|
||||||
ActivityRepository,
|
ActivityRepository,
|
||||||
EmailLogRepository,
|
EmailLogRepository,
|
||||||
UserSettingsRepository
|
UserSettingsRepository,
|
||||||
|
ImpersonationRepository
|
||||||
];
|
];
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class HelperService {
|
|||||||
private readonly systemRepository: KeySystemRepository,
|
private readonly systemRepository: KeySystemRepository,
|
||||||
private readonly cylinderRepository: CylinderRepository,
|
private readonly cylinderRepository: CylinderRepository,
|
||||||
private readonly keyRepo: KeyRepository,
|
private readonly keyRepo: KeyRepository,
|
||||||
private cacheManager: Cache
|
// private cacheManager: Cache
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
||||||
@@ -61,11 +61,11 @@ export class HelperService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async cache() {
|
async cache() {
|
||||||
const value = await this.cacheManager.store.keys()
|
// const value = await this.cacheManager.store.keys()
|
||||||
console.log(value)
|
// console.log(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteKeyArchiveCache() {
|
async deleteKeyArchiveCache() {
|
||||||
await this.cacheManager.del('/key/archive');
|
// await this.cacheManager.del('/key/archive');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
client/src/app/model/interface/customer.interface.ts
Normal file
6
client/src/app/model/interface/customer.interface.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export interface ICustomer {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatetAt: String;
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import { AG_GRID_LOCALE_DE } from '@ag-grid-community/locale';
|
|||||||
import { AgGridAngular } from 'ag-grid-angular';
|
import { AgGridAngular } from 'ag-grid-angular';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { AgGridContainerComponent } from '../../../../shared/ag-grid/components/ag-grid-container/ag-grid-container.component';
|
import { AgGridContainerComponent } from '../../../../shared/ag-grid/components/ag-grid-container/ag-grid-container.component';
|
||||||
|
import { ICustomer } from '../../../../model/interface/customer.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-handover-dialog',
|
selector: 'app-handover-dialog',
|
||||||
@@ -82,8 +83,8 @@ export class HandoverDialogComponent extends AgGridContainerComponent {
|
|||||||
|
|
||||||
isLoading: boolean = false;
|
isLoading: boolean = false;
|
||||||
|
|
||||||
customers: { name: string, id: string }[] = [];
|
customers: ICustomer[] = [];
|
||||||
filteredCustomers: Observable<any[]> = new Observable();
|
filteredCustomers: Observable<ICustomer[]> = new Observable();
|
||||||
|
|
||||||
handoverForm = new FormGroup({
|
handoverForm = new FormGroup({
|
||||||
customer: new FormControl<any>(null, Validators.required),
|
customer: new FormControl<any>(null, Validators.required),
|
||||||
@@ -125,18 +126,14 @@ export class HandoverDialogComponent extends AgGridContainerComponent {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCustomers() {
|
async loadCustomers() {
|
||||||
|
const customers = await this.api.refreshCustomers()
|
||||||
|
|
||||||
return new Promise(async resolve => {
|
|
||||||
const customers = await this.api.getCustomers();
|
|
||||||
this.customers = customers;
|
this.customers = customers;
|
||||||
this.filteredCustomers = this.handoverForm.controls.customer.valueChanges.pipe(
|
this.filteredCustomers = this.handoverForm.controls.customer.valueChanges.pipe(
|
||||||
startWith(''),
|
startWith(''),
|
||||||
map(value => this._filter(value || '')),
|
map(value => this._filter(value || '')),
|
||||||
);
|
);
|
||||||
resolve(customers)
|
return Promise.resolve()
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _filter(value: string): any[] {
|
private _filter(value: string): any[] {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { IUser } from '../model/interface/user.interface';
|
|||||||
import { IKey } from '../model/interface/key.interface';
|
import { IKey } from '../model/interface/key.interface';
|
||||||
import { ICylinder } from '../model/interface/cylinder.interface';
|
import { ICylinder } from '../model/interface/cylinder.interface';
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
import { HotToastService } from '@ngxpert/hot-toast';
|
||||||
|
import { ICustomer } from '../model/interface/customer.interface';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -16,6 +17,7 @@ export class ApiService {
|
|||||||
public keys: BehaviorSubject<IKey[]> = new BehaviorSubject<IKey[]>([]);
|
public keys: BehaviorSubject<IKey[]> = new BehaviorSubject<IKey[]>([]);
|
||||||
public cylinders: BehaviorSubject<ICylinder[]> = new BehaviorSubject<ICylinder[]>([]);
|
public cylinders: BehaviorSubject<ICylinder[]> = new BehaviorSubject<ICylinder[]>([]);
|
||||||
public systems: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);
|
public systems: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);
|
||||||
|
public customers: BehaviorSubject<ICustomer[]> = new BehaviorSubject<ICustomer[]>([]);
|
||||||
|
|
||||||
|
|
||||||
public user: BehaviorSubject<IUser> = new BehaviorSubject<IUser>(null!);
|
public user: BehaviorSubject<IUser> = new BehaviorSubject<IUser>(null!);
|
||||||
@@ -55,6 +57,9 @@ export class ApiService {
|
|||||||
return this.http.get<IKey[]>('api/key')
|
return this.http.get<IKey[]>('api/key')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* triggert das Laden der Schlüssel vom Server und speichert sie in keys
|
||||||
|
*/
|
||||||
refreshKeys(): void{
|
refreshKeys(): void{
|
||||||
this.getKeys().subscribe({
|
this.getKeys().subscribe({
|
||||||
next: keys => {
|
next: keys => {
|
||||||
@@ -138,15 +143,24 @@ export class ApiService {
|
|||||||
return this.http.post('api/customer', data);
|
return this.http.post('api/customer', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCustomers(): Promise<any[]> {
|
private getCustomers(): Observable<ICustomer[]> {
|
||||||
return new Promise(resolve => {
|
return this.http.get<ICustomer[]>('api/customer')
|
||||||
this.http.get<any[]>('api/customer').subscribe({
|
|
||||||
next: (customers) => resolve(customers),
|
|
||||||
error: (err) => {
|
|
||||||
this.toast.error('Fehler beim Laden der Mieter');
|
|
||||||
resolve([]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* triggert das Laden der Schlüssel vom Server und speichert sie in keys
|
||||||
|
*/
|
||||||
|
refreshCustomers(): Promise<ICustomer[]> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.getCustomers().subscribe({
|
||||||
|
next: customers => {
|
||||||
|
this.customers.next(customers);
|
||||||
|
resolve(customers)
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.toast.error('Fehler beim Laden der Schlüssel');
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -176,6 +190,12 @@ export class ApiService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht das System vom Server und zeigt Toast an.
|
||||||
|
* Aktualisiert die Systeme danach
|
||||||
|
* @param system zu löschen
|
||||||
|
* @returns true oder false
|
||||||
|
*/
|
||||||
deleteSystem(system: any): Promise<boolean> {
|
deleteSystem(system: any): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
this.http.delete(`api/system${system.id}`).pipe(
|
this.http.delete(`api/system${system.id}`).pipe(
|
||||||
@@ -239,6 +259,11 @@ export class ApiService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert die Schließanlagen im Behaviour Subject
|
||||||
|
* systems
|
||||||
|
* @returns Promise wenn geladen
|
||||||
|
*/
|
||||||
refreshSystems(): Promise<void> {
|
refreshSystems(): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.getSystems().subscribe({
|
this.getSystems().subscribe({
|
||||||
|
|||||||
Reference in New Issue
Block a user