Skip to content
This repository has been archived by the owner on Feb 25, 2023. It is now read-only.

Commit

Permalink
gravel/glass: user management
Browse files Browse the repository at this point in the history
The default credentials set up during deployment are: admin/aquarium

Fixes: #421
Fixes: #23
Fixes: #26

Signed-off-by: Volker Theile <vtheile@suse.com>
  • Loading branch information
votdev committed Jul 2, 2021
1 parent dec8909 commit e73dafb
Show file tree
Hide file tree
Showing 62 changed files with 1,731 additions and 200 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -21,3 +21,5 @@ tests/vagrant/setups/
venv/
virtualenv/
wheelhouse/
.coverage
coverage.xml
1 change: 1 addition & 0 deletions images/aquarium/config.sh
Expand Up @@ -103,6 +103,7 @@ if [[ "$kiwi_profiles" == *"Vagrant"* ]]; then
fi

pip install fastapi==0.63.0 uvicorn==0.13.3 websockets==8.1 \
bcrypt==3.2.0 pyjwt==2.1.0 python-multipart==0.0.5 \
git+https://github.com/aquarist-labs/aetcd3/@edf633045ce61c7bbac4d4a6ca15b14f8acfe9cd
baseInsertService aquarium-boot
baseInsertService sshd
Expand Down
16 changes: 14 additions & 2 deletions src/aquarium.py
Expand Up @@ -39,12 +39,14 @@
local,
devices,
nfs,
auth,
user,
)

logger: logging.Logger = fastapi_logger


async def aquarium_startup(aquarium_app: FastAPI, aquarium_api: FastAPI):
async def aquarium_startup(_: FastAPI, aquarium_api: FastAPI):
lvl = "INFO" if not os.getenv("AQUARIUM_DEBUG") else "DEBUG"
setup_logging(lvl)
logger.info("Aquarium startup!")
Expand Down Expand Up @@ -100,7 +102,7 @@ async def aquarium_startup(aquarium_app: FastAPI, aquarium_api: FastAPI):
aquarium_api.state.nodemgr = nodemgr


async def aquarium_shutdown(aquarium_app: FastAPI, aquarium_api: FastAPI):
async def aquarium_shutdown(_: FastAPI, aquarium_api: FastAPI):
logger.info("Aquarium shutdown!")
await aquarium_api.state.gstate.shutdown()
logger.info("shutting down node manager")
Expand Down Expand Up @@ -136,6 +138,14 @@ def aquarium_factory(
{
"name": "devices",
"description": "Obtain and perform operations on cluster devices"
},
{
"name": "auth",
"description": "Operations related to user authentication"
},
{
"name": "user",
"description": "Operations related to user management"
}
]

Expand Down Expand Up @@ -163,6 +173,8 @@ async def on_shutdown():
aquarium_api.include_router(nodes.router)
aquarium_api.include_router(devices.router)
aquarium_api.include_router(nfs.router)
aquarium_api.include_router(auth.router)
aquarium_api.include_router(user.router)

#
# mounts
Expand Down
9 changes: 8 additions & 1 deletion src/glass/src/app/app-routing.module.ts
Expand Up @@ -25,9 +25,12 @@ import { HostsPageComponent } from '~/app/pages/hosts-page/hosts-page.component'
import { InstallModePageComponent } from '~/app/pages/install-mode-page/install-mode-page.component';
import { InstallCreateWizardPageComponent } from '~/app/pages/install-wizard/install-create-wizard-page/install-create-wizard-page.component';
import { InstallJoinWizardPageComponent } from '~/app/pages/install-wizard/install-join-wizard-page/install-join-wizard-page.component';
import { LoginPageComponent } from '~/app/pages/login-page/login-page.component';
import { NotFoundPageComponent } from '~/app/pages/not-found-page/not-found-page.component';
import { ServicesPageComponent } from '~/app/pages/services-page/services-page.component';
import { UsersPageComponent } from '~/app/pages/users-page/users-page.component';
import { WelcomePageComponent } from '~/app/pages/welcome-page/welcome-page.component';
import { AuthGuardService } from '~/app/shared/services/auth-guard.service';
import { StatusRouteGuardService } from '~/app/shared/services/status-route-guard.service';

const routes: Routes = [
Expand All @@ -40,14 +43,17 @@ const routes: Routes = [
{
path: 'dashboard',
data: { breadcrumb: TEXT('Dashboard') },
canActivate: [AuthGuardService],
canActivateChild: [AuthGuardService],
children: [
{ path: '', component: DashboardPageComponent },
{
path: 'services',
component: ServicesPageComponent,
data: { breadcrumb: TEXT('Services') }
},
{ path: 'hosts', component: HostsPageComponent, data: { breadcrumb: TEXT('Hosts') } }
{ path: 'hosts', component: HostsPageComponent, data: { breadcrumb: TEXT('Hosts') } },
{ path: 'users', component: UsersPageComponent, data: { breadcrumb: TEXT('Users') } }
]
}
]
Expand All @@ -74,6 +80,7 @@ const routes: Routes = [
path: '',
component: BlankLayoutComponent,
children: [
{ path: 'login', component: LoginPageComponent },
{
path: '404',
component: NotFoundPageComponent
Expand Down
@@ -1 +1,3 @@
<router-outlet></router-outlet>
<block-ui class="mat-typography">
<router-outlet></router-outlet>
</block-ui>
Expand Up @@ -2,18 +2,20 @@ <h1 mat-dialog-title>{{ config.title }}</h1>
<mat-dialog-content class="mat-typography">
<glass-declarative-form [config]="config"></glass-declarative-form>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button *ngIf="config.okButtonVisible"
mat-flat-button
cdkFocusInitial
[class]="config.okButtonClass"
(click)="onOK()">
{{ config.okButtonText! | translate }}
</button>
<mat-dialog-actions align="end"
fxLayout="row"
fxLayoutGap="10px">
<button *ngIf="config.cancelButtonVisible"
mat-flat-button
[class]="config.cancelButtonClass"
[mat-dialog-close]="config.cancelButtonResult">
{{ config.cancelButtonText! | translate }}
</button>
<glass-submit-button *ngIf="config.submitButtonVisible"
[form]="form?.formGroup"
[formId]="config.id"
cdkFocusInitial
(buttonClick)="onOK()">
{{ config.submitButtonText! | translate }}
</glass-submit-button>
</mat-dialog-actions>
Expand Up @@ -23,18 +23,19 @@ export class DeclarativeFormModalComponent {
) {
this.config = _.defaultsDeep(data, {
fields: [],
okButtonVisible: true,
okButtonText: TEXT('OK'),
submitButtonVisible: true,
submitButtonText: TEXT('OK'),
submitButtonResult: undefined,
cancelButtonVisible: true,
cancelButtonText: TEXT('Cancel'),
cancelButtonResult: false
});
}

onOK(): void {
const result = _.isUndefined(this.config.okButtonResult)
const result = _.isUndefined(this.config.submitButtonResult)
? this.form.values
: this.config.okButtonResult;
: this.config.submitButtonResult;
this.matDialogRef.close(result);
}
}
Expand Up @@ -130,7 +130,9 @@
</div>
</form>
</mat-dialog-content>
<mat-dialog-actions align="end">
<mat-dialog-actions align="end"
fxLayout="row"
fxLayoutGap="10px">
<button mat-flat-button
[mat-dialog-close]="false">
<span translate>Cancel</span>
Expand Down
Expand Up @@ -16,6 +16,11 @@ export class NavigationBarComponent implements OnInit {
icon: 'mdi:apps',
route: '/dashboard'
},
{
name: TEXT('Users'),
icon: 'mdi:account-multiple',
route: '/dashboard/users'
},
{
name: TEXT('Hosts'),
icon: 'mdi:server-network',
Expand Down
10 changes: 9 additions & 1 deletion src/glass/src/app/core/top-bar/top-bar.component.html
@@ -1,17 +1,25 @@
<mat-toolbar color="primary">
<mat-toolbar class="glass-top-bar"
color="primary">
<button mat-icon-button
matTooltip="{{ 'Toggle navigation' | translate }}"
(click)="onToggleNavigationBar()">
<mat-icon svgIcon="mdi:menu"></mat-icon>
</button>
<glass-breadcrumbs></glass-breadcrumbs>
<span fxFlex></span>
<span class="username">{{ username }}</span>
<mat-divider vertical="true"></mat-divider>
<glass-language-button></glass-language-button>
<button mat-icon-button
matTooltip="{{ 'Help' | translate }}"
[matMenuTriggerFor]="helpMenu">
<mat-icon svgIcon="mdi:help"></mat-icon>
</button>
<button mat-icon-button
matTooltip="{{ 'Logout' | translate }}"
(click)="onLogout()">
<mat-icon svgIcon="mdi:logout"></mat-icon>
</button>
</mat-toolbar>

<mat-menu #helpMenu="matMenu">
Expand Down
20 changes: 18 additions & 2 deletions src/glass/src/app/core/top-bar/top-bar.component.scss
@@ -1,7 +1,23 @@
@use '~@angular/material' as mat;
@import 'styles.scss';

.mat-toolbar {
::ng-deep button .mat-icon {
$mat-typography-config: mat.define-typography-config();

.glass-top-bar {
::ng-deep &.mat-toolbar {
.mat-icon {
color: $glass-color-primary-complementary;
}
.mat-divider {
height: 24px;
background-color: $glass-color-primary-complementary;
}
}

.username {
color: $glass-color-primary-complementary;
font-size: mat.font-size($mat-typography-config, body-1);
font-weight: mat.font-weight($mat-typography-config, body-1);
padding: 16px;
}
}
37 changes: 35 additions & 2 deletions src/glass/src/app/core/top-bar/top-bar.component.ts
@@ -1,5 +1,11 @@
import { Component, Input } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { marker as TEXT } from '@biesbjerg/ngx-translate-extract-marker';

import { DialogComponent } from '~/app/shared/components/dialog/dialog.component';
import { AuthService } from '~/app/shared/services/api/auth.service';
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
import { DialogService } from '~/app/shared/services/dialog.service';

@Component({
selector: 'glass-top-bar',
Expand All @@ -11,9 +17,36 @@ export class TopBarComponent {
@Input('navigationSidenav')
navigationSidenav!: MatSidenav;

constructor() {}
username: string | null;

constructor(
private authService: AuthService,
private authStorageService: AuthStorageService,
private dialogService: DialogService
) {
this.username = this.authStorageService.getUsername();
}

onToggleNavigationBar() {
onToggleNavigationBar(): void {
this.navigationSidenav.toggle();
}

onLogout(): void {
this.dialogService.open(
DialogComponent,
(res: boolean) => {
if (res) {
this.authService.logout().subscribe();
}
},
{
width: '40%',
data: {
type: 'yesNo',
icon: 'question',
message: TEXT('Do you really want to logout?')
}
}
);
}
}
6 changes: 5 additions & 1 deletion src/glass/src/app/material.modules.ts
Expand Up @@ -17,8 +17,10 @@ import { ReactiveFormsModule } from '@angular/forms';
import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatRippleModule } from '@angular/material/core';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
Expand Down Expand Up @@ -62,7 +64,9 @@ import { DomSanitizer } from '@angular/platform-browser';
MatRadioModule,
MatRippleModule,
MatSelectModule,
MatBadgeModule
MatBadgeModule,
MatCheckboxModule,
MatDividerModule
]
})
export class MaterialModule {
Expand Down
@@ -1,6 +1,6 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';

Expand All @@ -16,7 +16,7 @@ describe('DashboardPageComponent', () => {
imports: [
HttpClientTestingModule,
PagesModule,
BrowserAnimationsModule,
NoopAnimationsModule,
RouterTestingModule,
TranslateModule.forRoot()
]
Expand Down
4 changes: 2 additions & 2 deletions src/glass/src/app/pages/hosts-page/hosts-page.component.ts
Expand Up @@ -50,7 +50,7 @@ export class HostsPageComponent {
this.dialogService.open(DeclarativeFormModalComponent, undefined, {
width: '40%',
data: {
title: 'Authentication Token',
title: TEXT('Authentication Token'),
subtitle: TEXT(
'Use this token to authenticate a new node when adding it to the cluster.'
),
Expand All @@ -64,7 +64,7 @@ export class HostsPageComponent {
class: 'glass-text-monospaced'
}
],
okButtonVisible: false,
submitButtonVisible: false,
cancelButtonText: TEXT('Close')
}
});
Expand Down
18 changes: 18 additions & 0 deletions src/glass/src/app/pages/login-page/login-page.component.html
@@ -0,0 +1,18 @@
<div class="glass-login-page">
<div class="glass-login-page-background"></div>
<div class="glass-login-page-content"
fxLayout="column">
<mat-card>
<mat-card-content>
<glass-declarative-form [config]="config"></glass-declarative-form>
</mat-card-content>
<mat-card-actions fxLayoutAlign="center center">
<glass-submit-button [form]="form?.formGroup"
[formId]="config.id"
(buttonClick)="onLogin()">
<span translate>Login</span>
</glass-submit-button>
</mat-card-actions>
</mat-card>
</div>
</div>
29 changes: 29 additions & 0 deletions src/glass/src/app/pages/login-page/login-page.component.scss
@@ -0,0 +1,29 @@
@import 'styles.scss';

.glass-login-page {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #001b38;

.glass-login-page-background {
position: absolute;
height: 100%;
width: 100%;
background-color: $glass-color-primary;
background-image: url('/assets/images/mohamed-ahsan-pV4utCVv6vk-unsplash.jpg');
background-blend-mode: multiply;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
}

.glass-login-page-content {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}

0 comments on commit e73dafb

Please sign in to comment.