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

View File

@@ -8,18 +8,19 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { APPUserService } from './user.service';
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/idp/client/client.service';
import { AppClientService } from '../client/appclient.service';
@UseGuards(AuthGuard, RolesGuard)
@Controller('app/user')
export class UserController {
constructor(
private userService: APPUserService,
private clientService: ClientService,
private userService: AppUserService,
private clientService: AppClientService,
) {}
@Get()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,11 @@
<h3 matDialogTitle>Client Redirect-URIs</h3>
<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) {
<div class="admin__item flex-row">
<span class="admin__name">{{ uri.uri }}</span>
@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>
}

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common';
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 { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
@@ -29,6 +29,8 @@ export class CreateClientComponent {
@ViewChild('stepper') stepper: MatStepper;
@Output('oncreate') createdClient = new EventEmitter();
createClient = new FormGroup({
clientName: 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({
next: data => {
console.log(this.stepper)
this.client = data;
this.createClient.enable();
this.stepper.next();
this.isSaving = false;
this.createdClient.emit();
}
})

View File

@@ -14,12 +14,13 @@
flex-direction: column;
}
.card-container__list{
margin: 0 auto;
margin: -12px;
display: flex;
flex-direction: column;
gap: 12px;
width: 500px;
overflow: auto;
padding: 12px;
}
.create-container{
@@ -70,10 +71,15 @@
background-position: center;
background-size: 20px;
background-repeat: no-repeat;
border-radius: 4px;
border-radius: 8px;
transition: all 0.2s ease-in-out;
&: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() {
this.dialog.open(CreateClientComponent, {
const ref = this.dialog.open(CreateClientComponent, {
panelClass: 'create-client__dialog'
})
ref.componentInstance.createdClient.subscribe(() => {
this.load();
})
}
openDeleteDialog(client: Client) {