diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index ebc3214ce91830b8876620d274fce13fb9918781..a9a0140c340e79b1933bd9476dc669568f79a763 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,6 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { LoginComponent } from './login/login.component'; +import { ResetComponent } from './reset/reset.component'; +import { ResetConfirmComponent} from './reset-confirm/reset-confirm.component'; import { UiGalleryComponent } from './shared'; import { InternalErrorComponent, NotFoundComponent, AuthGuard, MapsAPIResolver } from './core'; @@ -23,6 +25,14 @@ const routes: Routes = [ path: 'inscription', loadChildren: './signup/signup.module#SignupModule', }, + { + path: 'reinitialiser_mdp', + component: ResetComponent, + }, + { + path: 'rest-auth/password/reset/confirm/:uid/:token', + component: ResetConfirmComponent, + }, { path: '500', component: InternalErrorComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0a79000a449f1af148df3ee14ff6526719bb1653..8c6fa795c2ad7f2e2aa3cc1ad6afb55d0799507a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -34,6 +34,8 @@ import { LoginComponent } from './login/login.component'; // Services import { MessageService } from './core'; +import { ResetComponent } from './reset/reset.component'; +import { ResetConfirmComponent } from './reset-confirm/reset-confirm.component'; registerLocaleData(localeFR); @@ -41,6 +43,8 @@ registerLocaleData(localeFR); declarations: [ AppComponent, LoginComponent, + ResetComponent, + ResetConfirmComponent, ], imports: [ BrowserModule, diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 52cec91f2858e664101be6c9b098f2babbfa341f..e7c506f92a021ccc02772f55aaef1367b4bca067 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -19,6 +19,8 @@ class StoredToken extends SimpleStoredItem { key = 'oser-cs-user-token'; } export class AuthService { private loginUrl = environment.apiUrl + 'auth/get-token/'; + private resetUrl = environment.apiUrl + 'rest-auth/password/reset/'; + private resetConfirmUrl = environment.apiUrl + 'rest-auth/password/reset/confirm/'; fromGuard: boolean; redirectUrl: string; @@ -45,6 +47,20 @@ export class AuthService { ); } + reset(email: string): Observable<boolean> { + console.log("reset function of auth service"); + return this.http.post<any>(this.resetUrl, { email }).pipe( + map(() => true), + ); + } + + resetConfirm(uid: string, token: string, new_password1: string, new_password2: string): Observable<boolean> { + console.log("reset confirm function"); + return this.http.post<any>(this.resetConfirmUrl, { uid, token, new_password1, new_password2 }).pipe( + map(() => true), + ); + } + redirectLogin() { this.router.navigate(['/connexion']); } diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html index b670f762fb31e0e809b67eb7f1d557f6348edb46..58676ecb6fbf8c1b8c108b7f3414a7fbd5c3821a 100644 --- a/src/app/login/login.component.html +++ b/src/app/login/login.component.html @@ -19,7 +19,9 @@ Se connecter <i *ngIf="loading" class="fa fa-spinner fa-pulse"></i> </button> </div> - + <div class="text-center"> + <br> + <a routerLink="/reinitialiser_mdp">Mot de passe oublié ?</a></div> </form> <messages></messages> @@ -28,4 +30,4 @@ Ou crée ton <a routerLink="/inscription">compte tutoré</a> </p> -</app-form-page> +</app-form-page> \ No newline at end of file diff --git a/src/app/reset-confirm/reset-confirm.component.html b/src/app/reset-confirm/reset-confirm.component.html new file mode 100644 index 0000000000000000000000000000000000000000..b9d07f9a30ba2ca9a6958783b04259985392e3fe --- /dev/null +++ b/src/app/reset-confirm/reset-confirm.component.html @@ -0,0 +1,24 @@ +<app-form-page> + <h1>Réinitialiser le mot de passe</h1> + <form [formGroup]="formGroup" (ngSubmit)="resetConfirm()"> + + <!-- Password field --> + <mat-form-field class="block"> + <input matInput type="password" formControlName="new_password1" placeholder="Mot de passe" required autofocus /> + </mat-form-field> + + <!-- Confirm password --> + <mat-form-field class="block"> + <input matInput type="password" formControlName="new_password2" placeholder="Confirmer le mot de passe" required + autofocus /> + </mat-form-field> + + <!-- Submit--> + <div class="text-center"> + <button mat-raised-button color="primary" [disabled]="!formGroup.valid || loading" id="login-btn"> + Confirmer<i *ngIf="loading" class="fa fa-spinner fa-pulse"></i> + </button> + </div> + </form> + +</app-form-page> \ No newline at end of file diff --git a/src/app/reset-confirm/reset-confirm.component.scss b/src/app/reset-confirm/reset-confirm.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..2e0715852a867e745f1483f4f38c515007e32c31 --- /dev/null +++ b/src/app/reset-confirm/reset-confirm.component.scss @@ -0,0 +1,38 @@ +@import '~sass/variables'; +@import '~sass/mixins'; + +.logo { + width: 100%; + max-width: 20em; + height: auto; + display: block; + margin: auto; +} + +.form-group { + display: flex; + flex-flow: column; +} + +.has-error { + input { + @include style-danger; + } +} +#login-btn { + margin-top: 1em; +} + +.form-control { + position: relative; + font-size: 16px; + height: auto; + padding: 10px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.block { + width: 100%; +} diff --git a/src/app/reset-confirm/reset-confirm.component.spec.ts b/src/app/reset-confirm/reset-confirm.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d5f02d9a5b900816071e1606ffd0e818bd5cced --- /dev/null +++ b/src/app/reset-confirm/reset-confirm.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ResetConfirmComponent } from './reset-confirm.component'; + +describe('ResetConfirmComponent', () => { + let component: ResetConfirmComponent; + let fixture: ComponentFixture<ResetConfirmComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ResetConfirmComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ResetConfirmComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/reset-confirm/reset-confirm.component.ts b/src/app/reset-confirm/reset-confirm.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc2502362d0b5d579ce32a636720f65f2b9a625d --- /dev/null +++ b/src/app/reset-confirm/reset-confirm.component.ts @@ -0,0 +1,68 @@ +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { MatSnackBar } from '@angular/material'; +import { AuthService, MessageService } from 'app/core'; +import { of } from 'rxjs'; +import { filter, map, tap, catchError } from 'rxjs/operators'; + +@Component({ + selector: 'app-reset-confirm', + templateUrl: './reset-confirm.component.html', + styleUrls: ['./reset-confirm.component.scss'] +}) +export class ResetConfirmComponent implements OnInit { + + loading: boolean = false; + defaultRedirectUrl: string = '/connexion'; + formGroup: FormGroup; + public uid: string; + public token: string; + + constructor( + private router: Router, + private auth: AuthService, + private messageService: MessageService, + private fb: FormBuilder, + private snackBar: MatSnackBar, + private route: ActivatedRoute + ) { } + + ngOnInit() { + if (this.auth.fromGuard) { + this.messageService.error('Oops ! Vous devez vous connecter pour accéder à cette page.'); + } + if (this.auth.fromUnauthorized) { + this.messageService.error("Oops ! Vous n'avez pas les permissions requises pour accéder à cette page."); + } + + this.createForm(); + this.uid = this.route.snapshot.paramMap.get('uid'); + this.token = this.route.snapshot.paramMap.get('token'); + console.log(this.uid); + console.log(this.token); + } + private createForm() { + this.formGroup = this.fb.group({ + new_password1: '', + new_password2: '' + }); + } + + resetConfirm() { + this.loading = true; + const { new_password1, new_password2 } = this.formGroup.value; + this.messageService.clear(); + this.auth.resetConfirm(this.uid, this.token, new_password1, new_password2).pipe( + catchError(() => { + this.snackBar.open('Erreur lors de la réinitialisation du mot de passe : vérifiez que les mots de passes sont identiques et assez forts (8 caractères). Evitez également les mots de passes courants', 'OK', { duration: 5000 }); + return of(false); + }), + tap(() => this.loading = false), + filter(Boolean), + map(() =>this.auth.redirectUrl ? this.auth.redirectUrl : this.defaultRedirectUrl), + tap(() => this.snackBar.open('Mot de passe réinitialisé', 'OK', { duration: 2000 })), + tap((redirectUrl: string) => this.router.navigate([redirectUrl])), + ).subscribe()} + +} diff --git a/src/app/reset/reset.component.html b/src/app/reset/reset.component.html new file mode 100644 index 0000000000000000000000000000000000000000..eee197047ddcc91ac09e79faf39fda5ac6a55fc8 --- /dev/null +++ b/src/app/reset/reset.component.html @@ -0,0 +1,19 @@ +<app-form-page> + <h1>Réinitialisation du mot de passe</h1> + + <form [formGroup]="formGroup" (ngSubmit)="reset()"> + + <!-- Email field --> + <mat-form-field class="block"> + <input matInput type="email" formControlName="email" required placeholder="Adresse email" autofocus> + </mat-form-field> + + <!-- Submit --> + <div class="text-center"> + <button mat-raised-button color="primary" [disabled]="!formGroup.valid || loading" id="login-btn"> + Recevoir un mail<i *ngIf="loading" class="fa fa-spinner fa-pulse"></i> + </button> + </div> + </form> + +</app-form-page> \ No newline at end of file diff --git a/src/app/reset/reset.component.scss b/src/app/reset/reset.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..2e0715852a867e745f1483f4f38c515007e32c31 --- /dev/null +++ b/src/app/reset/reset.component.scss @@ -0,0 +1,38 @@ +@import '~sass/variables'; +@import '~sass/mixins'; + +.logo { + width: 100%; + max-width: 20em; + height: auto; + display: block; + margin: auto; +} + +.form-group { + display: flex; + flex-flow: column; +} + +.has-error { + input { + @include style-danger; + } +} +#login-btn { + margin-top: 1em; +} + +.form-control { + position: relative; + font-size: 16px; + height: auto; + padding: 10px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.block { + width: 100%; +} diff --git a/src/app/reset/reset.component.spec.ts b/src/app/reset/reset.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..15ce951092d54ab68fdd487dd9d340edc08439fc --- /dev/null +++ b/src/app/reset/reset.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ResetComponent } from './reset.component'; + +describe('ResetComponent', () => { + let component: ResetComponent; + let fixture: ComponentFixture<ResetComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ResetComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ResetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/reset/reset.component.ts b/src/app/reset/reset.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..f35be7adbddac5fb4410176634036a50b7b5f981 --- /dev/null +++ b/src/app/reset/reset.component.ts @@ -0,0 +1,59 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { MatSnackBar } from '@angular/material'; +import { AuthService, MessageService } from 'app/core'; +import { of } from 'rxjs'; +import { filter, map, tap, catchError } from 'rxjs/operators'; + +@Component({ + selector: 'app-reset', + templateUrl: './reset.component.html', + styleUrls: ['./reset.component.scss'] +}) +export class ResetComponent implements OnInit { + + loading: boolean = false; + defaultRedirectUrl: string = '/'; + formGroup: FormGroup; + + constructor( + private router: Router, + private auth: AuthService, + private messageService: MessageService, + private fb: FormBuilder, + private snackBar: MatSnackBar, + ) { } + + ngOnInit() { + if (this.auth.fromGuard) { + this.messageService.error('Oops ! Vous devez vous connecter pour accéder à cette page.'); + } + if (this.auth.fromUnauthorized) { + this.messageService.error("Oops ! Vous n'avez pas les permissions requises pour accéder à cette page."); + } + this.createForm(); + } + + private createForm() { + this.formGroup = this.fb.group({ + email: '', + }); + } + + reset() { + this.loading = true; + const { email } = this.formGroup.value; + this.messageService.clear(); + this.auth.reset(email).pipe( + catchError(() => { + this.snackBar.open('Adresse incorrecte', 'OK', { duration: 5000 }); + return of(false); + }), + tap(() => this.loading = false), + filter(Boolean), + map(() =>this.auth.redirectUrl ? this.auth.redirectUrl : this.defaultRedirectUrl), + tap(() => this.snackBar.open('Mail de réinitialisation envoyé !', 'OK', { duration: 2000 })), + tap((redirectUrl: string) => this.router.navigate([redirectUrl])), + ).subscribe()} +}