Bläddra i källkod

fix api and login?

master
Daniel 1 år sedan
förälder
incheckning
f63132d7dc
27 ändrade filer med 268 tillägg och 305 borttagningar
  1. +1
    -1
      angular/openapi.json
  2. +21
    -24
      angular/openapi.yaml
  3. +4
    -6
      angular/src/app/_forms/apiForms.ts
  4. +1
    -2
      angular/src/app/_models/index.ts
  5. +0
    -12
      angular/src/app/_models/user.ts
  6. +10
    -63
      angular/src/app/_services/account.service.ts
  7. +0
    -23
      angular/src/app/_services/log.service.ts
  8. +0
    -2
      angular/src/app/_views/account/account-routing.module.ts
  9. +0
    -2
      angular/src/app/_views/account/account.module.ts
  10. +0
    -43
      angular/src/app/_views/account/register.component.html
  11. +0
    -59
      angular/src/app/_views/account/register.component.ts
  12. +0
    -1
      angular/src/app/_views/dashboard/dashboard.component.ts
  13. +2
    -2
      angular/src/app/_views/home/home.component.ts
  14. +2
    -2
      angular/src/app/_views/user/user-detail/user-detail.component.ts
  15. +3
    -3
      angular/src/app/app.component.ts
  16. +0
    -1
      angular/src/app/core/api/v1/.openapi-generator/FILES
  17. +2
    -8
      angular/src/app/core/api/v1/model/authResponse.ts
  18. +6
    -0
      angular/src/app/core/api/v1/model/authResponseUser.ts
  19. +0
    -1
      angular/src/app/core/api/v1/model/models.ts
  20. +2
    -0
      angular/src/app/core/api/v1/model/user.ts
  21. +2
    -0
      angular/src/app/core/api/v1/model/userJsonld.ts
  22. +1
    -0
      httpdocs/composer.json
  23. +175
    -3
      httpdocs/composer.lock
  24. +6
    -0
      httpdocs/src/ApiResource/UserApi.php
  25. +1
    -0
      httpdocs/src/Mapper/UserEntityToApiMapper.php
  26. +3
    -33
      httpdocs/src/OpenApi/AuthDecorator.php
  27. +26
    -14
      httpdocs/src/Security/JwtAuthenticator.php

+ 1
- 1
angular/openapi.json
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 21
- 24
angular/openapi.yaml Visa fil

@@ -2400,6 +2400,16 @@ components:
type: string
active:
type: boolean
roles:
readOnly: true
type: array
items:
type: string
token:
readOnly: true
type:
- string
- 'null'
createdAt:
readOnly: true
type:
@@ -2474,6 +2484,16 @@ components:
type: string
active:
type: boolean
roles:
readOnly: true
type: array
items:
type: string
token:
readOnly: true
type:
- string
- 'null'
createdAt:
readOnly: true
type:
@@ -2905,31 +2925,8 @@ components:
AuthResponse:
type: object
properties:
token:
type: string
readOnly: true
id:
type: integer
readOnly: true
email:
type: string
readOnly: true
firstName:
type: string
readOnly: true
lastName:
type: string
readOnly: true
roles:
type: array
items:
type: string
readOnly: true
user:
type: object
allOf:
-
$ref: '#/components/schemas/User'
$ref: '#/components/schemas/User.jsonld'
readOnly: true
responses: { }
parameters: { }


+ 4
- 6
angular/src/app/_forms/apiForms.ts Visa fil

@@ -87,6 +87,8 @@ export const userForm = new FormGroup({
fullName: new FormControl(null, []),
password: new FormControl(null, []),
active: new FormControl(null, []),
roles: new FormControl(null, []),
token: new FormControl(null, []),
createdAt: new FormControl(null, [])
});

@@ -100,6 +102,8 @@ export const userJsonldForm = new FormGroup({
fullName: new FormControl(null, []),
password: new FormControl(null, []),
active: new FormControl(null, []),
roles: new FormControl(null, []),
token: new FormControl(null, []),
createdAt: new FormControl(null, [])
});

@@ -193,11 +197,5 @@ export const credentialsForm = new FormGroup({
});

export const authResponseForm = new FormGroup({
token: new FormControl(null, []),
id: new FormControl(null, []),
email: new FormControl(null, []),
firstName: new FormControl(null, []),
lastName: new FormControl(null, []),
roles: new FormControl(null, []),
user: new FormControl(null, [])
});

+ 1
- 2
angular/src/app/_models/index.ts Visa fil

@@ -1,2 +1 @@
export * from './alert';
export * from './user';
export * from './alert';

+ 0
- 12
angular/src/app/_models/user.ts Visa fil

@@ -1,12 +0,0 @@
import {UserJsonld} from "@app/core/api/v1";

export class User {
id?: string;
email?: string;
password?: string;
firstName?: string;
lastName?: string;
roles?: string[];
token?: string;
userResource?: UserJsonld;
}

+ 10
- 63
angular/src/app/_services/account.service.ts Visa fil

@@ -4,21 +4,19 @@ import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { environment } from '@environments/environment';
import { User } from '@app/_models';
import {AuthService} from "@app/core/api/v1";
import {AuthService, UserJsonld} from "@app/core/api/v1";


@Injectable({ providedIn: 'root' })
export class AccountService {

private userSubject: BehaviorSubject<User | null>;
public user: Observable<User | null>;
private userSubject: BehaviorSubject<UserJsonld | null>;
public user: Observable<UserJsonld | null>;

constructor(
private router: Router,
private http: HttpClient,
private loginCheckService: AuthService
private authService: AuthService
) {
this.userSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('user')!));
this.user = this.userSubject.asObservable();
@@ -29,24 +27,12 @@ export class AccountService {
}

login(email: string, password: string) {
return this.loginCheckService.postCredentialsItem(
{ email, password }
).pipe(map(authResponse => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(authResponse.user));
if (authResponse.user) {
this.userSubject.next(authResponse.user);
}
return authResponse.user;
}));
//
// return this.http.post<User>(`${environment.apiUrl}/auth`, { email, password })
// .pipe(map(user => {
// // store user details and jwt token in local storage to keep user logged in between page refreshes
// localStorage.setItem('user', JSON.stringify(user));
// this.userSubject.next(user);
// return user;
// }));
return this.authService.postCredentialsItem({ email, password })
.pipe(map(response => {
localStorage.setItem('user', JSON.stringify(response.user));
this.userSubject.next(response.user || null);
return response.user;
}));
}

logout() {
@@ -73,43 +59,4 @@ export class AccountService {
}
return false;
}

register(user: User) {
return this.http.post(`${environment.apiUrl}/users/register`, user);
}

getAll() {
return this.http.get<User[]>(`${environment.apiUrl}/users`);
}

getById(id: string) {
return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
}

update(id: string, params: any) {
return this.http.put(`${environment.apiUrl}/users/${id}`, params)
.pipe(map(x => {
// update stored user if the logged in user updated their own record
if (id == this.userValue?.id) {
// update local storage
const user = { ...this.userValue, ...params };
localStorage.setItem('user', JSON.stringify(user));

// publish updated user to subscribers
this.userSubject.next(user);
}
return x;
}));
}

delete(id: string) {
return this.http.delete(`${environment.apiUrl}/users/${id}`)
.pipe(map(x => {
// auto logout if the logged in user deleted their own record
if (id == this.userValue?.id) {
this.logout();
}
return x;
}));
}
}

+ 0
- 23
angular/src/app/_services/log.service.ts Visa fil

@@ -1,23 +0,0 @@
import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {User} from "@app/_models";
import {environment} from "@environments/environment";

@Injectable({
providedIn: 'root'
})
export class LogService {

constructor(
private httpClient: HttpClient
) {

}

getLogs(lines: number) {
return this.httpClient.get(
`${environment.apiUrl}/logs/logs/${lines}`,
);
}

}

+ 0
- 2
angular/src/app/_views/account/account-routing.module.ts Visa fil

@@ -3,14 +3,12 @@ import { Routes, RouterModule } from '@angular/router';

import { LayoutComponent } from './layout.component';
import { LoginComponent } from './login.component';
import { RegisterComponent } from './register.component';

const routes: Routes = [
{
path: '', component: LayoutComponent,
children: [
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent }
]
}
];


+ 0
- 2
angular/src/app/_views/account/account.module.ts Visa fil

@@ -5,7 +5,6 @@ import { CommonModule } from '@angular/common';
import { AccountRoutingModule } from './account-routing.module';
import { LayoutComponent } from './layout.component';
import { LoginComponent } from './login.component';
import { RegisterComponent } from './register.component';

@NgModule({
imports: [
@@ -16,7 +15,6 @@ import { RegisterComponent } from './register.component';
declarations: [
LayoutComponent,
LoginComponent,
RegisterComponent
]
})
export class AccountModule { }

+ 0
- 43
angular/src/app/_views/account/register.component.html Visa fil

@@ -1,43 +0,0 @@
<div class="card">
<h4 class="card-header">Register</h4>
<div class="card-body">
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="mb-3">
<label class="form-label">First Name</label>
<input type="text" formControlName="firstName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f['firstName'].errors }" />
<div *ngIf="submitted && f['firstName'].errors" class="invalid-feedback">
<div *ngIf="f['firstName'].hasError('required')">First Name is required</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Last Name</label>
<input type="text" formControlName="lastName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f['lastName'].errors }" />
<div *ngIf="submitted && f['lastName'].errors" class="invalid-feedback">
<div *ngIf="f['lastName'].hasError('required')">Last Name is required</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" formControlName="username" class="form-control" [ngClass]="{ 'is-invalid': submitted && f['username'].errors }" />
<div *ngIf="submitted && f['username'].errors" class="invalid-feedback">
<div *ngIf="f['username'].hasError('required')">Username is required</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': submitted && f['password'].errors }" />
<div *ngIf="submitted && f['password'].errors" class="invalid-feedback">
<div *ngIf="f['password'].hasError('required')">Password is required</div>
<div *ngIf="f['password'].hasError('minlength')">Password must be at least 6 characters</div>
</div>
</div>
<div>
<button [disabled]="loading" class="btn btn-primary">
<span *ngIf="loading" class="spinner-border spinner-border-sm me-1"></span>
Register
</button>
<a routerLink="../login" class="btn btn-link">Cancel</a>
</div>
</form>
</div>
</div>

+ 0
- 59
angular/src/app/_views/account/register.component.ts Visa fil

@@ -1,59 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';

import { AccountService, AlertService } from '@app/_services';

@Component({ templateUrl: 'register.component.html' })
export class RegisterComponent implements OnInit {
form!: FormGroup;
loading = false;
submitted = false;

constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private accountService: AccountService,
private alertService: AlertService
) { }

ngOnInit() {
this.form = this.formBuilder.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
username: ['', Validators.required],
password: ['', [Validators.required, Validators.minLength(6)]]
});
}

// convenience getter for easy access to form fields
get f() { return this.form.controls; }

onSubmit() {
this.submitted = true;

// reset alerts on submit
this.alertService.clear();

// stop here if form is invalid
if (this.form.invalid) {
return;
}

this.loading = true;
this.accountService.register(this.form.value)
.pipe(first())
.subscribe({
next: () => {
this.alertService.success('Registration successful', { keepAfterRouteChange: true });
this.router.navigate(['../login'], { relativeTo: this.route });
},
error: error => {
this.alertService.error(error);
this.loading = false;
}
});
}
}

+ 0
- 1
angular/src/app/_views/dashboard/dashboard.component.ts Visa fil

@@ -1,5 +1,4 @@
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {LogService} from "@app/_services/log.service";
import {FormControl} from "@angular/forms";

@Component({


+ 2
- 2
angular/src/app/_views/home/home.component.ts Visa fil

@@ -1,8 +1,8 @@
import {AfterViewInit, Component, OnInit} from '@angular/core';

import {User} from '@app/_models';
import {AccountService} from '@app/_services';
import {AppHelperService} from "@app/_helpers/app-helper.service";
import {UserJsonld} from "@app/core/api/v1";

@Component({
templateUrl: 'home.component.html',
@@ -10,7 +10,7 @@ import {AppHelperService} from "@app/_helpers/app-helper.service";
})
export class HomeComponent implements OnInit, AfterViewInit {

protected user: User | null;
protected user: UserJsonld | null;

constructor(
private accountService: AccountService,


+ 2
- 2
angular/src/app/_views/user/user-detail/user-detail.component.ts Visa fil

@@ -49,8 +49,8 @@ export class UserDetailComponent implements OnInit, AfterViewInit {
}

setIsCurrentUser() {
if (this.accountService.userValue?.userResource) {
let user = this.accountService.userValue?.userResource;
if (this.accountService.userValue) {
let user = this.accountService.userValue;
this.isCurrentUser = this.appHelperService.extractId(this.user.id) == user?.id;
}
}


+ 3
- 3
angular/src/app/app.component.ts Visa fil

@@ -1,11 +1,11 @@
import {ChangeDetectorRef, Component, Inject, OnInit, Renderer2} from '@angular/core';
import {AccountService} from './_services';
import {User} from './_models';
import {TranslateService} from "@ngx-translate/core";
import {environment} from "@environments/environment";
import {Subscription} from "rxjs";
import {LoadingService} from "@app/_services/loading.service";
import {DOCUMENT} from "@angular/common";
import {UserJsonld} from "@app/core/api/v1";

@Component({
selector: 'app-root',
@@ -14,7 +14,7 @@ import {DOCUMENT} from "@angular/common";
})
export class AppComponent implements OnInit {
protected readonly environment = environment;
protected user?: User | null;
protected user?: UserJsonld | null;
protected loadingSub: Subscription;
protected loading: boolean = false;
protected isLeftHanded = false;
@@ -55,7 +55,7 @@ export class AppComponent implements OnInit {
// TODO: Hilfsfunktion - entfernen
copyTokenToClipboard() {
const el = document.createElement('textarea');
el.value = this.user?.token !== undefined ? this.user.token : "";
el.value = this.user?.token || "";
document.body.appendChild(el);
el.select();
document.execCommand('copy');


+ 0
- 1
angular/src/app/core/api/v1/.openapi-generator/FILES Visa fil

@@ -33,7 +33,6 @@ model/apiUsersGetCollection200Response.ts
model/apiVesselsGetCollection200Response.ts
model/apiZonesGetCollection200Response.ts
model/authResponse.ts
model/authResponseUser.ts
model/credentials.ts
model/location.ts
model/locationJsonld.ts


+ 2
- 8
angular/src/app/core/api/v1/model/authResponse.ts Visa fil

@@ -9,16 +9,10 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { AuthResponseUser } from './authResponseUser';
import { UserJsonld } from './userJsonld';


export interface AuthResponse {
readonly token?: string;
readonly id?: number;
readonly email?: string;
readonly firstName?: string;
readonly lastName?: string;
roles?: Array<string>;
user?: AuthResponseUser;
readonly user?: UserJsonld;
}


+ 6
- 0
angular/src/app/core/api/v1/model/authResponseUser.ts Visa fil

@@ -9,9 +9,13 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { LocationJsonldContext } from './locationJsonldContext';


export interface AuthResponseUser {
context?: LocationJsonldContext;
readonly id?: any | null;
readonly type?: any | null;
readonly dbId?: any | null;
email: any | null;
firstName: any | null;
@@ -24,6 +28,8 @@ export interface AuthResponseUser {
*/
password?: any | null;
active?: any | null;
roles?: any | null;
readonly token?: any | null;
readonly createdAt?: any | null;
}


+ 0
- 1
angular/src/app/core/api/v1/model/models.ts Visa fil

@@ -13,7 +13,6 @@ export * from './apiUsersGetCollection200Response';
export * from './apiVesselsGetCollection200Response';
export * from './apiZonesGetCollection200Response';
export * from './authResponse';
export * from './authResponseUser';
export * from './credentials';
export * from './location';
export * from './locationJsonld';


+ 2
- 0
angular/src/app/core/api/v1/model/user.ts Visa fil

@@ -27,6 +27,8 @@ export interface User {
*/
password?: string;
active?: boolean;
roles?: Array<string>;
readonly token?: string | null;
readonly createdAt?: string | null;
}


+ 2
- 0
angular/src/app/core/api/v1/model/userJsonld.ts Visa fil

@@ -31,6 +31,8 @@ export interface UserJsonld {
*/
password?: string;
active?: boolean;
roles?: Array<string>;
readonly token?: string | null;
readonly createdAt?: string | null;
}


+ 1
- 0
httpdocs/composer.json Visa fil

@@ -24,6 +24,7 @@
"symfony/flex": "^2",
"symfony/form": "7.1.*",
"symfony/framework-bundle": "7.1.*",
"symfony/http-client": "7.1.*",
"symfony/property-access": "7.1.*",
"symfony/property-info": "7.1.*",
"symfony/runtime": "7.1.*",


+ 175
- 3
httpdocs/composer.lock Visa fil

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "85ee4ff0a9e8228f5ef5dae1cf910077",
"content-hash": "1d97e4496ebfcc30dce08cc0102bd47e",
"packages": [
{
"name": "api-platform/doctrine-common",
@@ -4876,6 +4876,178 @@
],
"time": "2024-09-20T13:35:23+00:00"
},
{
"name": "symfony/http-client",
"version": "v7.1.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "e221bfd51e70f12568e2f84890e6a5ffed0c35c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/e221bfd51e70f12568e2f84890e6a5ffed0c35c7",
"reference": "e221bfd51e70f12568e2f84890e6a5ffed0c35c7",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"php-http/discovery": "<1.15",
"symfony/http-foundation": "<6.4"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "1.0",
"symfony/http-client-implementation": "3.0"
},
"require-dev": {
"amphp/amp": "^2.5",
"amphp/http-client": "^4.2.1",
"amphp/http-tunnel": "^1.0",
"amphp/socket": "^1.1",
"guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/rate-limiter": "^6.4|^7.0",
"symfony/stopwatch": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
"homepage": "https://symfony.com",
"keywords": [
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.1.10"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-30T18:35:03+00:00"
},
{
"name": "symfony/http-client-contracts",
"version": "v3.5.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
"reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645",
"reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Test/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to HTTP clients",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"support": {
"source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-07T08:49:48+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v7.1.5",
@@ -8644,7 +8816,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@@ -8652,6 +8824,6 @@
"ext-ctype": "*",
"ext-iconv": "*"
},
"platform-dev": [],
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

+ 6
- 0
httpdocs/src/ApiResource/UserApi.php Visa fil

@@ -94,6 +94,12 @@ class UserApi
#[ApiProperty(security: 'object === null or is_granted("EDIT", object)')]
public bool $active;

#[ApiProperty(writable: false)]
public array $roles = [];

#[ApiProperty(writable: false)]
public ?string $token = null;

#[ApiProperty(writable: false)]
public ?\DateTimeImmutable $createdAt = null;



+ 1
- 0
httpdocs/src/Mapper/UserEntityToApiMapper.php Visa fil

@@ -42,6 +42,7 @@ class UserEntityToApiMapper implements MapperInterface
$dto->firstName = $entity->getFirstName();
$dto->lastName = $entity->getLastName();
$dto->image = $entity->getImage();
$dto->roles = $entity->getRoles();
$dto->imageUrl = $this->fileUrlService->getFileUrl($entity->getImage());
$dto->fullName = $entity->getFirstName() . " " . $entity->getLastName();
$dto->createdAt = $entity->getCreatedAt();


+ 3
- 33
httpdocs/src/OpenApi/AuthDecorator.php Visa fil

@@ -38,41 +38,11 @@ final class AuthDecorator implements OpenApiFactoryInterface
$schemas['AuthResponse'] = new \ArrayObject([
'type' => 'object',
'properties' => [
'token' => [
'type' => 'string',
'readOnly' => true,
],
'id' => [
'type' => 'integer',
'readOnly' => true,
],
'email' => [
'type' => 'string',
'readOnly' => true,
],
'firstName' => [
'type' => 'string',
'readOnly' => true,
],
'lastName' => [
'type' => 'string',
'readOnly' => true,
],
'roles' => [
'type' => 'array',
'items' => [
'type' => 'string'
],
'readOnly' => true,
],
'user' => [
'type' => 'object',
'allOf' => [
['$ref' => '#/components/schemas/User']
],
'$ref' => '#/components/schemas/User.jsonld',
'readOnly' => true,
],
],
]
]
]);

$pathItem = new PathItem(


+ 26
- 14
httpdocs/src/Security/JwtAuthenticator.php Visa fil

@@ -2,8 +2,14 @@
// src/Security/JwtAuthenticator.php
namespace App\Security;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\IriConverterInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use App\ApiResource\UserApi;
use App\Entity\User;
use App\Repository\UserRepository;
use App\State\EntityToDtoStateProvider;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -15,14 +21,19 @@ use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfonycasts\MicroMapper\MicroMapperInterface;

class JwtAuthenticator extends AbstractAuthenticator
{
public function __construct(
private JWTTokenManagerInterface $jwtManager,
private UserRepository $userRepository,
private MicroMapperInterface $microMapper
private MicroMapperInterface $microMapper,
private HttpClientInterface $httpClient,
private SerializerInterface $serializer,
private SerializerContextBuilderInterface $serializerContextBuilder,
) {}

public function supports(Request $request): ?bool
@@ -49,21 +60,22 @@ class JwtAuthenticator extends AbstractAuthenticator

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
/** @var User $user */
$user = $token->getUser();
$userApi = $this->microMapper->map($user, UserApi::class);

$jwtToken = $this->jwtManager->create($user);
// NOTE: This is a necessary workaround, since it is ot possible to map this in the usual api platform style at this point
$userApiArray = [];
$userApiArray['@id'] = '/api/users/' . $user->getId();
$userApiArray['@type'] = 'User';
foreach (get_object_vars($userApi) as $property => $value) {
if ($property !== 'id') {
$userApiArray[$property] = $value;
}
}
$userApiArray['token'] = $this->jwtManager->create($user);

return new JsonResponse([
'token' => $jwtToken,
'id' => $user->getId(),
'email' => $user->getEmail(),
'firstName' => $user->getFirstName(),
'lastName' => $user->getLastName(),
'roles' => $user->getRoles(),
'user' => $this->microMapper->map(
$user, UserApi::class
)
]);
return new JsonResponse(['user' => $userApiArray]);
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response


Laddar…
Avbryt
Spara