mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
Implement scan all policy configuration
This commit is contained in:
parent
22a4e91a79
commit
1db36d99fb
@ -41,7 +41,18 @@ export class BoolValueItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ComplexValueItem {
|
||||||
|
value: any | { [key: string]: any | any[] };
|
||||||
|
editable: boolean;
|
||||||
|
|
||||||
|
public constructor(v: any | { [key: string]: any | any[] }, e: boolean) {
|
||||||
|
this.value = v;
|
||||||
|
this.editable = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Configuration {
|
export class Configuration {
|
||||||
|
[key: string]: any | any[]
|
||||||
auth_mode: StringValueItem;
|
auth_mode: StringValueItem;
|
||||||
project_creation_restriction: StringValueItem;
|
project_creation_restriction: StringValueItem;
|
||||||
self_registration: BoolValueItem;
|
self_registration: BoolValueItem;
|
||||||
@ -63,6 +74,7 @@ export class Configuration {
|
|||||||
verify_remote_cert: BoolValueItem;
|
verify_remote_cert: BoolValueItem;
|
||||||
token_expiration: NumberValueItem;
|
token_expiration: NumberValueItem;
|
||||||
cfg_expiration: NumberValueItem;
|
cfg_expiration: NumberValueItem;
|
||||||
|
scan_all_policy: ComplexValueItem;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
this.auth_mode = new StringValueItem("db_auth", true);
|
this.auth_mode = new StringValueItem("db_auth", true);
|
||||||
@ -83,8 +95,14 @@ export class Configuration {
|
|||||||
this.email_ssl = new BoolValueItem(false, true);
|
this.email_ssl = new BoolValueItem(false, true);
|
||||||
this.email_username = new StringValueItem("", true);
|
this.email_username = new StringValueItem("", true);
|
||||||
this.email_password = new StringValueItem("", true);
|
this.email_password = new StringValueItem("", true);
|
||||||
this.token_expiration = new NumberValueItem(5, true);
|
this.token_expiration = new NumberValueItem(30, true);
|
||||||
this.cfg_expiration = new NumberValueItem(30, true);
|
this.cfg_expiration = new NumberValueItem(30, true);
|
||||||
this.verify_remote_cert = new BoolValueItem(false, true);
|
this.verify_remote_cert = new BoolValueItem(false, true);
|
||||||
|
this.scan_all_policy = new ComplexValueItem({
|
||||||
|
type: "daily",
|
||||||
|
parameters: {
|
||||||
|
daily_time: 0
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
19
src/ui_ng/lib/src/config/index.ts
Normal file
19
src/ui_ng/lib/src/config/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { ReplicationConfigComponent } from './replication/replication-config.component';
|
||||||
|
import { SystemSettingsComponent } from './system/system-settings.component';
|
||||||
|
import { VulnerabilityConfigComponent } from './vulnerability/vulnerability-config.component';
|
||||||
|
import { RegistryConfigComponent } from './registry-config.component';
|
||||||
|
|
||||||
|
export * from './config';
|
||||||
|
export * from './replication/replication-config.component';
|
||||||
|
export * from './system/system-settings.component';
|
||||||
|
export * from './vulnerability/vulnerability-config.component';
|
||||||
|
export * from './registry-config.component';
|
||||||
|
|
||||||
|
export const CONFIGURATION_DIRECTIVES: Type<any>[] = [
|
||||||
|
ReplicationConfigComponent,
|
||||||
|
SystemSettingsComponent,
|
||||||
|
VulnerabilityConfigComponent,
|
||||||
|
RegistryConfigComponent
|
||||||
|
];
|
@ -0,0 +1,7 @@
|
|||||||
|
export const REGISTRY_CONFIG_HTML: string = `
|
||||||
|
<div>
|
||||||
|
<replication-config [(replicationConfig)]="config"></replication-config>
|
||||||
|
<system-settings [(systemSettings)]="config"></system-settings>
|
||||||
|
<vulnerability-config [(vulnerabilityConfig)]="config"></vulnerability-config>
|
||||||
|
</div>
|
||||||
|
`;
|
98
src/ui_ng/lib/src/config/registry-config.component.spec.ts
Normal file
98
src/ui_ng/lib/src/config/registry-config.component.spec.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
|
||||||
|
import { ReplicationConfigComponent } from './replication/replication-config.component';
|
||||||
|
import { SystemSettingsComponent } from './system/system-settings.component';
|
||||||
|
import { VulnerabilityConfigComponent } from './vulnerability/vulnerability-config.component';
|
||||||
|
import { RegistryConfigComponent } from './registry-config.component';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ConfigurationService,
|
||||||
|
ConfigurationDefaultService,
|
||||||
|
ScanningResultService,
|
||||||
|
ScanningResultDefaultService
|
||||||
|
} from '../service/index';
|
||||||
|
import { Configuration } from './config';
|
||||||
|
|
||||||
|
describe('RegistryConfigComponent (inline template)', () => {
|
||||||
|
|
||||||
|
let comp: RegistryConfigComponent;
|
||||||
|
let fixture: ComponentFixture<RegistryConfigComponent>;
|
||||||
|
let cfgService: ConfigurationService;
|
||||||
|
let spy: jasmine.Spy;
|
||||||
|
let saveSpy: jasmine.Spy;
|
||||||
|
let mockConfig: Configuration = new Configuration();
|
||||||
|
mockConfig.token_expiration.value = 90;
|
||||||
|
mockConfig.verify_remote_cert.value = true;
|
||||||
|
mockConfig.scan_all_policy.value = {
|
||||||
|
type: "daily",
|
||||||
|
parameters: {
|
||||||
|
daily_time: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let config: IServiceConfig = {
|
||||||
|
configurationEndpoint: '/api/configurations/testing'
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ReplicationConfigComponent,
|
||||||
|
SystemSettingsComponent,
|
||||||
|
VulnerabilityConfigComponent,
|
||||||
|
RegistryConfigComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ErrorHandler,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
{ provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
||||||
|
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RegistryConfigComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
|
||||||
|
cfgService = fixture.debugElement.injector.get(ConfigurationService);
|
||||||
|
spy = spyOn(cfgService, 'getConfigurations').and.returnValue(Promise.resolve(mockConfig));
|
||||||
|
saveSpy = spyOn(cfgService, 'saveConfigurations').and.returnValue(Promise.resolve(true));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render configurations to the view', async(() => {
|
||||||
|
expect(spy.calls.count()).toEqual(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
let el: HTMLInputElement = fixture.nativeElement.querySelector('input[type="text"]');
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.value).toEqual('30');
|
||||||
|
|
||||||
|
let el2: HTMLInputElement = fixture.nativeElement.querySelector('input[type="checkbox"]');
|
||||||
|
expect(el2).toBeTruthy();
|
||||||
|
expect(el2.value).toEqual('on');
|
||||||
|
|
||||||
|
let el3: HTMLInputElement = fixture.nativeElement.querySelector('input[type="time"]');
|
||||||
|
expect(el3).toBeTruthy();
|
||||||
|
expect(el3.value).toEqual("08:00");
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should save the configuration changes', async(() => {
|
||||||
|
comp.save();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(saveSpy.calls.any).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
110
src/ui_ng/lib/src/config/registry-config.component.ts
Normal file
110
src/ui_ng/lib/src/config/registry-config.component.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
|
||||||
|
|
||||||
|
import { Configuration, ComplexValueItem } from './config';
|
||||||
|
import { REGISTRY_CONFIG_HTML } from './registry-config.component.html';
|
||||||
|
import { ConfigurationService } from '../service/index';
|
||||||
|
import { toPromise } from '../utils';
|
||||||
|
import { ErrorHandler } from '../error-handler';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'hbr-registry-config',
|
||||||
|
template: REGISTRY_CONFIG_HTML
|
||||||
|
})
|
||||||
|
export class RegistryConfigComponent implements OnInit {
|
||||||
|
config: Configuration = new Configuration();
|
||||||
|
configCopy: Configuration;
|
||||||
|
|
||||||
|
@Output() configChanged: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private configService: ConfigurationService,
|
||||||
|
private errorHandler: ErrorHandler
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
//Initialize
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load configurations
|
||||||
|
load(): void {
|
||||||
|
toPromise<Configuration>(this.configService.getConfigurations())
|
||||||
|
.then((config: Configuration) => {
|
||||||
|
this.configCopy = Object.assign({}, config);
|
||||||
|
this.config = config;
|
||||||
|
})
|
||||||
|
.catch(error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save configuration changes
|
||||||
|
save(): void {
|
||||||
|
let changes: { [key: string]: any | any[] } = this.getChanges();
|
||||||
|
|
||||||
|
if (this._isEmptyObject(changes)) {
|
||||||
|
//Guard code, do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fix policy parameters issue
|
||||||
|
let scanningAllPolicy = changes["scan_all_policy"];
|
||||||
|
if (scanningAllPolicy &&
|
||||||
|
scanningAllPolicy.type !== "daily" &&
|
||||||
|
scanningAllPolicy.parameters) {
|
||||||
|
delete (scanningAllPolicy.parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
toPromise<any>(this.configService.saveConfigurations(changes))
|
||||||
|
.then(() => {
|
||||||
|
this.configChanged.emit(changes);
|
||||||
|
})
|
||||||
|
.catch(error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
//Reset to the values of copy
|
||||||
|
let changes: { [key: string]: any | any[] } = this.getChanges();
|
||||||
|
for (let prop in changes) {
|
||||||
|
this.config[prop] = Object.assign({}, this.configCopy[prop]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getChanges(): { [key: string]: any | any[] } {
|
||||||
|
let changes: { [key: string]: any | any[] } = {};
|
||||||
|
if (!this.config || !this.configCopy) {
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let prop in this.config) {
|
||||||
|
let field = this.configCopy[prop];
|
||||||
|
if (field && field.editable) {
|
||||||
|
if (!this._compareValue(field.value, this.config[prop].value)) {
|
||||||
|
changes[prop] = this.config[prop].value;
|
||||||
|
//Number
|
||||||
|
if (typeof field.value === "number") {
|
||||||
|
changes[prop] = +changes[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Trim string value
|
||||||
|
if (typeof field.value === "string") {
|
||||||
|
changes[prop] = ('' + changes[prop]).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//private
|
||||||
|
_compareValue(a: any, b: any): boolean {
|
||||||
|
if ((a && !b) || (!a && b)) return false;
|
||||||
|
if (!a && !b) return true;
|
||||||
|
|
||||||
|
return JSON.stringify(a) === JSON.stringify(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//private
|
||||||
|
_isEmptyObject(obj: any): boolean {
|
||||||
|
return !obj || JSON.stringify(obj) === "{}";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
export const REPLICATION_CONFIG_HTML: string = `
|
||||||
|
<form #replicationConfigFrom="ngForm" class="compact">
|
||||||
|
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
|
||||||
|
<label style="font-size:14px;font-weight:600;">Image Replication</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="verifyRemoteCert">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</label>
|
||||||
|
<clr-checkbox name="verifyRemoteCert" id="verifyRemoteCert" [(ngModel)]="replicationConfig.verify_remote_cert.value" [disabled]="!editable">
|
||||||
|
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-right" style="top:-8px;">
|
||||||
|
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
||||||
|
<span class="tooltip-content">{{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate }}</span>
|
||||||
|
</a>
|
||||||
|
</clr-checkbox>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
`;
|
@ -0,0 +1,35 @@
|
|||||||
|
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
import { REPLICATION_CONFIG_HTML } from './replication-config.component.html';
|
||||||
|
import { Configuration } from '../config';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'replication-config',
|
||||||
|
template: REPLICATION_CONFIG_HTML
|
||||||
|
})
|
||||||
|
export class ReplicationConfigComponent {
|
||||||
|
config: Configuration;
|
||||||
|
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
get replicationConfig(): Configuration {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
set replicationConfig(cfg: Configuration) {
|
||||||
|
this.config = cfg;
|
||||||
|
this.configChange.emit(this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild("replicationConfigFrom") replicationConfigForm: NgForm;
|
||||||
|
|
||||||
|
get editable(): boolean {
|
||||||
|
return this.replicationConfig &&
|
||||||
|
this.replicationConfig.verify_remote_cert &&
|
||||||
|
this.replicationConfig.verify_remote_cert.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isValid(): boolean {
|
||||||
|
return this.replicationConfigForm && this.replicationConfigForm.valid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
export const SYSTEM_SETTINGS_HTML: string = `
|
||||||
|
<form #systemConfigFrom="ngForm" class="compact">
|
||||||
|
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
|
||||||
|
<label style="font-size:14px;font-weight:600;">System Settings</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="tokenExpiration" class="required">{{'CONFIG.TOKEN_EXPIRATION' | translate}}</label>
|
||||||
|
<label for="tokenExpiration" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="tokenExpirationInput.invalid && (tokenExpirationInput.dirty || tokenExpirationInput.touched)">
|
||||||
|
<input name="tokenExpiration" type="text" #tokenExpirationInput="ngModel" [(ngModel)]="systemSettings.token_expiration.value"
|
||||||
|
required
|
||||||
|
pattern="^[1-9]{1}[0-9]*$"
|
||||||
|
id="tokenExpiration"
|
||||||
|
size="20" [disabled]="!editable">
|
||||||
|
<span class="tooltip-content">
|
||||||
|
{{'TOOLTIP.NUMBER_REQUIRED' | translate}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||||
|
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
||||||
|
<span class="tooltip-content">{{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
`;
|
35
src/ui_ng/lib/src/config/system/system-settings.component.ts
Normal file
35
src/ui_ng/lib/src/config/system/system-settings.component.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
import { SYSTEM_SETTINGS_HTML } from './system-settings.component.html';
|
||||||
|
import { Configuration } from '../config';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'system-settings',
|
||||||
|
template: SYSTEM_SETTINGS_HTML
|
||||||
|
})
|
||||||
|
export class SystemSettingsComponent {
|
||||||
|
config: Configuration;
|
||||||
|
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
get systemSettings(): Configuration {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
set systemSettings(cfg: Configuration) {
|
||||||
|
this.config = cfg;
|
||||||
|
this.configChange.emit(this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
|
||||||
|
|
||||||
|
get editable(): boolean {
|
||||||
|
return this.systemSettings &&
|
||||||
|
this.systemSettings.token_expiration &&
|
||||||
|
this.systemSettings.token_expiration.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isValid(): boolean {
|
||||||
|
return this.systemSettingsForm && this.systemSettingsForm.valid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
export const VULNERABILITY_CONFIG_HTML: string = `
|
||||||
|
<form #systemConfigFrom="ngForm" class="compact">
|
||||||
|
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
|
||||||
|
<label class="section-title">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label>
|
||||||
|
<div class="select">
|
||||||
|
<select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="vulnerabilityConfig.scan_all_policy.value.type">
|
||||||
|
<option value="none">{{ 'CONFIG.SCANNING.NONE_POLICY' | translate }}</option>
|
||||||
|
<option value="daily">{{ 'CONFIG.SCANNING.DAILY_POLICY' | translate }}</option>
|
||||||
|
<option value="on_refresh">{{ 'CONFIG.SCANNING.REFRESH_POLICY' | translate }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input type="time" name="dailyTimePicker" [disabled]="!editable" [hidden]="!showTimePicker" [(ngModel)]="dailyTime" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-group-override">
|
||||||
|
<button class="btn btn-primary btn-sm" style="width:160px;" (click)="scanNow()">{{ 'CONFIG.SCANNING.SCAN_NOW' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const VULNERABILITY_CONFIG_STYLES: string = `
|
||||||
|
.form-group-override {
|
||||||
|
padding-left: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 14px !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
`;
|
@ -0,0 +1,165 @@
|
|||||||
|
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
import { Configuration } from '../config';
|
||||||
|
import { VULNERABILITY_CONFIG_HTML, VULNERABILITY_CONFIG_STYLES } from './vulnerability-config.component.template';
|
||||||
|
import { ScanningResultService } from '../../service/scanning.service';
|
||||||
|
import { ErrorHandler } from '../../error-handler';
|
||||||
|
import { toPromise } from '../../utils';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
const ONE_HOUR_SECONDS: number = 3600;
|
||||||
|
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'vulnerability-config',
|
||||||
|
template: VULNERABILITY_CONFIG_HTML,
|
||||||
|
styles: [VULNERABILITY_CONFIG_STYLES]
|
||||||
|
})
|
||||||
|
export class VulnerabilityConfigComponent {
|
||||||
|
_localTime: Date = new Date();
|
||||||
|
|
||||||
|
config: Configuration;
|
||||||
|
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
get vulnerabilityConfig(): Configuration {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
set vulnerabilityConfig(cfg: Configuration) {
|
||||||
|
this.config = cfg;
|
||||||
|
if (this.config.scan_all_policy &&
|
||||||
|
this.config.scan_all_policy.value) {
|
||||||
|
if (this.config.scan_all_policy.value.type === "daily"){
|
||||||
|
if(!this.config.scan_all_policy.value.parameters){
|
||||||
|
this.config.scan_all_policy.value.parameters = {
|
||||||
|
daily_time: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.configChange.emit(this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
//UTC time
|
||||||
|
get dailyTime(): string {
|
||||||
|
if (!(this.config &&
|
||||||
|
this.config.scan_all_policy &&
|
||||||
|
this.config.scan_all_policy.value &&
|
||||||
|
this.config.scan_all_policy.value.type === "daily")) {
|
||||||
|
return "00:00";
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeOffset: number = 0;//seconds
|
||||||
|
if (this.config.scan_all_policy.value.parameters) {
|
||||||
|
let daily_time = this.config.scan_all_policy.value.parameters.daily_time;
|
||||||
|
if (daily_time && typeof daily_time === "number") {
|
||||||
|
timeOffset = +daily_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert to current time
|
||||||
|
let timezoneOffset: number = this._localTime.getTimezoneOffset();
|
||||||
|
//Local time
|
||||||
|
timeOffset = timeOffset - timezoneOffset * 60;
|
||||||
|
if (timeOffset < 0) {
|
||||||
|
timeOffset = timeOffset + ONE_DAY_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeOffset >= ONE_DAY_SECONDS) {
|
||||||
|
timeOffset -= ONE_DAY_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//To time string
|
||||||
|
let hours: number = Math.floor(timeOffset / ONE_HOUR_SECONDS);
|
||||||
|
let minutes: number = Math.floor((timeOffset - hours * ONE_HOUR_SECONDS) / 60);
|
||||||
|
|
||||||
|
let timeStr: string = "" + hours;
|
||||||
|
if (hours < 10) {
|
||||||
|
timeStr = "0" + timeStr;
|
||||||
|
}
|
||||||
|
if (minutes < 10) {
|
||||||
|
timeStr += ":0";
|
||||||
|
} else {
|
||||||
|
timeStr += ":";
|
||||||
|
}
|
||||||
|
timeStr += minutes;
|
||||||
|
|
||||||
|
return timeStr;
|
||||||
|
}
|
||||||
|
set dailyTime(v: string) {
|
||||||
|
if (!v || v === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(this.config &&
|
||||||
|
this.config.scan_all_policy &&
|
||||||
|
this.config.scan_all_policy.value &&
|
||||||
|
this.config.scan_all_policy.value.type === "daily")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.config.scan_all_policy.value.parameters) {
|
||||||
|
this.config.scan_all_policy.value.parameters = {
|
||||||
|
daily_time: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let values: string[] = v.split(":");
|
||||||
|
if (!values || values.length !== 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hours: number = +values[0];
|
||||||
|
let minutes: number = +values[1];
|
||||||
|
//Convert to UTC time
|
||||||
|
let timezoneOffset: number = this._localTime.getTimezoneOffset();
|
||||||
|
let utcTimes: number = hours * ONE_HOUR_SECONDS + minutes * 60;
|
||||||
|
utcTimes += timezoneOffset * 60;
|
||||||
|
if (utcTimes < 0) {
|
||||||
|
utcTimes += ONE_DAY_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utcTimes >= ONE_DAY_SECONDS) {
|
||||||
|
utcTimes -= ONE_DAY_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.config.scan_all_policy.value.parameters.daily_time = utcTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
|
||||||
|
|
||||||
|
get editable(): boolean {
|
||||||
|
return this.vulnerabilityConfig &&
|
||||||
|
this.vulnerabilityConfig.scan_all_policy &&
|
||||||
|
this.vulnerabilityConfig.scan_all_policy.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isValid(): boolean {
|
||||||
|
return this.systemSettingsForm && this.systemSettingsForm.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get showTimePicker(): boolean {
|
||||||
|
return this.vulnerabilityConfig &&
|
||||||
|
this.vulnerabilityConfig.scan_all_policy &&
|
||||||
|
this.vulnerabilityConfig.scan_all_policy.value &&
|
||||||
|
this.vulnerabilityConfig.scan_all_policy.value.type === "daily";
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private scanningService: ScanningResultService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private translate: TranslateService) { }
|
||||||
|
|
||||||
|
scanNow(): void {
|
||||||
|
toPromise<any>(this.scanningService.startScanningAll())
|
||||||
|
.then(() => {
|
||||||
|
this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_SUCCESS").subscribe((res: string) => {
|
||||||
|
this.errorHandler.info(res);
|
||||||
|
});
|
||||||
|
//TODO:
|
||||||
|
//Change button disable status.
|
||||||
|
})
|
||||||
|
.catch(error => this.errorHandler.error(error))
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ import { INLINE_ALERT_DIRECTIVES } from './inline-alert/index';
|
|||||||
import { DATETIME_PICKER_DIRECTIVES } from './datetime-picker/index';
|
import { DATETIME_PICKER_DIRECTIVES } from './datetime-picker/index';
|
||||||
import { VULNERABILITY_DIRECTIVES } from './vulnerability-scanning/index';
|
import { VULNERABILITY_DIRECTIVES } from './vulnerability-scanning/index';
|
||||||
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from './push-image/index';
|
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from './push-image/index';
|
||||||
|
import { CONFIGURATION_DIRECTIVES } from './config/index';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SystemInfoService,
|
SystemInfoService,
|
||||||
@ -37,7 +38,9 @@ import {
|
|||||||
TagService,
|
TagService,
|
||||||
TagDefaultService,
|
TagDefaultService,
|
||||||
ScanningResultService,
|
ScanningResultService,
|
||||||
ScanningResultDefaultService
|
ScanningResultDefaultService,
|
||||||
|
ConfigurationService,
|
||||||
|
ConfigurationDefaultService
|
||||||
} from './service/index';
|
} from './service/index';
|
||||||
import {
|
import {
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
@ -68,7 +71,8 @@ export const DefaultServiceConfig: IServiceConfig = {
|
|||||||
langMessageLoader: "local",
|
langMessageLoader: "local",
|
||||||
langMessagePathForHttpLoader: "i18n/langs/",
|
langMessagePathForHttpLoader: "i18n/langs/",
|
||||||
langMessageFileSuffixForHttpLoader: "-lang.json",
|
langMessageFileSuffixForHttpLoader: "-lang.json",
|
||||||
localI18nMessageVariableMap: {}
|
localI18nMessageVariableMap: {},
|
||||||
|
configurationEndpoint: "/api/configurations"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +107,10 @@ export interface HarborModuleConfig {
|
|||||||
tagService?: Provider,
|
tagService?: Provider,
|
||||||
|
|
||||||
//Service implementation for vulnerability scanning
|
//Service implementation for vulnerability scanning
|
||||||
scanningService?: Provider
|
scanningService?: Provider,
|
||||||
|
|
||||||
|
//Service implementation for configuration
|
||||||
|
configService?: Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,7 +152,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
|||||||
CREATE_EDIT_RULE_DIRECTIVES,
|
CREATE_EDIT_RULE_DIRECTIVES,
|
||||||
DATETIME_PICKER_DIRECTIVES,
|
DATETIME_PICKER_DIRECTIVES,
|
||||||
VULNERABILITY_DIRECTIVES,
|
VULNERABILITY_DIRECTIVES,
|
||||||
PUSH_IMAGE_BUTTON_DIRECTIVES
|
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||||
|
CONFIGURATION_DIRECTIVES
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
LOG_DIRECTIVES,
|
LOG_DIRECTIVES,
|
||||||
@ -164,6 +172,7 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
|||||||
DATETIME_PICKER_DIRECTIVES,
|
DATETIME_PICKER_DIRECTIVES,
|
||||||
VULNERABILITY_DIRECTIVES,
|
VULNERABILITY_DIRECTIVES,
|
||||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||||
|
CONFIGURATION_DIRECTIVES,
|
||||||
TranslateModule
|
TranslateModule
|
||||||
],
|
],
|
||||||
providers: []
|
providers: []
|
||||||
@ -176,13 +185,14 @@ export class HarborLibraryModule {
|
|||||||
providers: [
|
providers: [
|
||||||
config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig },
|
config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig },
|
||||||
config.errorHandler || { provide: ErrorHandler, useClass: DefaultErrorHandler },
|
config.errorHandler || { provide: ErrorHandler, useClass: DefaultErrorHandler },
|
||||||
config.systemInfoService || { provide: SystemInfoService,useClass: SystemInfoDefaultService },
|
config.systemInfoService || { provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||||
config.logService || { provide: AccessLogService, useClass: AccessLogDefaultService },
|
config.logService || { provide: AccessLogService, useClass: AccessLogDefaultService },
|
||||||
config.endpointService || { provide: EndpointService, useClass: EndpointDefaultService },
|
config.endpointService || { provide: EndpointService, useClass: EndpointDefaultService },
|
||||||
config.replicationService || { provide: ReplicationService, useClass: ReplicationDefaultService },
|
config.replicationService || { provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||||
config.repositoryService || { provide: RepositoryService, useClass: RepositoryDefaultService },
|
config.repositoryService || { provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||||
config.tagService || { provide: TagService, useClass: TagDefaultService },
|
config.tagService || { provide: TagService, useClass: TagDefaultService },
|
||||||
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
|
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
||||||
//Do initializing
|
//Do initializing
|
||||||
TranslateServiceInitializer,
|
TranslateServiceInitializer,
|
||||||
{
|
{
|
||||||
@ -201,13 +211,14 @@ export class HarborLibraryModule {
|
|||||||
providers: [
|
providers: [
|
||||||
config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig },
|
config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig },
|
||||||
config.errorHandler || { provide: ErrorHandler, useClass: DefaultErrorHandler },
|
config.errorHandler || { provide: ErrorHandler, useClass: DefaultErrorHandler },
|
||||||
config.systemInfoService || { provide: SystemInfoService,useClass: SystemInfoDefaultService },
|
config.systemInfoService || { provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||||
config.logService || { provide: AccessLogService, useClass: AccessLogDefaultService },
|
config.logService || { provide: AccessLogService, useClass: AccessLogDefaultService },
|
||||||
config.endpointService || { provide: EndpointService, useClass: EndpointDefaultService },
|
config.endpointService || { provide: EndpointService, useClass: EndpointDefaultService },
|
||||||
config.replicationService || { provide: ReplicationService, useClass: ReplicationDefaultService },
|
config.replicationService || { provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||||
config.repositoryService || { provide: RepositoryService, useClass: RepositoryDefaultService },
|
config.repositoryService || { provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||||
config.tagService || { provide: TagService, useClass: TagDefaultService },
|
config.tagService || { provide: TagService, useClass: TagDefaultService },
|
||||||
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
|
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,5 @@ export * from './replication/index';
|
|||||||
export * from './vulnerability-scanning/index';
|
export * from './vulnerability-scanning/index';
|
||||||
export * from './i18n/index';
|
export * from './i18n/index';
|
||||||
export * from './push-image/index';
|
export * from './push-image/index';
|
||||||
export * from './third-party/index';
|
export * from './third-party/index';
|
||||||
|
export * from './config/index';
|
@ -172,4 +172,12 @@ export interface IServiceConfig {
|
|||||||
* @memberOf IServiceConfig
|
* @memberOf IServiceConfig
|
||||||
*/
|
*/
|
||||||
localI18nMessageVariableMap?: { [key: string]: any };
|
localI18nMessageVariableMap?: { [key: string]: any };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base endpoint of configuration service.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberOf IServiceConfig
|
||||||
|
*/
|
||||||
|
configurationEndpoint?: string;
|
||||||
}
|
}
|
42
src/ui_ng/lib/src/service/configuration.service.spec.ts
Normal file
42
src/ui_ng/lib/src/service/configuration.service.spec.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConfigurationService, ConfigurationDefaultService } from './configuration.service';
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
|
||||||
|
describe('ConfigurationService', () => {
|
||||||
|
const mockConfig: IServiceConfig = {
|
||||||
|
configurationEndpoint: "/api/configurations/testing"
|
||||||
|
};
|
||||||
|
|
||||||
|
let config: IServiceConfig;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ConfigurationDefaultService,
|
||||||
|
{
|
||||||
|
provide: ConfigurationService,
|
||||||
|
useClass: ConfigurationDefaultService
|
||||||
|
}, {
|
||||||
|
provide: SERVICE_CONFIG,
|
||||||
|
useValue: mockConfig
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
config = TestBed.get(SERVICE_CONFIG);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be initialized', inject([ConfigurationDefaultService], (service: ConfigurationService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should inject the right config', () => {
|
||||||
|
expect(config).toBeTruthy();
|
||||||
|
expect(config.configurationEndpoint).toEqual("/api/configurations/testing");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
69
src/ui_ng/lib/src/service/configuration.service.ts
Normal file
69
src/ui_ng/lib/src/service/configuration.service.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { Injectable, Inject } from "@angular/core";
|
||||||
|
import 'rxjs/add/observable/of';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
import { Http } from '@angular/http';
|
||||||
|
import { HTTP_JSON_OPTIONS } from '../utils';
|
||||||
|
import { Configuration } from '../config/config'
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service used to get and save registry-related configurations.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @abstract
|
||||||
|
* @class ConfigurationService
|
||||||
|
*/
|
||||||
|
export abstract class ConfigurationService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get configurations.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @returns {(Observable<Configuration> | Promise<Configuration> | Configuration)}
|
||||||
|
*
|
||||||
|
* @memberOf ConfigurationService
|
||||||
|
*/
|
||||||
|
abstract getConfigurations(): Observable<Configuration> | Promise<Configuration> | Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save configurations.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @returns {(Observable<Configuration> | Promise<Configuration> | Configuration)}
|
||||||
|
*
|
||||||
|
* @memberOf ConfigurationService
|
||||||
|
*/
|
||||||
|
abstract saveConfigurations(changedConfigs: any | { [key: string]: any | any[] }): Observable<any> | Promise<any> | any;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ConfigurationDefaultService extends ConfigurationService {
|
||||||
|
_baseUrl: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: Http,
|
||||||
|
@Inject(SERVICE_CONFIG) private config: IServiceConfig) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._baseUrl = this.config && this.config.configurationEndpoint ?
|
||||||
|
this.config.configurationEndpoint : "/api/configurations";
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigurations(): Observable<Configuration> | Promise<Configuration> | Configuration {
|
||||||
|
return this.http.get(this._baseUrl, HTTP_JSON_OPTIONS).toPromise()
|
||||||
|
.then(response => response.json() as Configuration)
|
||||||
|
.catch(error => Promise.reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
saveConfigurations(changedConfigs: any | { [key: string]: any | any[] }): Observable<any> | Promise<any> | any {
|
||||||
|
if (!changedConfigs) {
|
||||||
|
return Promise.reject("Bad argument!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.put(this._baseUrl, JSON.stringify(changedConfigs), HTTP_JSON_OPTIONS)
|
||||||
|
.toPromise()
|
||||||
|
.then(() => { })
|
||||||
|
.catch(error => Promise.reject(error));
|
||||||
|
}
|
||||||
|
}
|
@ -6,4 +6,5 @@ export * from './replication.service';
|
|||||||
export * from './repository.service';
|
export * from './repository.service';
|
||||||
export * from './tag.service';
|
export * from './tag.service';
|
||||||
export * from './RequestQueryParams';
|
export * from './RequestQueryParams';
|
||||||
export * from './scanning.service';
|
export * from './scanning.service';
|
||||||
|
export * from './configuration.service';
|
@ -53,6 +53,16 @@ export abstract class ScanningResultService {
|
|||||||
* @memberOf ScanningResultService
|
* @memberOf ScanningResultService
|
||||||
*/
|
*/
|
||||||
abstract startVulnerabilityScanning(repoName: string, tagId: string): Observable<any> | Promise<any> | any;
|
abstract startVulnerabilityScanning(repoName: string, tagId: string): Observable<any> | Promise<any> | any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger the scanning all action.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @returns {(Observable<any> | Promise<any> | any)}
|
||||||
|
*
|
||||||
|
* @memberOf ScanningResultService
|
||||||
|
*/
|
||||||
|
abstract startScanningAll(): Observable<any> | Promise<any> | any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -95,4 +105,10 @@ export class ScanningResultDefaultService extends ScanningResultService {
|
|||||||
.then(() => { return true })
|
.then(() => { return true })
|
||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startScanningAll(): Observable<any> | Promise<any> | any {
|
||||||
|
return this.http.post(`${this._baseUrl}/scanAll`,{}).toPromise()
|
||||||
|
.then(() => {return true})
|
||||||
|
.catch(error => Promise.reject(error));
|
||||||
|
}
|
||||||
}
|
}
|
@ -117,6 +117,10 @@ export class ResultTipComponent implements OnInit {
|
|||||||
return "VULNERABILITY.SINGULAR";
|
return "VULNERABILITY.SINGULAR";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packageText(count: number): string {
|
||||||
|
return count > 1 ? "VULNERABILITY.PACKAGES" : "VULNERABILITY.PACKAGE";
|
||||||
|
}
|
||||||
|
|
||||||
public get completeTimestamp(): Date {
|
public get completeTimestamp(): Date {
|
||||||
return this.summary && this.summary.update_time ? this.summary.update_time : new Date();
|
return this.summary && this.summary.update_time ? this.summary.update_time : new Date();
|
||||||
}
|
}
|
||||||
|
@ -13,23 +13,23 @@ export const TIP_COMPONENT_HTML: string = `
|
|||||||
<div class="bar-summary bar-tooltip-fon">
|
<div class="bar-summary bar-tooltip-fon">
|
||||||
<div *ngIf="hasHigh" class="bar-summary-item">
|
<div *ngIf="hasHigh" class="bar-summary-item">
|
||||||
<clr-icon shape="exclamation-circle" class="is-error" size="24"></clr-icon>
|
<clr-icon shape="exclamation-circle" class="is-error" size="24"></clr-icon>
|
||||||
<span>{{highCount}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }} {{ highSuffix | translate }}</span>
|
<span>{{highCount}} {{packageText(highCount) | translate }} {{'VULNERABILITY.SEVERITY.HIGH' | translate }} {{ highSuffix | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasMedium" class="bar-summary-item">
|
<div *ngIf="hasMedium" class="bar-summary-item">
|
||||||
<clr-icon *ngIf="hasMedium" shape="exclamation-triangle" class="is-warning" size="24"></clr-icon>
|
<clr-icon *ngIf="hasMedium" shape="exclamation-triangle" class="is-warning" size="24"></clr-icon>
|
||||||
<span>{{mediumCount}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }} {{ mediumSuffix | translate }}</span>
|
<span>{{mediumCount}} {{packageText(mediumCount) | translate }} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }} {{ mediumSuffix | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasLow" class="bar-summary-item">
|
<div *ngIf="hasLow" class="bar-summary-item">
|
||||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||||
<span>{{lowCount}} {{'VULNERABILITY.SEVERITY.LOW' | translate }} {{ lowSuffix | translate }}</span>
|
<span>{{lowCount}} {{packageText(lowCount) | translate }} {{'VULNERABILITY.SEVERITY.LOW' | translate }} {{ lowSuffix | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasUnknown" class="bar-summary-item">
|
<div *ngIf="hasUnknown" class="bar-summary-item">
|
||||||
<clr-icon shape="help" size="24"></clr-icon>
|
<clr-icon shape="help" size="24"></clr-icon>
|
||||||
<span>{{unknownCount}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }} {{ unknownSuffix | translate }}</span>
|
<span>{{unknownCount}} {{packageText(unknownCount) | translate }} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }} {{ unknownSuffix | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasNone" class="bar-summary-item">
|
<div *ngIf="hasNone" class="bar-summary-item">
|
||||||
<clr-icon shape="check-circle" class="is-success" size="24"></clr-icon>
|
<clr-icon shape="check-circle" class="is-success" size="24"></clr-icon>
|
||||||
<span>{{noneCount}} {{'VULNERABILITY.SEVERITY.NONE' | translate }} {{ noneSuffix | translate }}</span>
|
<span>{{noneCount}} {{packageText(noneCount) | translate }} {{'VULNERABILITY.SEVERITY.NONE' | translate }} {{ noneSuffix | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"clarity-icons": "^0.9.8",
|
"clarity-icons": "^0.9.8",
|
||||||
"clarity-ui": "^0.9.8",
|
"clarity-ui": "^0.9.8",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"harbor-ui": "^0.2.25",
|
"harbor-ui": "^0.2.40",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
@ -15,7 +15,7 @@ import { Component, Input, ViewChild } from '@angular/core';
|
|||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
import { Configuration } from '../config';
|
import { Configuration } from 'harbor-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'config-auth',
|
selector: 'config-auth',
|
||||||
|
@ -15,50 +15,24 @@
|
|||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<button id="config-system" class="btn btn-link nav-link" aria-controls="system_settings" [class.active]='isCurrentTabLink("config-system")' type="button" (click)='tabLinkClick("config-system")'>{{'CONFIG.SYSTEM' | translate }}</button>
|
<button id="config-system" class="btn btn-link nav-link" aria-controls="system_settings" [class.active]='isCurrentTabLink("config-system")' type="button" (click)='tabLinkClick("config-system")'>{{'CONFIG.SYSTEM' | translate }}</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li role="presentation" class="nav-item">
|
||||||
|
<button id="config-vulnerability" class="btn btn-link nav-link" aria-controls="vulnerability" [class.active]='isCurrentTabLink("config-vulnerability")' type="button" (click)='tabLinkClick("config-vulnerability")'>Vulnerability</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'>
|
<section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'>
|
||||||
<config-auth [ldapConfig]="allConfig"></config-auth>
|
<config-auth [ldapConfig]="allConfig"></config-auth>
|
||||||
</section>
|
</section>
|
||||||
<section id="replication" role="tabpanel" aria-labelledby="config-replication" [hidden]='!isCurrentTabContent("replication")'>
|
<section id="replication" role="tabpanel" aria-labelledby="config-replication" [hidden]='!isCurrentTabContent("replication")'>
|
||||||
<form #repoConfigFrom="ngForm" class="form">
|
<replication-config [(replicationConfig)]="allConfig"></replication-config>
|
||||||
<section class="form-block">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="verifyRemoteCert">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</label>
|
|
||||||
<clr-checkbox name="verifyRemoteCert" id="verifyRemoteCert" [(ngModel)]="allConfig.verify_remote_cert.value" [disabled]="disabled(allConfig.verify_remote_cert)">
|
|
||||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-right" style="top:-8px;">
|
|
||||||
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
|
||||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate }}</span>
|
|
||||||
</a>
|
|
||||||
</clr-checkbox>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</form>
|
|
||||||
</section>
|
</section>
|
||||||
<section id="email" role="tabpanel" aria-labelledby="config-email" [hidden]='!isCurrentTabContent("email")'>
|
<section id="email" role="tabpanel" aria-labelledby="config-email" [hidden]='!isCurrentTabContent("email")'>
|
||||||
<config-email [mailConfig]="allConfig"></config-email>
|
<config-email [mailConfig]="allConfig"></config-email>
|
||||||
</section>
|
</section>
|
||||||
<section id="system_settings" role="tabpanel" aria-labelledby="config-system" [hidden]='!isCurrentTabContent("system_settings")'>
|
<section id="system_settings" role="tabpanel" aria-labelledby="config-system" [hidden]='!isCurrentTabContent("system_settings")'>
|
||||||
<form #systemConfigFrom="ngForm" class="form">
|
<system-settings [(systemSettings)]="allConfig"></system-settings>
|
||||||
<section class="form-block">
|
</section>
|
||||||
<div class="form-group">
|
<section id="vulnerability" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
|
||||||
<label for="tokenExpiration" class="required">{{'CONFIG.TOKEN_EXPIRATION' | translate}}</label>
|
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
|
||||||
<label for="tokenExpiration" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="tokenExpirationInput.invalid && (tokenExpirationInput.dirty || tokenExpirationInput.touched)">
|
|
||||||
<input name="tokenExpiration" type="text" #tokenExpirationInput="ngModel" [(ngModel)]="allConfig.token_expiration.value"
|
|
||||||
required
|
|
||||||
pattern="^[1-9]{1}[\d]*$"
|
|
||||||
id="tokenExpiration"
|
|
||||||
size="40" [disabled]="disabled(allConfig.token_expiration)">
|
|
||||||
<span class="tooltip-content">
|
|
||||||
{{'TOOLTIP.NUMBER_REQUIRED' | translate}}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
|
||||||
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
|
||||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</form>
|
|
||||||
</section>
|
</section>
|
||||||
<div>
|
<div>
|
||||||
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>
|
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>
|
||||||
@ -67,6 +41,7 @@
|
|||||||
<button type="button" class="btn btn-outline" (click)="testLDAPServer()" *ngIf="showLdapServerBtn" [disabled]="!isLDAPConfigValid()">{{'BUTTON.TEST_LDAP' | translate}}</button>
|
<button type="button" class="btn btn-outline" (click)="testLDAPServer()" *ngIf="showLdapServerBtn" [disabled]="!isLDAPConfigValid()">{{'BUTTON.TEST_LDAP' | translate}}</button>
|
||||||
<span id="forTestingMail" class="spinner spinner-inline" [hidden]="hideMailTestingSpinner"></span>
|
<span id="forTestingMail" class="spinner spinner-inline" [hidden]="hideMailTestingSpinner"></span>
|
||||||
<span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideLDAPTestingSpinner"></span>
|
<span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideLDAPTestingSpinner"></span>
|
||||||
|
<button type="button" class="btn btn-primary" (click)="consoleTest()">CONSOLE</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -13,12 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { NgForm } from '@angular/forms';
|
|
||||||
|
|
||||||
import { ConfigurationService } from './config.service';
|
import { ConfigurationService } from './config.service';
|
||||||
import { Configuration } from './config';
|
|
||||||
import { ConfirmationTargets, ConfirmationState } from '../shared/shared.const';;
|
import { ConfirmationTargets, ConfirmationState } from '../shared/shared.const';;
|
||||||
import { StringValueItem } from './config';
|
|
||||||
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message'
|
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message'
|
||||||
@ -29,13 +26,22 @@ import { ConfigurationEmailComponent } from './email/config-email.component';
|
|||||||
import { AppConfigService } from '../app-config.service';
|
import { AppConfigService } from '../app-config.service';
|
||||||
import { SessionService } from '../shared/session.service';
|
import { SessionService } from '../shared/session.service';
|
||||||
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
||||||
|
import {
|
||||||
|
Configuration,
|
||||||
|
StringValueItem,
|
||||||
|
ComplexValueItem,
|
||||||
|
ReplicationConfigComponent,
|
||||||
|
SystemSettingsComponent,
|
||||||
|
VulnerabilityConfigComponent
|
||||||
|
} from 'harbor-ui';
|
||||||
|
|
||||||
const fakePass = "aWpLOSYkIzJTTU4wMDkx";
|
const fakePass = "aWpLOSYkIzJTTU4wMDkx";
|
||||||
const TabLinkContentMap = {
|
const TabLinkContentMap = {
|
||||||
"config-auth": "authentication",
|
"config-auth": "authentication",
|
||||||
"config-replication": "replication",
|
"config-replication": "replication",
|
||||||
"config-email": "email",
|
"config-email": "email",
|
||||||
"config-system": "system_settings"
|
"config-system": "system_settings",
|
||||||
|
"config-vulnerability": "vulnerability"
|
||||||
};
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -52,8 +58,9 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
testingMailOnGoing: boolean = false;
|
testingMailOnGoing: boolean = false;
|
||||||
testingLDAPOnGoing: boolean = false;
|
testingLDAPOnGoing: boolean = false;
|
||||||
|
|
||||||
@ViewChild("repoConfigFrom") repoConfigForm: NgForm;
|
@ViewChild(ReplicationConfigComponent) replicationConfig: ReplicationConfigComponent;
|
||||||
@ViewChild("systemConfigFrom") systemConfigForm: NgForm;
|
@ViewChild(SystemSettingsComponent) systemSettingsConfig: SystemSettingsComponent;
|
||||||
|
@ViewChild(VulnerabilityConfigComponent) vulnerabilityConfig: VulnerabilityConfigComponent;
|
||||||
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
|
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
|
||||||
@ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent;
|
@ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent;
|
||||||
|
|
||||||
@ -64,6 +71,11 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private session: SessionService) { }
|
private session: SessionService) { }
|
||||||
|
|
||||||
|
consoleTest(): void {
|
||||||
|
console.log(this.allConfig, this.originalCopy);
|
||||||
|
console.log("-------------");
|
||||||
|
console.log(this.getChanges());
|
||||||
|
}
|
||||||
isCurrentTabLink(tabId: string): boolean {
|
isCurrentTabLink(tabId: string): boolean {
|
||||||
return this.currentTabId === tabId;
|
return this.currentTabId === tabId;
|
||||||
}
|
}
|
||||||
@ -101,6 +113,9 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
case "config-system":
|
case "config-system":
|
||||||
properties = ["token_expiration"];
|
properties = ["token_expiration"];
|
||||||
break;
|
break;
|
||||||
|
case "config-vulnerability":
|
||||||
|
properties = ["scan_all_policy"];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -146,10 +161,12 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
public isValid(): boolean {
|
||||||
return this.repoConfigForm &&
|
return this.replicationConfig &&
|
||||||
this.repoConfigForm.valid &&
|
this.replicationConfig.isValid &&
|
||||||
this.systemConfigForm &&
|
this.systemSettingsConfig &&
|
||||||
this.systemConfigForm.valid &&
|
this.systemSettingsConfig.isValid &&
|
||||||
|
this.vulnerabilityConfig &&
|
||||||
|
this.vulnerabilityConfig.isValid &&
|
||||||
this.mailConfig &&
|
this.mailConfig &&
|
||||||
this.mailConfig.isValid() &&
|
this.mailConfig.isValid() &&
|
||||||
this.authConfig &&
|
this.authConfig &&
|
||||||
@ -191,7 +208,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public tabLinkClick(tabLink: string) {
|
public tabLinkClick(tabLink: string) {
|
||||||
//Whether has unsave changes in current tab
|
//Whether has unsaved changes in current tab
|
||||||
let changes = this.hasUnsavedChangesOfCurrentTab();
|
let changes = this.hasUnsavedChangesOfCurrentTab();
|
||||||
if (!changes) {
|
if (!changes) {
|
||||||
this.currentTabId = tabLink;
|
this.currentTabId = tabLink;
|
||||||
@ -210,6 +227,14 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
public save(): void {
|
public save(): void {
|
||||||
let changes = this.getChanges();
|
let changes = this.getChanges();
|
||||||
if (!this.isEmpty(changes)) {
|
if (!this.isEmpty(changes)) {
|
||||||
|
//Fix policy parameters issue
|
||||||
|
let scanningAllPolicy = changes["scan_all_policy"];
|
||||||
|
if (scanningAllPolicy &&
|
||||||
|
scanningAllPolicy.type !== "daily" &&
|
||||||
|
scanningAllPolicy.parameters) {
|
||||||
|
delete (scanningAllPolicy.parameters);
|
||||||
|
}
|
||||||
|
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.configService.saveConfiguration(changes)
|
this.configService.saveConfiguration(changes)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
@ -247,7 +272,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
if (!this.isEmpty(changes)) {
|
if (!this.isEmpty(changes)) {
|
||||||
this.confirmUnsavedChanges(changes);
|
this.confirmUnsavedChanges(changes);
|
||||||
} else {
|
} else {
|
||||||
//Inprop situation, should not come here
|
//Invalid situation, should not come here
|
||||||
console.error("Nothing changed");
|
console.error("Nothing changed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,7 +285,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
* @memberOf ConfigurationComponent
|
* @memberOf ConfigurationComponent
|
||||||
*/
|
*/
|
||||||
public testMailServer(): void {
|
public testMailServer(): void {
|
||||||
if(this.testingMailOnGoing){
|
if (this.testingMailOnGoing) {
|
||||||
return;//Should not come here
|
return;//Should not come here
|
||||||
}
|
}
|
||||||
let mailSettings = {};
|
let mailSettings = {};
|
||||||
@ -296,7 +321,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public testLDAPServer(): void {
|
public testLDAPServer(): void {
|
||||||
if(this.testingLDAPOnGoing){
|
if (this.testingLDAPOnGoing) {
|
||||||
return;//Should not come here
|
return;//Should not come here
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,14 +389,14 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
retrieveConfig(): void {
|
retrieveConfig(): void {
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.configService.getConfiguration()
|
this.configService.getConfiguration()
|
||||||
.then(configurations => {
|
.then((configurations: Configuration) => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
|
|
||||||
//Add two password fields
|
//Add two password fields
|
||||||
configurations.email_password = new StringValueItem(fakePass, true);
|
configurations.email_password = new StringValueItem(fakePass, true);
|
||||||
configurations.ldap_search_password = new StringValueItem(fakePass, true);
|
configurations.ldap_search_password = new StringValueItem(fakePass, true);
|
||||||
this.allConfig = configurations;
|
|
||||||
|
|
||||||
|
this.allConfig = configurations;
|
||||||
//Keep the original copy of the data
|
//Keep the original copy of the data
|
||||||
this.originalCopy = this.clone(configurations);
|
this.originalCopy = this.clone(configurations);
|
||||||
})
|
})
|
||||||
@ -390,8 +415,8 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
*
|
*
|
||||||
* @memberOf ConfigurationComponent
|
* @memberOf ConfigurationComponent
|
||||||
*/
|
*/
|
||||||
getChanges(): any {
|
getChanges(): { [key: string]: any | any[] } {
|
||||||
let changes = {};
|
let changes: { [key: string]: any | any[] } = {};
|
||||||
if (!this.allConfig || !this.originalCopy) {
|
if (!this.allConfig || !this.originalCopy) {
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
@ -399,16 +424,16 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
for (let prop in this.allConfig) {
|
for (let prop in this.allConfig) {
|
||||||
let field = this.originalCopy[prop];
|
let field = this.originalCopy[prop];
|
||||||
if (field && field.editable) {
|
if (field && field.editable) {
|
||||||
if (field.value != this.allConfig[prop].value) {
|
if (!this.compareValue(field.value, this.allConfig[prop].value)) {
|
||||||
changes[prop] = this.allConfig[prop].value;
|
changes[prop] = this.allConfig[prop].value;
|
||||||
//Fix boolean issue
|
//Number
|
||||||
if (typeof field.value === "boolean") {
|
if (typeof field.value === "number") {
|
||||||
changes[prop] = changes[prop] ? "1" : "0";
|
changes[prop] = +changes[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Trim string value
|
//Trim string value
|
||||||
if(typeof field.value === "string") {
|
if (typeof field.value === "string") {
|
||||||
changes[prop] = (''+changes[prop]).trim();
|
changes[prop] = ('' + changes[prop]).trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -417,6 +442,19 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private
|
||||||
|
compareValue(a: any, b: any): boolean {
|
||||||
|
if ((a && !b) || (!a && b)) return false;
|
||||||
|
if (!a && !b) return true;
|
||||||
|
|
||||||
|
return JSON.stringify(a) === JSON.stringify(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//private
|
||||||
|
isEmpty(obj: any): boolean {
|
||||||
|
return !obj || JSON.stringify(obj) === "{}";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Deep clone the configuration object
|
* Deep clone the configuration object
|
||||||
@ -428,18 +466,11 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
* @memberOf ConfigurationComponent
|
* @memberOf ConfigurationComponent
|
||||||
*/
|
*/
|
||||||
clone(src: Configuration): Configuration {
|
clone(src: Configuration): Configuration {
|
||||||
let dest = new Configuration();
|
|
||||||
if (!src) {
|
if (!src) {
|
||||||
return dest;//Empty
|
return new Configuration();//Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let prop in src) {
|
return JSON.parse(JSON.stringify(src));
|
||||||
if (src[prop]) {
|
|
||||||
dest[prop] = Object.assign({}, src[prop]); //Deep copy inner object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -464,14 +495,6 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmpty(obj: any) {
|
|
||||||
for (let key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
disabled(prop: any): boolean {
|
disabled(prop: any): boolean {
|
||||||
return !(prop && prop.editable);
|
return !(prop && prop.editable);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Headers, Http, RequestOptions } from '@angular/http';
|
import { Headers, Http, RequestOptions } from '@angular/http';
|
||||||
import 'rxjs/add/operator/toPromise';
|
import 'rxjs/add/operator/toPromise';
|
||||||
|
|
||||||
import { Configuration } from './config';
|
import { Configuration } from 'harbor-ui';
|
||||||
|
|
||||||
const configEndpoint = "/api/configurations";
|
const configEndpoint = "/api/configurations";
|
||||||
const emailEndpoint = "/api/email/ping";
|
const emailEndpoint = "/api/email/ping";
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
import { Component, Input, ViewChild } from '@angular/core';
|
import { Component, Input, ViewChild } from '@angular/core';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
import { Configuration } from '../config';
|
import { Configuration } from 'harbor-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'config-email',
|
selector: 'config-email',
|
||||||
|
@ -403,6 +403,15 @@
|
|||||||
"UID": "LDAP UID",
|
"UID": "LDAP UID",
|
||||||
"SCOPE": "LDAP Scope"
|
"SCOPE": "LDAP Scope"
|
||||||
},
|
},
|
||||||
|
"SCANNING": {
|
||||||
|
"TRIGGER_SCAN_ALL_SUCCESS": "Trigger scan all successfully!",
|
||||||
|
"TITLE": "Vulnerability Scanning",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SCAN_NOW": "SCAN NOW",
|
||||||
|
"NONE_POLICY": "None",
|
||||||
|
"DAILY_POLICY": "Daily At",
|
||||||
|
"REFRESH_POLICY": "Upon Refresh"
|
||||||
|
},
|
||||||
"TEST_MAIL_SUCCESS": "Connection to mail server is verified.",
|
"TEST_MAIL_SUCCESS": "Connection to mail server is verified.",
|
||||||
"TEST_LDAP_SUCCESS": "Connection to LDAP server is verified.",
|
"TEST_LDAP_SUCCESS": "Connection to LDAP server is verified.",
|
||||||
"TEST_MAIL_FAILED": "Failed to verify mail server with error: {{param}}.",
|
"TEST_MAIL_FAILED": "Failed to verify mail server with error: {{param}}.",
|
||||||
|
@ -404,6 +404,15 @@
|
|||||||
"UID": "LDAP UID",
|
"UID": "LDAP UID",
|
||||||
"SCOPE": "LDAP Ámbito"
|
"SCOPE": "LDAP Ámbito"
|
||||||
},
|
},
|
||||||
|
"SCANNING": {
|
||||||
|
"TRIGGER_SCAN_ALL_SUCCESS": "Trigger scan all successfully!",
|
||||||
|
"TITLE": "Vulnerability Scanning",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SCAN_NOW": "SCAN NOW",
|
||||||
|
"NONE_POLICY": "None",
|
||||||
|
"DAILY_POLICY": "Daily At",
|
||||||
|
"REFRESH_POLICY": "Upon Refresh"
|
||||||
|
},
|
||||||
"TEST_MAIL_SUCCESS": "La conexión al servidor de correo ha sido verificada.",
|
"TEST_MAIL_SUCCESS": "La conexión al servidor de correo ha sido verificada.",
|
||||||
"TEST_LDAP_SUCCESS": "La conexión al servidor LDAP ha sido verificada.",
|
"TEST_LDAP_SUCCESS": "La conexión al servidor LDAP ha sido verificada.",
|
||||||
"TEST_MAIL_FAILED": "Fallo al verificar el servidor de correo con el error: {{param}}.",
|
"TEST_MAIL_FAILED": "Fallo al verificar el servidor de correo con el error: {{param}}.",
|
||||||
|
@ -403,6 +403,15 @@
|
|||||||
"UID": "LDAP用户UID的属性",
|
"UID": "LDAP用户UID的属性",
|
||||||
"SCOPE": "LDAP搜索范围"
|
"SCOPE": "LDAP搜索范围"
|
||||||
},
|
},
|
||||||
|
"SCANNING": {
|
||||||
|
"TRIGGER_SCAN_ALL_SUCCESS": "成功启动扫描所有镜像任务!",
|
||||||
|
"TITLE": "缺陷扫描",
|
||||||
|
"SCAN_ALL": "扫描所有",
|
||||||
|
"SCAN_NOW": "开始扫描",
|
||||||
|
"NONE_POLICY": "无",
|
||||||
|
"DAILY_POLICY": "每日",
|
||||||
|
"REFRESH_POLICY": "缺陷库刷新后"
|
||||||
|
},
|
||||||
"TEST_MAIL_SUCCESS": "邮件服务器的连通正常。",
|
"TEST_MAIL_SUCCESS": "邮件服务器的连通正常。",
|
||||||
"TEST_LDAP_SUCCESS": "LDAP服务器的连通正常。",
|
"TEST_LDAP_SUCCESS": "LDAP服务器的连通正常。",
|
||||||
"TEST_MAIL_FAILED": "验证邮件服务器失败,错误: {{param}}。",
|
"TEST_MAIL_FAILED": "验证邮件服务器失败,错误: {{param}}。",
|
||||||
|
Loading…
Reference in New Issue
Block a user