mirror of
https://github.com/loewexy/pdnsmanager.git
synced 2025-01-16 03:02:22 +01:00
Implemented credential editor
This commit is contained in:
parent
f8da1e68a3
commit
29f97e781e
9 changed files with 387 additions and 5 deletions
14
frontend/src/app/apitypes/Credential.apitype.ts
Normal file
14
frontend/src/app/apitypes/Credential.apitype.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export class CredentialApitype {
|
||||||
|
|
||||||
|
public id = 0;
|
||||||
|
|
||||||
|
public description = '';
|
||||||
|
|
||||||
|
public type = '';
|
||||||
|
|
||||||
|
public key: string = null;
|
||||||
|
|
||||||
|
constructor(init: Object) {
|
||||||
|
Object.assign(this, init);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { EditCredentialsComponent } from './pages/edit-credentials/edit-credentials.component';
|
||||||
import { NativeGuard } from './services/native-guard.service';
|
import { NativeGuard } from './services/native-guard.service';
|
||||||
import { LoggedOutGuard } from './services/logged-out-guard.service';
|
import { LoggedOutGuard } from './services/logged-out-guard.service';
|
||||||
import { CreateUserComponent } from './pages/create-user/create-user.component';
|
import { CreateUserComponent } from './pages/create-user/create-user.component';
|
||||||
|
@ -51,6 +52,16 @@ const routes: Routes = [
|
||||||
component: EditAuthComponent,
|
component: EditAuthComponent,
|
||||||
data: { type: 'NATIVE' }
|
data: { type: 'NATIVE' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'domains/master/:domainId/records/:recordId/credentials',
|
||||||
|
component: EditCredentialsComponent,
|
||||||
|
data: { type: 'MASTER' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'domains/native/:domainId/records/:recordId/credentials',
|
||||||
|
component: EditCredentialsComponent,
|
||||||
|
data: { type: 'NATIVE' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
canActivate: [AdminGuard],
|
canActivate: [AdminGuard],
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { CredentialsOperation } from './operations/credentials.operations';
|
||||||
|
import { EditCredentialsComponent } from './pages/edit-credentials/edit-credentials.component';
|
||||||
import { EditAuthAddComponent } from './pages/edit-auth/edit-auth-add.component';
|
import { EditAuthAddComponent } from './pages/edit-auth/edit-auth-add.component';
|
||||||
import { EditAuthLineComponent } from './pages/edit-auth/edit-auth-line.component';
|
import { EditAuthLineComponent } from './pages/edit-auth/edit-auth-line.component';
|
||||||
import { RecordsOperation } from './operations/records.operations';
|
import { RecordsOperation } from './operations/records.operations';
|
||||||
|
@ -69,7 +71,8 @@ import { UsersComponent } from './pages/users/users.component';
|
||||||
CreateUserComponent,
|
CreateUserComponent,
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
EditAuthLineComponent,
|
EditAuthLineComponent,
|
||||||
EditAuthAddComponent
|
EditAuthAddComponent,
|
||||||
|
EditCredentialsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -85,6 +88,7 @@ import { UsersComponent } from './pages/users/users.component';
|
||||||
DomainsOperation,
|
DomainsOperation,
|
||||||
UsersOperation,
|
UsersOperation,
|
||||||
RecordsOperation,
|
RecordsOperation,
|
||||||
|
CredentialsOperation,
|
||||||
AuthGuard,
|
AuthGuard,
|
||||||
AdminGuard,
|
AdminGuard,
|
||||||
NativeGuard,
|
NativeGuard,
|
||||||
|
|
111
frontend/src/app/operations/credentials.operations.ts
Normal file
111
frontend/src/app/operations/credentials.operations.ts
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import { CredentialApitype } from './../apitypes/Credential.apitype';
|
||||||
|
import { SoaApitype } from './../apitypes/Soa.apitype';
|
||||||
|
import { DomainApitype } from './../apitypes/Domain.apitype';
|
||||||
|
import { ListApitype } from './../apitypes/List.apitype';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpService } from '../services/http.service';
|
||||||
|
import { StateService } from '../services/state.service';
|
||||||
|
import { SessionApitype } from '../apitypes/Session.apitype';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CredentialsOperation {
|
||||||
|
|
||||||
|
constructor(private http: HttpService, private gs: StateService) { }
|
||||||
|
|
||||||
|
public async getList(recordId: number): Promise<ListApitype<CredentialApitype>> {
|
||||||
|
try {
|
||||||
|
return new ListApitype<CredentialApitype>(await this.http.get(['/records', recordId.toString(), 'credentials']));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return new ListApitype<CredentialApitype>({ paging: {}, results: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete(recordId: number, credentialId: number): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.http.delete(['/records', recordId.toString(), 'credentials', credentialId.toString()]);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSingle(recordId: number, credentialId: number): Promise<CredentialApitype> {
|
||||||
|
try {
|
||||||
|
return new CredentialApitype(await this.http.get(['/records', recordId.toString(), 'credentials', credentialId.toString()]));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return new CredentialApitype({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateKey(recordId: number, credentalId: number, description: string, key: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.http.put(['/records', recordId.toString(), 'credentials', credentalId.toString()], {
|
||||||
|
description: description,
|
||||||
|
key: key
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response.status || e.response.status === 400) {
|
||||||
|
throw new Error('The key is not a valid public key!');
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updatePassword(recordId: number, credentalId: number, description: string, password: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
description: description
|
||||||
|
};
|
||||||
|
|
||||||
|
if (password.length > 0) {
|
||||||
|
data['password'] = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.http.put(['/records', recordId.toString(), 'credentials', credentalId.toString()], data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createKey(recordId: number, description: string, key: string): Promise<CredentialApitype> {
|
||||||
|
try {
|
||||||
|
const result = new DomainApitype(await this.http.post(['/records', recordId.toString(), 'credentials'], {
|
||||||
|
description: description,
|
||||||
|
type: 'key',
|
||||||
|
key: key
|
||||||
|
}));
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response.status || e.response.status === 400) {
|
||||||
|
throw new Error('The key is not a valid public key!');
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
return new CredentialApitype({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createPassword(recordId: number, description: string, password: string): Promise<CredentialApitype> {
|
||||||
|
try {
|
||||||
|
const result = new DomainApitype(await this.http.post(['/records', recordId.toString(), 'credentials'], {
|
||||||
|
description: description,
|
||||||
|
type: 'password',
|
||||||
|
password: password
|
||||||
|
}));
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return new CredentialApitype({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Router, ActivationEnd, ActivatedRoute } from '@angular/router';
|
||||||
import { ModalOptionsDatatype } from './../../datatypes/modal-options.datatype';
|
import { ModalOptionsDatatype } from './../../datatypes/modal-options.datatype';
|
||||||
import { ModalService } from './../../services/modal.service';
|
import { ModalService } from './../../services/modal.service';
|
||||||
import { RecordsOperation } from './../../operations/records.operations';
|
import { RecordsOperation } from './../../operations/records.operations';
|
||||||
|
@ -28,7 +29,8 @@ export class EditAuthLineComponent implements OnInit, OnChanges {
|
||||||
public inputPriority: FormControl;
|
public inputPriority: FormControl;
|
||||||
public inputTtl: FormControl;
|
public inputTtl: FormControl;
|
||||||
|
|
||||||
constructor(private fb: FormBuilder, public gs: StateService, private records: RecordsOperation, private modal: ModalService) {
|
constructor(private fb: FormBuilder, public gs: StateService, private records: RecordsOperation,
|
||||||
|
private modal: ModalService, private router: Router, private route: ActivatedRoute) {
|
||||||
this.setupFormControls();
|
this.setupFormControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +89,7 @@ export class EditAuthLineComponent implements OnInit, OnChanges {
|
||||||
await this.modal.showMessage(new ModalOptionsDatatype({
|
await this.modal.showMessage(new ModalOptionsDatatype({
|
||||||
heading: 'Confirm deletion',
|
heading: 'Confirm deletion',
|
||||||
body: 'Are you shure you want to delete the ' + this.inputType.value +
|
body: 'Are you shure you want to delete the ' + this.inputType.value +
|
||||||
' record ' + this.fullName() + ' with content ' + this.inputContent.value + '?',
|
' record ' + this.fullName() + ' with content ' + this.inputContent.value + '?',
|
||||||
acceptText: 'Delete',
|
acceptText: 'Delete',
|
||||||
dismisText: 'Cancel',
|
dismisText: 'Cancel',
|
||||||
acceptClass: 'danger'
|
acceptClass: 'danger'
|
||||||
|
@ -101,6 +103,6 @@ export class EditAuthLineComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onRemoteClick() {
|
public async onRemoteClick() {
|
||||||
|
this.router.navigate(['./records', this.entry.id.toString(), 'credentials'], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6 col-lg-4">
|
||||||
|
<div class="btn-group btn-group-sm mb-3">
|
||||||
|
<button disabled class="btn btn-secondary">Add credential</button>
|
||||||
|
<button class="btn btn-secondary" (click)="onAddKey()">Key</button>
|
||||||
|
<button class="btn btn-secondary" (click)="onAddPassword()">Password</button>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let credential of credentialList">
|
||||||
|
<td>{{ credential.description }}</td>
|
||||||
|
<td>{{ credential.type }}</td>
|
||||||
|
<td>
|
||||||
|
<app-fa-icon class="mx-1 cursor-pointer" icon="edit" (click)="onEditClick(credential.id)"></app-fa-icon>
|
||||||
|
<app-fa-icon class="mx-1 cursor-pointer" icon="trash" (click)="onRemoveCredential(credential)"></app-fa-icon>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-8 col-lg-4 offset-lg-1">
|
||||||
|
<form *ngIf="editType === 'key'" [formGroup]="keyForm" (submit)="onSubmit()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Description</label>
|
||||||
|
<input type="text" class="form-control auto-invalid" formControlName="description" appFocus />
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Description can not be empty.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Key</label>
|
||||||
|
<textarea class="form-control auto-invalid" rows=10 formControlName="key"></textarea>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Key is required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<app-alert *ngIf="keyInvalid">
|
||||||
|
<app-alert-message>The key is invalid.</app-alert-message>
|
||||||
|
</app-alert>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary float-right" [disabled]="!keyForm.valid">Save</button>
|
||||||
|
</form>
|
||||||
|
<form *ngIf="editType === 'password'" [formGroup]="passwordForm" (submit)="onSubmit()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Description</label>
|
||||||
|
<input type="text" class="form-control auto-invalid" formControlName="description" appFocus />
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Description can not be empty.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Password</label>
|
||||||
|
<input type="password" class="form-control auto-validstate" formControlName="password" [placeholder]="editId !== 0 ? '(unchanged)': ''"
|
||||||
|
/>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Password is required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Repeat password</label>
|
||||||
|
<input type="password" class="form-control auto-validstate" formControlName="password2" [placeholder]="editId !== 0 ? '(unchanged)': ''"
|
||||||
|
/>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Passwords do not match.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary float-right" [disabled]="!passwordForm.valid">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,158 @@
|
||||||
|
import { CredentialApitype } from './../../apitypes/Credential.apitype';
|
||||||
|
import { RecordsOperation } from './../../operations/records.operations';
|
||||||
|
import { CredentialsOperation } from './../../operations/credentials.operations';
|
||||||
|
import { RecordApitype } from './../../apitypes/Record.apitype';
|
||||||
|
import { PagingApitype } from './../../apitypes/Paging.apitype';
|
||||||
|
import { PermissionApitype } from './../../apitypes/Permission.apitype';
|
||||||
|
import { ModalService } from './../../services/modal.service';
|
||||||
|
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
|
||||||
|
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ModalOptionsDatatype } from '../../datatypes/modal-options.datatype';
|
||||||
|
import { PasswordValidationUtil } from '../../utils/password-validation.util';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-edit-credentials',
|
||||||
|
templateUrl: './edit-credentials.component.html',
|
||||||
|
styleUrls: ['./edit-credentials.component.scss']
|
||||||
|
})
|
||||||
|
export class EditCredentialsComponent implements OnInit {
|
||||||
|
public keyForm: FormGroup;
|
||||||
|
public passwordForm: FormGroup;
|
||||||
|
|
||||||
|
public editType = '';
|
||||||
|
public editId = 0;
|
||||||
|
|
||||||
|
public keyInvalid = false;
|
||||||
|
|
||||||
|
public credentialList: CredentialApitype[] = [];
|
||||||
|
|
||||||
|
public domainId = 0;
|
||||||
|
public recordId = 0;
|
||||||
|
public record: RecordApitype = new RecordApitype({});
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder, private route: ActivatedRoute, private credentials: CredentialsOperation,
|
||||||
|
private router: Router, private modal: ModalService, public records: RecordsOperation) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.createForm();
|
||||||
|
|
||||||
|
this.route.paramMap.subscribe((params) => this.initControl(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initControl(params: ParamMap) {
|
||||||
|
this.recordId = +params.get('recordId');
|
||||||
|
this.domainId = +params.get('domainId');
|
||||||
|
|
||||||
|
this.loadCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
private createForm() {
|
||||||
|
this.keyForm = this.fb.group({
|
||||||
|
description: ['', Validators.required],
|
||||||
|
key: ['', Validators.required]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.passwordForm = this.fb.group({
|
||||||
|
description: ['', Validators.required],
|
||||||
|
password: ['', Validators.required],
|
||||||
|
password2: ['']
|
||||||
|
}, { validator: PasswordValidationUtil.matchPassword });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onSubmit() {
|
||||||
|
this.keyInvalid = false;
|
||||||
|
try {
|
||||||
|
if (this.editId === 0) {
|
||||||
|
if (this.editType === 'key') {
|
||||||
|
const v = this.keyForm.value;
|
||||||
|
await this.credentials.createKey(this.recordId, v.description, v.key);
|
||||||
|
} else if (this.editType === 'password') {
|
||||||
|
const v = this.passwordForm.value;
|
||||||
|
await this.credentials.createPassword(this.recordId, v.description, v.password);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.editType === 'key') {
|
||||||
|
const v = this.keyForm.value;
|
||||||
|
await this.credentials.updateKey(this.recordId, this.editId, v.description, v.key);
|
||||||
|
} else if (this.editType === 'password') {
|
||||||
|
const v = this.passwordForm.value;
|
||||||
|
await this.credentials.updatePassword(this.recordId, this.editId, v.description, v.password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editId = 0;
|
||||||
|
this.editType = '';
|
||||||
|
await this.loadCredentials();
|
||||||
|
} catch (e) {
|
||||||
|
this.keyInvalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onAddKey() {
|
||||||
|
this.editId = 0;
|
||||||
|
this.editType = 'key';
|
||||||
|
this.keyInvalid = false;
|
||||||
|
|
||||||
|
this.keyForm.reset({
|
||||||
|
description: '',
|
||||||
|
key: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onAddPassword() {
|
||||||
|
this.editId = 0;
|
||||||
|
this.editType = 'password';
|
||||||
|
this.passwordForm.controls['password'].setValidators(Validators.required);
|
||||||
|
|
||||||
|
this.passwordForm.reset({
|
||||||
|
description: '',
|
||||||
|
password: '',
|
||||||
|
password2: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onEditClick(credentialId: number) {
|
||||||
|
const credential = await this.credentials.getSingle(this.recordId, credentialId);
|
||||||
|
|
||||||
|
if (credential.type === 'key') {
|
||||||
|
this.editType = 'key';
|
||||||
|
this.editId = credentialId;
|
||||||
|
this.keyInvalid = false;
|
||||||
|
this.keyForm.reset({
|
||||||
|
description: credential.description,
|
||||||
|
key: credential.key
|
||||||
|
});
|
||||||
|
} else if (credential.type === 'password') {
|
||||||
|
this.editType = 'password';
|
||||||
|
this.editId = credentialId;
|
||||||
|
this.passwordForm.controls['password'].clearValidators();
|
||||||
|
this.passwordForm.reset({
|
||||||
|
description: credential.description,
|
||||||
|
password: '',
|
||||||
|
password2: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async loadCredentials() {
|
||||||
|
const res = await this.credentials.getList(this.recordId);
|
||||||
|
|
||||||
|
this.credentialList = res.results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onRemoveCredential(credential: CredentialApitype) {
|
||||||
|
try {
|
||||||
|
await this.modal.showMessage(new ModalOptionsDatatype({
|
||||||
|
heading: 'Confirm deletion',
|
||||||
|
body: 'Are you shure you want to delete the credential ' + credential.description + '?',
|
||||||
|
acceptText: 'Delete',
|
||||||
|
dismisText: 'Cancel',
|
||||||
|
acceptClass: 'danger'
|
||||||
|
}));
|
||||||
|
await this.credentials.delete(this.recordId, credential.id);
|
||||||
|
await this.loadCredentials();
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ import { ModalOptionsDatatype } from '../../datatypes/modal-options.datatype';
|
||||||
import { PasswordValidationUtil } from '../../utils/password-validation.util';
|
import { PasswordValidationUtil } from '../../utils/password-validation.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-user',
|
selector: 'app-edit-user',
|
||||||
templateUrl: './edit-user.component.html',
|
templateUrl: './edit-user.component.html',
|
||||||
styleUrls: ['./edit-user.component.scss']
|
styleUrls: ['./edit-user.component.scss']
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue