* spec entfernt

* wip

* wip

* wip

* wip

* wip

* rework

* done

---------

Co-authored-by: Bastian Wagner <bastian.wagner@softconcis.de>
This commit is contained in:
Bastian Wagner
2024-09-11 16:01:55 +02:00
committed by GitHub
parent 2880925cd0
commit 2359c9c5e9
16 changed files with 63 additions and 30 deletions

View File

@@ -0,0 +1,10 @@
import { Injectable } from '@nestjs/common';
import { ClientRepository, RedirectRepository } from 'src/model';
import { ClientBaseService } from 'src/shared/client/client.base.service';
@Injectable()
export class AppClientService extends ClientBaseService {
constructor(clientRepo: ClientRepository, uriRepo: RedirectRepository) {
super(clientRepo, uriRepo);
}
}

View File

@@ -1,18 +1,16 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { MailerService } from '@nestjs-modules/mailer'; import { MailerService } from '@nestjs-modules/mailer';
import { ResetPWMailConfig } from '../model/mailconfig.interface'; import { IResetPWMailConfig } from 'src/model';
@Injectable() @Injectable()
export class MailService { export class MailService {
constructor( constructor(
private mailerService: MailerService, private mailerService: MailerService,
private readonly configService: ConfigService, private readonly configService: ConfigService,
) { ) {}
// this.sendMail();
}
sendResetMail(config: ResetPWMailConfig) { sendResetMail(config: IResetPWMailConfig) {
let baseUrl = this.configService.get<string>('CLIENT_URL'); let baseUrl = this.configService.get<string>('CLIENT_URL');
if (baseUrl.endsWith('/')) if (baseUrl.endsWith('/'))
baseUrl = baseUrl.substring(0, baseUrl.length - 1); baseUrl = baseUrl.substring(0, baseUrl.length - 1);
@@ -20,7 +18,7 @@ export class MailService {
baseUrl += '/' + config.url + '?resetcode=' + config.code; baseUrl += '/' + config.url + '?resetcode=' + config.code;
this.mailerService.sendMail({ this.mailerService.sendMail({
to: 'mail@bastian-wagner.de', to: config.to,
from: this.configService.get<string>('MAILER_FROM'), from: this.configService.get<string>('MAILER_FROM'),
subject: 'Passwort zurücksetzen', subject: 'Passwort zurücksetzen',
template: './pw-reset', template: './pw-reset',

View File

@@ -8,18 +8,19 @@ import {
Req, Req,
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { APPUserService } from './user.service';
import { AppUserService } from './user.service';
import { AuthGuard, Roles, RolesGuard } from 'src/shared/secure/guards'; import { AuthGuard, Roles, RolesGuard } from 'src/shared/secure/guards';
import { IAuthenticatedRequest, Client, RedirectUri } from 'src/model'; import { IAuthenticatedRequest, Client, RedirectUri } from 'src/model';
import { CreateClientDto } from 'src/model/dto/create-client.dto'; import { CreateClientDto } from 'src/model/dto/create-client.dto';
import { ClientService } from 'src/idp/client/client.service'; import { AppClientService } from '../client/appclient.service';
@UseGuards(AuthGuard, RolesGuard) @UseGuards(AuthGuard, RolesGuard)
@Controller('app/user') @Controller('app/user')
export class UserController { export class UserController {
constructor( constructor(
private userService: APPUserService, private userService: AppUserService,
private clientService: ClientService, private clientService: AppClientService,
) {} ) {}
@Get() @Get()

View File

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

View File

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

View File

@@ -19,6 +19,9 @@ export class ActivityLog {
@Column({ default: 0, type: 'int' }) @Column({ default: 0, type: 'int' })
loginCounter: number; loginCounter: number;
@Column({ default: 0, type: 'int', name: 'session_login' })
loginWithSessionCounter: number;
@Column({ default: 0, type: 'int' }) @Column({ default: 0, type: 'int' })
applicationLogin: number; applicationLogin: number;
@@ -47,6 +50,12 @@ export class ActivityLogRepository extends Repository<ActivityLog> {
return this.save(entity); return this.save(entity);
} }
async logSavedLogin(): Promise<ActivityLog> {
const entity = await this.getTodaysActivityLog();
entity.loginWithSessionCounter += 1;
return this.save(entity);
}
async logLogin(): Promise<ActivityLog> { async logLogin(): Promise<ActivityLog> {
const entity = await this.getTodaysActivityLog(); const entity = await this.getTodaysActivityLog();
entity.loginCounter += 1; entity.loginCounter += 1;

View File

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

View File

@@ -1,4 +1,4 @@
export interface ResetPWMailConfig { export interface IResetPWMailConfig {
to: string; to: string;
code: string; code: string;
url: string; url: string;

View File

@@ -126,15 +126,12 @@ export class UsersService {
if (getUserAccessToken) { if (getUserAccessToken) {
user.accessToken = this.createAccessToken(user); user.accessToken = this.createAccessToken(user);
user.refreshToken = this.createRefreshToken(user); user.refreshToken = this.createRefreshToken(user);
// this.logger.log(
// `User logged in with code on client ${clientId}`,
// 'systemlogin',
// );
return user; return user;
} }
const token = await this.createAuthToken(user, client); const token = await this.createAuthToken(user, client);
this.logger.log(`User logged in with code on client ${clientId}`, 'login'); this.logger.log(`User logged in with code on client ${clientId}`, 'login');
this.activityRepo.logSavedLogin();
return token; return token;
} }

View File

@@ -1,10 +1,11 @@
<h3 matDialogTitle>Client Redirect-URIs</h3> <h3 matDialogTitle>Client Redirect-URIs</h3>
<div mat-dialog-content class="content"> <div mat-dialog-content class="content">
<div class="mat-body" style="margin: 0 4px;">Trage hier alle Redirect URIS ein. Sie müssen mit dem gesendeten Parameter exakt überein stimmen.</div>
@for (uri of client.redirectUris; track $index) { @for (uri of client.redirectUris; track $index) {
<div class="admin__item flex-row"> <div class="admin__item flex-row">
<span class="admin__name">{{ uri.uri }}</span> <span class="admin__name">{{ uri.uri }}</span>
@if(client.redirectUris.length > 1) { @if(client.redirectUris.length > 1) {
<button mat-raised-button color="warn" class="remove_btn" (click)="removeUri(uri)" ><mat-icon>delete</mat-icon></button> <button mat-button color="warn" class="remove_btn" (click)="removeUri(uri)" ><mat-icon>delete</mat-icon></button>
} }
</div> </div>
} }

View File

@@ -17,5 +17,5 @@ mat-icon {
.remove_btn { .remove_btn {
min-width: 0; min-width: 0;
padding: 0 14px; padding: 0 12px;
} }

View File

@@ -34,7 +34,7 @@
<button mat-raised-button (click)="create()" [disabled]="createClient.invalid || isSaving"> <button mat-raised-button (click)="create()" [disabled]="createClient.invalid || isSaving">
<div class="flex-row"> <div class="flex-row">
<mat-spinner [diameter]="16" *ngIf="isSaving"></mat-spinner> <mat-spinner [diameter]="16" *ngIf="isSaving"></mat-spinner>
<div>Next</div> <div>Weiter</div>
</div> </div>
</button> </button>
</mat-step> </mat-step>
@@ -44,6 +44,9 @@
@if (client && client.admins) { @if (client && client.admins) {
<app-client-admins [client]="client" ></app-client-admins> <app-client-admins [client]="client" ></app-client-admins>
} }
<button mat-raised-button mat-dialog-close="" [disabled]="isSaving">
Schließen
</button>
</div> </div>
</mat-step> </mat-step>
</mat-stepper> </mat-stepper>

View File

@@ -1,4 +1,4 @@
form { form, .admin_container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24px;
@@ -13,6 +13,7 @@ form {
height: 100%; height: 100%;
} }
mat-dialog-content { mat-dialog-content, app-client-admins {
flex: 1 1 auto; flex: 1 1 auto;
} }

View File

@@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component, inject, ViewChild } from '@angular/core'; import { Component, EventEmitter, inject, Output, ViewChild } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
@@ -29,6 +29,8 @@ export class CreateClientComponent {
@ViewChild('stepper') stepper: MatStepper; @ViewChild('stepper') stepper: MatStepper;
@Output('oncreate') createdClient = new EventEmitter();
createClient = new FormGroup({ createClient = new FormGroup({
clientName: new FormControl(null, [Validators.required, Validators.minLength(4), Validators.maxLength(200)]), clientName: new FormControl(null, [Validators.required, Validators.minLength(4), Validators.maxLength(200)]),
clientSecret: new FormControl(null, [Validators.required, Validators.minLength(4), Validators.maxLength(200)]), clientSecret: new FormControl(null, [Validators.required, Validators.minLength(4), Validators.maxLength(200)]),
@@ -55,11 +57,11 @@ export class CreateClientComponent {
) )
.subscribe({ .subscribe({
next: data => { next: data => {
console.log(this.stepper)
this.client = data; this.client = data;
this.createClient.enable(); this.createClient.enable();
this.stepper.next(); this.stepper.next();
this.isSaving = false; this.isSaving = false;
this.createdClient.emit();
} }
}) })

View File

@@ -14,12 +14,13 @@
flex-direction: column; flex-direction: column;
} }
.card-container__list{ .card-container__list{
margin: 0 auto; margin: -12px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
width: 500px; width: 500px;
overflow: auto; overflow: auto;
padding: 12px;
} }
.create-container{ .create-container{
@@ -70,10 +71,15 @@
background-position: center; background-position: center;
background-size: 20px; background-size: 20px;
background-repeat: no-repeat; background-repeat: no-repeat;
border-radius: 4px; border-radius: 8px;
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
&:hover { &:hover {
background-color: #ccc; background-color: #e0e0e0;
} }
}
.deleting {
box-shadow: 0px 2px 4px -1px rgb(255 0 0 / 20%), 0px 4px 5px 0px rgb(255 0 0 / 14%), 0px 1px 10px 0px rgb(255 0 0 / 12%);
} }

View File

@@ -65,9 +65,12 @@ export class DashboardComponent implements OnInit {
} }
createClient() { createClient() {
this.dialog.open(CreateClientComponent, { const ref = this.dialog.open(CreateClientComponent, {
panelClass: 'create-client__dialog' panelClass: 'create-client__dialog'
}) })
ref.componentInstance.createdClient.subscribe(() => {
this.load();
})
} }
openDeleteDialog(client: Client) { openDeleteDialog(client: Client) {