Create personal info, registered services and personal-token pages

This commit is contained in:
Konstantinos Triantafyllou 2023-06-26 11:33:10 +03:00
parent 54bbc5a413
commit 59b129e284
28 changed files with 1209 additions and 23 deletions

View File

@ -49,6 +49,7 @@
"src/assets/common-assets/library-css/material.scss" "src/assets/common-assets/library-css/material.scss"
], ],
"scripts": [ "scripts": [
"src/assets/common-assets/js/copy.js",
"node_modules/uikit/dist/js/uikit.min.js", "node_modules/uikit/dist/js/uikit.min.js",
"node_modules/uikit/dist/js/uikit-icons.min.js", "node_modules/uikit/dist/js/uikit-icons.min.js",
"node_modules/jquery/dist/jquery.js" "node_modules/jquery/dist/jquery.js"

View File

@ -0,0 +1,10 @@
.uk-border-circle {
width: 100px;
height: 100px;
position: relative;
& > img {
max-width: 64px;
max-height: 64px;
}
}

View File

@ -0,0 +1,400 @@
import {ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {Subscription} from "rxjs";
import {UserManagementService} from "../openaireLibrary/services/user-management.service";
import {User} from "../openaireLibrary/login/utils/helper.class";
import {PersonalInfo, PersonalService} from "../services/personal.service";
import {NotificationHandler} from "../openaireLibrary/utils/notification-handler";
import {API, ApisService} from "../services/apis.service";
import {AlertModal} from "../openaireLibrary/utils/modal/alert";
import {StringUtils} from "../openaireLibrary/utils/string-utils.class";
import {properties} from "../../environments/environment";
import {Stakeholder} from "../openaireLibrary/monitor/entities/stakeholder";
import {CommunityInfo} from "../openaireLibrary/connect/community/communityInfo";
import {UtilitiesService} from "../openaireLibrary/services/utilities.service";
declare var copy;
@Component({
selector: `apis`,
template: `
<div page-content>
<div header class="uk-section-small uk-container uk-container-small uk-flex uk-flex-right@m uk-flex-center">
<button (click)="openEditModal()" class="uk-button uk-button-primary uk-flex uk-flex-middle">
<icon name="add" [flex]="true"></icon>
<span class="uk-margin-xsmall-left">New Service</span>
</button>
</div>
<div inner class="uk-section-small uk-container uk-container-small">
<div *ngIf="loading" class="uk-height-large uk-position-relative">
<loading class="uk-position-center"></loading>
</div>
<div *ngIf="!loading">
<div class="uk-alert-primary uk-alert uk-margin-top-remove uk-flex uk-flex-middle">
<icon name="info" [flex]="true"></icon>
<span class="uk-margin-small-left">You can register up to 5 services.
For more information please read the <a
href="https://graph.openaire.eu/develop/authentication.html" target="_blank">OpenAIRE API Authentication documentation</a>.</span>
</div>
<div *ngIf="apis.length === 0"
class="uk-margin-large-top uk-card uk-card-default uk-height-small uk-position-relative">
<div class="uk-position-center uk-text-bold">You have not registered any service yet!</div>
</div>
<table *ngIf="apis.length > 0" class="uk-table uk-table-divider uk-margin-medium-top">
<thead>
<tr>
<th>Name</th>
<th>Client ID</th>
<th>Creation Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let api of apis; let i=index">
<td>{{api.service.name}}</td>
<td>{{api.service.clientId}}</td>
<td>{{api.service.creationDate | date: 'dd-MM-YYYY HH:mm'}}</td>
<td>
<span class="uk-flex uk-flex-middle">
<icon name="edit" class="clickable" customClass="uk-text-primary"
(click)="openEditModal(i)"></icon>
<icon name="delete" class="clickable uk-margin-xsmall-left"
customClass="uk-text-danger" (click)="openDeleteModal(i)"></icon>
<icon name="info" class="clickable uk-margin-xsmall-left"
customClass="uk-text-secondary" (click)="openInfoModal(i)"></icon>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<modal-alert #editModal (alertOutput)="save()" [large]="true"
[okDisabled]="form?.disabled || form?.invalid || form?.pristine">
<div *ngIf="form" class="uk-grid" uk-grid>
<div class="uk-width-1-2@m uk-width-1-1" input placeholder="Service Name"
[formInput]="form.get('name')"></div>
<div class="uk-width-1-2@m uk-width-1-1" input type="logoURL" placeholder="Service URL"
[formInput]="form.get('url')"></div>
<div class="uk-width-1-1" input type="logoURL" placeholder="Logo URL"
[formInput]="form.get('logoURL')">
</div>
<div class="uk-width-1-1">
<label class="uk-text-bold">Security Level</label>
<div id="security-hint" class="uk-margin">Register your service to get a client id and a client
secret. Use the client id and secret to make your requests. <a
href="https://graph.openaire.eu/develop/basic.html" target="_blank">Read more...</a>
</div>
<div class="uk-flex uk-flex-middle">
<div class="uk-margin-small-right">
<label><input type="radio" name="securityLevel" [disabled]="index !== -1"
class="uk-radio" (ngModelChange)="securityChanged($event)"
[ngModel]="advanced" [value]="false"><span
class="uk-margin-small-left">Basic</span></label>
</div>
<div>
<label><input type="radio" name="securityLevel" [disabled]="index !== -1"
class="uk-radio" (ngModelChange)="securityChanged($event)"
[ngModel]="advanced" [value]="true"><span class="uk-margin-small-left">Advanced</span></label>
</div>
</div>
</div>
<ng-container *ngIf="advanced">
<div class="uk-width-1-1">
<div class="uk-flex uk-flex-between">
<label class="uk-text-bold">Public Key</label>
<div class="uk-flex uk-flex-middle">
<div class="uk-margin-small-right">
<label><input type="radio" name="keyType" class="uk-radio"
(ngModelChange)="keyTypeChanged($event)" [ngModel]="byValue"
[value]="true"><span
class="uk-margin-small-left">By Value</span></label>
</div>
<div>
<label><input type="radio" name="keyType" class="uk-radio"
(ngModelChange)="keyTypeChanged($event)" [ngModel]="byValue"
[value]="false"><span
class="uk-margin-small-left">By URI</span></label>
</div>
</div>
</div>
</div>
<div *ngIf="byValue" class="uk-width-1-1" input type="textarea" [formInput]="form.get('value')"
rows="5" placeholder="Key Value" [hint]="hint"></div>
<div *ngIf="!byValue" class="uk-width-1-1" type="logoURL" input [formInput]="form.get('uri')"
placeholder="Key URI"></div>
</ng-container>
</div>
</modal-alert>
<modal-alert #deleteModal [overflowBody]="false" (alertOutput)="confirm()"></modal-alert>
<modal-alert #infoModal [large]="false">
<div *ngIf="api?.details">
<ul class="uk-list uk-list-divider">
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Service Name</div>
<div class="uk-width-expand uk-text-break">{{api.details.clientName}}</div>
</div>
</li>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">
<div class="uk-text-bold uk-flex uk-flex-middle">
<span class="uk-margin-xsmall-right">Client Id</span>
<a class="uk-link uk-link-reset" (click)="copyToClipboard('client-id')"><icon name="content_copy" customClass="uk-text-secondary" [flex]="true"></icon></a>
</div>
</div>
<div id="client-id" class="uk-width-expand uk-text-break">{{api.details.clientId}}</div>
</div>
</li>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Scope</div>
<div class="uk-width-expand uk-text-break">openid</div>
</div>
</li>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Grant type</div>
<div class="uk-width-expand uk-text-break">{{api.details.grantTypes.join(', ')}}</div>
</div>
</li>
<ng-template [ngIf]="!api.service.keyType" [ngIfElse]="key">
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">
<div class="uk-text-bold uk-flex uk-flex-middle">
<span class="uk-margin-xsmall-right">Client Secret</span>
<a class="uk-link uk-link-reset" (click)="copyToClipboard('client-secret')"><icon name="content_copy" customClass="uk-text-secondary" [flex]="true"></icon></a>
</div>
</div>
<div id="client-secret" class="uk-width-expand uk-text-break">{{api.details.clientSecret}}</div>
</div>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Authentication method</div>
<div class="uk-width-expand uk-text-break">Client Secret Basic</div>
</div>
</li>
</ng-template>
<ng-template #key>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Authentication method</div>
<div class="uk-width-expand uk-text-break">Asymmetrically-signed JWT assertion</div>
</div>
</li>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Token Endpoint Authentication Signing Algorithm</div>
<div class="uk-width-expand uk-text-break">RSASSA using SHA-256 hash algorithm</div>
</div>
</li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">
<div class="uk-text-bold uk-flex uk-flex-middle">
<span class="uk-margin-xsmall-right">Public Key</span>
<a class="uk-link uk-link-reset" (click)="copyToClipboard('public-key')"><icon name="content_copy" customClass="uk-text-secondary" [flex]="true"></icon></a>
</div>
</div>
<div *ngIf="api.details.jwksUri" id="public-key" class="uk-width-expand uk-text-break">{{api.details.jwksUri}}</div>
<div *ngIf="api.details.jwks" id="public-key" class="uk-width-expand uk-text-break">
<pre><code class="uk-overflow-auto">{{api.details.jwks.keys[0] | json}}</code></pre>
</div>
</div>
</ng-template>
<li>
<div class="uk-flex">
<div class="uk-text-bold uk-width-small uk-margin-xsmall-right">Creation Date</div>
<div class="uk-width-expand uk-text-break">{{api.service.creationDate | date: 'dd-mm-YYYY hh:MM'}}</div>
</div>
</li>
</ul>
</div>
</modal-alert>
`,
styleUrls: ['apis.component.less']
})
export class ApisComponent implements OnInit, OnDestroy {
form: FormGroup;
loading: boolean = true;
subscriptions: any[] = [];
user: User;
apis: API[] = [];
api: API;
index: number = -1;
advanced: boolean = false;
byValue: boolean = true;
hint = '{"kty": ..., "e": ... , "use": ... , "kid": ..., "alg": ... , "n": ...}';
@ViewChild("editModal") editModal: AlertModal;
@ViewChild("deleteModal") deleteModal: AlertModal;
@ViewChild("infoModal") infoModal: AlertModal;
constructor(private fb: FormBuilder,
private apisService: ApisService,
private utilsService: UtilitiesService,
private cdr: ChangeDetectorRef,
private userManagementService: UserManagementService) {
}
ngOnInit() {
this.loading = true;
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
this.subscriptions.push(this.apisService.getMyServices().subscribe(apis => {
this.apis = apis;
this.loading = false;
}, error => {
console.error(error);
this.apis = [];
this.loading = false;
}));
}));
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if(subscription instanceof Subscription) {
subscription.unsubscribe();
}
})
}
openEditModal(index: number = -1) {
let api = new API();
this.index = index;
this.editModal.okButtonLeft = false;
if(index !== -1) {
api = this.apis[index];
this.editModal.alertTitle = "Edit Service";
this.editModal.okButtonText = "Update";
} else {
this.editModal.alertTitle = "Create Service";
this.editModal.okButtonText = "Create";
}
this.form = this.fb.group({
name: this.fb.control(api.service.name, Validators.required),
keyType: this.fb.control(api.service.keyType),
logoURL: this.fb.control(api.details.logoUri, [StringUtils.urlValidator()]),
url: this.fb.control(api.service.url, [Validators.required, StringUtils.urlValidator()]),
uri: this.fb.control(api.details.jwksUri, [StringUtils.urlValidator()]),
value: this.fb.control(null, [StringUtils.jsonValidator('The format should be {"kty": ..., "e": ... , "use": ... , "kid": ..., "alg": ... , "n": ...}')])
});
if(api.details?.jwks?.keys) {
this.form.get('value').setValue(JSON.stringify(api.details.jwks.keys[0]));
}
this.advanced = !!this.form.get('keyType').getRawValue();
this.byValue = this.advanced && this.form.get('keyType').getRawValue() === 'value';
this.formChanges();
this.editModal.open();
}
openDeleteModal(index: number) {
this.index = index;
let api = this.apis[this.index];
this.deleteModal.alertTitle = 'Delete Service';
this.deleteModal.alertMessage = true;
this.deleteModal.message = 'You are going to delete ' + api.service.name + '. Are you sure you want to proceed?';
this.deleteModal.okButtonText = 'Yes';
this.deleteModal.cancelButtonText = 'No';
this.deleteModal.open();
}
openInfoModal(index: number) {
this.api = this.apis[index];
this.infoModal.okButton = false;
this.infoModal.cancelButtonText = 'Close';
this.infoModal.alertTitle = 'Details for ' + this.api.details.clientName;
this.infoModal.open();
}
save() {
this.loading = true;
if(this.index === -1) {
this.subscriptions.push(this.apisService.create(this.form.getRawValue()).subscribe(api => {
this.apis.push(api)
NotificationHandler.rise('Your service has been created successfully.');
this.loading = false;
}, error => {
console.error(error);
NotificationHandler.rise('An error has occurred. Please try again later.', 'danger');
this.loading = false;
}));
} else {
this.subscriptions.push(this.apisService.save(this.apis[this.index].service.id, this.form.getRawValue()).subscribe(api => {
this.apis[this.index] = api;
NotificationHandler.rise('Your service has been created successfully.');
this.loading = false;
}, error => {
console.error(error);
NotificationHandler.rise('An error has occurred. Please try again later.', 'danger');
this.loading = false;
}));
}
}
confirm() {
this.loading = true;
this.subscriptions.push(this.apisService.delete(this.apis[this.index].service.id).subscribe(() => {
this.apis.splice(this.index, 1);
NotificationHandler.rise('Your service has been created deleted.');
this.loading = false;
}, error => {
console.error(error);
NotificationHandler.rise('An error has occurred. Please try again later.', 'danger');
this.loading = false;
}));
}
securityChanged(event) {
this.advanced = event;
if(this.advanced) {
this.keyTypeChanged(true);
} else {
this.form.get('keyType').setValue(null);
}
}
keyTypeChanged(event) {
this.byValue = event;
if(this.byValue) {
this.form.get('keyType').setValue('value');
} else {
this.form.get('keyType').setValue('uri');
}
}
formChanges() {
this.subscriptions.push(this.form.get('keyType').valueChanges.subscribe(value => {
if(value === 'value') {
this.form.get('value').addValidators(Validators.required);
this.form.get('uri').removeValidators(Validators.required);
} else if(value === 'uri') {
this.form.get('value').removeValidators(Validators.required);
this.form.get('uri').addValidators(Validators.required);
} else {
this.form.get('value').removeValidators(Validators.required);
this.form.get('uri').removeValidators(Validators.required);
}
this.form.get('value').updateValueAndValidity();
this.form.get('uri').updateValueAndValidity();
this.cdr.detectChanges();
}));
}
jsonPretty(value: any) {
return JSON.stringify(value, null, 2);
}
public static getLogoUrl(api: API): string {
return api?.details?.logoUri?api.details.logoUri:"assets/common-assets/placeholder.png";
}
copyToClipboard(id: string) {
if(copy.exec(id)) {
NotificationHandler.rise('Copied to clipboard!');
} else {
NotificationHandler.rise('Unable to copy to clipboard!', 'danger');
}
}
}

View File

@ -0,0 +1,29 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {ApisComponent} from "./apis.component";
import {RouterModule} from "@angular/router";
import {PreviousRouteRecorder} from "../openaireLibrary/utils/piwik/previousRouteRecorder.guard";
import {LoginGuard} from "../openaireLibrary/login/loginGuard.guard";
import {PageContentModule} from "../openaireLibrary/dashboard/sharedComponents/page-content/page-content.module";
import {InputModule} from "../openaireLibrary/sharedComponents/input/input.module";
import {LoadingModule} from "../openaireLibrary/utils/loading/loading.module";
import {HasPersonalInfoGuard} from "../services/hasPersonalInfo.guard";
import {IconsModule} from "../openaireLibrary/utils/icons/icons.module";
import {AlertModalModule} from "../openaireLibrary/utils/modal/alertModal.module";
import {FormsModule} from "@angular/forms";
@NgModule({
imports: [CommonModule, RouterModule.forChild([
{
path: '',
component: ApisComponent,
canActivate: [LoginGuard, HasPersonalInfoGuard],
canDeactivate: [PreviousRouteRecorder]
}
]), PageContentModule, InputModule, LoadingModule, IconsModule, AlertModalModule, FormsModule],
declarations: [ApisComponent],
exports: [ApisComponent]
})
export class ApisModule {
}

View File

@ -1,10 +1,55 @@
import { NgModule } from '@angular/core'; import {NgModule} from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import {RouterModule, Routes} from '@angular/router';
import {ErrorPageComponent} from "./error/errorPage.component";
import {AdminLoginGuard} from "./openaireLibrary/login/adminLoginGuard.guard";
const routes: Routes = []; const routes: Routes = [
{
path: '',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
data: {hasSidebar: false}
},
{
path: 'personal-info',
loadChildren: () => import('./personal-info/personal-info.module').then(m => m.PersonalInfoModule)
},
{
path: 'personal-token',
loadChildren: () => import('./personal-token/personal-token.module').then(m => m.PersonalTokenModule)
},
{
path: 'apis',
loadChildren: () => import('./apis/apis.module').then(m => m.ApisModule)
},
{
path: 'user-info',
loadChildren: () => import('./login/libUser.module').then(m => m.LibUserModule),
data: {hasSidebar: false}
},
{
path: 'reload',
loadChildren: () => import('./reload/libReload.module').then(m => m.LibReloadModule),
data: {hasSidebar: false, hasHeader: false}
},
{
path: 'theme',
loadChildren: () => import('./openaireLibrary/utils/theme/theme.module').then(m => m.ThemeModule),
canActivateChild: [AdminLoginGuard],
data: {hasSidebar: false, hasHeader: false}
},
{
path: 'error',
pathMatch: 'full',
component: ErrorPageComponent,
data: {hasSidebar: false}
},
];
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes, {
onSameUrlNavigation: 'reload'
})],
exports: [RouterModule] exports: [RouterModule]
}) })
export class AppRoutingModule { } export class AppRoutingModule {
}

View File

@ -1,11 +1,114 @@
import { Component } from '@angular/core'; import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
import {EnvProperties} from "./openaireLibrary/utils/properties/env-properties";
import {properties} from "../environments/environment";
import {LayoutService} from "./openaireLibrary/dashboard/sharedComponents/sidebar/layout.service";
import {SmoothScroll} from "./openaireLibrary/utils/smooth-scroll";
import {UserManagementService} from "./openaireLibrary/services/user-management.service";
import {Subscriber} from "rxjs";
import {User} from "./openaireLibrary/login/utils/helper.class";
import {MenuItem} from "./openaireLibrary/sharedComponents/menu";
import {Header} from "./openaireLibrary/sharedComponents/navigationBar.component";
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
template: ` template: `
<div>test</div> <div *ngIf="loading">
` <loading [full]="true"></loading>
</div>
<div *ngIf="!loading">
<div class="sidebar_main_swipe" [class.sidebar_main_active]="open && hasSidebar"
[class.sidebar_mini]="!open && hasSidebar"
[class.sidebar_hover]="hover">
<div id="modal-container"></div>
<navbar *ngIf="hasHeader" portal="client-management" [header]="menuHeader"
[userMenuItems]=userMenuItems [menuItems]="menuItems" [user]="user"></navbar>
<div>
<dashboard-sidebar *ngIf="hasSidebar" [items]="sideBarItems" [backItem]="backItem"></dashboard-sidebar>
<main>
<router-outlet></router-outlet>
</main>
</div>
</div>
</div>
`
}) })
export class AppComponent { export class AppComponent implements OnInit, OnDestroy {
title = 'client-management-portal'; title = 'OpenAIRE APIs Authentication';
properties: EnvProperties = properties;
user: User;
loading = true;
hasSidebar: boolean = false;
hasHeader: boolean = true;
sideBarItems: MenuItem[] = [];
menuItems: MenuItem[] = [];
backItem: MenuItem;
menuHeader: Header = {
route: "/",
url: null,
title: "OpenAIRE APIs Authentication",
logoUrl: 'assets/common-assets/common/Logo_Horizontal.png',
logoSmallUrl: 'assets/common-assets/common/Logo_Small.png',
position: 'center',
badge: false
};
userMenuItems: MenuItem[] = [];
private subscriptions: any[] = [];
constructor(private cdr: ChangeDetectorRef,
private smoothScroll: SmoothScroll,
private layoutService: LayoutService,
private userManagementService: UserManagementService) {
}
ngOnInit() {
this.subscriptions.push(this.layoutService.hasSidebar.subscribe(hasSidebar => {
this.hasSidebar = hasSidebar;
this.cdr.detectChanges();
}));
this.subscriptions.push(this.layoutService.hasHeader.subscribe(hasHeader => {
this.hasHeader = hasHeader;
this.cdr.detectChanges();
}));
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
if (user) {
this.user = user;
} else if (this.user) {
this.user = user;
}
this.buildMenu();
this.layoutService.setOpen(true);
this.loading = false;
}));
}
public ngOnDestroy() {
this.subscriptions.forEach(value => {
if (value instanceof Subscriber) {
value.unsubscribe();
}
});
this.userManagementService.clearSubscriptions();
this.layoutService.clearSubscriptions();
this.smoothScroll.clearSubscriptions();
}
public get open() {
return this.layoutService.open;
}
public get hover() {
return this.layoutService.hover;
}
buildMenu() {
this.backItem = new MenuItem('overview', 'Home', '', '/', false, [], [], null, {name: 'west'});
let items: MenuItem[] = [
new MenuItem('personal-info', 'Personal Info', '', '/personal-info', false, [], [], null, {name: 'person'}),
new MenuItem('personal-token', 'Personal Token', '', '/personal-token', false, [], [], null, {name: 'verified_user'}),
new MenuItem('apis', 'Registered Services', '', '/apis', false, [], [], null, {name: 'apps'}),
];
this.sideBarItems = items;
this.userMenuItems = items;
this.hasSidebar = this.hasSidebar && this.sideBarItems.length > 1;
}
} }

View File

@ -3,14 +3,30 @@ import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import {ErrorPageComponent} from "./error/errorPage.component";
import {ErrorModule} from "./openaireLibrary/error/error.module";
import {SharedModule} from "./openaireLibrary/shared/shared.module";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {LoadingModule} from "./openaireLibrary/utils/loading/loading.module";
import {SideBarModule} from "./openaireLibrary/dashboard/sharedComponents/sidebar/sideBar.module";
import {NavigationBarModule} from "./openaireLibrary/sharedComponents/navigationBar.module";
import {HttpClientModule} from "@angular/common/http";
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent AppComponent,
ErrorPageComponent
], ],
imports: [ imports: [
BrowserAnimationsModule,
LoadingModule,
HttpClientModule,
SideBarModule,
BrowserModule, BrowserModule,
AppRoutingModule AppRoutingModule,
ErrorModule,
SharedModule,
NavigationBarModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@ -0,0 +1,13 @@
import {Component} from '@angular/core';
@Component({
selector: 'openaire-error',
template: `
<div class="uk-section">
<error></error>
</div>
`
})
export class ErrorPageComponent {
}

View File

@ -0,0 +1,38 @@
import {Component, OnInit} from "@angular/core";
@Component({
selector: 'home',
template: `
<div class="uk-container uk-container-small uk-section uk-text-center">
<h2>OpenAIRE APIs Authentication</h2>
<div class="uk-margin-large-top">The OpenAIRE APIs can be accessed over HTTPS both by authenticated and unauthenticated requests. To achieve <span class="uk-text-bold">better rate limits</span> you need to make <span class="uk-text-bold">authenticated requests.</span></div>
<div class="uk-alert uk-alert-primary uk-flex uk-flex-middle uk-margin-medium-top">
<icon name="info" [type]="'outlined'" [flex]="true"></icon>
<span class="uk-margin-small-left">For more information please read the <a href="https://graph.openaire.eu/develop/authentication.html" target="_blank">OpenAIRE API Authentication documentation</a>.</span>
</div>
<div class="uk-grid uk-grid-large uk-child-width-1-2@m uk-child-width-1-1 uk-margin-large-top" uk-grid>
<div>
<a routerLink="personal-token" class="uk-card uk-card-default uk-card-hover uk-link-reset uk-card-body uk-flex uk-flex-column uk-flex-center">
<h6>Personal Token</h6>
<div>
Get access to the OpenAIRE APIs with your personal access and refresh token.
</div>
</a>
</div>
<div>
<a routerLink="apis" class="uk-card uk-card-default uk-card-hover uk-link-reset uk-card-body uk-flex uk-flex-column uk-flex-center">
<h6>Registered Services</h6>
<div>
Register your services to get access to the OpenAIRE APIs.
</div>
</a>
</div>
</div>
</div>
`
})
export class HomeComponent implements OnInit {
ngOnInit() {
}
}

View File

@ -0,0 +1,17 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {RouterModule} from "@angular/router";
import {PreviousRouteRecorder} from "../openaireLibrary/utils/piwik/previousRouteRecorder.guard";
import {HomeComponent} from "./home.component";
import {IconsModule} from "../openaireLibrary/utils/icons/icons.module";
@NgModule({
declarations: [HomeComponent],
imports: [CommonModule, RouterModule.forChild([
{path: '', component: HomeComponent, canDeactivate: [PreviousRouteRecorder]}
]), IconsModule],
exports: [HomeComponent]
})
export class HomeModule {
}

View File

@ -0,0 +1,24 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {OpenaireUserComponent} from './user.component';
import {UserRoutingModule} from './user-routing.module';
import {UserModule} from '../openaireLibrary/login/user.module';
import {PreviousRouteRecorder} from '../openaireLibrary/utils/piwik/previousRouteRecorder.guard';
import {SubscribeService} from '../openaireLibrary/utils/subscribe/subscribe.service';
import {EmailService} from "../openaireLibrary/utils/email/email.service";
@NgModule({
imports: [
CommonModule, FormsModule,
UserRoutingModule, UserModule
],
providers: [PreviousRouteRecorder, SubscribeService, EmailService],
declarations: [
OpenaireUserComponent
]
})
export class LibUserModule {
}

View File

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import {OpenaireUserComponent } from './user.component';
import {PreviousRouteRecorder} from '../openaireLibrary/utils/piwik/previousRouteRecorder.guard';
@NgModule({
imports: [
RouterModule.forChild([
{ path: '', component: OpenaireUserComponent, canDeactivate: [PreviousRouteRecorder]},
])
]
})
export class UserRoutingModule { }

View File

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
@Component({
selector: 'openaire-user',
template: `<user></user>`
})
export class OpenaireUserComponent {
}

@ -1 +1 @@
Subproject commit e6d4be54540ff1b23acb88728be9e0e9dd1ebdd1 Subproject commit 7d2eb68914b5fc53a4e3dbf8fe22c4f67b8bff7f

View File

@ -0,0 +1,129 @@
import {Component, OnDestroy, OnInit} from "@angular/core";
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {Subscription} from "rxjs";
import {UserManagementService} from "../openaireLibrary/services/user-management.service";
import {User} from "../openaireLibrary/login/utils/helper.class";
import {PersonalInfo, PersonalService} from "../services/personal.service";
import {NotificationHandler} from "../openaireLibrary/utils/notification-handler";
import {NavigationEnd, Router} from "@angular/router";
@Component({
selector: `personal-form`,
template: `
<div page-content>
<div inner class="uk-section uk-container uk-container-small">
<div *ngIf="loading" class="uk-height-viewport uk-position-relative">
<loading class="uk-position-center"></loading>
</div>
<ng-container *ngIf="form">
<div *ngIf="message" class="uk-alert uk-alert-warning uk-margin-medium-bottom uk-text-center">
In order to visit Personal Token and Registered Services you have to fill the personal info form below. In case you already have registered services, they are still active but you cannot edit them.
</div>
<div class="uk-text-center uk-margin-large-bottom">
<h3>Personal Info</h3>
</div>
<div class="uk-grid" uk-grid>
<div class="uk-width-1-2@m uk-width-1-1" input [formInput]="form.get('name')"
[placeholder]="'Name'"></div>
<div class="uk-width-1-2@m uk-width-1-1" input [formInput]="form.get('surname')"
[placeholder]="'Surname'"></div>
<div class="uk-width-1-1" input [formInput]="form.get('email')" [placeholder]="'Email'"></div>
<div class="uk-width-1-2@m uk-width-1-1" input [formInput]="form.get('affiliation')"
[placeholder]="'Affiliation'"></div>
<div class="uk-width-1-2@m uk-width-1-1" input [formInput]="form.get('position')"
[placeholder]="'Position'"></div>
</div>
<div class="uk-flex uk-flex-right uk-margin-medium-top">
<a class="uk-button uk-button-default uk-margin-small-right" routerLink="/">Cancel</a>
<button class="uk-button uk-button-primary" [disabled]="form.pristine || form.invalid || form.disabled"
[class.uk-disabled]="form.pristine || form.invalid || form.disabled" (click)="save()">Save
</button>
</div>
</ng-container>
</div>
</div>
`
})
export class PersonalInfoComponent implements OnInit, OnDestroy {
form: FormGroup;
loading: boolean = true;
subscriptions: any[] = [];
user: User;
info: PersonalInfo = null;
message: boolean = false;
constructor(private fb: FormBuilder,
private personalService: PersonalService,
private userManagementService: UserManagementService,
private router: Router) {
this.subscriptions.push(this.router.events.subscribe(event => {
if(event instanceof NavigationEnd) {
const navigation = this.router.getCurrentNavigation();
if (navigation && navigation.extras.state) {
this.message = navigation.extras.state.message;
} else {
this.message = false;
}
}
}));
}
ngOnInit() {
this.subscriptions.push(this.personalService.getPersonalInfo().subscribe(info => {
this.info = info;
this.initForm();
this.message = false;
}, error => {
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
this.initForm();
}));
}));
}
initForm() {
if(this.info) {
this.form = this.fb.group({
name: this.fb.control(this.info.name, Validators.required),
surname: this.fb.control(this.info.surname, Validators.required),
email: this.fb.control({value: this.info.email, disabled: true}, Validators.required),
affiliation: this.fb.control(this.info.affiliation, Validators.required),
position: this.fb.control(this.info.position, Validators.required),
});
} else {
this.form = this.fb.group({
name: this.fb.control(this.user.firstname, Validators.required),
surname: this.fb.control(this.user.lastname, Validators.required),
email: this.fb.control({value: this.user.email, disabled: true}, Validators.required),
affiliation: this.fb.control('', Validators.required),
position: this.fb.control('', Validators.required),
});
}
this.form.markAsPristine();
this.loading = false;
}
save() {
this.loading = true;
this.subscriptions.push(this.personalService.savePersonalInfo(this.form.getRawValue()).subscribe(info => {
this.info = info;
NotificationHandler.rise('Your personal info has been saved successfully.');
this.initForm();
this.loading = false;
this.message = false;
}, error => {
console.error(error);
NotificationHandler.rise('An error has occurred. Please try again later.', 'danger');
this.initForm();
this.loading = false;
}))
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if(subscription instanceof Subscription) {
subscription.unsubscribe();
}
})
}
}

View File

@ -0,0 +1,20 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {PersonalInfoComponent} from "./personal-info.component";
import {RouterModule} from "@angular/router";
import {PreviousRouteRecorder} from "../openaireLibrary/utils/piwik/previousRouteRecorder.guard";
import {LoginGuard} from "../openaireLibrary/login/loginGuard.guard";
import {PageContentModule} from "../openaireLibrary/dashboard/sharedComponents/page-content/page-content.module";
import {InputModule} from "../openaireLibrary/sharedComponents/input/input.module";
import {LoadingModule} from "../openaireLibrary/utils/loading/loading.module";
@NgModule({
imports: [CommonModule, RouterModule.forChild([
{path: '', component: PersonalInfoComponent, canActivate: [LoginGuard], canDeactivate: [PreviousRouteRecorder]}
]), PageContentModule, InputModule, LoadingModule],
declarations: [PersonalInfoComponent],
exports: [PersonalInfoComponent]
})
export class PersonalInfoModule {
}

View File

@ -0,0 +1,112 @@
import {Component, OnInit, ViewChild} from "@angular/core";
import {UserManagementService} from "../openaireLibrary/services/user-management.service";
import {NotificationHandler} from "../openaireLibrary/utils/notification-handler";
import {AlertModal} from "../openaireLibrary/utils/modal/alert";
declare var copy;
@Component({
selector: 'home',
template: `
<div page-content>
<div inner class="uk-container uk-container-small uk-section">
<div class="uk-alert uk-alert-primary uk-flex uk-flex-middle uk-margin-medium-top">
<icon name="info" [type]="'outlined'" [flex]="true"></icon>
<span class="uk-margin-small-left">
For further information on how to use the tokens please visit the
<a href="https://graph.openaire.eu/develop/personalToken.html" target="_blank">OpenAIRE API Authentication documentation</a>.
</span>
</div>
<div class="uk-margin-large-bottom">
<h5 class="uk-text-primary">Your personal access token is</h5>
<div class="clipboard-wrapper uk-width-1-1 uk-flex uk-flex-middle uk-flex-center">
<div class="uk-width-expand">
<pre class="uk-margin-remove-bottom uk-flex">
<code id="accessToken" class="uk-margin-small-left uk-margin-small-top uk-margin-small-bottom">{{accessToken}}</code>
</pre>
</div>
<a class="uk-width-auto uk-margin-small-left uk-margin-xsmall-right" (click)="copyToClipboard('accessToken')">
<icon name="content_copy"></icon>
</a>
</div>
<div class="uk-flex uk-flex-middle uk-margin-small-top">
<icon name="info" [type]="'outlined'" [flex]="true"></icon>
<span class="uk-margin-small-left">Your access token is <span class="uk-text-bold">valid for an hour</span>.</span>
</div>
<div class="uk-text-warning uk-flex uk-flex-middle uk-margin-small-top">
<icon name="warning" [flex]="true"></icon>
<span class="uk-margin-small-left">Do not share your personal access token. Send your personal access token only over HTTPS.</span>
</div>
</div>
<div *ngIf="!showRefreshToken">
<h5 class="uk-text-primary">Do you need a refresh token?</h5>
<div class="uk-flex uk-flex-middle uk-margin-small-top">
<icon name="info" [type]="'outlined'" [flex]="true"></icon>
<span class="uk-margin-small-left">OpenAIRE refresh token <span class="uk-text-bold">expires after 1 month</span> and allows you to programmatically get a new access token.</span>
</div>
<button (click)="openRefreshTokenModal()" class="uk-button uk-button-primary uk-margin-medium-top">Get a refresh Token</button>
</div>
<div *ngIf="showRefreshToken">
<h5 class="uk-text-primary">Your refresh token is</h5>
<div class="clipboard-wrapper uk-width-1-1 uk-flex uk-flex-middle uk-flex-center">
<div class="uk-width-expand">
<pre class="uk-margin-remove-bottom uk-flex">
<code id="refreshToken" class="uk-margin-small-left uk-margin-small-top uk-margin-small-bottom">{{refreshToken}}</code>
</pre>
</div>
<a class="uk-width-auto uk-margin-small-left uk-margin-xsmall-right" (click)="copyToClipboard('refreshToken')">
<icon name="content_copy"></icon>
</a>
</div>
<div class="uk-flex uk-flex-middle uk-margin-small-top">
<icon name="info" [type]="'outlined'" [flex]="true"></icon>
<span class="uk-margin-small-left">OpenAIRE refresh token <span class="uk-text-bold">expires after 1 month</span> and allows you to programmatically get a new access token.</span>
</div>
<div class="uk-text-warning uk-flex uk-flex-middle uk-margin-small-top">
<icon name="warning" [flex]="true"></icon>
<span class="uk-margin-small-left">Please copy your refresh token and store it confidentially. You will not be able to retrieve it.
Do not share your refresh token. Send your refresh token only over HTTPS.</span>
</div>
</div>
</div>
</div>
<modal-alert #refreshTokenModal (alertOutput)="getRefreshToken()" [overflowBody]="false"></modal-alert>
`
})
export class PersonalTokenComponent implements OnInit {
accessToken: string;
refreshToken: string;
showRefreshToken: boolean = false;
subscriptions: any[] = [];
@ViewChild("refreshTokenModal") modal: AlertModal;
constructor(private userManagementService: UserManagementService) {
}
ngOnInit() {
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.accessToken = user.accessToken;
this.refreshToken = user.refreshToken;
}))
}
getRefreshToken() {
this.showRefreshToken = true;
}
copyToClipboard(id: string) {
if(copy.exec(id)) {
NotificationHandler.rise('Copied to clipboard!');
} else {
NotificationHandler.rise('Unable to copy to clipboard!', 'danger');
}
}
openRefreshTokenModal() {
this.modal.alertTitle = 'Get refresh token';
this.modal.message = 'In case you already have a refresh token, it will no longer be valid. Do you want to proceed?';
this.modal.okButtonText = 'Get Refresh Token';
this.modal.okButtonLeft = false;
this.modal.open();
}
}

View File

@ -0,0 +1,21 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {RouterModule} from "@angular/router";
import {PreviousRouteRecorder} from "../openaireLibrary/utils/piwik/previousRouteRecorder.guard";
import {PersonalTokenComponent} from "./personal-token.component";
import {IconsModule} from "../openaireLibrary/utils/icons/icons.module";
import {LoginGuard} from "../openaireLibrary/login/loginGuard.guard";
import {PageContentModule} from "../openaireLibrary/dashboard/sharedComponents/page-content/page-content.module";
import {AlertModalModule} from "../openaireLibrary/utils/modal/alertModal.module";
import {HasPersonalInfoGuard} from "../services/hasPersonalInfo.guard";
@NgModule({
declarations: [PersonalTokenComponent],
imports: [CommonModule, RouterModule.forChild([
{path: '', component: PersonalTokenComponent, canActivate: [LoginGuard, HasPersonalInfoGuard], canDeactivate: [PreviousRouteRecorder]}
]), IconsModule, PageContentModule, AlertModalModule],
exports: [PersonalTokenComponent]
})
export class PersonalTokenModule {
}

View File

@ -0,0 +1,13 @@
import { NgModule} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { ReloadModule } from '../openaireLibrary/reload/reload.module';
@NgModule({
imports: [
CommonModule, FormsModule, RouterModule, ReloadModule
]
})
export class LibReloadModule { }

View File

@ -0,0 +1,86 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {properties} from "../../environments/environment";
import {CustomOptions} from "../openaireLibrary/services/servicesUtils/customOptions.class";
export class RegisteredService {
id: number;
clientId: string;
creationDate: Date;
keyType: "url" | "value" | null;
name: string;
owner: string;
registrationAccessToken: string;
url: string;
}
export class Details {
clientId: string;
clientIdIssuedAt: number;
clientName: string;
clientSecret: string;
clientSecretExpiresAt: number;
contacts: string[];
grantTypes: string[];
jwks: {
keys: [
{
alg: string;
e: string;
kid: string;
kty: string;
n: string
}
];
valid: boolean
};
jwksUri: string;
logoUri: string;
policyUri: string;
redirectUris: string[];
registrationAccessToken: string;
registrationClientUri: string;
scope: string;
tokenEndpointAuthMethod: string;
tokenEndpointAuthSigningAlg: string;
}
export class API {
service: RegisteredService;
details: Details;
constructor() {
this.service = new RegisteredService();
this.details = new Details();
}
}
@Injectable({
providedIn: 'root'
})
export class ApisService {
constructor(private http: HttpClient) {
}
public getAll(): Observable<API[]> {
return this.http.get<API[]>(properties.clientManagementUrl + "/apis/all", CustomOptions.registryOptions());
}
public getMyServices(): Observable<API[]> {
return this.http.get<API[]>(properties.clientManagementUrl + "/apis/my-services", CustomOptions.registryOptions());
}
public create(form: any): Observable<API> {
return this.http.post<API>(properties.clientManagementUrl + "/apis/save/new", form, CustomOptions.registryOptions());
}
public save(id: number, form: any): Observable<API> {
return this.http.post<API>(properties.clientManagementUrl + "/apis/save/" + id, form, CustomOptions.registryOptions());
}
public delete(id: number): Observable<void> {
return this.http.delete<void>(properties.clientManagementUrl + "/apis/delete/" + id, CustomOptions.registryOptions());
}
}

View File

@ -0,0 +1,24 @@
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from "@angular/router";
import {Injectable} from "@angular/core";
import {Observable, of} from "rxjs";
import {PersonalService} from "./personal.service";
import {catchError, map, tap} from "rxjs/operators";
@Injectable({
providedIn: 'root'
})
export class HasPersonalInfoGuard implements CanActivate {
constructor(private personalService: PersonalService,
private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.personalService.getPersonalInfo().pipe(catchError(() => of(null)), map(info => !!info), tap(hasInfo => {
if(!hasInfo) {
this.router.navigate(['/personal-info'], {skipLocationChange: true, state: {message: true}});
}
}));
}
}

View File

@ -0,0 +1,31 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {properties} from "../../environments/environment";
import {CustomOptions} from "../openaireLibrary/services/servicesUtils/customOptions.class";
export interface PersonalInfo {
id: string;
email: string;
name: string;
surname: string;
affiliation: string;
position: string;
}
@Injectable({
providedIn: 'root'
})
export class PersonalService {
constructor(private http: HttpClient) {
}
public getPersonalInfo() {
return this.http.get<PersonalInfo>(properties.clientManagementUrl + '/personal/my-info', CustomOptions.registryOptions());
}
public savePersonalInfo(personalInfo: PersonalInfo) {
return this.http.post<PersonalInfo>(properties.clientManagementUrl + '/personal/save', personalInfo, CustomOptions.registryOptions());
}
}

@ -1 +1 @@
Subproject commit f15bbfa7265f170e06b256f086f2acedfa3f72e0 Subproject commit 39cb4e56d0320975fbd08fb5e9c68b87b0c82a10

@ -1 +1 @@
Subproject commit 468b2b008f474c88dacc52991a5231693833f1f9 Subproject commit 1e06310b8b2b56f131754eaded615b6cf32c5848

View File

@ -0,0 +1,14 @@
import {EnvProperties} from '../app/openaireLibrary/utils/properties/env-properties';
export let properties: EnvProperties = {
environment: 'production',
dashboard: 'client-management-portal',
useCache: false,
loginUrl: "https://beta.services.openaire.eu/client-management/openid_connect_login",
userInfoUrl: "https://beta.services.openaire.eu/client-management/userInfo",
clientManagementUrl: "https://beta.services.openaire.eu/client-management",
logoutUrl: "https://beta.services.openaire.eu/client-management/openid_logout",
cookieDomain: '.openaire.eu',
errorLink: '/error',
};

View File

@ -4,9 +4,10 @@ export let properties: EnvProperties = {
environment: 'production', environment: 'production',
dashboard: 'client-management-portal', dashboard: 'client-management-portal',
useCache: false, useCache: false,
loginUrl: "https://services.openaire.eu/login-service/openid_connect_login", loginUrl: "https://services.openaire.eu/client-management/openid_connect_login",
userInfoUrl: "https://services.openaire.eu/login-service/userInfo", userInfoUrl: "https://services.openaire.eu/client-management/userInfo",
logoutUrl: "https://services.openaire.eu/login-service/openid_logout", clientManagementUrl: "https://services.openaire.eu/client-management",
logoutUrl: "https://services.openaire.eu/client-management/openid_logout",
cookieDomain: '.openaire.eu', cookieDomain: '.openaire.eu',
errorLink: '/error', errorLink: '/error',
}; };

View File

@ -9,9 +9,12 @@ export let properties: EnvProperties = {
environment: 'development', environment: 'development',
dashboard: 'client-management-portal', dashboard: 'client-management-portal',
useCache: false, useCache: false,
loginUrl: "http://mpagasas.di.uoa.gr:19080/login-service/openid_connect_login", loginUrl: "http://mpagasas.di.uoa.gr:19580/client-management/openid_connect_login",
userInfoUrl: "http://mpagasas.di.uoa.gr:19080/login-service/userInfo", userInfoUrl: "http://mpagasas.di.uoa.gr:19580/client-management/userInfo",
logoutUrl: "http://mpagasas.di.uoa.gr:19080/login-service/openid_logout", clientManagementUrl: "http://mpagasas.di.uoa.gr:19580/client-management",
logoutUrl: "http://mpagasas.di.uoa.gr:19580/client-management/openid_logout",
cookieDomain: '.di.uoa.gr', cookieDomain: '.di.uoa.gr',
errorLink: '/error', errorLink: '/error',
domain: 'http://mpagasas.di.uoa.gr:5001',
baseLink: ''
}; };

View File

@ -1 +1,12 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@import "~src/assets/openaire-theme/less/_import";
@import "~src/assets/common-assets/less/general";
@import "~src/assets/common-assets/less/user";
@import "~src/assets/common-assets/less/dashboard";
.uk-alert-primary {
a, .uk-link {
text-decoration: underline;
}
}