mirror of
https://github.com/google/nomulus.git
synced 2025-08-21 08:44:38 +02:00
Add settings to console home page, update settings->security styles (#2144)
This commit is contained in:
parent
6c18ea9cff
commit
5eb44c165c
14 changed files with 156 additions and 77 deletions
|
@ -45,39 +45,41 @@ import { ResourcesWidgetComponent } from './home/widgets/resources-widget.compon
|
||||||
import { EppWidgetComponent } from './home/widgets/epp-widget.component';
|
import { EppWidgetComponent } from './home/widgets/epp-widget.component';
|
||||||
import { BillingWidgetComponent } from './home/widgets/billing-widget.component';
|
import { BillingWidgetComponent } from './home/widgets/billing-widget.component';
|
||||||
import { DomainsWidgetComponent } from './home/widgets/domains-widget.component';
|
import { DomainsWidgetComponent } from './home/widgets/domains-widget.component';
|
||||||
|
import { SettingsWidgetComponent } from './home/widgets/settings-widget.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HomeComponent,
|
BillingWidgetComponent,
|
||||||
TldsComponent,
|
|
||||||
HeaderComponent,
|
|
||||||
SettingsComponent,
|
|
||||||
SettingsContactComponent,
|
|
||||||
ContactDetailsDialogComponent,
|
ContactDetailsDialogComponent,
|
||||||
RegistrarComponent,
|
|
||||||
SecurityComponent,
|
|
||||||
EmptyRegistrar,
|
|
||||||
RegistrarSelectorComponent,
|
|
||||||
ContactWidgetComponent,
|
ContactWidgetComponent,
|
||||||
DomainsWidgetComponent,
|
DomainsWidgetComponent,
|
||||||
PromotionsWidgetComponent,
|
EmptyRegistrar,
|
||||||
TldsWidgetComponent,
|
|
||||||
ResourcesWidgetComponent,
|
|
||||||
EppWidgetComponent,
|
EppWidgetComponent,
|
||||||
BillingWidgetComponent,
|
HeaderComponent,
|
||||||
|
HomeComponent,
|
||||||
|
PromotionsWidgetComponent,
|
||||||
|
RegistrarComponent,
|
||||||
|
RegistrarSelectorComponent,
|
||||||
|
ResourcesWidgetComponent,
|
||||||
|
SecurityComponent,
|
||||||
|
SettingsComponent,
|
||||||
|
SettingsContactComponent,
|
||||||
|
SettingsWidgetComponent,
|
||||||
|
TldsComponent,
|
||||||
|
TldsWidgetComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientModule,
|
|
||||||
FormsModule,
|
|
||||||
MaterialModule,
|
|
||||||
BrowserModule,
|
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
|
BrowserModule,
|
||||||
|
FormsModule,
|
||||||
|
HttpClientModule,
|
||||||
|
MaterialModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
GlobalLoaderService,
|
|
||||||
BackendService,
|
BackendService,
|
||||||
|
GlobalLoaderService,
|
||||||
RegistrarGuard,
|
RegistrarGuard,
|
||||||
{
|
{
|
||||||
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||||
|
|
|
@ -3,7 +3,15 @@
|
||||||
<button mat-icon-button aria-label="Open menu" (click)="toggleNavPane()">
|
<button mat-icon-button aria-label="Open menu" (click)="toggleNavPane()">
|
||||||
<mat-icon>menu</mat-icon>
|
<mat-icon>menu</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span>Google Registry</span>
|
<span>
|
||||||
|
<a
|
||||||
|
[routerLink]="'/home'"
|
||||||
|
routerLinkActive="active"
|
||||||
|
class="console-app__logo"
|
||||||
|
>
|
||||||
|
Google Registry
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<app-registrar-selector />
|
<app-registrar-selector />
|
||||||
<button mat-icon-button aria-label="Open FAQ">
|
<button mat-icon-button aria-label="Open FAQ">
|
||||||
|
|
|
@ -12,8 +12,11 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
.console-app__header {
|
.console-app {
|
||||||
margin-top: 0;
|
&__logo {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.spacer {
|
.spacer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div app-contact-widget class="console-app__widget-wrapper__wide"></div>
|
<div app-contact-widget class="console-app__widget-wrapper__wide"></div>
|
||||||
<div app-tlds-widget></div>
|
<div app-tlds-widget></div>
|
||||||
<div app-promotions-widget class="console-app__widget-wrapper__wide"></div>
|
<div app-promotions-widget class="console-app__widget-wrapper__wide"></div>
|
||||||
<div app-promotions-widget class="console-app__widget-wrapper__wide"></div>
|
<div app-settings-widget class="console-app__widget-wrapper__wide"></div>
|
||||||
<div app-resources-widget></div>
|
<div app-resources-widget></div>
|
||||||
<div app-billing-widget></div>
|
<div app-billing-widget></div>
|
||||||
<div app-epp-widget></div>
|
<div app-epp-widget></div>
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<mat-card class="console-app__widget-wrapper__wide">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="console-app__widget">
|
||||||
|
<div class="console-app__widget_left">
|
||||||
|
<mat-icon class="console-app__widget-icon">settings</mat-icon>
|
||||||
|
<h1 class="console-app__widget-title">Settings</h1>
|
||||||
|
<h4 class="secondary-text text-center">
|
||||||
|
Configure registrar settings, manage console users, and view activity
|
||||||
|
log.
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="console-app__widget_right">
|
||||||
|
<button mat-button color="primary" class="console-app__widget-link">
|
||||||
|
Contact Information
|
||||||
|
</button>
|
||||||
|
<p class="secondary-text">Manage Primary, Technical, etc contacts.</p>
|
||||||
|
<button mat-button color="primary" class="console-app__widget-link">
|
||||||
|
Security
|
||||||
|
</button>
|
||||||
|
<p class="secondary-text">
|
||||||
|
Manage IP allow lists and SSL certificates.
|
||||||
|
</p>
|
||||||
|
<button mat-button color="primary" class="console-app__widget-link">
|
||||||
|
Nomulus Password
|
||||||
|
</button>
|
||||||
|
<p class="secondary-text">Reset your Nomulus password.</p>
|
||||||
|
<button mat-button color="primary" class="console-app__widget-link">
|
||||||
|
User Management
|
||||||
|
</button>
|
||||||
|
<p class="secondary-text">Create and manage console user accounts</p>
|
||||||
|
<button mat-button color="primary" class="console-app__widget-link">
|
||||||
|
Registrar Management
|
||||||
|
</button>
|
||||||
|
<p class="secondary-text">Create and manage registrar accounts</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: '[app-settings-widget]',
|
||||||
|
templateUrl: './settings-widget.component.html',
|
||||||
|
})
|
||||||
|
export class SettingsWidgetComponent {
|
||||||
|
constructor() {}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { BackendService } from '../shared/services/backend.service';
|
import { BackendService } from '../shared/services/backend.service';
|
||||||
import { Subject } from 'rxjs';
|
import { Observable, Subject, tap } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
GlobalLoader,
|
GlobalLoader,
|
||||||
GlobalLoaderService,
|
GlobalLoaderService,
|
||||||
|
@ -54,24 +54,33 @@ export class RegistrarService implements GlobalLoader {
|
||||||
private backend: BackendService,
|
private backend: BackendService,
|
||||||
private globalLoader: GlobalLoaderService
|
private globalLoader: GlobalLoaderService
|
||||||
) {
|
) {
|
||||||
this.backend.getRegistrars().subscribe((r) => {
|
this.loadRegistrars().subscribe((r) => {
|
||||||
this.globalLoader.stopGlobalLoader(this);
|
this.globalLoader.stopGlobalLoader(this);
|
||||||
this.registrars = r;
|
|
||||||
});
|
});
|
||||||
this.globalLoader.startGlobalLoader(this);
|
this.globalLoader.startGlobalLoader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateRegistrar(registrarId: string) {
|
|
||||||
this.activeRegistrarId = registrarId;
|
|
||||||
this.activeRegistrarIdChange.next(registrarId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get registrar(): Registrar {
|
public get registrar(): Registrar {
|
||||||
return this.registrars.filter(
|
return this.registrars.filter(
|
||||||
(r) => r.registrarId === this.activeRegistrarId
|
(r) => r.registrarId === this.activeRegistrarId
|
||||||
)[0];
|
)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateRegistrar(registrarId: string) {
|
||||||
|
this.activeRegistrarId = registrarId;
|
||||||
|
this.activeRegistrarIdChange.next(registrarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadRegistrars(): Observable<Registrar[]> {
|
||||||
|
return this.backend.getRegistrars().pipe(
|
||||||
|
tap((registrars) => {
|
||||||
|
if (registrars) {
|
||||||
|
this.registrars = registrars;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
loadingTimeout() {
|
loadingTimeout() {
|
||||||
// TODO: Decide what to do when timeout happens
|
// TODO: Decide what to do when timeout happens
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ export class ContactDetailsDialogComponent {
|
||||||
operationObservable.subscribe({
|
operationObservable.subscribe({
|
||||||
complete: this.onCloseCallback.bind(this),
|
complete: this.onCloseCallback.bind(this),
|
||||||
error: (err: HttpErrorResponse) => {
|
error: (err: HttpErrorResponse) => {
|
||||||
this._snackBar.open(err.statusText, undefined, {
|
this._snackBar.open(err.error, undefined, {
|
||||||
duration: 1500,
|
duration: 1500,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -148,7 +148,8 @@ export default class ContactComponent {
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private bottomSheet: MatBottomSheet,
|
private bottomSheet: MatBottomSheet,
|
||||||
private breakpointObserver: BreakpointObserver,
|
private breakpointObserver: BreakpointObserver,
|
||||||
public contactService: ContactService
|
public contactService: ContactService,
|
||||||
|
private _snackBar: MatSnackBar
|
||||||
) {
|
) {
|
||||||
// TODO: Refactor to registrarId service
|
// TODO: Refactor to registrarId service
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
@ -170,7 +171,13 @@ export default class ContactComponent {
|
||||||
|
|
||||||
deleteContact(contact: Contact) {
|
deleteContact(contact: Contact) {
|
||||||
if (confirm(`Please confirm contact ${contact.name} delete`)) {
|
if (confirm(`Please confirm contact ${contact.name} delete`)) {
|
||||||
this.contactService.deleteContact(contact).subscribe();
|
this.contactService.deleteContact(contact).subscribe({
|
||||||
|
error: (err: HttpErrorResponse) => {
|
||||||
|
this._snackBar.open(err.error, undefined, {
|
||||||
|
duration: 1500,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
dataSource.ipAddressAllowList.length > 0
|
dataSource.ipAddressAllowList.length > 0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div *ngFor="let item of dataSource.ipAddressAllowList; index as index">
|
<div
|
||||||
<div>{{ item.value }}</div>
|
*ngFor="let item of dataSource.ipAddressAllowList; index as index"
|
||||||
|
class="settings-security__ipRecord"
|
||||||
|
>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
&__ipRecord {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
&__section {
|
&__section {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
|
@ -13,9 +13,14 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { SecurityService, SecuritySettings } from './security.service';
|
import {
|
||||||
|
SecurityService,
|
||||||
|
SecuritySettings,
|
||||||
|
apiToUiConverter,
|
||||||
|
} from './security.service';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-security',
|
selector: 'app-security',
|
||||||
|
@ -30,33 +35,19 @@ export default class SecurityComponent {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public securityService: SecurityService,
|
public securityService: SecurityService,
|
||||||
private _snackBar: MatSnackBar
|
private _snackBar: MatSnackBar,
|
||||||
|
public registrarService: RegistrarService
|
||||||
) {
|
) {
|
||||||
this.loading = true;
|
this.dataSource = apiToUiConverter(this.registrarService.registrar);
|
||||||
this.securityService.fetchSecurityDetails().subscribe({
|
|
||||||
complete: () => {
|
|
||||||
this.dataSource = this.securityService.securitySettings;
|
|
||||||
this.loading = false;
|
|
||||||
},
|
|
||||||
error: (err: HttpErrorResponse) => {
|
|
||||||
this._snackBar.open(err.error, undefined, {
|
|
||||||
duration: 1500,
|
|
||||||
});
|
|
||||||
this.loading = false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enableEdit() {
|
enableEdit() {
|
||||||
this.inEdit = true;
|
this.inEdit = true;
|
||||||
this.dataSource = JSON.parse(
|
|
||||||
JSON.stringify(this.securityService.securitySettings)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disableEdit() {
|
cancel() {
|
||||||
this.inEdit = false;
|
this.inEdit = false;
|
||||||
this.dataSource = this.securityService.securitySettings;
|
this.resetDataSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
createIpEntry() {
|
createIpEntry() {
|
||||||
|
@ -68,7 +59,7 @@ export default class SecurityComponent {
|
||||||
this.securityService.saveChanges(this.dataSource).subscribe({
|
this.securityService.saveChanges(this.dataSource).subscribe({
|
||||||
complete: () => {
|
complete: () => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.dataSource = this.securityService.securitySettings;
|
this.resetDataSource();
|
||||||
},
|
},
|
||||||
error: (err: HttpErrorResponse) => {
|
error: (err: HttpErrorResponse) => {
|
||||||
this._snackBar.open(err.error, undefined, {
|
this._snackBar.open(err.error, undefined, {
|
||||||
|
@ -76,16 +67,15 @@ export default class SecurityComponent {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.disableEdit();
|
this.cancel();
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.dataSource = this.securityService.securitySettings;
|
|
||||||
this.inEdit = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeIpEntry(index: number) {
|
removeIpEntry(index: number) {
|
||||||
this.dataSource.ipAddressAllowList =
|
this.dataSource.ipAddressAllowList =
|
||||||
this.dataSource.ipAddressAllowList?.filter((_, i) => i != index);
|
this.dataSource.ipAddressAllowList?.filter((_, i) => i != index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetDataSource() {
|
||||||
|
this.dataSource = apiToUiConverter(this.registrarService.registrar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { tap } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||||
|
|
||||||
|
@ -61,16 +61,6 @@ export class SecurityService {
|
||||||
private registrarService: RegistrarService
|
private registrarService: RegistrarService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
fetchSecurityDetails() {
|
|
||||||
return this.backend
|
|
||||||
.getSecuritySettings(this.registrarService.activeRegistrarId)
|
|
||||||
.pipe(
|
|
||||||
tap((securitySettings: SecuritySettingsBackendModel) => {
|
|
||||||
this.securitySettings = apiToUiConverter(securitySettings);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveChanges(newSecuritySettings: SecuritySettings) {
|
saveChanges(newSecuritySettings: SecuritySettings) {
|
||||||
return this.backend
|
return this.backend
|
||||||
.postSecuritySettings(
|
.postSecuritySettings(
|
||||||
|
@ -78,8 +68,8 @@ export class SecurityService {
|
||||||
uiToApiConverter(newSecuritySettings)
|
uiToApiConverter(newSecuritySettings)
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
tap((_) => {
|
switchMap(() => {
|
||||||
this.securitySettings = newSecuritySettings;
|
return this.registrarService.loadRegistrars();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ body {
|
||||||
&-link {
|
&-link {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-bottom: 0.5rem;
|
height: 20px !important;
|
||||||
}
|
}
|
||||||
&-title {
|
&-title {
|
||||||
color: var(--primary) !important;
|
color: var(--primary) !important;
|
||||||
|
@ -65,6 +65,10 @@ body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-left: 1px solid var(--secondary);
|
border-left: 1px solid var(--secondary);
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
.secondary-text {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class RegistrarsAction implements JsonGetAction {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ImmutableList<Registrar> registrars =
|
ImmutableList<Registrar> registrars =
|
||||||
Streams.stream(Registrar.loadAllCached())
|
Streams.stream(Registrar.loadAll())
|
||||||
.filter(r -> r.getType() == Registrar.Type.REAL)
|
.filter(r -> r.getType() == Registrar.Type.REAL)
|
||||||
.collect(ImmutableList.toImmutableList());
|
.collect(ImmutableList.toImmutableList());
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue