mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-09 09:27:42 +01:00
add ui for project level policy
This commit is contained in:
parent
367f447991
commit
69ffd7117c
@ -47,6 +47,7 @@ func initRouters() {
|
|||||||
beego.Router("/harbor/projects/:id/replications", &controllers.IndexController{})
|
beego.Router("/harbor/projects/:id/replications", &controllers.IndexController{})
|
||||||
beego.Router("/harbor/projects/:id/members", &controllers.IndexController{})
|
beego.Router("/harbor/projects/:id/members", &controllers.IndexController{})
|
||||||
beego.Router("/harbor/projects/:id/logs", &controllers.IndexController{})
|
beego.Router("/harbor/projects/:id/logs", &controllers.IndexController{})
|
||||||
|
beego.Router("/harbor/projects/:id/configs", &controllers.IndexController{})
|
||||||
beego.Router("/harbor/tags/:id/*", &controllers.IndexController{})
|
beego.Router("/harbor/tags/:id/*", &controllers.IndexController{})
|
||||||
|
|
||||||
beego.Router("/harbor/users", &controllers.IndexController{})
|
beego.Router("/harbor/users", &controllers.IndexController{})
|
||||||
|
@ -24,6 +24,7 @@ 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 { CONFIGURATION_DIRECTIVES } from './config/index';
|
||||||
import { JOB_LOG_VIEWER_DIRECTIVES } from './job-log-viewer/index';
|
import { JOB_LOG_VIEWER_DIRECTIVES } from './job-log-viewer/index';
|
||||||
|
import { PROJECT_POLICY_CONFIG_DIRECTIVES } from './project-policy-config/index';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SystemInfoService,
|
SystemInfoService,
|
||||||
@ -43,7 +44,9 @@ import {
|
|||||||
ConfigurationService,
|
ConfigurationService,
|
||||||
ConfigurationDefaultService,
|
ConfigurationDefaultService,
|
||||||
JobLogService,
|
JobLogService,
|
||||||
JobLogDefaultService
|
JobLogDefaultService,
|
||||||
|
ProjectService,
|
||||||
|
ProjectDefaultService,
|
||||||
} from './service/index';
|
} from './service/index';
|
||||||
import {
|
import {
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
@ -68,6 +71,7 @@ export const DefaultServiceConfig: IServiceConfig = {
|
|||||||
replicationRuleEndpoint: "/api/policies/replication",
|
replicationRuleEndpoint: "/api/policies/replication",
|
||||||
replicationJobEndpoint: "/api/jobs/replication",
|
replicationJobEndpoint: "/api/jobs/replication",
|
||||||
vulnerabilityScanningBaseEndpoint: "/api/repositories",
|
vulnerabilityScanningBaseEndpoint: "/api/repositories",
|
||||||
|
projectPolicyEndpoint: "/api/projects/configs",
|
||||||
enablei18Support: false,
|
enablei18Support: false,
|
||||||
defaultLang: DEFAULT_LANG,
|
defaultLang: DEFAULT_LANG,
|
||||||
langCookieKey: DEFAULT_LANG_COOKIE_KEY,
|
langCookieKey: DEFAULT_LANG_COOKIE_KEY,
|
||||||
@ -118,7 +122,10 @@ export interface HarborModuleConfig {
|
|||||||
configService?: Provider,
|
configService?: Provider,
|
||||||
|
|
||||||
//Service implementation for job log
|
//Service implementation for job log
|
||||||
jobLogService?: Provider
|
jobLogService?: Provider,
|
||||||
|
|
||||||
|
//Service implementation for project policy
|
||||||
|
projectPolicyService?: Provider,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +169,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
|||||||
VULNERABILITY_DIRECTIVES,
|
VULNERABILITY_DIRECTIVES,
|
||||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||||
CONFIGURATION_DIRECTIVES,
|
CONFIGURATION_DIRECTIVES,
|
||||||
JOB_LOG_VIEWER_DIRECTIVES
|
JOB_LOG_VIEWER_DIRECTIVES,
|
||||||
|
PROJECT_POLICY_CONFIG_DIRECTIVES
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
LOG_DIRECTIVES,
|
LOG_DIRECTIVES,
|
||||||
@ -183,7 +191,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
|||||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||||
CONFIGURATION_DIRECTIVES,
|
CONFIGURATION_DIRECTIVES,
|
||||||
JOB_LOG_VIEWER_DIRECTIVES,
|
JOB_LOG_VIEWER_DIRECTIVES,
|
||||||
TranslateModule
|
TranslateModule,
|
||||||
|
PROJECT_POLICY_CONFIG_DIRECTIVES
|
||||||
],
|
],
|
||||||
providers: []
|
providers: []
|
||||||
})
|
})
|
||||||
@ -204,6 +213,7 @@ export class HarborLibraryModule {
|
|||||||
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
||||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||||
|
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
//Do initializing
|
//Do initializing
|
||||||
TranslateServiceInitializer,
|
TranslateServiceInitializer,
|
||||||
{
|
{
|
||||||
@ -232,6 +242,7 @@ export class HarborLibraryModule {
|
|||||||
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
||||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||||
|
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
ChannelService
|
ChannelService
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -17,4 +17,5 @@ export * from './push-image/index';
|
|||||||
export * from './third-party/index';
|
export * from './third-party/index';
|
||||||
export * from './config/index';
|
export * from './config/index';
|
||||||
export * from './job-log-viewer/index';
|
export * from './job-log-viewer/index';
|
||||||
export * from './channel/index';
|
export * from './channel/index';
|
||||||
|
export * from './project-policy-config/index';
|
||||||
|
8
src/ui_ng/lib/src/project-policy-config/index.ts
Normal file
8
src/ui_ng/lib/src/project-policy-config/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
import { ProjectPolicyConfigComponent } from './project-policy-config.component';
|
||||||
|
|
||||||
|
export * from './project-policy-config.component'
|
||||||
|
|
||||||
|
export const PROJECT_POLICY_CONFIG_DIRECTIVES: Type<any>[] = [
|
||||||
|
ProjectPolicyConfigComponent
|
||||||
|
];
|
@ -0,0 +1,9 @@
|
|||||||
|
export const PROJECT_POLICY_CONFIG_STYLE = `#severity-blk div
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
`;
|
@ -0,0 +1,51 @@
|
|||||||
|
export const PROJECT_POLICY_CONFIG_TEMPLATE = `
|
||||||
|
<form #projectPolicyForm="ngForm">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.REGISTRY' | translate }}</label>
|
||||||
|
<div class="form-content">
|
||||||
|
<clr-checkbox [(ngModel)]="projectPolicy.Public" name="public"
|
||||||
|
[clrDisabled]="!hasProjectAdminRole">{{ 'PROJECT_CONFIG.PUBLIC_TOGGLE' | translate }}</clr-checkbox>
|
||||||
|
<div>
|
||||||
|
<label> {{ 'PROJECT_CONFIG.PUBLIC_POLICY' | translate }} </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SECURITY' | translate }}</label>
|
||||||
|
<div class="form-content">
|
||||||
|
<div>
|
||||||
|
<clr-checkbox [(ngModel)]="projectPolicy.ContentTrust" name="content-trust"
|
||||||
|
[clrDisabled]="!hasProjectAdminRole">{{ 'PROJECT_CONFIG.CONTENT_TRUST_TOGGLE' | translate }}</clr-checkbox>
|
||||||
|
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.CONTENT_TRUST_POLCIY' | translate }} </label></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<clr-checkbox [(ngModel)]="projectPolicy.PreventVulImg" name="prevent-vulenrability-image" [clrDisabled]="!hasProjectAdminRole">{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_TOGGLE' | translate }}</clr-checkbox>
|
||||||
|
<div class="chk-explain">
|
||||||
|
<label>
|
||||||
|
<div id="severity-blk">
|
||||||
|
<div>{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_1' | translate }}</div>
|
||||||
|
<div class="select">
|
||||||
|
<select id="severity" name="severity" [(ngModel)]="projectPolicy.PreventVulImgServerity" [disabled]="!projectPolicy.PreventVulImg">
|
||||||
|
<option *ngFor='let s of severityOptions' [ngValue]="s.severity">{{ s.severityLevel | translate | uppercase }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div> {{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_2' | translate }} </div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SCAN' | translate }}</label>
|
||||||
|
<div class="form-content">
|
||||||
|
<clr-checkbox [(ngModel)]="projectPolicy.ScanImgOnPush" name="scan-image-on-push" [clrDisabled]="!hasProjectAdminRole">{{ 'PROJECT_CONFIG.AUTOSCAN_TOGGLE' | translate }}</clr-checkbox>
|
||||||
|
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.AUTOSCAN_POLICY' | translate }}</label></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges() || !hasProjectAdminRole">{{'BUTTON.SAVE' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges() || !hasProjectAdminRole">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
|
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
||||||
|
</section>
|
||||||
|
</form>`;
|
@ -0,0 +1,45 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { ProjectPolicyConfigComponent } from './project-policy-config.component';
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { ProjectService, ProjectDefaultService} from '../service/project.service';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig} from '../service.config';
|
||||||
|
|
||||||
|
describe('ProjectPolicyConfigComponent', () => {
|
||||||
|
let component: ProjectPolicyConfigComponent;
|
||||||
|
let fixture: ComponentFixture<ProjectPolicyConfigComponent>;
|
||||||
|
|
||||||
|
let config: IServiceConfig = {
|
||||||
|
projectPolicyEndpoint: '/api/projects/testing/:id/'
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [SharedModule],
|
||||||
|
declarations: [
|
||||||
|
ProjectPolicyConfigComponent,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ErrorHandler,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
{ provide: ProjectService, useClass: ProjectDefaultService }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProjectPolicyConfigComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.projectId = 1;
|
||||||
|
component.hasProjectAdminRole = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,155 @@
|
|||||||
|
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
|
import { PROJECT_POLICY_CONFIG_TEMPLATE } from './project-policy-config.component.html';
|
||||||
|
import { PROJECT_POLICY_CONFIG_STYLE } from './project-policy-config.component.css';
|
||||||
|
import { toPromise, compareValue, clone } from '../utils';
|
||||||
|
import { ProjectService } from '../service/project.service';
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
|
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||||
|
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { Project } from './project';
|
||||||
|
|
||||||
|
export class ProjectPolicy {
|
||||||
|
Public: boolean;
|
||||||
|
ContentTrust: boolean;
|
||||||
|
PreventVulImg: boolean;
|
||||||
|
PreventVulImgServerity: string;
|
||||||
|
ScanImgOnPush: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.Public = false;
|
||||||
|
this.ContentTrust = false;
|
||||||
|
this.PreventVulImg = false;
|
||||||
|
this.PreventVulImgServerity = 'low';
|
||||||
|
this.ScanImgOnPush = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initByProject(pro: Project) {
|
||||||
|
this.Public = pro.metadata.public === 'true' ? true : false;
|
||||||
|
this.ContentTrust = pro.metadata.enable_content_trust === 'true' ? true : false;
|
||||||
|
this.PreventVulImg = pro.metadata.prevent_vul === 'true' ? true : false;
|
||||||
|
if (pro.metadata.severity) { this.PreventVulImgServerity = pro.metadata.severity; };
|
||||||
|
this.ScanImgOnPush = pro.metadata.auto_scan === 'true' ? true : false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'hbr-project-policy-config',
|
||||||
|
template: PROJECT_POLICY_CONFIG_TEMPLATE,
|
||||||
|
styles: [PROJECT_POLICY_CONFIG_STYLE]
|
||||||
|
})
|
||||||
|
export class ProjectPolicyConfigComponent implements OnInit {
|
||||||
|
onGoing = false;
|
||||||
|
@Input() projectId: number;
|
||||||
|
@Input() projectName = 'unknown';
|
||||||
|
|
||||||
|
@Input() hasSignedIn: boolean;
|
||||||
|
@Input() hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
|
@ViewChild('cfgConfirmationDialog') confirmationDlg: ConfirmationDialogComponent;
|
||||||
|
|
||||||
|
orgProjectPolicy = new ProjectPolicy();
|
||||||
|
projectPolicy = new ProjectPolicy();
|
||||||
|
|
||||||
|
severityOptions = [
|
||||||
|
{severity: 'high', severityLevel: 'VULNERABILITY.SEVERITY.HIGH'},
|
||||||
|
{severity: 'medium', severityLevel: 'VULNERABILITY.SEVERITY.MEDIUM'},
|
||||||
|
{severity: 'low', severityLevel: 'VULNERABILITY.SEVERITY.LOW'},
|
||||||
|
{severity: 'negligible', severityLevel: 'VULNERABILITY.SEVERITY.NEGLIGIBLE'},
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private translate: TranslateService,
|
||||||
|
private projectService: ProjectService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (!this.projectId) {
|
||||||
|
this.errorHandler.error('Project ID cannot be unset.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieve(state?: State): any {
|
||||||
|
toPromise<Project>(this.projectService.
|
||||||
|
getProject(this.projectId))
|
||||||
|
.then(
|
||||||
|
response => {
|
||||||
|
this.orgProjectPolicy.initByProject(response);
|
||||||
|
this.projectPolicy.initByProject(response);
|
||||||
|
},
|
||||||
|
error => this.errorHandler.error(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProjectPolicy(projectId: string|number, pp: ProjectPolicy) {
|
||||||
|
this.projectService.updateProjectPolicy(projectId, pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
let flag = false;
|
||||||
|
if (!this.projectPolicy.PreventVulImg || this.severityOptions.some(x => x.severity === this.projectPolicy.PreventVulImgServerity)) {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChanges() {
|
||||||
|
return compareValue(this.orgProjectPolicy, this.projectPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
if (!this.hasChanges()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onGoing = true;
|
||||||
|
toPromise<any>(this.projectService.updateProjectPolicy(this.projectId, this.projectPolicy))
|
||||||
|
.then(() => {
|
||||||
|
this.onGoing = false;
|
||||||
|
|
||||||
|
this.translate.get('CONFIG.SAVE_SUCCESS').subscribe((res: string) => {
|
||||||
|
this.errorHandler.info(res);
|
||||||
|
});
|
||||||
|
this.refresh();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
this.onGoing = false;
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(): void {
|
||||||
|
let msg = new ConfirmationMessage(
|
||||||
|
'CONFIG.CONFIRM_TITLE',
|
||||||
|
'CONFIG.CONFIRM_SUMMARY',
|
||||||
|
'',
|
||||||
|
{},
|
||||||
|
ConfirmationTargets.CONFIG
|
||||||
|
);
|
||||||
|
this.confirmationDlg.open(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.projectPolicy = clone(this.orgProjectPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmCancel(ack: ConfirmationAcknowledgement): void {
|
||||||
|
if (ack && ack.source === ConfirmationTargets.CONFIG &&
|
||||||
|
ack.state === ConfirmationState.CONFIRMED) {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/ui_ng/lib/src/project-policy-config/project.ts
Normal file
30
src/ui_ng/lib/src/project-policy-config/project.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export class Project {
|
||||||
|
project_id: number;
|
||||||
|
owner_id: number;
|
||||||
|
name: string;
|
||||||
|
creation_time: Date;
|
||||||
|
creation_time_str: string;
|
||||||
|
deleted: number;
|
||||||
|
owner_name: string;
|
||||||
|
togglable: boolean;
|
||||||
|
update_time: Date;
|
||||||
|
current_user_role_id: number;
|
||||||
|
repo_count: number;
|
||||||
|
has_project_admin_role: boolean;
|
||||||
|
is_member: boolean;
|
||||||
|
role_name: string;
|
||||||
|
metadata: {
|
||||||
|
public: string | boolean;
|
||||||
|
enable_content_trust: string | boolean;
|
||||||
|
prevent_vul: string | boolean;
|
||||||
|
severity: string;
|
||||||
|
auto_scan: string | boolean;
|
||||||
|
};
|
||||||
|
constructor () {
|
||||||
|
this.metadata.public = false;
|
||||||
|
this.metadata.enable_content_trust = false;
|
||||||
|
this.metadata.prevent_vul = false;
|
||||||
|
this.metadata.severity = 'low';
|
||||||
|
this.metadata.auto_scan = false;
|
||||||
|
}
|
||||||
|
}
|
@ -80,6 +80,14 @@ export interface IServiceConfig {
|
|||||||
*/
|
*/
|
||||||
vulnerabilityScanningBaseEndpoint?: string;
|
vulnerabilityScanningBaseEndpoint?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base endpoint of the service used to handle project policy.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberOf IServiceConfig
|
||||||
|
*/
|
||||||
|
projectPolicyEndpoint?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To determine whether or not to enable the i18 multiple languages supporting.
|
* To determine whether or not to enable the i18 multiple languages supporting.
|
||||||
*
|
*
|
||||||
|
@ -8,4 +8,5 @@ export * from './tag.service';
|
|||||||
export * from './RequestQueryParams';
|
export * from './RequestQueryParams';
|
||||||
export * from './scanning.service';
|
export * from './scanning.service';
|
||||||
export * from './configuration.service';
|
export * from './configuration.service';
|
||||||
export * from './job-log.service';
|
export * from './job-log.service';
|
||||||
|
export * from './project.service';
|
||||||
|
@ -242,4 +242,4 @@ export interface TagClickEvent {
|
|||||||
project_id: string | number;
|
project_id: string | number;
|
||||||
repository_name: string;
|
repository_name: string;
|
||||||
tag_name: string;
|
tag_name: string;
|
||||||
}
|
}
|
||||||
|
85
src/ui_ng/lib/src/service/project.service.ts
Normal file
85
src/ui_ng/lib/src/service/project.service.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import 'rxjs/add/observable/of';
|
||||||
|
import { Http, Headers, RequestOptions } from '@angular/http';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
|
||||||
|
import { Project } from '../project-policy-config/project';
|
||||||
|
import { ProjectPolicy } from '../project-policy-config/project-policy-config.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the service methods to handle the Prject related things.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @abstract
|
||||||
|
* @class ProjectService
|
||||||
|
*/
|
||||||
|
export abstract class ProjectService {
|
||||||
|
/**
|
||||||
|
* Get Infomations of a specific Project.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param {string|number} [projectId]
|
||||||
|
* @returns {(Observable<Project> | Promise<Project> | Project)}
|
||||||
|
*
|
||||||
|
* @memberOf ProjectService
|
||||||
|
*/
|
||||||
|
abstract getProject(projectId: number | string): Observable<Project> | Promise<Project> | Project;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified project.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param {(number | string)} projectId
|
||||||
|
* @param {ProjectPolicy} projectPolicy
|
||||||
|
* @returns {(Observable<any> | Promise<any> | any)}
|
||||||
|
*
|
||||||
|
* @memberOf EndpointService
|
||||||
|
*/
|
||||||
|
abstract updateProjectPolicy(projectId: number | string, projectPolicy: ProjectPolicy): Observable<any> | Promise<any> | any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement default service for project.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class ProjectDefaultService
|
||||||
|
* @extends {ProjectService}
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class ProjectDefaultService extends ProjectService {
|
||||||
|
|
||||||
|
headers = new Headers({'Content-type': 'application/json'});
|
||||||
|
options = new RequestOptions({'headers': this.headers});
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: Http,
|
||||||
|
@Inject(SERVICE_CONFIG) private config: IServiceConfig
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getProject(projectId: number | string): Observable<Project> | Promise<Project> | Project {
|
||||||
|
if (!projectId) {
|
||||||
|
return Promise.reject('Bad argument');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.get(`/api/projects/${projectId}`)
|
||||||
|
.map(response => response.json())
|
||||||
|
.catch(error => Observable.throw(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateProjectPolicy(projectId: number | string, projectPolicy: ProjectPolicy): any {
|
||||||
|
return this.http
|
||||||
|
.put(`/api/projects/${projectId}`, { 'metadata': {
|
||||||
|
'public': projectPolicy.Public ? 'true' : 'false',
|
||||||
|
'enable_content_trust': projectPolicy.ContentTrust ? 'true' : 'false',
|
||||||
|
'prevent_vul': projectPolicy.PreventVulImg ? 'true' : 'false',
|
||||||
|
'severity': projectPolicy.PreventVulImgServerity,
|
||||||
|
'auto_scan': projectPolicy.ScanImgOnPush ? 'true' : 'false'
|
||||||
|
} }, this.options)
|
||||||
|
.map(response => response.status)
|
||||||
|
.catch(error => Observable.throw(error));
|
||||||
|
}
|
||||||
|
}
|
@ -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.4.85",
|
"harbor-ui": "0.4.91",
|
||||||
"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",
|
||||||
|
@ -24,6 +24,7 @@ import { ConfigurationModule } from './config/config.module';
|
|||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { AppConfigService } from './app-config.service';
|
import { AppConfigService } from './app-config.service';
|
||||||
import {SkinableConfig} from "./skinable-config.service";
|
import {SkinableConfig} from "./skinable-config.service";
|
||||||
|
import { ProjectConfigComponent } from './project/project-config/project-config.component';
|
||||||
|
|
||||||
export function initConfig(configService: AppConfigService, skinableService: SkinableConfig) {
|
export function initConfig(configService: AppConfigService, skinableService: SkinableConfig) {
|
||||||
return () => {
|
return () => {
|
||||||
@ -39,6 +40,7 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
|
ProjectConfigComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
@ -49,10 +51,10 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AppConfigService,
|
AppConfigService,
|
||||||
SkinableConfig,
|
SkinableConfig,
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initConfig,
|
useFactory: initConfig,
|
||||||
deps: [ AppConfigService, SkinableConfig],
|
deps: [ AppConfigService, SkinableConfig],
|
||||||
multi: true
|
multi: true
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,7 @@ import { TagRepositoryComponent } from './repository/tag-repository/tag-reposito
|
|||||||
import { ReplicationPageComponent } from './replication/replication-page.component';
|
import { ReplicationPageComponent } from './replication/replication-page.component';
|
||||||
import { MemberComponent } from './project/member/member.component';
|
import { MemberComponent } from './project/member/member.component';
|
||||||
import { AuditLogComponent } from './log/audit-log.component';
|
import { AuditLogComponent } from './log/audit-log.component';
|
||||||
|
import { ProjectConfigComponent } from './project/project-config/project-config.component'
|
||||||
|
|
||||||
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
||||||
import { SystemAdminGuard } from './shared/route/system-admin-activate.service';
|
import { SystemAdminGuard } from './shared/route/system-admin-activate.service';
|
||||||
@ -133,6 +134,10 @@ const harborRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'logs',
|
path: 'logs',
|
||||||
component: AuditLogComponent
|
component: AuditLogComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'configs',
|
||||||
|
component: ProjectConfigComponent
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="top: 8px;">
|
||||||
|
<hbr-project-policy-config [projectId]="projectId" [projectName]="projectName" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole"></hbr-project-policy-config>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProjectConfigComponent } from './project-config.component';
|
||||||
|
|
||||||
|
describe('ProjectConfigPageComponent', () => {
|
||||||
|
let component: ProjectConfigComponent;
|
||||||
|
let fixture: ComponentFixture<ProjectConfigComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProjectConfigComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProjectConfigComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
|
import { SessionService } from '../../shared/session.service';
|
||||||
|
import { SessionUser } from '../../shared/session-user';
|
||||||
|
import { Project } from '../project';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-project-config',
|
||||||
|
templateUrl: './project-config.component.html',
|
||||||
|
styleUrls: ['./project-config.component.css']
|
||||||
|
})
|
||||||
|
export class ProjectConfigComponent implements OnInit {
|
||||||
|
|
||||||
|
projectId: number;
|
||||||
|
projectName: string;
|
||||||
|
currentUser: SessionUser;
|
||||||
|
hasSignedIn: boolean;
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private session: SessionService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||||
|
this.currentUser = this.session.getCurrentUser();
|
||||||
|
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if (resolverData) {
|
||||||
|
let pro: Project = <Project>resolverData['projectResolver'];
|
||||||
|
this.hasProjectAdminRole = pro.has_project_admin_role;
|
||||||
|
this.projectName = pro.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,9 @@
|
|||||||
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
||||||
<a class="nav-link" routerLink="replications" routerLinkActive="active">{{'PROJECT_DETAIL.REPLICATION' | translate}}</a>
|
<a class="nav-link" routerLink="replications" routerLinkActive="active">{{'PROJECT_DETAIL.REPLICATION' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
||||||
|
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
@ -23,14 +23,14 @@ import { RoleMapping } from '../../shared/shared.const';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'project-detail',
|
selector: 'project-detail',
|
||||||
templateUrl: "project-detail.component.html",
|
templateUrl: 'project-detail.component.html',
|
||||||
styleUrls: [ 'project-detail.component.css' ]
|
styleUrls: [ 'project-detail.component.css' ]
|
||||||
})
|
})
|
||||||
export class ProjectDetailComponent {
|
export class ProjectDetailComponent {
|
||||||
|
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
currentProject: Project;
|
currentProject: Project;
|
||||||
|
|
||||||
isMember: boolean;
|
isMember: boolean;
|
||||||
roleName: string;
|
roleName: string;
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export class ProjectDetailComponent {
|
|||||||
private projectService: ProjectService) {
|
private projectService: ProjectService) {
|
||||||
|
|
||||||
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
||||||
this.route.data.subscribe(data=>{
|
this.route.data.subscribe(data => {
|
||||||
this.currentProject = <Project>data['projectResolver'];
|
this.currentProject = <Project>data['projectResolver'];
|
||||||
this.isMember = this.currentProject.is_member;
|
this.isMember = this.currentProject.is_member;
|
||||||
this.roleName = RoleMapping[this.currentProject.role_name];
|
this.roleName = RoleMapping[this.currentProject.role_name];
|
||||||
|
@ -161,7 +161,22 @@
|
|||||||
"REPLICATION": "Replication",
|
"REPLICATION": "Replication",
|
||||||
"USERS": "Members",
|
"USERS": "Members",
|
||||||
"LOGS": "Logs",
|
"LOGS": "Logs",
|
||||||
"PROJECTS": "Projects"
|
"PROJECTS": "Projects",
|
||||||
|
"CONFIG": "Configuration"
|
||||||
|
},
|
||||||
|
"PROJECT_CONFIG": {
|
||||||
|
"REGISTRY": "Project registry",
|
||||||
|
"PUBLIC_TOGGLE": "Public",
|
||||||
|
"PUBLIC_POLICY": "Making a project registry public will make all repositories accessible to everyone.",
|
||||||
|
"SECURITY": "Deployment security",
|
||||||
|
"CONTENT_TRUST_TOGGLE": "Enable content trust",
|
||||||
|
"CONTENT_TRUST_POLCIY": "Only allow verified images to be deployed.",
|
||||||
|
"PREVENT_VULNERABLE_TOGGLE": "Prevent vulnerable images from running.",
|
||||||
|
"PREVENT_VULNERABLE_1": "Prevent images with vulnerability serverity of",
|
||||||
|
"PREVENT_VULNERABLE_2": "and above from being deployed.",
|
||||||
|
"SCAN": "Vulnerability scanning",
|
||||||
|
"AUTOSCAN_TOGGLE": "Automatically scan images on push",
|
||||||
|
"AUTOSCAN_POLICY": "Automatically scan images when they are pushed to the project registry."
|
||||||
},
|
},
|
||||||
"MEMBER": {
|
"MEMBER": {
|
||||||
"NEW_MEMBER": "New Member",
|
"NEW_MEMBER": "New Member",
|
||||||
|
@ -161,7 +161,22 @@
|
|||||||
"REPLICATION": "Replicación",
|
"REPLICATION": "Replicación",
|
||||||
"USERS": "Miembros",
|
"USERS": "Miembros",
|
||||||
"LOGS": "Logs",
|
"LOGS": "Logs",
|
||||||
"PROJECTS": "Proyectos"
|
"PROJECTS": "Proyectos",
|
||||||
|
"CONFIG": "Configuración"
|
||||||
|
},
|
||||||
|
"PROJECT_CONFIG": {
|
||||||
|
"REGISTRY": "Registro de proyectos",
|
||||||
|
"PUBLIC_TOGGLE": "Público",
|
||||||
|
"PUBLIC_POLICY": "Hacer público un registro de proyecto hará que todos los repositorios sean accesibles para todos.",
|
||||||
|
"SECURITY": "Seguridad de despliegue",
|
||||||
|
"CONTENT_TRUST_TOGGLE": "Habilitar la confianza de contenido",
|
||||||
|
"CONTENT_TRUST_POLCIY": "Solo permita la implementación de imágenes verificadas.",
|
||||||
|
"PREVENT_VULNERABLE_TOGGLE": "Evitar que se ejecuten imágenes vulnerables.",
|
||||||
|
"PREVENT_VULNERABLE_1": "Impedir imágenes con la gravedad de la vulnerabilidad de",
|
||||||
|
"PREVENT_VULNERABLE_2": "y más arriba de ser desplegado.",
|
||||||
|
"SCAN": "Escaneo de vulnerabilidad",
|
||||||
|
"AUTOSCAN_TOGGLE": "Escanee automáticamente las imágenes al instante",
|
||||||
|
"AUTOSCAN_POLICY": "Escanee automáticamente las imágenes cuando son enviadas al registro del proyecto."
|
||||||
},
|
},
|
||||||
"MEMBER": {
|
"MEMBER": {
|
||||||
"NEW_MEMBER": "Nuevo miembro",
|
"NEW_MEMBER": "Nuevo miembro",
|
||||||
|
@ -161,7 +161,22 @@
|
|||||||
"REPLICATION": "复制",
|
"REPLICATION": "复制",
|
||||||
"USERS": "成员",
|
"USERS": "成员",
|
||||||
"LOGS": "日志",
|
"LOGS": "日志",
|
||||||
"PROJECTS": "项目"
|
"PROJECTS": "项目",
|
||||||
|
"CONFIG": "配置管理"
|
||||||
|
},
|
||||||
|
"PROJECT_CONFIG": {
|
||||||
|
"REGISTRY": "项目仓库",
|
||||||
|
"PUBLIC_TOGGLE": "公开",
|
||||||
|
"PUBLIC_POLICY": "此项目的仓库对所有访客公开。",
|
||||||
|
"SECURITY": "部署安全",
|
||||||
|
"CONTENT_TRUST_TOGGLE": "内容信任",
|
||||||
|
"CONTENT_TRUST_POLCIY": "仅允许部署通过认证的镜像。",
|
||||||
|
"PREVENT_VULNERABLE_TOGGLE": "阻止潜在漏洞镜像",
|
||||||
|
"PREVENT_VULNERABLE_1": "阻止危害级别",
|
||||||
|
"PREVENT_VULNERABLE_2": "以上的镜像运行。",
|
||||||
|
"SCAN": "漏洞扫描",
|
||||||
|
"AUTOSCAN_TOGGLE": "自动扫描镜像",
|
||||||
|
"AUTOSCAN_POLICY": "当镜像上传后,自动进行扫描"
|
||||||
},
|
},
|
||||||
"MEMBER": {
|
"MEMBER": {
|
||||||
"NEW_MEMBER": "新建成员",
|
"NEW_MEMBER": "新建成员",
|
||||||
|
Loading…
Reference in New Issue
Block a user