Commit 667787b1 authored by Revant Nandgaonkar's avatar Revant Nandgaonkar

chore(communication-server, admin-client): Add UI

add, update OAuth 2.0 Providers
list for providers
retrieve single provider

add ui to manage oauth2 provider
parent 54b5f434
......@@ -37,6 +37,8 @@ export class Oauth2ProviderAggregateService extends AggregateRoot {
}
async list(offset: number, limit: number, search?: string, sort?: string) {
offset = Number(offset);
limit = Number(offset);
return this.oauth2Provider.list(offset, limit);
}
}
......@@ -10,7 +10,7 @@ export class OAuth2Provider extends BaseEntity {
uuid: string;
@Column()
appName: string;
name: string;
@Column()
authServerURL: string;
......
......@@ -3,7 +3,7 @@ import { IsString, IsUrl, IsNotEmpty, IsOptional } from 'class-validator';
export class OAuth2ProviderDto {
@IsString()
@IsNotEmpty()
appName: string;
name: string;
@IsUrl()
authServerURL: string;
......
......@@ -17,6 +17,7 @@ import { ServiceTypeComponent } from './infrastructure-ui/service-type/service-t
import { CommunicationSettingsComponent } from './communication-ui/communication-settings/communication-settings.component';
import { InfrastructureSettingsComponent } from './infrastructure-ui/infrastructure-settings/infrastructure-settings.component';
import { IdpSettingsComponent } from './identity-provider-ui/idp-settings/idp-settings.component';
import { OAuth2ProviderComponent } from './communication-ui/oauth2-provider/oauth2-provider.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent },
......@@ -56,6 +57,11 @@ const routes: Routes = [
component: ListingComponent,
canActivate: [AuthGuard],
},
{
path: 'oauth2_provider/list',
component: ListingComponent,
canActivate: [AuthGuard],
},
{
path: 'service/list',
component: ListingComponent,
......@@ -101,6 +107,11 @@ const routes: Routes = [
component: CloudStorageComponent,
canActivateChild: [AuthGuard],
},
{
path: 'oauth2_provider/:id',
component: OAuth2ProviderComponent,
canActivateChild: [AuthGuard],
},
{
path: 'service/:id',
component: ServiceComponent,
......@@ -114,22 +125,22 @@ const routes: Routes = [
{
path: 'auth_settings',
component: AuthSettingsComponent,
canActivateChild: [AuthGuard],
canActivate: [AuthGuard],
},
{
path: 'communication_settings',
component: CommunicationSettingsComponent,
canActivateChild: [AuthGuard],
canActivate: [AuthGuard],
},
{
path: 'infrastructure_settings',
component: InfrastructureSettingsComponent,
canActivateChild: [AuthGuard],
canActivate: [AuthGuard],
},
{
path: 'idp_settings',
component: IdpSettingsComponent,
canActivateChild: [AuthGuard],
canActivate: [AuthGuard],
},
{ path: '', redirectTo: 'home', pathMatch: 'full' },
......
......@@ -6,12 +6,15 @@ import { CloudStorageService } from './cloud-storage/cloud-storage.service';
import { EmailService } from './email/email.service';
import { SharedImportsModule } from '../shared-imports/shared-imports.module';
import { CommunicationSettingsComponent } from './communication-settings/communication-settings.component';
import { OAuth2ProviderComponent } from './oauth2-provider/oauth2-provider.component';
import { OAuth2ProviderService } from './oauth2-provider/oauth2-provider.service';
@NgModule({
declarations: [
EmailComponent,
CloudStorageComponent,
CommunicationSettingsComponent,
OAuth2ProviderComponent,
],
imports: [CommonModule, SharedImportsModule],
exports: [
......@@ -19,6 +22,6 @@ import { CommunicationSettingsComponent } from './communication-settings/communi
CloudStorageComponent,
CommunicationSettingsComponent,
],
providers: [EmailService, CloudStorageService],
providers: [EmailService, CloudStorageService, OAuth2ProviderService],
})
export class CommunicationUIModule {}
<div class="card-container">
<form [formGroup]="providerForm">
<mat-card>
<mat-card-title>{{ name || "New OAuth 2.0 Provider" }}</mat-card-title>
<mat-card-content fxLayout="column">
<mat-form-field>
<input
matInput
placeholder="Name"
formControlName="name"
mat-raised-button
required>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Authorization Server URL"
formControlName="authServerURL"
mat-raised-button
required>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Client ID"
formControlName="clientId"
mat-raised-button
required>
</mat-form-field>
<mat-form-field>
<input
[type]="hideClientSecret ? 'password' : 'text'"
matInput placeholder="Client Secret" formControlName="clientSecret"
mat-raised-button>
<mat-icon
matSuffix
(click)="hideClientSecret = !hideClientSecret">
{{hideClientSecret ? 'visibility' : 'visibility_off'}}
</mat-icon>
</mat-form-field>
<mat-form-field *ngIf="uuid !== 'new'">
<input
matInput
readonly
placeholder="Redirect URI"
formControlName="redirectURI"
mat-raised-button>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Profile URL"
formControlName="profileURL"
mat-raised-button>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Token Endpoint"
formControlName="tokenURL"
mat-raised-button
required>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Introspection Endpoint"
formControlName="introspectionURL"
mat-raised-button>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Authorization Endpoint"
formControlName="authorizationURL"
mat-raised-button
required>
</mat-form-field>
<mat-form-field>
<input
matInput
placeholder="Revocation Endpoint"
formControlName="revocationURL"
mat-raised-button
required>
</mat-form-field>
</mat-card-content>
<mat-card-actions>
<button
(click)="updateProvider()"
mat-flat-button color="primary"
[disabled]="!providerForm.valid"
*ngIf="name">Update</button>
<button
(click)="createProvider()"
mat-flat-button
color="primary"
[disabled]="!providerForm.valid"
*ngIf="!name">Create</button>
</mat-card-actions>
</mat-card>
</form>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { OAuth2ProviderComponent } from './oauth2-provider.component';
import { MaterialModule } from '../../shared-imports/material/material.module';
import { of } from 'rxjs';
import { OAuth2ProviderService } from './oauth2-provider.service';
describe('OAuth2ProviderComponent', () => {
let component: OAuth2ProviderComponent;
let fixture: ComponentFixture<OAuth2ProviderComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
BrowserAnimationsModule,
],
providers: [
{
provide: OAuth2ProviderService,
useValue: {
getProvider: (...args) => of({}),
generateRedirectURL: (...args) => '',
},
},
],
declarations: [OAuth2ProviderComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OAuth2ProviderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { OAuth2ProviderService } from './oauth2-provider.service';
import { MatSnackBar } from '@angular/material';
import { Router, ActivatedRoute } from '@angular/router';
import {
CREATE_SUCCESSFUL,
CLOSE,
CREATE_ERROR,
UPDATE_SUCCESSFUL,
UPDATE_ERROR,
} from 'src/app/constants/messages';
import { NEW_ID } from 'src/app/constants/common';
export const OAUTH2_PROVIDER_LIST_ROUTE = '/oauth2_provider/list';
@Component({
selector: 'app-oauth2-provider',
templateUrl: './oauth2-provider.component.html',
styleUrls: ['./oauth2-provider.component.css'],
})
export class OAuth2ProviderComponent implements OnInit {
uuid: string;
hideClientSecret: boolean = true;
name: string;
providerForm = new FormGroup({
name: new FormControl(),
authServerURL: new FormControl(),
clientId: new FormControl(),
clientSecret: new FormControl(),
redirectURI: new FormControl(),
profileURL: new FormControl(),
tokenURL: new FormControl(),
introspectionURL: new FormControl(),
authorizationURL: new FormControl(),
revocationURL: new FormControl(),
});
constructor(
private service: OAuth2ProviderService,
private snackBar: MatSnackBar,
private router: Router,
route: ActivatedRoute,
) {
this.uuid = route.snapshot.params.id;
}
ngOnInit() {
if (this.uuid !== NEW_ID) {
this.service.getProvider(this.uuid).subscribe({
next: response => {
this.name = response.name;
this.providerForm.controls.name.setValue(response.name);
this.providerForm.controls.authServerURL.setValue(
response.authServerURL,
);
this.providerForm.controls.clientId.setValue(response.clientId);
this.providerForm.controls.clientSecret.setValue(
response.clientSecret,
);
this.providerForm.controls.redirectURI.setValue(
this.service.generateRedirectURL(this.uuid),
);
this.providerForm.controls.profileURL.setValue(response.profileURL);
this.providerForm.controls.tokenURL.setValue(response.tokenURL);
this.providerForm.controls.introspectionURL.setValue(
response.introspectionURL,
);
this.providerForm.controls.authorizationURL.setValue(
response.authorizationURL,
);
this.providerForm.controls.revocationURL.setValue(
response.revocationURL,
);
},
});
}
}
createProvider() {
this.service
.createProvider(
this.providerForm.controls.name.value,
this.providerForm.controls.authServerURL.value,
this.providerForm.controls.clientId.value,
this.providerForm.controls.clientSecret.value,
this.providerForm.controls.profileURL.value,
this.providerForm.controls.tokenURL.value,
this.providerForm.controls.introspectionURL.value,
this.providerForm.controls.authorizationURL.value,
this.providerForm.controls.revocationURL.value,
)
.subscribe({
next: response => {
this.snackBar.open(CREATE_SUCCESSFUL, CLOSE, { duration: 2000 });
this.router.navigateByUrl(OAUTH2_PROVIDER_LIST_ROUTE);
},
error: error => {
this.snackBar.open(CREATE_ERROR, CLOSE, { duration: 2000 });
},
});
}
updateProvider() {
this.service
.updateProvider(
this.uuid,
this.providerForm.controls.name.value,
this.providerForm.controls.authServerURL.value,
this.providerForm.controls.clientId.value,
this.providerForm.controls.clientSecret.value,
this.providerForm.controls.profileURL.value,
this.providerForm.controls.tokenURL.value,
this.providerForm.controls.introspectionURL.value,
this.providerForm.controls.authorizationURL.value,
this.providerForm.controls.revocationURL.value,
)
.subscribe({
next: response => {
this.snackBar.open(UPDATE_SUCCESSFUL, CLOSE, { duration: 2000 });
this.router.navigateByUrl(OAUTH2_PROVIDER_LIST_ROUTE);
},
error: error => {
this.snackBar.open(UPDATE_ERROR, CLOSE, { duration: 2000 });
},
});
}
}
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { OAuthService } from 'angular-oauth2-oidc';
import { OAuth2ProviderService } from './oauth2-provider.service';
import { oauthServiceStub } from '../../common/testing-helpers';
describe('OAuth2ProviderService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{
provide: OAuthService,
useValue: oauthServiceStub,
},
],
}),
);
it('should be created', () => {
const service: OAuth2ProviderService = TestBed.get(OAuth2ProviderService);
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { StorageService } from '../../common/services/storage/storage.service';
import { COMMUNICATION_SERVER } from 'src/app/constants/storage';
@Injectable({
providedIn: 'root',
})
export class OAuth2ProviderService {
authorizationHeader: HttpHeaders;
constructor(
private readonly http: HttpClient,
private readonly storageService: StorageService,
private readonly oauthService: OAuthService,
) {
this.authorizationHeader = new HttpHeaders({
Authorization: 'Bearer ' + this.oauthService.getAccessToken(),
});
}
createProvider(
name: string,
authServerURL: string,
clientId: string,
clientSecret: string,
profileURL: string,
tokenURL: string,
introspectionURL: string,
authorizationURL: string,
revocationURL: string,
) {
const url = `${this.storageService.getServiceURL(
COMMUNICATION_SERVER,
)}/oauth2_provider/v1/add_provider`;
const payload = {
name,
authServerURL,
clientId,
clientSecret,
profileURL,
tokenURL,
introspectionURL,
authorizationURL,
revocationURL,
};
return this.http.post(url, payload, {
headers: this.authorizationHeader,
});
}
updateProvider(
uuid: string,
name: string,
authServerURL: string,
clientId: string,
clientSecret: string,
profileURL: string,
tokenURL: string,
introspectionURL: string,
authorizationURL: string,
revocationURL: string,
) {
const url = `${this.storageService.getServiceURL(
COMMUNICATION_SERVER,
)}/oauth2_provider/v1/update_provider/${uuid}`;
const payload = {
name,
authServerURL,
clientId,
clientSecret,
profileURL,
tokenURL,
introspectionURL,
authorizationURL,
revocationURL,
};
return this.http.post(url, payload, {
headers: this.authorizationHeader,
});
}
getProvider(uuid) {
const url = `${this.storageService.getServiceURL(
COMMUNICATION_SERVER,
)}/oauth2_provider/v1/retrieve_provider/${uuid}`;
return this.http.get<any>(url, { headers: this.authorizationHeader });
}
generateRedirectURL(uuid: string) {
return (
this.storageService.getServiceURL(COMMUNICATION_SERVER) +
'/oauth2_provider/callback/' +
uuid
);
}
}
......@@ -34,7 +34,7 @@ export class ListingService {
) {
let baseUrl = this.storageService.getInfo(ISSUER_URL);
if (['storage', 'email'].includes(model)) {
if (['storage', 'email', 'oauth2_provider'].includes(model)) {
baseUrl = this.storageService.getServiceURL(COMMUNICATION_SERVER);
}
......
......@@ -7,7 +7,7 @@
[disableClose]="!(isHandset$ | async)"
[opened]="!(isHandset$ | async)">
<mat-toolbar color="primary">Admin</mat-toolbar>
<ng-container *ngIf="loggedIn">
<ng-container *ngIf="tokenIsValid">
<mat-accordion>
<mat-expansion-panel class="mat-elevation-z0">
<mat-expansion-panel-header>
......@@ -36,7 +36,7 @@
</mat-expansion-panel>
</mat-accordion>
</ng-container>
<ng-container *ngIf="loggedIn && isCommunicationEnabled">
<ng-container *ngIf="tokenIsValid && isCommunicationEnabled">
<mat-accordion>
<mat-expansion-panel class="mat-elevation-z0">
<mat-expansion-panel-header>
......@@ -45,12 +45,13 @@
<mat-nav-list>
<a mat-list-item routerLink="/email/list">Email</a>
<a mat-list-item routerLink="/storage/list">Storage</a>
<!-- <a mat-list-item routerLink="/oauth2_provider/list">OAuth2 Provider</a> -->
<a mat-list-item routerLink="/communication_settings">Settings</a>
</mat-nav-list>
</mat-expansion-panel>
</mat-accordion>
</ng-container>
<ng-container *ngIf="loggedIn && isIdentityProviderAvailable">
<ng-container *ngIf="tokenIsValid && isIdentityProviderAvailable">
<mat-accordion>
<mat-expansion-panel class="mat-elevation-z0">
<mat-expansion-panel-header>
......@@ -62,16 +63,15 @@
</mat-expansion-panel>
</mat-accordion>
</ng-container>
<ng-container *ngIf="tokenIsValid || loggedIn">
<mat-accordion>
<mat-expansion-panel class="mat-elevation-z0">
<mat-expansion-panel-header>Account</mat-expansion-panel-header>
<mat-nav-list>
<a mat-list-item (click)="logout()">Logout</a>
</mat-nav-list>
</mat-expansion-panel>
</mat-accordion>
</ng-container>
<mat-accordion>
<mat-expansion-panel class="mat-elevation-z0">
<mat-expansion-panel-header>Account</mat-expansion-panel-header>
<mat-nav-list>
<a mat-list-item *ngIf="!loggedIn" (click)="login()">Login</a>
<a mat-list-item *ngIf="loggedIn" (click)="logout()">Logout</a>
</mat-nav-list>
</mat-expansion-panel>
</mat-accordion>
</mat-drawer>
<mat-drawer-content>
<mat-toolbar color="primary">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment