diff --git a/src/app/AppConfiguration.ts b/src/app/AppConfiguration.ts index 6c31cb6cae039a9d77bc6f3e6aa3c730b5963c3a..5750c035292b4c6bddc7fb6906033bb7841e54cc 100644 --- a/src/app/AppConfiguration.ts +++ b/src/app/AppConfiguration.ts @@ -1,6 +1,7 @@ import { Injectable } from "@angular/core"; import { HttpClient } from '@angular/common/http'; import { environment } from 'src/environments/environment.prod'; +import { TokenStorageService } from "./authentication/services/token-storage.service"; @Injectable() export class AppConfiguration { @@ -10,14 +11,19 @@ export class AppConfiguration { fdppassword: string; elasticurl: string; smartapiurl: string; - - constructor(private httpClient: HttpClient){}; -public ensureInit(): Promise<any> { - return new Promise((r, e) => { - this.httpClient.get(environment.fdpUrl+"/setting/load").subscribe((content: AppConfiguration) => {Object.assign(this, content);r(this);}, reason => e(reason)); - }); - }; + constructor(private httpClient: HttpClient, private storageService: TokenStorageService) { }; + + public ensureInit(): Promise<any> { + const email = (this.storageService.getUser()).email; + return this.httpClient.get(environment.smartharvesterUrl + "/harvester/api/user/" + email).toPromise().then((resp:Response) => { + if (resp.status === 200 ) { + return true; + } else { + return false; + } + }) + } -} +} diff --git a/src/app/accessapi/accessapi.component.html b/src/app/accessapi/accessapi.component.html deleted file mode 100644 index d3568432ef69e09e5f947d3f3aef40ee9ebf5115..0000000000000000000000000000000000000000 --- a/src/app/accessapi/accessapi.component.html +++ /dev/null @@ -1,69 +0,0 @@ - -<form [formGroup]="Form" > - - <label>Upload and fill form width repository description file: </label><br> - <input type="file" id="repofile" name="repofile " class="btn btn-primary" (change)="swaggertoyaml('fill')" ><br> - <label>Or describe your repository and save a new file for publishing in the FDP database:</label><br> - - <div class="form-group"> - <label>title</label> - <input type="text" class="form-control" formControlName="title" required> - </div> - <div class="form-group"> - <label>Description</label> - <textarea class="form-control" formControlName="description"></textarea> - </div> - <div class="form-group"> - <label>version</label> - <input type="text" class="form-control" formControlName="version"> - </div> - <div class="form-group"> - <label>url</label> - <input type="text" class="form-control" formControlName="url"> - </div> - <div class="form-group"> - <label>Description</label> - <textarea class="form-control" formControlName="urldescription"></textarea> - </div> - <div class="form-group"> - <label>paths</label> - <input type="text" class="form-control" formControlName="paths"> - </div> <div class="form-group"> - <label>verb</label> - <input type="text" class="form-control" formControlName="verb"> -</div> <div class="form-group"> - <label>summary</label> - <input type="text" class="form-control" formControlName="summary"> -</div> - <div class="form-group"> - <label>description</label> - <textarea class="form-control" formControlName="verbdescription"></textarea> - </div> - <div class="form-group"> - <label>responses</label> - <input type="text" class="form-control" formControlName="responses"> - </div> - <div class="form-group"> - <label>description</label> - <textarea class="form-control" formControlName="responsedescription"></textarea> - </div> - <div class="form-group"> - <label>content</label> - <input type="text" class="form-control" formControlName="content"> - </div> - <div class="form-group"> - <label>schema</label> - <input type="text" class="form-control" formControlName="schema"> - </div> - <div class="form-group"> - <label>items</label> - <input type="text" class="form-control" formControlName="items"> - </div> - - <div> - <br> - <button type="submit" (click)="swaggertoyaml('submit')" class="btn btn-primary">Save swagger api description file</button> - </div> -</form> - - diff --git a/src/app/accessapi/accessapi.component.spec.ts b/src/app/accessapi/accessapi.component.spec.ts deleted file mode 100644 index c57085f1477ce7dc7cd0199ad8d24158a44bba56..0000000000000000000000000000000000000000 --- a/src/app/accessapi/accessapi.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { AccessapiComponent } from './accessapi.component'; - -describe('AccessapiComponent', () => { - let component: AccessapiComponent; - let fixture: ComponentFixture<AccessapiComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ AccessapiComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AccessapiComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/accessapi/accessapi.component.ts b/src/app/accessapi/accessapi.component.ts deleted file mode 100644 index 5e2f37b81cb8bfb59a18b23646b0e8f4bd7ee9d8..0000000000000000000000000000000000000000 --- a/src/app/accessapi/accessapi.component.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Validators } from '@angular/forms'; -import { FormArray } from '@angular/forms'; -import { HttpClient } from '@angular/common/http'; -import { FileSaverService } from 'ngx-filesaver'; -import { FormControl} from '@angular/forms'; -import { FormGroup} from '@angular/forms'; - - -@Component({ - selector: 'app-accessapi', - templateUrl: './accessapi.component.html', - styleUrls: ['./accessapi.component.scss'] -}) -export class AccessapiComponent implements OnInit { - - - public fileName: string; - public importFile: File; - - Form = new FormGroup({ - title: new FormControl('', Validators.required), - description: new FormControl(''), - version: new FormControl(''), - url: new FormControl(''), - urldescription: new FormControl(''), - paths: new FormControl(''), - verb: new FormControl(''), - summary: new FormControl(''), - verbdescription: new FormControl(''), - responses: new FormControl(''), - responsedescription: new FormControl(''), - content: new FormControl(''), - schema: new FormControl(''), - items: new FormControl(''), - filetofill: new FormControl(), - }); - - - constructor( - private _httpClient: HttpClient, - private _FileSaverService: FileSaverService, - ) { } - - ngOnInit() { - } - - swaggertoyaml(buttonType) { -if (buttonType == 'submit') { -let data: string; - -data ='openapi: 3.0.0 \n\ -info: \n\ - title: '+ this.Form.value.title +' \n\ - description: '+ this.Form.value.description +' \n\ - version: '+ this.Form.value.version +' \n\ -server: \n\ - - url: '+ this.Form.value.url +' \n\ - description: '+ this.Form.value.urldescription +' \n\ -paths: \n\ - '+this.Form.value.paths+': \n\ - '+ this.Form.value.verb +': \n\ - summary: '+ this.Form.value.summary +' \n\ - description: '+ this.Form.value.verbdescription +' \n\ - responses: \n\ - '+ this.Form.value.responses +': \n\ - description: '+ this.Form.value.responsedescription +' \n\ - content: \n\ - '+ this.Form.value.content +': \n\ - schema: \n\ - type: '+ this.Form.value.schema +' \n\ - items: \n\ - type: '+ this.Form.value.items +' \n\ -' - - const fileName = `swagger.yaml`; - const fileType = this._FileSaverService.genType(fileName); - const txtBlob = new Blob([data], { type: fileType }); - - this._FileSaverService.save(txtBlob, fileName); - } - - if (buttonType == 'fill') { - const files = (event.target as HTMLInputElement).files; - this.importFile = files[0]; - - let text = ""; - let lines = []; - let line = ""; - let map = new Map(); - let fileReader = new FileReader(); - fileReader.onload = (event) => { - text = fileReader.result as string; - - lines = text.split("\n"); - let j = 1; - for ( let i = 0; i < lines.length; i++) { - line = lines[i].split(": "); - if (line[0].trim() == "description") { - map.set("description"+j,line[1]); - j++; - } else { - map.set(line[0].trim(),line[1]); - } - console.log(line); - } - -console.log(map); - - this.Form.setValue({ - title: map.get('title'), - description: map.get('description1'), - version: map.get('version'), - url: map.get('- url'), - urldescription: map.get('description2'), - paths: '', - verb: '', - summary: map.get('summary'), - verbdescription: map.get('description3'), - responses: '', - responsedescription: map.get('description4'), - content: '', - schema: map.get('schema'), - items: map.get('items'), - filetofill: '' - }); - - } - fileReader.readAsText(this.importFile); - - - } - - } - -} diff --git a/src/app/api.service.spec.ts b/src/app/api.service.spec.ts deleted file mode 100644 index c0310ae68257073a88a5d5d84c69a673a6864b32..0000000000000000000000000000000000000000 --- a/src/app/api.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { ApiService } from './api.service'; - -describe('ApiService', () => { - let service: ApiService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(ApiService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/api.service.ts b/src/app/api.service.ts deleted file mode 100644 index 776566e11bc3df77833f9d99a3ac03cdc1e137a9..0000000000000000000000000000000000000000 --- a/src/app/api.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root' -}) -export class ApiService { - - constructor() { } -} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index a78e3ca466fe9581e68b197859059b49ad96098a..79dd92633f33b4794ec44d99e821b8960a73b970 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,19 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Route } from '@angular/router'; -import { UserComponent } from './user/user.component'; import { RepositoryComponent } from './repository/repository.component'; -import { AccessapiComponent } from './accessapi/accessapi.component'; -import { SettingfdpComponent } from './settingfdp/settingfdp.component'; -import { SettingsmartapiComponent } from './settingsmartapi/settingsmartapi.component'; -import { RepositoryinfoComponent } from './repositoryinfo/repositoryinfo.component'; -import { PublishApiComponent } from './publishapi/publishapi.component' import { SearchComponent } from './search/search.component'; -import { AuthenticationComponent } from './authentication/authentication.component'; import { SigninComponent } from './authentication/signin/signin.component'; import { SignupComponent } from './authentication/signup/signup.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { AuthGuardService } from './authentication/services/auth.guard'; import { StatsComponent } from './stats/stats.component'; +import { CallbackComponent } from './callback/callback.component'; +import { FdpGuard } from './authentication/services/fdp.guard'; +import { RepositoryinfoComponent } from './repositoryinfo/repositoryinfo.component'; +import { PublishApiComponent } from './publishapi/publishapi.component'; export interface ICustomRoute extends Route { name?: string; @@ -21,27 +18,19 @@ export interface ICustomRoute extends Route { const routes: ICustomRoute[] = [ - { path: '', redirectTo: '/auth/signin', pathMatch: 'full' }, - { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuardService], + { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, + {path: 'callback', component: CallbackComponent}, + { path: 'dashboard', component: DashboardComponent, canActivate: [ FdpGuard], children: [ { path: 'simplesearch', component: SearchComponent }, - { path: 'user', component: UserComponent }, { path: 'repository', component: RepositoryComponent }, { path: 'repositoryinfo', component: RepositoryinfoComponent }, - { path: 'accessapi', component: AccessapiComponent }, { path: 'stats', component: StatsComponent }, - /* { path: 'settingfdp', component: SettingfdpComponent }, - { path: 'settingsmartharvester', component: SettingsmartapiComponent },*/ { path: 'publishapi', component: PublishApiComponent }, - { path: 'advancedsearch', component: SearchComponent }, ] }, - { path: 'auth', component: AuthenticationComponent,name:'', - children: [ - { path: 'signup', component: SignupComponent, data: { animation: 'SignUp'} }, - { path: 'signin', component: SigninComponent, data: { animation: 'SignIn'} }, - ] - } + {path: 'fdpsignin', component: SignupComponent, canActivate: [AuthGuardService]}, + { path: 'login', component: SigninComponent} ] @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d1e31af8c3f4018d78a2e0c96a6f70f1dd5b7df4..c95b7f81cf32e8d72b25e10e0015f8cb02ed7fe7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,10 +13,8 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSidenavModule } from '@angular/material/sidenav'; import { RepositoryComponent } from './repository/repository.component'; -import { AccessapiComponent } from './accessapi/accessapi.component'; import { DatasetsComponent } from './datasets/datasets.component'; import { SettingfdpComponent } from './settingfdp/settingfdp.component'; -import { SettingsmartapiComponent } from './settingsmartapi/settingsmartapi.component'; import { UserComponent } from './user/user.component'; import { ReactiveFormsModule } from '@angular/forms'; @@ -24,7 +22,7 @@ import { FormsModule } from '@angular/forms'; import { RepositoryinfoComponent } from './repositoryinfo/repositoryinfo.component'; -import { HttpClientModule, HttpClient } from '@angular/common/http'; +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { FileSaverModule } from 'ngx-filesaver'; import { AppConfiguration } from './AppConfiguration'; import { PublishApiComponent } from './publishapi/publishapi.component'; @@ -44,8 +42,8 @@ import { NbToastrModule } from '@nebular/theme'; import { DatasetsDialogComponent } from './datasets/datasets-dialog/datasets-dialog.component'; import { NgProgressModule } from 'ngx-progressbar'; import { NgProgressHttpModule } from 'ngx-progressbar/http'; - - +import { AuthHeaderInterceptor } from './authentication/interceptor/authHeader.interceptor'; +import { CallbackComponent } from './callback/callback.component'; @@ -53,10 +51,8 @@ import { NgProgressHttpModule } from 'ngx-progressbar/http'; declarations: [ AppComponent, RepositoryComponent, - AccessapiComponent, DatasetsComponent, SettingfdpComponent, - SettingsmartapiComponent, UserComponent, RepositoryinfoComponent, PublishApiComponent, @@ -66,6 +62,8 @@ import { NgProgressHttpModule } from 'ngx-progressbar/http'; MappingComponent, FeedbackDialogComponent, DatasetsDialogComponent, + CallbackComponent, + ], imports: [ BrowserModule, @@ -110,11 +108,11 @@ import { NgProgressHttpModule } from 'ngx-progressbar/http'; providers: [ AppConfiguration, ParseXmlService, - /*{ - provide: APP_INITIALIZER, - useFactory: AppConfigurationFactory, - deps: [AppConfiguration, HttpClient], multi: true - },*/ + { + provide: HTTP_INTERCEPTORS, + useClass: AuthHeaderInterceptor, + multi: true + } ], bootstrap: [AppComponent] }) diff --git a/src/app/authentication/admin/account.admin.component.ts b/src/app/authentication/admin/account.admin.component.ts index dc713762d84fd3475ebd89e4f6d577fa10c1e4cd..77a838e9f2d9aae4ca9407c90dec8a813776df82 100644 --- a/src/app/authentication/admin/account.admin.component.ts +++ b/src/app/authentication/admin/account.admin.component.ts @@ -72,8 +72,8 @@ export class AccountAdminComponent implements OnInit { } logout(){ - this.authService.logout(); - this.router.navigateByUrl('/auth/login'); + //this.authService.logout(); + this.router.navigateByUrl('/login'); } toggleSidebar(): boolean { this.sidebarService.toggle(true); diff --git a/src/app/authentication/authentication.module.ts b/src/app/authentication/authentication.module.ts index 217ccf29a89c13e8f92e3ca93f5d284c7e907dff..c2fc23238ab34fb185ed041eb528ddb4767216f2 100644 --- a/src/app/authentication/authentication.module.ts +++ b/src/app/authentication/authentication.module.ts @@ -6,15 +6,15 @@ import { BrowserModule } from '@angular/platform-browser'; import { AuthenticationComponent } from './authentication.component'; import { RouterModule } from '@angular/router'; import { SigninComponent } from './signin/signin.component'; -import { SignoutComponent } from './signout/signout.component'; import { SignupComponent } from './signup/signup.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AccountAdminComponent } from './admin/account.admin.component'; +import { ConfirmValidatorDirective } from './validators/confirm-validator.directive'; @NgModule({ - declarations: [SigninComponent,AuthenticationComponent, SigninComponent, SignoutComponent, SignupComponent, AccountAdminComponent], + declarations: [SigninComponent,AuthenticationComponent, SigninComponent, SignupComponent, AccountAdminComponent, ConfirmValidatorDirective], imports: [ CommonModule, FormsModule, @@ -25,7 +25,8 @@ import { AccountAdminComponent } from './admin/account.admin.component'; NebularModule ], exports:[ - SigninComponent + SigninComponent, + ConfirmValidatorDirective ] }) export class AuthenticationModule { diff --git a/src/app/authentication/interceptor/auth.interceptor.ts b/src/app/authentication/interceptor/auth.interceptor.ts deleted file mode 100644 index e1aaa168e9893016822c978fc3a8129faa401f1d..0000000000000000000000000000000000000000 --- a/src/app/authentication/interceptor/auth.interceptor.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; - -import { TokenStorageService } from '../services/token-storage.service'; -import { Observable } from 'rxjs'; - -const TOKEN_HEADER_KEY = 'x-access-token'; - -@Injectable() -export class AuthInterceptor implements HttpInterceptor { - constructor(private token: TokenStorageService) { } - - intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { - let authReq = req; - const token = this.token.getToken(); - if (token != null) { - authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, token) }); - } - return next.handle(authReq); - } -} - -export const authInterceptorProviders = [ - { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } -]; \ No newline at end of file diff --git a/src/app/authentication/interceptor/authHeader.interceptor.ts b/src/app/authentication/interceptor/authHeader.interceptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..40cf012c5b64bb373d15bdc771b6de1a9eb99275 --- /dev/null +++ b/src/app/authentication/interceptor/authHeader.interceptor.ts @@ -0,0 +1,19 @@ +import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; + +import { Observable } from 'rxjs'; + +const TOKEN_HEADER_KEY = 'x-access-token'; + +@Injectable() +export class AuthHeaderInterceptor implements HttpInterceptor { + constructor() { } + + intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { + let authReq = req + + return next.handle(authReq); + } +} + diff --git a/src/app/authentication/services/auth.guard.ts b/src/app/authentication/services/auth.guard.ts index f75eb5220ec787d55be7a478b55ee62ee80a9506..107be570c02c7efcf69603c65526489166a34a20 100644 --- a/src/app/authentication/services/auth.guard.ts +++ b/src/app/authentication/services/auth.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { AuthService } from './auth.service'; @@ -7,12 +7,19 @@ import { AuthService } from './auth.service'; providedIn: 'root' }) export class AuthGuardService implements CanActivate { - constructor(private authService: AuthService){} - + constructor(private authService: AuthService, private router: Router) { } + canActivate( _next: ActivatedRouteSnapshot, _state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { - return this.authService.isLoggedIn(); + + if (this.authService.isLoggedIn()) { + return true; + } + this.router.navigate(['/login']); + return false; + + } - + } diff --git a/src/app/authentication/services/auth.service.ts b/src/app/authentication/services/auth.service.ts index a4ee7ca60067b8f0f8e0333b80286ff41dc68ecb..6995ed1b48fe5568886cef0f7a2b66694260eb47 100644 --- a/src/app/authentication/services/auth.service.ts +++ b/src/app/authentication/services/auth.service.ts @@ -4,6 +4,8 @@ import { Observable } from 'rxjs'; import { TokenStorageService } from './token-storage.service'; import { AppConfiguration } from 'src/app/AppConfiguration'; import { environment } from 'src/environments/environment'; +import { SmartHarvesterUser } from 'src/app/user/model/user'; +import { Router } from '@angular/router'; @@ -20,24 +22,30 @@ const httpOptions = { }) export class AuthService { + private authorizationEndpoint = '/oauth2/authorization/oidc'; + private tokenEndpoint = '/login/oauth2/code/oidc'; + private baseUrl = environment.smartharvesterUrl; + private FdpBaseUrl = environment.fdpUrl; + private tokenKey = 'auth-token;' - constructor(private http: HttpClient, private tokenService: TokenStorageService, private appConfig: AppConfiguration) { } + httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + this.getToken() + }) + }; + constructor(private http: HttpClient, private tokenService: TokenStorageService, private appConfig: AppConfiguration, private router: Router) { } - - login(credentials): Observable<any> { - let data: any - data = this.http.post(AUTH_API + '/signin', { - email: credentials.email, - password: credentials.password - }, httpOptions); - return data; - } - - register(user): Observable<any> { - return this.http.post(AUTH_API + '/signup', { + FdpSignUp(user: SmartHarvesterUser): Observable<any> { + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization' : `Baerer ${this.getToken()}` }) + }; + + return this.http.post(`${this.baseUrl}/harvester/auth/signup`, { firstName: user.firstName, lastName: user.lastName, email: user.email, @@ -45,32 +53,47 @@ export class AuthService { }, httpOptions); } + getFdpToken(email: string, password: string): Observable<any> { + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json'}) + }; + return this.http.post(`${this.FdpBaseUrl}/tokens`, { + email: email, + password: password + }, httpOptions) + } - update(user): Observable<any> { - return this.http.post(AUTH_API + '/user', { - catalog_id: user.cat_id - }, httpOptions); + + login() { + + window.location.href = this.baseUrl + this.authorizationEndpoint; } + updateToken(token) { + this.tokenService.saveTokenSmartHarveser(token) + } - getF2DSAuthToken(credentials): Observable<any> { - return this.http.post(FDP_URL + '/tokens', { - email: credentials.email, - password: credentials.password - }, httpOptions); + fetchToken(code: string, state: string): Observable<any> { + return this.http.get(`${this.baseUrl}${this.tokenEndpoint}?code=${code}&state=${state}`); } - public isLoggedIn() { - return this.tokenService.getToken() !== null; + getToken() { + return this.tokenService.getToken(); } - public logout() { - const config = { - headers: new HttpHeaders({ - 'Content-Type': 'application/x-www-form-urlencoded' - }) - }; - this.tokenService.signOut(); - return this.http.post(`${AUTH_API}/logout`, null, config); + isLoggedIn(): boolean { + const token = this.getToken(); + return (token !== null); + } + + isFdpLoggedIn(): boolean { + + return !this.tokenService.getFDPToken(); + } + + + + logout() { + return this.http.post(this.baseUrl + '/logout', this.getToken(), this.httpOptions); } } diff --git a/src/app/authentication/services/fdp.guard.spec.ts b/src/app/authentication/services/fdp.guard.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dddae70d50b50bc4ee13a40e8e3ca5a6a1388049 --- /dev/null +++ b/src/app/authentication/services/fdp.guard.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { FdpGuard } from './fdp.guard'; + +describe('FdpGuard', () => { + let guard: FdpGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(FdpGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/src/app/authentication/services/fdp.guard.ts b/src/app/authentication/services/fdp.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..db094c07ba63f7d46c4a470f3107ce63562d5876 --- /dev/null +++ b/src/app/authentication/services/fdp.guard.ts @@ -0,0 +1,24 @@ + +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { AuthService } from './auth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class FdpGuard implements CanActivate { + constructor(private authService: AuthService, private router: Router) { + } + canActivate( + _next: ActivatedRouteSnapshot, + _state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { + if (!this.authService.isFdpLoggedIn()) { + + return true; + } + this.router.navigate(['/fdpsignin']) + return false; + } + +} diff --git a/src/app/authentication/services/token-storage.service.ts b/src/app/authentication/services/token-storage.service.ts index 00b7d5f5237b9b4abea503f4a87c06621aecc0c4..b949094e4f57e64c3ac0d6223782f02720adcd33 100644 --- a/src/app/authentication/services/token-storage.service.ts +++ b/src/app/authentication/services/token-storage.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { AuthService } from './auth.service'; +import { Cookie } from 'ng2-cookies'; const TOKEN_KEY = 'auth-token'; const USER_KEY = 'auth-user'; @@ -15,18 +15,22 @@ export class TokenStorageService { window.sessionStorage.clear(); } - public saveToken(token: string,f2dsToken:string): void { + public saveTokenSmartHarveser(SmartHarvesterToken: string): void { window.sessionStorage.removeItem(TOKEN_KEY); - window.sessionStorage.removeItem(FDP_TOKEN_KEY); - window.sessionStorage.setItem(TOKEN_KEY, token); - window.sessionStorage.setItem(FDP_TOKEN_KEY, f2dsToken); + + window.sessionStorage.setItem(TOKEN_KEY, SmartHarvesterToken); + + } + + public saveTokenFDP(fdpToken: string): void { + Cookie.set('fdp_token',fdpToken, 1 ) } public getToken(): string { return window.sessionStorage.getItem(TOKEN_KEY); } public getFDPToken(): string { - return window.sessionStorage.getItem(FDP_TOKEN_KEY); + return Cookie.get('fdp_token'); } public saveUser(user): void { @@ -38,8 +42,8 @@ export class TokenStorageService { return JSON.parse(sessionStorage.getItem(USER_KEY)); } public removeToken(){ - window.localStorage.removeItem(TOKEN_KEY); - window.sessionStorage.removeItem(FDP_TOKEN_KEY); + window.sessionStorage.removeItem(TOKEN_KEY); + window.sessionStorage.removeItem(USER_KEY); } diff --git a/src/app/authentication/signin/signin.component.html b/src/app/authentication/signin/signin.component.html index da87c7586666cee65149426c57081f36b1fc756b..ac675e9a90c83b9d5d0dc6db9bff4f824e73dc5b 100644 --- a/src/app/authentication/signin/signin.component.html +++ b/src/app/authentication/signin/signin.component.html @@ -1,65 +1,17 @@ - <div style="display: flex;justify-content: center;"> - <nb-card status="info" size="small" style="width: 24em;"> - <nb-card-header style="text-align: center;">Welcome to the F2DS API register</nb-card-header> - <nb-card-body class="example-items-col" style="overflow: hidden;"> - - <div class="col-md-12"> - <div class="card card-container"> - <form *ngIf="!isLoggedIn" name="formGroup" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate> - <!--Email --> - <div class="form-group"> - <label for="email">Email</label> - <nb-form-field> - <nb-icon nbPrefix icon="at-outline" pack="eva"></nb-icon> - <input nbInput fullWidth type="text" class="form-control" name="email" [(ngModel)]="user.email" - required #email="ngModel" /> - </nb-form-field> - <!-- Email error--> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> - Email is required! - </div> - </div> - <!--Password--> - <div class="form-group"> - <label for="password">Password</label> - <nb-form-field> - <input nbInput fullWidth [type]="getInputType()" class="form-control" name="password" - [(ngModel)]="user.password" required minlength="6" #password="ngModel" /> - <button type="button" nbSuffix nbButton ghost (click)="toggleShowPassword()"> - <nb-icon [icon]="showPassword ? 'eye-outline' : 'eye-off-2-outline'" pack="eva" - [attr.aria-label]="showPassword ? 'hide password' : 'show password'"> - </nb-icon> - </button> - </nb-form-field> - <!-- Password error--> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && password.invalid"> - <div *ngIf="password.errors.required">Password is required</div> - <div *ngIf="password.errors.minlength"> - Password must be at least 6 characters - </div> - </div> - </div> - <br> - <div class="form-group" style="text-align: center;"> - <button nbButton status="info" class="btn btn-primary btn-block"> - Login - </button> - </div> - <div class="form-group"> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && isLoginFailed"> - Login failed: {{ errorMessage }} - </div> - </div> - </form> - <section class="another-action" aria-label="Register" style="text-align: center; margin-top: 20px;"> - Don't have an account ? <a class="text-link" routerLink="../signup">Register</a> - </section> - <!----> - <div class="alert alert-success" *ngIf="isLoggedIn"> - Logged in as - </div> - </div> - </div> - </nb-card-body> - </nb-card> +<nb-layout> + <nb-layout-header fixed> + <img width="80" alt="Angular Logo" src="assets/images/logo.png" /> + <h3 style="width: 100%;text-align: center;"> <strong></strong></h3> + <div style="float: right;"> + <button (click)="login()" status="primary" nbButton>login</button> </div> + + + </nb-layout-header> + + + + <nb-layout-footer>Contact us</nb-layout-footer> + +</nb-layout> + diff --git a/src/app/authentication/signin/signin.component.ts b/src/app/authentication/signin/signin.component.ts index 63f28e0f3d67f9113cf5d2ad07ab6e31dad7466c..806e82e100f23b84a8c86560bc267b17e0b2fb02 100644 --- a/src/app/authentication/signin/signin.component.ts +++ b/src/app/authentication/signin/signin.component.ts @@ -21,63 +21,19 @@ export class SigninComponent implements OnInit { isLoginFailed = false; errorMessage = ''; showPassword = false; - user: SmartHarvesterUser = new SmartHarvesterUser({}); + user: SmartHarvesterUser = new SmartHarvesterUser(); constructor(private authService: AuthService, private router: Router, private tokenStorage: TokenStorageService) { } ngOnInit(): void { - if (this.tokenStorage.getToken()) { - // Uncomment line to avoid re-authentication if token is still valid - //this.isLoggedIn = true; - } } - //get formControls() { return this.formGroup.controls; } - - onSubmit(): void { - - - - this.authService.login(this.user).subscribe( - data => { - this.authService.getF2DSAuthToken(this.user).subscribe((response:F2DSResponse) => { - if (response) this.f2dsToken = response.token; - console.log("F2ds token is : ", this.f2dsToken); - this.tokenStorage.saveToken(data.accessToken, this.f2dsToken); - this.tokenStorage.saveUser(data); - - this.isLoginFailed = false; - this.isLoggedIn = true; - console.log("User data : ", data) - this.router.navigateByUrl('/dashboard'); - }) - - //this.reloadPage(); - }, - err => { - this.errorMessage = err.error.message; - console.error("Error : ", this.errorMessage) - this.isLoginFailed = true; - } - ); - } - - reloadPage(): void { - window.location.reload(); - } - - - getInputType() { - if (this.showPassword) { - return 'text'; - } - return 'password'; + login() { + this.authService.login(); } - toggleShowPassword() { - this.showPassword = !this.showPassword; - } + } diff --git a/src/app/authentication/signout/signout.component.html b/src/app/authentication/signout/signout.component.html deleted file mode 100644 index 9e1beb8d7763687e625d3fc0d85fcbf8c3b6fa32..0000000000000000000000000000000000000000 --- a/src/app/authentication/signout/signout.component.html +++ /dev/null @@ -1 +0,0 @@ -<p>signout works!</p> diff --git a/src/app/authentication/signout/signout.component.scss b/src/app/authentication/signout/signout.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/authentication/signout/signout.component.ts b/src/app/authentication/signout/signout.component.ts deleted file mode 100644 index 4b44c0ad87bdad2da5256875d1c21cc9f776165c..0000000000000000000000000000000000000000 --- a/src/app/authentication/signout/signout.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-signout', - templateUrl: './signout.component.html', - styleUrls: ['./signout.component.scss'] -}) -export class SignoutComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { - } - - - -} diff --git a/src/app/authentication/signup/signup.component.html b/src/app/authentication/signup/signup.component.html index 5b59546cf28afd2388ab13e2d37d9820fc7b42c8..4b67802404d798b6f38bac2ae5e3eac9cc21c2d8 100644 --- a/src/app/authentication/signup/signup.component.html +++ b/src/app/authentication/signup/signup.component.html @@ -1,91 +1,197 @@ - <div style="display: flex;justify-content: center;"> - <nb-card status="success" size="small" style="width: 24em;height: auto;"> - <nb-card-header style="text-align: center;">Create an account to F2DS API register</nb-card-header> - <nb-card-body class="example-items-col" style="overflow: hidden;"> +<nb-layout> + <nb-layout-header fixed> + <img width="80" alt="Angular Logo" src="assets/images/logo.png" /> + <h3 style="width: 100%;text-align: center;"> <strong></strong></h3> + <!--User badge--> - <div class="col-md-12"> - <div class="card card-container"> - <form *ngIf="!isLoggedIn" name="formGroup" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" - novalidate> - <div class="form-group"> - <label for="firstName">First Name</label> - <nb-form-field> - <input nbInput fullWidth type="text" class="form-control" name="firstName" - [(ngModel)]="user.firstName" required #email="ngModel" /> - </nb-form-field> - <!-- Email error--> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> - First name is required! - </div> - </div> - <!--Last name--> - <div class="form-group"> - <label for="firstName">Last Name</label> - <nb-form-field> - <input nbInput fullWidth type="text" class="form-control" name="lastName" - [(ngModel)]="user.lastName" required #email="ngModel" /> - </nb-form-field> - <!-- Email error--> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> - Last name is required! - </div> - </div> - <!--Email --> - <div class="form-group"> - <label for="email">Email</label> - <nb-form-field> - <nb-icon nbPrefix icon="at-outline" pack="eva"></nb-icon> - <input nbInput fullWidth type="text" class="form-control" name="email" - [(ngModel)]="user.email" required #email="ngModel" /> - </nb-form-field> - <!-- Email error--> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> - Email is required! - </div> - </div> - <!--Password--> - <div class="form-group"> - <label for="password">Password</label> - <nb-form-field> - <input nbInput fullWidth type="password" class="form-control" name="password" - [(ngModel)]="user.password" required minlength="6" #password="ngModel" /> - - </nb-form-field> - <label for="password">Confirm password</label> - <nb-form-field> - <input nbInput fullWidth type="password" class="form-control" name="passwordConfirm" - [(ngModel)]="user.passwordConfirm" required minlength="6" #password="ngModel" /> - - </nb-form-field> - <!-- Password error--> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && password.invalid"> - <div *ngIf="password.errors.required">Password is required</div> - <div *ngIf="password.errors.minlength"> - Password must be at least 6 characters - </div> - </div> - </div> - <br> - <div class="form-group" style="text-align: center;"> - <button nbButton status="success" class="btn btn-primary btn-block"> - Sign Up - </button> - </div> - <div class="form-group"> - <div class="alert alert-danger" role="alert" *ngIf="f.submitted && isLoginFailed"> - Login failed: {{ errorMessage }} - </div> - </div> - </form> - <section class="another-action" aria-label="Register" style="text-align: center; margin-top: 20px;"> - Already have an account ? <a class="text-link" routerLink="../signin">Sign In</a> - </section> - <!----> - <div class="alert alert-success" *ngIf="isLoggedIn"> - Logged in as - </div> - </div> + <nb-user style="margin-right: 10px;" name="{{user.firstName}}" title="{{user.lastName}}" + nbContextMenuTag="my-context-menu" badgePosition="right"> + </nb-user> + <div style="float: right;"> + <button (click)="logout()" status="danger" nbButton>logout</button> + </div> + </nb-layout-header> + <nb-layout-column> + <div style="display: flex;justify-content: center;"> + <nb-card status="success" size="small" style="width: 24em;height: auto;"> + <nb-card-header style="text-align: center;">Welcome to F2DS {{user.firstName}} {{user.lastName}}. + </nb-card-header> + <ng-container *ngIf="isRegistred; then registred; else notRegistred"> + + </ng-container> + </nb-card> + </div> + </nb-layout-column> + + + <nb-layout-footer>Contact us</nb-layout-footer> + +</nb-layout> + +<!-- block create account--> +<ng-template #notRegistred> + <nb-card-body class="example-items-col" style="overflow: hidden;"> + You are not yet registred on Fair Data Point. Please enter a password to register an account on the FDP. + <div class="card card-container"> + <form name="formGroup" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate> + <div class="form-group"> + <label for="firstName">First Name</label> + <nb-form-field> + <input nbInput fullWidth type="text" class="form-control" name="firstName" + [(ngModel)]="userFDP.firstName" required #firstName="ngModel" + [disabled]="userFDP.firstName !== null" /> + </nb-form-field> + <!-- Email error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> + First name is required! + </div> + </div> + <!--Last name--> + <div class="form-group"> + <label for="firstName">Last Name</label> + <nb-form-field> + <input nbInput fullWidth type="text" class="form-control" name="lastName" + [(ngModel)]="userFDP.lastName" required #lastname="ngModel" + [disabled]="userFDP.lastName !== null" /> + </nb-form-field> + <!-- Email error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> + Last name is required! + </div> + </div> + <!--Email --> + <div class="form-group"> + <label for="email">Email</label> + <nb-form-field> + <nb-icon nbPrefix icon="at-outline" pack="eva"></nb-icon> + <input nbInput fullWidth type="text" class="form-control" name="email" [(ngModel)]="userFDP.email" + required #email="ngModel" [disabled]="userFDP.email !== null" /> + </nb-form-field> + <!-- Email error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> + Email is required! + </div> + </div> + <!--Password--> + <div class="form-group"> + <label for="password">Password</label> + <nb-form-field> + <input nbInput fullWidth [type]="getInputType()" class="form-control" name="password" + [(ngModel)]="userFDP.password" required minlength="6" #password="ngModel" compare-password="passwordConfirm" parent="true"/> + <button type="button" nbSuffix nbButton ghost (click)="toggleShowPassword()"> + <nb-icon [icon]="showPassword ? 'eye-outline' : 'eye-off-2-outline'" pack="eva" + [attr.aria-label]="showPassword ? 'hide password' : 'show password'"> + </nb-icon> + </button> + </nb-form-field> + <!-- Password error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && password.invalid"> + <div *ngIf="password.errors.required">Password is required</div> + <div *ngIf="password.errors.minlength"> + Password must be at least 6 characters + </div> + </div> + </div> + <!--Password confirm--> + <div class="form-group"> + <label for="passwordConfirm">Confirm Password</label> + <nb-form-field> + <input nbInput fullWidth [type]="getInputType()" class="form-control" name="passwordConfirm" + [(ngModel)]="userFDP.passwordConfirm" required minlength="6" #passwordConfirm="ngModel" compare-password="password" /> + <button type="button" nbSuffix nbButton ghost (click)="toggleShowPassword()"> + <nb-icon [icon]="showPassword ? 'eye-outline' : 'eye-off-2-outline'" pack="eva" + [attr.aria-label]="showPassword ? 'hide password' : 'show password'"> + </nb-icon> + </button> + </nb-form-field> + <!-- Password confirm error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && passwordConfirm.invalid"> + <div *ngIf="passwordConfirm.errors.required">Password is required</div> + <div *ngIf="passwordConfirm.errors.minlength"> + Password must be at least 6 characters </div> - </nb-card-body> - </nb-card> + <div *ngIf="passwordConfirm.errors.compare">Password Doesn't match</div> + </div> + </div> + + <br> + <div class="form-group" style="text-align: center;"> + <button nbButton status="success" class="btn btn-primary btn-block" > + Sign Up + </button> + </div> + <div class="form-group"> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && isLoginFailed"> + Login failed: {{ errorMessage }} + </div> + </div> + </form> + </div> + <div class="col-md-12"> + <div class="card card-container"> + + </div> + </div> + </nb-card-body> +</ng-template> + +<!-- block registred--> +<ng-template #registred> + <nb-card-body class="example-items-col" style="overflow: hidden;"> + You are already registred on Fair Data Point. Please enter your password to login to your FDP account. + <div class="card card-container"> + <form name="formGroup" (ngSubmit)="f.form.valid && onSubmitResgistred()" #f="ngForm" novalidate> + + <!--Email --> + <div class="form-group"> + <label for="email">Email</label> + <nb-form-field> + <nb-icon nbPrefix icon="at-outline" pack="eva"></nb-icon> + <input nbInput fullWidth type="text" class="form-control" name="email" [(ngModel)]="userFDP.email" + required #email="ngModel" [disabled]="userFDP.email !== null" /> + </nb-form-field> + <!-- Email error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && email.invalid"> + Email is required! + </div> + </div> + <!--Password--> + <div class="form-group"> + <label for="password">Password</label> + <nb-form-field> + <input nbInput fullWidth [type]="getInputType()" class="form-control" name="password" + [(ngModel)]="userFDP.password" required minlength="6" #password="ngModel" compare-password="passwordConfirm"/> + <button type="button" nbSuffix nbButton ghost (click)="toggleShowPassword()"> + <nb-icon [icon]="showPassword ? 'eye-outline' : 'eye-off-2-outline'" pack="eva" + [attr.aria-label]="showPassword ? 'hide password' : 'show password'"> + </nb-icon> + </button> + </nb-form-field> + <!-- Password error--> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && password.invalid"> + <div *ngIf="password.errors.required">Password is required</div> + <div *ngIf="password.errors.minlength"> + Password must be at least 6 characters + </div> + </div> + </div> + <br> + <div class="form-group" style="text-align: center;"> + <button nbButton status="success" class="btn btn-primary btn-block" > + Sign In + </button> + </div> + <div class="form-group"> + <div class="alert alert-danger" role="alert" *ngIf="f.submitted && errorMessage"> + Login failed: {{ errorMessage }} + </div> + </div> + </form> + </div> + <div class="col-md-12"> + <div class="card card-container"> + </div> + </div> + </nb-card-body> +</ng-template> \ No newline at end of file diff --git a/src/app/authentication/signup/signup.component.scss b/src/app/authentication/signup/signup.component.scss index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f21864571fb881d9a6ba1ada440e10e2171f5467 100644 --- a/src/app/authentication/signup/signup.component.scss +++ b/src/app/authentication/signup/signup.component.scss @@ -0,0 +1,3 @@ +.alert-danger{ + color: red; +} \ No newline at end of file diff --git a/src/app/authentication/signup/signup.component.ts b/src/app/authentication/signup/signup.component.ts index 8a757692fe84b9f1c0de3813244802f9175a806e..3955dc577421b017f48b4a8bd46f85f189717325 100644 --- a/src/app/authentication/signup/signup.component.ts +++ b/src/app/authentication/signup/signup.component.ts @@ -1,48 +1,121 @@ -import { Component, OnInit } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; import { Router } from '@angular/router'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { SmartHarvesterUser } from 'src/app/user/model/user'; +import { environment } from 'src/environments/environment'; import { AuthService } from '../services/auth.service'; +import { TokenStorageService } from '../services/token-storage.service'; +import { F2DSResponse } from '../signin/signin.component'; @Component({ selector: 'app-signup', templateUrl: './signup.component.html', styleUrls: ['./signup.component.scss'] }) -export class SignupComponent implements OnInit { +export class SignupComponent implements OnInit, OnDestroy { - isSubmitted = false; - isLoggedIn = false; + isSubmitted = false; + isRegistred = false; isLoginFailed = false; errorMessage = ''; showPassword = false; - user: SmartHarvesterUser = new SmartHarvesterUser({}); - + user: SmartHarvesterUser = new SmartHarvesterUser(); + userFDP: SmartHarvesterUser = new SmartHarvesterUser();$ + private _isDead = new Subject(); + + + constructor(private authService: AuthService, private router: Router, private storageService: TokenStorageService, private http: HttpClient) { + + } - constructor(private authService: AuthService, private router: Router) { } ngOnInit(): void { + const httpOptions = { + headers: new HttpHeaders({ + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + this.authService.getToken() + }) + }; + this.http.get(environment.smartharvesterUrl + '/harvester/api/username', httpOptions) + .pipe(takeUntil(this._isDead)) + .subscribe( + data => { + this.user.email = data['principal']['userInfo']['email']; + this.user.lastName = data['principal']['userInfo']['familyName']; + this.user.firstName = data['principal']['userInfo']['givenName']; + this.storageService.saveUser(this.user); + this.userFDP = this.user; + }, + err => console.log(err.error.message), + () => { + this.http.get(`${environment.smartharvesterUrl}/harvester/api/user/${this.user.email}`, httpOptions) + .pipe(takeUntil(this._isDead)) + .subscribe( + (resp) => { + if (resp) { + this.isRegistred = true; + } + }) + }); } - onSubmit(): void { - + onSubmit() { + this.authService.FdpSignUp(this.userFDP) + .pipe(takeUntil(this._isDead)) + .subscribe( + data => {console.log(data) }, + err => { this.errorMessage = err.error.message }, + () => { + this.authService.getFdpToken(this.userFDP.email, this.userFDP.password) + .pipe(takeUntil(this._isDead)) + .subscribe( + (resp: F2DSResponse) => { this.storageService.saveTokenFDP(resp.token) }, + err => this.errorMessage = err.error.message, + () => { this.router.navigate(['/dashboard']) } + ); + }); + } - this.authService.register(this.user).subscribe( - data => { - console.log("Response : ", data); - }, - err => { - this.errorMessage = err.error.message; - this.isLoginFailed = true; - }, - () => this.router.navigateByUrl('/auth/signin') + onSubmitResgistred() { + console.log('submit') + this.authService.getFdpToken(this.userFDP.email, this.userFDP.password) + .pipe(takeUntil(this._isDead)) + .subscribe( + (resp: F2DSResponse) => { this.storageService.saveTokenFDP(resp.token) }, + err => this.errorMessage = err.error.message, + () => { this.router.navigate(['/dashboard']) } ); } - reloadPage(): void { - window.location.reload(); - } + logout() { + this.authService.logout().subscribe( + value => this.router.navigateByUrl('/login'), + err => console.log(err), + () => this.storageService.removeToken() + ); + + } + + getInputType() { + if (this.showPassword) { + return 'text'; + } + return 'password'; + } + + toggleShowPassword() { + this.showPassword = !this.showPassword; + } + + ngOnDestroy(): void { + this._isDead.next(); + } + } diff --git a/src/app/authentication/validators/confirm-validator.directive.spec.ts b/src/app/authentication/validators/confirm-validator.directive.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..445b99cbc5286556ba0b7e2f52c3fbcc707ab657 --- /dev/null +++ b/src/app/authentication/validators/confirm-validator.directive.spec.ts @@ -0,0 +1,8 @@ +import { ConfirmValidatorDirective } from './confirm-validator.directive'; + +describe('ConfirmValidatorDirective', () => { + it('should create an instance', () => { + const directive = new ConfirmValidatorDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/authentication/validators/confirm-validator.directive.ts b/src/app/authentication/validators/confirm-validator.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebf84d5cdc60d3b8c10f53963cfed0f8998ecf9f --- /dev/null +++ b/src/app/authentication/validators/confirm-validator.directive.ts @@ -0,0 +1,39 @@ +import { Attribute, Directive } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; + +@Directive({ + selector: '[compare-password]', + providers: [{provide: NG_VALIDATORS, useExisting: ConfirmValidatorDirective, multi: true}] +}) +export class ConfirmValidatorDirective implements Validator{ + + constructor(@Attribute('compare-password') public comparer: string, + @Attribute('parent') public parent: string) { } + validate(control: AbstractControl): ValidationErrors { + const e = control.root.get(this.comparer); + + if (e && control.value !== e.value && !this.isParent) { + return {compare: true}; + } + + if (e && control.value === e.value && this.isParent) { + delete e.errors['compare']; + if (!Object.keys(e.errors).length) { + e.setErrors(null); + } + } + + if (e && control.value !== e.value && this.isParent) { + e.setErrors({compare: true}); + } + } + + private get isParent() { + if (!this.parent) { + return false; + } + return this.parent === 'true' ? true : false; + } + + +} diff --git a/src/app/callback/callback.component.html b/src/app/callback/callback.component.html new file mode 100644 index 0000000000000000000000000000000000000000..abaef7bf08cbff08b1191df6e5e54dbd52d05765 --- /dev/null +++ b/src/app/callback/callback.component.html @@ -0,0 +1,6 @@ + +<nb-card [nbSpinner]="true" nbSpinnerSize="giant" nbSpinnerStatus="primary" size="large"> + <nb-card-body> + Redirecting... + </nb-card-body> + </nb-card> diff --git a/src/app/accessapi/accessapi.component.scss b/src/app/callback/callback.component.scss similarity index 100% rename from src/app/accessapi/accessapi.component.scss rename to src/app/callback/callback.component.scss diff --git a/src/app/authentication/signout/signout.component.spec.ts b/src/app/callback/callback.component.spec.ts similarity index 55% rename from src/app/authentication/signout/signout.component.spec.ts rename to src/app/callback/callback.component.spec.ts index 600423eae5d7b364ae42084aba4f7e043fc802e0..848b5e3ad458d25269fe5cfda1bbf713783d2847 100644 --- a/src/app/authentication/signout/signout.component.spec.ts +++ b/src/app/callback/callback.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { SignoutComponent } from './signout.component'; +import { CallbackComponent } from './callback.component'; -describe('SignoutComponent', () => { - let component: SignoutComponent; - let fixture: ComponentFixture<SignoutComponent>; +describe('CallbackComponent', () => { + let component: CallbackComponent; + let fixture: ComponentFixture<CallbackComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ SignoutComponent ] + declarations: [ CallbackComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(SignoutComponent); + fixture = TestBed.createComponent(CallbackComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/callback/callback.component.ts b/src/app/callback/callback.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..072e023daac82607b2f1a66f4fd9ef3b2317b925 --- /dev/null +++ b/src/app/callback/callback.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { AuthService } from '../authentication/services/auth.service'; + +@Component({ + selector: 'app-callback', + templateUrl: './callback.component.html', + styleUrls: ['./callback.component.scss'] +}) +export class CallbackComponent implements OnInit { + + constructor(private route: ActivatedRoute, private router: Router, private authService: AuthService) { } + + ngOnInit(): void { + this.route.queryParams.subscribe(p => { + this.authService.fetchToken(p.code, p.state).subscribe(data => { + this.authService.updateToken(data.accessToken); + this.router.navigate(['/dashboard']); + }) + }) + } + +} diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index 57e29c2f935d4af3b161a13ef27eabf29168c229..0b462b964bf6830f13e00198f8f0d1aa3d2861d1 100644 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -8,14 +8,14 @@ <!--User badge--> - <nb-user style="white-space: pre;" - name="{{userData.firstName}}" - title="{{userData.lastName}}" + <nb-user style="margin-right: 10px;" + name="{{user.firstName}}" + title="{{user.lastName}}" [nbContextMenu]="menuItems" nbContextMenuTag="my-context-menu" badgePosition="right"> </nb-user> - <button (click)="logout()" nbContextMenuPlacement="right" outline nbButton>Logout</button> + <button (click)="logout()" nbContextMenuPlacement="right" status="danger" outline nbButton>Logout</button> </nb-layout-header> diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts index 2dab938b9bad67c03d3d0c6a8106b3af998f5bfe..abde7a53cb4a242f79f73a85ff0d69f5c70b1e12 100644 --- a/src/app/dashboard/dashboard.component.ts +++ b/src/app/dashboard/dashboard.component.ts @@ -1,6 +1,10 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, NavigationStart, Router } from '@angular/router'; import { NbMenuItem, NbSidebarService } from '@nebular/theme'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { environment } from 'src/environments/environment'; import { AuthService } from '../authentication/services/auth.service'; import { TokenStorageService } from '../authentication/services/token-storage.service'; import { SmartHarvesterUser } from '../user/model/user'; @@ -12,8 +16,9 @@ import { SmartHarvesterUser } from '../user/model/user'; }) export class DashboardComponent implements OnInit { - userData: SmartHarvesterUser; + user: SmartHarvesterUser = new SmartHarvesterUser(); routerUrl: string; + private _isDead = new Subject(); menuItems: NbMenuItem[] = [ { title: 'Home', @@ -72,7 +77,8 @@ export class DashboardComponent implements OnInit { constructor(private readonly sidebarService: NbSidebarService, private authService: AuthService, - private tokeService: TokenStorageService, private route: Router) { } + private tokeService: TokenStorageService, private route: Router, private http: HttpClient) { } + ngOnInit(): void { this.routerUrl = this.route.url; this.route.events.subscribe(event => { @@ -80,11 +86,28 @@ export class DashboardComponent implements OnInit { this.routerUrl = event.url; } }); - let loggedIn = this.authService.isLoggedIn; - if (loggedIn) { - this.userData = this.tokeService.getUser(); + if (this.tokeService.getUser() === null) { + const httpOptions = { + headers: new HttpHeaders({ + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + this.authService.getToken() + }) + }; + this.http.get(environment.smartharvesterUrl + '/harvester/api/username', httpOptions) + .pipe(takeUntil(this._isDead)) + .subscribe( + data => { + this.user.email = data['principal']['userInfo']['email']; + this.user.lastName = data['principal']['userInfo']['familyName']; + this.user.firstName = data['principal']['userInfo']['givenName']; + this.tokeService.saveUser(this.user); + }, + err => console.log(err.error.message), + () => {}); + } else { + this.user = this.tokeService.getUser(); } - } toggleSidebar(): boolean { @@ -94,8 +117,10 @@ export class DashboardComponent implements OnInit { logout() { this.authService.logout().subscribe( - value => this.route.navigateByUrl('/auth/signin') + value => this.route.navigateByUrl('/login'), + err => console.log(err), + () => this.tokeService.removeToken() ); - + } } diff --git a/src/app/datasets/services/dataset-crud.service.ts b/src/app/datasets/services/dataset-crud.service.ts index 838406e5e8f35aefe9c289a191e54a7f6a84d173..b2b96b616fd4e0fd7ebcb753e4b2372ede4bb501 100644 --- a/src/app/datasets/services/dataset-crud.service.ts +++ b/src/app/datasets/services/dataset-crud.service.ts @@ -34,7 +34,7 @@ export class DatasetCrudService { get tokenHarvesterHeader() { return { headers: new HttpHeaders({ - Authorization: 'Bearer ' + this.sessionStorage.getUser().token + Authorization: 'Bearer ' + this.sessionStorage.getToken() }) }; } @@ -170,7 +170,7 @@ export class DatasetCrudService { } getRequestHistorical(catId: string) { - if (this.sessionStorage.getUser().token == null) { + if (this.sessionStorage.getToken() == null) { return of([]); } @@ -179,7 +179,7 @@ export class DatasetCrudService { } createRequestHistorical(catId: string, request: string) { - if (this.sessionStorage.getUser().token == null) { + if (this.sessionStorage.getToken() == null) { return of(); } diff --git a/src/app/mapping/mapping.component.ts b/src/app/mapping/mapping.component.ts index 0b8ded42d672eca6498be222950fed7daa1a0e90..cccf2822d2345fba40152c26cbc933942cacfcdc 100644 --- a/src/app/mapping/mapping.component.ts +++ b/src/app/mapping/mapping.component.ts @@ -1,7 +1,7 @@ import { HttpResponse } from '@angular/common/http'; -import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; @@ -508,7 +508,6 @@ export class MappingComponent implements OnInit { let distributionPath: ResponseFileTsv[] = []; console.table(paths) paths.forEach(path => { - console.log(path.object_category); (path.object_category.includes('dcat:dataset')) ? datasetPath.push(path) : distributionPath.push(path); }) @@ -614,7 +613,6 @@ export class MappingComponent implements OnInit { return this.vocabularies.find((vocabulary: Property) => vocabulary.uri === uri).card; } - private getDistributionEmpty(properties: string, datasetId: string): string { return `@prefix dcat: <http://www.w3.org/ns/dcat#>. @prefix dct: <http://purl.org/dc/terms/>. diff --git a/src/app/mapping/service/mapping.service.ts b/src/app/mapping/service/mapping.service.ts index 6c8f27f70f1233af775d7599a6f1c00bdbbe2c9f..83213dc93f95ff9a3003b6e5370f1c6f48248f61 100644 --- a/src/app/mapping/service/mapping.service.ts +++ b/src/app/mapping/service/mapping.service.ts @@ -137,7 +137,7 @@ export class MappingService { 'match_type', 'object_category' ] - + lines.shift() for (let i = 0; i < lines.length; i++) { diff --git a/src/app/publishapi/publishapi.component.html b/src/app/publishapi/publishapi.component.html index a4ac40674dbf02df85e10ed30b0caf59a08e1a34..5e99df591243130b5383704e53aca7b08c1c434c 100644 --- a/src/app/publishapi/publishapi.component.html +++ b/src/app/publishapi/publishapi.component.html @@ -315,7 +315,7 @@ <p class="lorem"> Map your metadata schema with DCAT schema </p> - <app-mapping *ngIf="initLabelThree && datasets.ready" [catalogId]="openApi.info['x-catalog-id']" [type]="type"> + <app-mapping *ngIf="initLabelThree && datasets.ready" [catalogId]="openApi.info['x-catalog-id']" [type]="openApi.info['x-format']"> </app-mapping> <button class="prev-button" nbButton nbStepperPrevious (click)="initLabelThree=false; resetDataset(); datasets.ready = false">prev</button> diff --git a/src/app/publishapi/publishapi.component.ts b/src/app/publishapi/publishapi.component.ts index f3d443b0fcfc1276a3a5f2a42f1b7a35d59ab6d3..5caf7af56dcfe26be67b6c29566db137bd577208 100644 --- a/src/app/publishapi/publishapi.component.ts +++ b/src/app/publishapi/publishapi.component.ts @@ -330,6 +330,7 @@ export class PublishApiComponent implements OnInit { this.openApi = this.openApiService.getFromString(reader.result as string); this.openApi.cleanServers(servers[0].url); this.openApi.info['x-catalog-id'] = catId; + }; reader.readAsText(jsonFile); diff --git a/src/app/publishapi/services/openapi-service.ts b/src/app/publishapi/services/openapi-service.ts index c111bd0b924f033a86325639a9919121a4dd1bd3..4cfa0bcb7e1ac66435f63e3e0f21420a3e4c62b7 100644 --- a/src/app/publishapi/services/openapi-service.ts +++ b/src/app/publishapi/services/openapi-service.ts @@ -25,7 +25,7 @@ export class OpenApiService { get tokenHarvesterHeader() { return { headers: new HttpHeaders({ - Authorization: 'Bearer ' + this.sessionStorage.getUser().token + Authorization: 'Bearer ' + this.sessionStorage.getToken() }) }; } @@ -66,7 +66,7 @@ export class OpenApiService { } getByCatalogId(catId: string): Observable<OpenApiDTO> { - if (this.sessionStorage.getUser().token == null) { + if (this.sessionStorage.getToken() == null) { return of(); } @@ -74,7 +74,7 @@ export class OpenApiService { } publishOpenApi(openApi: OpenApi) { - if (this.sessionStorage.getUser().token == null) { + if (this.sessionStorage.getToken() == null) { return of(); } diff --git a/src/app/repository/services/publish-repository.service.ts b/src/app/repository/services/publish-repository.service.ts index f562c25dadc0b18e3cadc37d6b931c94749689fa..ad49bad8178b4c8d727ad35629eefb660f62a03e 100644 --- a/src/app/repository/services/publish-repository.service.ts +++ b/src/app/repository/services/publish-repository.service.ts @@ -5,6 +5,7 @@ import { AppConfiguration } from 'src/app/AppConfiguration'; import { AuthService } from 'src/app/authentication/services/auth.service'; import { environment } from 'src/environments/environment'; import { TokenStorageService } from 'src/app/authentication/services/token-storage.service'; +import { SmartHarvesterUser } from 'src/app/user/model/user'; interface PersistentUrlResponse { persistentUrl: string; @@ -57,7 +58,7 @@ export class PublishRepositoryService { async addUserCatalog(catId: string): Promise<any> { const tokenResponse = this.tokenService.getToken(); - const user = this.tokenService.getUser(); + const user: SmartHarvesterUser = this.tokenService.getUser(); return this.http.post(this.smartApiUrl + '/user/' + user.email + '/catalogs', catId, { headers: { Authorization: 'Bearer ' + tokenResponse } }) diff --git a/src/app/services/catalog.service.ts b/src/app/services/catalog.service.ts index 6c90a711ad2895820193dbcc2dfb54bd82753f17..2f0d632cbc7f44dfd29253ea8cf485e63bfeceb1 100644 --- a/src/app/services/catalog.service.ts +++ b/src/app/services/catalog.service.ts @@ -2,6 +2,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { environment } from 'src/environments/environment'; import { TokenStorageService } from '../authentication/services/token-storage.service'; +import { SmartHarvesterUser } from '../user/model/user'; export interface FdpApiResponseItem { subject: { @@ -44,7 +45,7 @@ export class CatalogService { } getAllCatalogId() { - const user = this.tokenService.getUser(); + const user: SmartHarvesterUser = this.tokenService.getUser(); return this.http.get<{ catId: string, uuid: string }[]>(this.smartApiUrl + '/user/' + user.email + '/catalogs', this.httpOptionsSmartHarvester); } diff --git a/src/app/settingsmartapi/settingsmartapi.component.html b/src/app/settingsmartapi/settingsmartapi.component.html deleted file mode 100644 index 29fd9f652e0e3dce7f999c659225c6937a7adc67..0000000000000000000000000000000000000000 --- a/src/app/settingsmartapi/settingsmartapi.component.html +++ /dev/null @@ -1,15 +0,0 @@ - -<form [formGroup]="Form" (ngSubmit)="SaveSmartapiSetting()"> - - <div class="form-group"> - <label>SmartApi Url - <input matInput placeholder="url http://" type="text" class="form-control" formControlName="smartapiurl" required> - </label> - </div> - - <div> - <br> - <button type="submit" class="btn btn-primary">Save SmartHarvester setting</button> - </div> - </form> - \ No newline at end of file diff --git a/src/app/settingsmartapi/settingsmartapi.component.scss b/src/app/settingsmartapi/settingsmartapi.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/settingsmartapi/settingsmartapi.component.spec.ts b/src/app/settingsmartapi/settingsmartapi.component.spec.ts deleted file mode 100644 index 3008aca1ba211589618f74e2eacefcdabf2de7da..0000000000000000000000000000000000000000 --- a/src/app/settingsmartapi/settingsmartapi.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SettingsmartapiComponent } from './settingsmartapi.component'; - -describe('SettingsmartapiComponent', () => { - let component: SettingsmartapiComponent; - let fixture: ComponentFixture<SettingsmartapiComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SettingsmartapiComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SettingsmartapiComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/settingsmartapi/settingsmartapi.component.ts b/src/app/settingsmartapi/settingsmartapi.component.ts deleted file mode 100644 index 7d5430d559176091765115d63b4c935c642e1dd5..0000000000000000000000000000000000000000 --- a/src/app/settingsmartapi/settingsmartapi.component.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { AppConfiguration } from '../AppConfiguration'; -import { HttpClient } from '@angular/common/http'; -import { FileSaverService } from 'ngx-filesaver'; -import { FormControl} from '@angular/forms'; -import { FormGroup} from '@angular/forms'; -import { Observable } from 'rxjs'; -import { environment } from 'src/environments/environment.prod'; - -@Component({ - selector: 'app-settingsmartapi', - templateUrl: './settingsmartapi.component.html', - styleUrls: ['./settingsmartapi.component.scss'] -}) -export class SettingsmartapiComponent implements OnInit { - - - Form = new FormGroup({ - smartapiurl: new FormControl(), -}); - - constructor( - private appConfig: AppConfiguration, - private http: HttpClient, - private _FileSaverService: FileSaverService - ) {} - - ngOnInit() { - this.Form.setValue({ - smartapiurl: this.appConfig.smartapiurl - }); - } - - - SaveSmartapiSetting() { - let data: string; - data ='\ - {\n\ - "fdpurl": "'+ this.appConfig.fdpurl +'", \n\ - "fdpemail": "'+ this.appConfig.fdpemail +'", \n\ - "fdppassword": "'+ this.appConfig.fdppassword +'", \n\ - "elasticurl": "'+ this.appConfig.elasticurl +'", \n\ - "smartapiurl": "'+ this.Form.value.smartapiurl +'" \n\ - }' - console.log(data); - return this.http.post(environment.fdpUrl+"/setting", data,{responseType: 'text'}).subscribe( (r)=>{console.log(r)}) ; - }; - -} diff --git a/src/app/stats/stats.component.html b/src/app/stats/stats.component.html index b745aa6391074a4dccff6dad96e11feca1e24f0c..77e578ad54412f2d55a710744ecdf8bd5f7b3208 100644 --- a/src/app/stats/stats.component.html +++ b/src/app/stats/stats.component.html @@ -1,12 +1,13 @@ - <div class="w3-container"> - <table class="w3-table-all w3-card-4"> - <thead style="background-color: #3366ff"> - <th style="text-align: center;">Number of Catalogs</th> - <th style="text-align: center;">Number of Datasets</th> - </thead> - <tbody > - <tr><td *ngFor="let stat of stats" style="text-align: center;"><strong>{{stat}}</strong></td></tr> + <table class="w3-table-all w3-card-4"> + <thead style="background-color: #3366ff"> + <th style="text-align: center;">Number of Catalogs</th> + <th style="text-align: center;">Number of Datasets</th> + </thead> + <tbody> + <tr> + <td *ngFor="let stat of stats" style="text-align: center;"><strong>{{stat}}</strong></td> + </tr> </tbody> - </table> - </div> \ No newline at end of file + </table> +</div> \ No newline at end of file diff --git a/src/app/user/model/user.ts b/src/app/user/model/user.ts index fbd9c6fd1b95733efdc8dc9f81792589561b3275..28db83f6d6ea3c1ed181258a3729d290076e8852 100644 --- a/src/app/user/model/user.ts +++ b/src/app/user/model/user.ts @@ -1,18 +1,9 @@ export class SmartHarvesterUser { public email: string; - public password: string; + public password?: string; public firstName?: string; public lastName?: string; public passwordConfirm?: string; - public id?: "5fbfb86b688c8577c7b8aeff" - public token?: "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0QGNpbmVzLmZyIiwiaWF0IjoxNjA2ODQzNjkwLCJleHAiOjE2MDY5MzAwOTB9.UcmKMRmIEyNLW_kCEEI83uMuDG3Lgf5BKeAHvOhhjiFsV-8keKXxy5VLyHR4LvX_7vZL9WN_H_49-sLxGFTJyQ" - public tokenType?: "Bearer"; + - constructor(params: any) { - Object.assign(this, params); - } - - isAnonyme(): boolean { - return this.email === undefined; - } } diff --git a/version b/version index 0828ab79473bf97cc6631bc41685fc0242a17e44..62ccda57fa53241f87ce3c272b4890f98327ed5c 100644 --- a/version +++ b/version @@ -1 +1 @@ -v18 \ No newline at end of file +v19