Reworked structure
This commit is contained in:
Bastian Wagner
2024-09-11 15:12:51 +02:00
committed by GitHub
parent 6f704a2576
commit 2880925cd0
40 changed files with 212 additions and 281 deletions

View File

@@ -6,7 +6,6 @@ import { Response } from 'express';
export class AppController {
@Get('*')
handleClientRoutes(@Res() res: Response) {
console.log("handle")
res.sendFile(join(__dirname, '..', 'client', 'index.html'));
}
}

View File

@@ -1,24 +1,18 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { LoggerModule } from './core/logger.module';
import { SessionMiddleware } from './core/session.middleware';
import { ClientModule } from './client/client.module';
import { ApplicationModule } from './application/application.module';
import { MailModule } from './application/mail/mail.module';
import { IDPModule } from './idp/idp.module';
@Module({
imports: [
AuthModule,
ClientModule,
ConfigModule.forRoot({
envFilePath: ['.env'],
isGlobal: true,
}),
LoggerModule,
ServeStaticModule.forRoot({
rootPath: join(__dirname, '../client'),
exclude: ['*/api*'],
@@ -39,19 +33,10 @@ import { MailModule } from './application/mail/mail.module';
}),
ApplicationModule,
MailModule,
// TypeOrmModule.forRoot({
// type: 'mysql',
// host: '85.215.137.185', // MySQL Hostname
// port: 3306, // MySQL Port (Standard ist 3306)
// username: 'root', // Dein MySQL-Benutzername
// password: 'Battlefield123', // Dein MySQL-Passwort
// database: 'global_users', // Name der Datenbank
// entities: [User, Client, RedirectUri, AuthorizationCode], // Hier werden deine Entitäten aufgelistet
// synchronize: true, // Setze dies auf `false` in der Produktion
// }),
IDPModule,
],
controllers: [],
providers: [AppService],
providers: [],
exports: [],
})
export class AppModule implements NestModule {

View File

@@ -1,8 +0,0 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ApplicationController } from './application.controller';
describe('ApplicationController', () => {
let controller: ApplicationController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ApplicationController],
}).compile();
controller = module.get<ApplicationController>(ApplicationController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -1,7 +1,7 @@
import { Body, Controller, Get, Post } from '@nestjs/common';
import { User } from 'src/model';
import { LoginUserDto } from 'src/model/dto';
import { UsersService } from 'src/users/users.service';
import { UsersService } from 'src/shared/users.service';
@Controller('app')
export class ApplicationController {

View File

@@ -1,13 +1,13 @@
import { Module } from '@nestjs/common';
import { ApplicationController } from './application.controller';
import { UserModule } from './user/user.module';
import { LoggerModule } from 'src/core/logger.module';
import { SecureModule } from 'src/core/secure/secure.module';
import { MailModule } from './mail/mail.module';
import { SharedModule } from 'src/shared/shared.module';
import { SecureModule } from 'src/shared/secure/secure.module';
@Module({
controllers: [ApplicationController],
providers: [],
imports: [LoggerModule, UserModule, SecureModule, MailModule],
imports: [UserModule, SecureModule, MailModule, SharedModule],
})
export class ApplicationModule {}

View File

@@ -0,0 +1,60 @@
import { Injectable } from '@nestjs/common';
import { ClientRepository, LogRepository, User, Client } from 'src/model';
@Injectable()
export class ApplicationService {
constructor(
private clientRepository: ClientRepository,
private logRepository: LogRepository,
) {}
getUserClients(user: User): Promise<Client[]> {
return this.clientRepository.find({
where: { admins: { id: user.id } },
relations: ['admins'],
order: { createdAt: 'ASC' },
});
}
async getUserLogins() {
const logs = await this.logRepository.find({
where: [{ type: 'login' }, { type: 'systemlogin' }],
order: { timestamp: 'asc' },
});
const first = new Date(logs[0].timestamp.toISOString());
const last = logs[logs.length - 1].timestamp.toISOString().substring(0, 10);
const res = {};
res[first.toISOString().substring(0, 10)] = {
logins: 0,
systemLogins: 0,
};
if (logs.length > 1) {
const current = first;
while (current.toISOString().substring(0, 10) <= last) {
res[current.toISOString().substring(0, 10)] = {
logins: 0,
systemLogins: 0,
};
current.setDate(current.getDate() + 1);
}
logs.forEach((l) => {
if (l.type == 'login') {
res[l.timestamp.toISOString().substring(0, 10)].logins += 1;
} else if (l.type == 'systemlogin') {
res[l.timestamp.toISOString().substring(0, 10)].systemLogins += 1;
}
});
}
return Object.entries(res).map(([date, count]) => ({
date: new Date(date),
count,
}));
}
}

View File

@@ -1,14 +1,14 @@
import { Module } from '@nestjs/common';
import { DatabaseModule } from 'src/core/database/database.module';
import { MailerModule } from '@nestjs-modules/mailer';
import { join } from 'path';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { MailService } from './mail.service';
import { SharedModule } from 'src/shared/shared.module';
@Module({
imports: [
DatabaseModule,
SharedModule,
MailerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MailService } from './mail.service';
describe('MailService', () => {
let service: MailService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MailService],
}).compile();
service = module.get<MailService>(MailService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
describe('UserController', () => {
let controller: UserController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UserController],
}).compile();
controller = module.get<UserController>(UserController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -8,17 +8,17 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { UserService } from './user.service';
import { AuthGuard, Roles, RolesGuard } from 'src/core/secure/guards';
import { AuthenticatedRequest, Client, RedirectUri } from 'src/model';
import { APPUserService } from './user.service';
import { AuthGuard, Roles, RolesGuard } from 'src/shared/secure/guards';
import { IAuthenticatedRequest, Client, RedirectUri } from 'src/model';
import { CreateClientDto } from 'src/model/dto/create-client.dto';
import { ClientService } from 'src/client/client.service';
import { ClientService } from 'src/idp/client/client.service';
@UseGuards(AuthGuard, RolesGuard)
@Controller('app/user')
export class UserController {
constructor(
private userService: UserService,
private userService: APPUserService,
private clientService: ClientService,
) {}
@@ -29,13 +29,13 @@ export class UserController {
@Roles('admin')
@Get('clients')
getClients(@Req() req: AuthenticatedRequest): Promise<Client[]> {
getClients(@Req() req: IAuthenticatedRequest): Promise<Client[]> {
return this.userService.getUserClients(req.user);
}
@Roles('admin')
@Post('client')
createClient(@Req() req: AuthenticatedRequest, @Body() b: CreateClientDto) {
createClient(@Req() req: IAuthenticatedRequest, @Body() b: CreateClientDto) {
return this.clientService.createClient(
req.user,
b.clientName,
@@ -47,13 +47,13 @@ export class UserController {
@Roles('admin')
@Delete('client/:id')
deleteClient(@Req() req: AuthenticatedRequest, @Param('id') id) {
deleteClient(@Req() req: IAuthenticatedRequest, @Param('id') id) {
return this.clientService.deleteClient(req.user, id);
}
@Post('clients/:id/redirect')
saveUris(
@Req() req: AuthenticatedRequest,
@Req() req: IAuthenticatedRequest,
@Param('id') id,
@Body() uris: RedirectUri[],
): Promise<RedirectUri[]> {

View File

@@ -1,13 +1,12 @@
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { SecureModule } from 'src/core/secure/secure.module';
import { UserService } from './user.service';
import { ClientService } from 'src/client/client.service';
import { DatabaseModule } from 'src/core/database/database.module';
import { APPUserService } from './user.service';
import { SharedModule } from 'src/shared/shared.module';
import { SecureModule } from 'src/shared/secure/secure.module';
@Module({
controllers: [UserController],
imports: [SecureModule, DatabaseModule],
providers: [UserService, ClientService],
imports: [SecureModule, SharedModule],
providers: [APPUserService],
})
export class UserModule {}

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UserService],
}).compile();
service = module.get<UserService>(UserService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { ClientRepository, LogRepository, User, Client } from 'src/model';
@Injectable()
export class UserService {
export class APPUserService {
constructor(
private clientRepository: ClientRepository,
private logRepository: LogRepository,

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get<AuthController>(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -1,31 +0,0 @@
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { ClientService } from 'src/client/client.service';
import { JwtModule } from '@nestjs/jwt';
import { NestjsFormDataModule } from 'nestjs-form-data';
import { LoggerModule } from 'src/core/logger.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { DatabaseModule } from 'src/core/database/database.module';
import { MailModule } from 'src/application/mail/mail.module';
import { SecureModule } from 'src/core/secure/secure.module';
@Module({
providers: [ClientService],
controllers: [AuthController],
imports: [
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
secret: config.get('JWT_SECRET'),
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN') },
}),
}),
NestjsFormDataModule,
LoggerModule,
DatabaseModule,
MailModule,
SecureModule,
],
})
export class AuthModule {}

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ClientService } from './client.service';
describe('ClientService', () => {
let service: ClientService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ClientService],
}).compile();
service = module.get<ClientService>(ClientService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -1,31 +0,0 @@
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { NestjsFormDataModule } from 'nestjs-form-data';
import { ClientService } from 'src/client/client.service';
import { UsersService } from 'src/users/users.service';
import { LoggerModule } from '../logger.module';
import { AuthGuard } from './guards/auth.guard';
import { DatabaseModule } from '../database/database.module';
import { RolesGuard } from './guards/roles.guard';
import { MailModule } from 'src/application/mail/mail.module';
@Module({
imports: [
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
secret: config.get('JWT_SECRET'),
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN') },
}),
}),
NestjsFormDataModule,
DatabaseModule,
LoggerModule,
MailModule,
],
providers: [UsersService, ClientService, AuthGuard, RolesGuard],
exports: [JwtModule, UsersService, AuthGuard, RolesGuard],
})
export class SecureModule {}

View File

@@ -1,11 +1,11 @@
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
import { ClientService } from 'src/client/client.service';
import { ClientService } from 'src/idp/client/client.service';
import { FormDataRequest } from 'nestjs-form-data';
import { CustomLogger } from 'src/core/custom.logger';
import { CreateUserDto } from 'src/model/dto/create-user.dto';
import { RequestResetPwDto, ResetPWDto } from 'src/model/dto';
import { Client } from 'src/model';
import { UsersService } from 'src/shared/users.service';
import { CustomLogger } from 'src/shared/logger/custom.logger';
@Controller('auth')
export class AuthController {

View File

@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { ClientService } from 'src/idp/client/client.service';
import { NestjsFormDataModule } from 'nestjs-form-data';
import { MailModule } from 'src/application/mail/mail.module';
import { SharedModule } from 'src/shared/shared.module';
import { SecureModule } from 'src/shared/secure/secure.module';
@Module({
providers: [ClientService],
controllers: [AuthController],
imports: [NestjsFormDataModule, SharedModule, MailModule, SecureModule],
})
export class AuthModule {}

View File

@@ -1,12 +1,11 @@
import { Module } from '@nestjs/common';
import { NestjsFormDataModule } from 'nestjs-form-data';
import { LoggerModule } from 'src/core/logger.module';
import { ClientController } from './client.controller';
import { DatabaseModule } from 'src/core/database/database.module';
import { SharedModule } from 'src/shared/shared.module';
@Module({
providers: [],
controllers: [ClientController],
imports: [NestjsFormDataModule, DatabaseModule, LoggerModule],
imports: [NestjsFormDataModule, SharedModule],
})
export class ClientModule {}

View File

@@ -0,0 +1,40 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import {
ClientRepository,
RedirectRepository,
Client,
RedirectUri,
} from 'src/model';
import { ClientBaseService } from 'src/shared/client/client.base.service';
@Injectable()
export class ClientService extends ClientBaseService {
constructor(clientRepo: ClientRepository, uriRepo: RedirectRepository) {
super(clientRepo, uriRepo);
}
async getClient(
clientId: string,
responseType: string,
redirectUri: string,
scope: string,
): Promise<Client> {
if (responseType !== 'code') {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
const client = await this.clientRepo.findById(clientId);
// console.log(client);
if (!client) {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
if (!client.redirectUris.some((u: RedirectUri) => u.uri === redirectUri)) {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
if (!scope) {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
return client;
}
}

11
idp/src/idp/idp.module.ts Normal file
View File

@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { SharedModule } from 'src/shared/shared.module';
import { AuthModule } from './auth/auth.module';
import { ClientModule } from './client/client.module';
@Module({
providers: [],
controllers: [],
imports: [SharedModule, AuthModule, ClientModule],
})
export class IDPModule {}

View File

@@ -1,5 +1,5 @@
import { User } from '../entity';
export interface AuthenticatedRequest extends Request {
export interface IAuthenticatedRequest extends Request {
user: User;
}

View File

@@ -1 +1,2 @@
export * from './authenticated.request';
export * from './logger.interface';

View File

@@ -1,4 +1,4 @@
export interface CustomLogger {
export interface ICustomLogger {
log(message: string, context?: string): void;
error(message: string, trace?: string, context?: string): void;
warn(message: string, context?: string): void;

View File

@@ -1,18 +1,17 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { HttpException, HttpStatus } from '@nestjs/common';
import {
Client,
ClientRepository,
RedirectRepository,
User,
Client,
RedirectUri,
User,
} from 'src/model';
import { v4 as uuidv4 } from 'uuid';
@Injectable()
export class ClientService {
export class ClientBaseService {
constructor(
private clientRepo: ClientRepository,
private uriRepo: RedirectRepository,
protected clientRepo: ClientRepository,
protected uriRepo: RedirectRepository,
) {}
async createClient(
@@ -43,31 +42,6 @@ export class ClientService {
return client;
}
async getClient(
clientId: string,
responseType: string,
redirectUri: string,
scope: string,
): Promise<Client> {
if (responseType !== 'code') {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
const client = await this.clientRepo.findById(clientId);
// console.log(client);
if (!client) {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
if (!client.redirectUris.some((u: RedirectUri) => u.uri === redirectUri)) {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
if (!scope) {
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
}
return client;
}
getClientById(clientId: string): Promise<Client> {
return this.clientRepo.findById(clientId);
}

2
idp/src/shared/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './logger/logger.module';
export * from './database/database.module';

View File

@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { CustomLogger } from './custom.logger';
import { DatabaseModule } from './database/database.module';
import { DatabaseModule } from '../database/database.module';
@Module({
providers: [CustomLogger],

View File

@@ -6,7 +6,7 @@ import {
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from 'src/users/users.service';
import { UsersService } from 'src/shared/users.service';
@Injectable()
export class AuthGuard implements CanActivate {

View File

@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { NestjsFormDataModule } from 'nestjs-form-data';
import { ClientService } from 'src/idp/client/client.service';
import { AuthGuard } from './guards/auth.guard';
import { RolesGuard } from './guards/roles.guard';
import { MailModule } from 'src/application/mail/mail.module';
import { SharedModule } from 'src/shared/shared.module';
@Module({
imports: [NestjsFormDataModule, SharedModule, MailModule, SharedModule],
providers: [ClientService, AuthGuard, RolesGuard],
exports: [AuthGuard, RolesGuard],
})
export class SecureModule {}

View File

@@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { UsersService } from './users.service';
import { MailService } from 'src/application/mail/mail.service';
import { ClientService } from 'src/idp/client/client.service';
import { LoggerModule } from './logger/logger.module';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
const MODULES = [DatabaseModule, LoggerModule];
const SERVICES = [UsersService, MailService, ClientService];
@Module({
imports: [
...MODULES,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
secret: config.get('JWT_SECRET'),
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN') },
}),
}),
],
controllers: [],
providers: [...SERVICES],
exports: [...MODULES, ...SERVICES, JwtModule],
})
export class SharedModule {}

View File

@@ -1,9 +1,8 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { v4 as uuidv4 } from 'uuid';
import * as bcrypt from 'bcrypt';
import { ClientService } from 'src/client/client.service';
import { ClientService } from 'src/idp/client/client.service';
import { JwtService } from '@nestjs/jwt';
import { CustomLogger } from 'src/core/custom.logger';
import { CreateUserDto } from 'src/model/dto/create-user.dto';
import { LoginUserDto, RequestResetPwDto, ResetPWDto } from 'src/model/dto';
import { MailService } from 'src/application/mail/mail.service';
@@ -17,6 +16,7 @@ import {
AuthorizationCode,
ActivityLogRepository,
} from 'src/model';
import { CustomLogger } from './logger/custom.logger';
@Injectable()
export class UsersService {

View File

@@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});