mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-23 00:57:44 +01:00
promission reset
Signed-off-by: Yogi_Wang <wang1084@126.com>
This commit is contained in:
parent
e12fd13c56
commit
1a551690d3
@ -19,8 +19,8 @@ import {
|
|||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef
|
ChangeDetectorRef
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { Subscription} from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import {forkJoin} from "rxjs";
|
import { forkJoin } from "rxjs";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { Comparator } from "../service/interface";
|
import { Comparator } from "../service/interface";
|
||||||
|
|
||||||
@ -29,9 +29,9 @@ import { EndpointService } from "../service/endpoint.service";
|
|||||||
|
|
||||||
import { ErrorHandler } from "../error-handler/index";
|
import { ErrorHandler } from "../error-handler/index";
|
||||||
|
|
||||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ConfirmationTargets,
|
ConfirmationTargets,
|
||||||
@ -42,8 +42,9 @@ import {
|
|||||||
import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component";
|
import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component";
|
||||||
import { toPromise, CustomComparator } from "../utils";
|
import { toPromise, CustomComparator } from "../utils";
|
||||||
|
|
||||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||||
import {OperationService} from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-endpoint",
|
selector: "hbr-endpoint",
|
||||||
@ -86,10 +87,10 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(private endpointService: EndpointService,
|
constructor(private endpointService: EndpointService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private ref: ChangeDetectorRef) {
|
private ref: ChangeDetectorRef) {
|
||||||
this.forceRefreshView(1000);
|
this.forceRefreshView(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,18 +209,18 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
|||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
});
|
||||||
}).catch(
|
}).catch(
|
||||||
error => {
|
error => {
|
||||||
if (error && error.status === 412) {
|
if (error && error.status === 412) {
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')).subscribe(res => {
|
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')).subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res);
|
operateChanges(operMessage, OperationState.failure, res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forcely refresh the view
|
// Forcely refresh the view
|
||||||
|
@ -29,7 +29,6 @@ import { CREATE_EDIT_LABEL_DIRECTIVES } from "./create-edit-label/index";
|
|||||||
import { LABEL_PIECE_DIRECTIVES } from "./label-piece/index";
|
import { LABEL_PIECE_DIRECTIVES } from "./label-piece/index";
|
||||||
import { HELMCHART_DIRECTIVE } from "./helm-chart/index";
|
import { HELMCHART_DIRECTIVE } from "./helm-chart/index";
|
||||||
import { IMAGE_NAME_INPUT_DIRECTIVES } from "./image-name-input/index";
|
import { IMAGE_NAME_INPUT_DIRECTIVES } from "./image-name-input/index";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SystemInfoService,
|
SystemInfoService,
|
||||||
SystemInfoDefaultService,
|
SystemInfoDefaultService,
|
||||||
@ -56,7 +55,9 @@ import {
|
|||||||
HelmChartService,
|
HelmChartService,
|
||||||
HelmChartDefaultService,
|
HelmChartDefaultService,
|
||||||
RetagService,
|
RetagService,
|
||||||
RetagDefaultService
|
RetagDefaultService,
|
||||||
|
UserPermissionService,
|
||||||
|
UserPermissionDefaultService
|
||||||
} from './service/index';
|
} from './service/index';
|
||||||
import {
|
import {
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
@ -68,7 +69,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { TranslateServiceInitializer } from './i18n/index';
|
import { TranslateServiceInitializer } from './i18n/index';
|
||||||
import { DEFAULT_LANG_COOKIE_KEY, DEFAULT_SUPPORTING_LANGS, DEFAULT_LANG } from './utils';
|
import { DEFAULT_LANG_COOKIE_KEY, DEFAULT_SUPPORTING_LANGS, DEFAULT_LANG } from './utils';
|
||||||
import { ChannelService } from './channel/index';
|
import { ChannelService } from './channel/index';
|
||||||
import { OperationService } from './operation/operation.service';
|
import { OperationService } from './operation/operation.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declare default service configuration; all the endpoints will be defined in
|
* Declare default service configuration; all the endpoints will be defined in
|
||||||
@ -151,6 +152,8 @@ export interface HarborModuleConfig {
|
|||||||
|
|
||||||
// Service implementation for helmchart
|
// Service implementation for helmchart
|
||||||
helmChartService?: Provider;
|
helmChartService?: Provider;
|
||||||
|
// Service implementation for userPermission
|
||||||
|
userPermissionService?: Provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,8 +251,9 @@ export class HarborLibraryModule {
|
|||||||
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 },
|
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
|
||||||
config.helmChartService || {provide: HelmChartService, useClass: HelmChartDefaultService},
|
config.helmChartService || { provide: HelmChartService, useClass: HelmChartDefaultService },
|
||||||
|
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||||
// Do initializing
|
// Do initializing
|
||||||
TranslateServiceInitializer,
|
TranslateServiceInitializer,
|
||||||
{
|
{
|
||||||
@ -281,8 +285,9 @@ export class HarborLibraryModule {
|
|||||||
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 },
|
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
|
||||||
config.helmChartService || {provide: HelmChartService, useClass: HelmChartDefaultService},
|
config.helmChartService || { provide: HelmChartService, useClass: HelmChartDefaultService },
|
||||||
|
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||||
ChannelService,
|
ChannelService,
|
||||||
OperationService
|
OperationService
|
||||||
]
|
]
|
||||||
|
@ -23,14 +23,14 @@
|
|||||||
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!developerRoleOrAbove" (click)="onChartUpload()">
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasUploadHelmChartsPermission" (click)="onChartUpload()">
|
||||||
<clr-icon shape="upload" size="16"></clr-icon>{{'HELM_CHART.UPLOAD' | translate}}
|
<clr-icon shape="upload" size="16"></clr-icon>{{'HELM_CHART.UPLOAD' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasProjectAdminRole || selectedRows.length<1"
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasDeleteHelmChartsPermission || selectedRows.length<1"
|
||||||
(click)="openChartDeleteModal(selectedRows)">
|
(click)="openChartDeleteModal(selectedRows)">
|
||||||
<clr-icon shape="trash" size="16"></clr-icon>{{'BUTTON.DELETE' | translate}}
|
<clr-icon shape="trash" size="16"></clr-icon>{{'BUTTON.DELETE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="selectedRows.length!==1" (click)="downloadLatestVersion()">
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasDownloadHelmChartsPermission ||selectedRows.length!==1" (click)="downloadLatestVersion()">
|
||||||
<clr-icon shape="download" size="16"></clr-icon>{{'HELM_CHART.DOWNLOAD' | translate}}
|
<clr-icon shape="download" size="16"></clr-icon>{{'HELM_CHART.DOWNLOAD' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
|
@ -17,9 +17,11 @@ import { SystemInfo, SystemInfoService, HelmChartItem } from "../service/index";
|
|||||||
import { ErrorHandler } from "../error-handler/error-handler";
|
import { ErrorHandler } from "../error-handler/error-handler";
|
||||||
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../utils";
|
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../utils";
|
||||||
import { HelmChartService } from "../service/helm-chart.service";
|
import { HelmChartService } from "../service/helm-chart.service";
|
||||||
import { DefaultHelmIcon} from "../shared/shared.const";
|
import { DefaultHelmIcon } from "../shared/shared.const";
|
||||||
import { Roles } from './../shared/shared.const';
|
import { Roles } from './../shared/shared.const';
|
||||||
import { OperationService } from "./../operation/operation.service";
|
import { OperationService } from "./../operation/operation.service";
|
||||||
|
import { UserPermissionService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
import {
|
import {
|
||||||
OperateInfo,
|
OperateInfo,
|
||||||
OperationState,
|
OperationState,
|
||||||
@ -45,7 +47,6 @@ export class HelmChartComponent implements OnInit {
|
|||||||
@Input() urlPrefix: string;
|
@Input() urlPrefix: string;
|
||||||
@Input() hasSignedIn: boolean;
|
@Input() hasSignedIn: boolean;
|
||||||
@Input() projectRoleID = Roles.OTHER;
|
@Input() projectRoleID = Roles.OTHER;
|
||||||
@Input() hasProjectAdminRole: boolean;
|
|
||||||
@Output() chartClickEvt = new EventEmitter<any>();
|
@Output() chartClickEvt = new EventEmitter<any>();
|
||||||
@Output() chartDownloadEve = new EventEmitter<string>();
|
@Output() chartDownloadEve = new EventEmitter<string>();
|
||||||
@Input() chartDefaultIcon: string = DefaultHelmIcon;
|
@Input() chartDefaultIcon: string = DefaultHelmIcon;
|
||||||
@ -76,24 +77,23 @@ export class HelmChartComponent implements OnInit {
|
|||||||
@ViewChild('chartUploadForm') uploadForm: NgForm;
|
@ViewChild('chartUploadForm') uploadForm: NgForm;
|
||||||
|
|
||||||
@ViewChild("confirmationDialog") confirmationDialog: ConfirmationDialogComponent;
|
@ViewChild("confirmationDialog") confirmationDialog: ConfirmationDialogComponent;
|
||||||
|
hasUploadHelmChartsPermission: boolean;
|
||||||
|
hasDownloadHelmChartsPermission: boolean;
|
||||||
|
hasDeleteHelmChartsPermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
private helmChartService: HelmChartService,
|
private helmChartService: HelmChartService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
public get registryUrl(): string {
|
public get registryUrl(): string {
|
||||||
return this.systemInfo ? this.systemInfo.registry_url : "";
|
return this.systemInfo ? this.systemInfo.registry_url : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public get developerRoleOrAbove(): boolean {
|
|
||||||
return this.projectRoleID === Roles.DEVELOPER || this.hasProjectAdminRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Get system info for tag views
|
// Get system info for tag views
|
||||||
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||||
@ -101,8 +101,21 @@ export class HelmChartComponent implements OnInit {
|
|||||||
.catch(error => this.errorHandler.error(error));
|
.catch(error => this.errorHandler.error(error));
|
||||||
this.lastFilteredChartName = "";
|
this.lastFilteredChartName = "";
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
this.getHelmPermissionRule(this.projectId);
|
||||||
|
}
|
||||||
|
getHelmPermissionRule(projectId: number): void {
|
||||||
|
let hasUploadHelmChartsPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART.KEY, USERSTATICPERMISSION.HELM_CHART.VALUE.UPLOAD);
|
||||||
|
let hasDownloadHelmChartsPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART.KEY, USERSTATICPERMISSION.HELM_CHART.VALUE.DOWNLOAD);
|
||||||
|
let hasDeleteHelmChartsPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART.KEY, USERSTATICPERMISSION.HELM_CHART.VALUE.DELETE);
|
||||||
|
forkJoin(hasUploadHelmChartsPermission, hasDownloadHelmChartsPermission, hasDeleteHelmChartsPermission).subscribe(permissions => {
|
||||||
|
this.hasUploadHelmChartsPermission = permissions[0] as boolean;
|
||||||
|
this.hasDownloadHelmChartsPermission = permissions[1] as boolean;
|
||||||
|
this.hasDeleteHelmChartsPermission = permissions[2] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFilterValue(value: string) {
|
updateFilterValue(value: string) {
|
||||||
this.lastFilteredChartName = value;
|
this.lastFilteredChartName = value;
|
||||||
this.refresh();
|
this.refresh();
|
||||||
@ -111,22 +124,22 @@ export class HelmChartComponent implements OnInit {
|
|||||||
refresh() {
|
refresh() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.helmChartService
|
this.helmChartService
|
||||||
.getHelmCharts(this.projectName)
|
.getHelmCharts(this.projectName)
|
||||||
.pipe(finalize(() => {
|
.pipe(finalize(() => {
|
||||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
setTimeout(() => clearInterval(hnd), 3000);
|
setTimeout(() => clearInterval(hnd), 3000);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}))
|
}))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
charts => {
|
charts => {
|
||||||
this.charts = charts.filter(x => x.name.includes(this.lastFilteredChartName));
|
this.charts = charts.filter(x => x.name.includes(this.lastFilteredChartName));
|
||||||
this.chartsCopy = charts.map(x => Object.assign({}, x));
|
this.chartsCopy = charts.map(x => Object.assign({}, x));
|
||||||
this.totalCount = charts.length;
|
this.totalCount = charts.length;
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
this.errorHandler.error(err);
|
this.errorHandler.error(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChartClick(item: HelmChartItem) {
|
onChartClick(item: HelmChartItem) {
|
||||||
@ -163,10 +176,10 @@ export class HelmChartComponent implements OnInit {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}))
|
}))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.translateService
|
this.translateService
|
||||||
.get("HELM_CHART.FILE_UPLOADED")
|
.get("HELM_CHART.FILE_UPLOADED")
|
||||||
.subscribe(res => this.errorHandler.info(res));
|
.subscribe(res => this.errorHandler.info(res));
|
||||||
},
|
},
|
||||||
err => this.errorHandler.error(err)
|
err => this.errorHandler.error(err)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -192,23 +205,23 @@ export class HelmChartComponent implements OnInit {
|
|||||||
this.operationService.publishInfo(operateMsg);
|
this.operationService.publishInfo(operateMsg);
|
||||||
|
|
||||||
return this.helmChartService.deleteHelmChart(this.projectName, chartName)
|
return this.helmChartService.deleteHelmChart(this.projectName, chartName)
|
||||||
.pipe(map(
|
.pipe(map(
|
||||||
() => operateChanges(operateMsg, OperationState.success),
|
() => operateChanges(operateMsg, OperationState.success),
|
||||||
err => operateChanges(operateMsg, OperationState.failure, err)
|
err => operateChanges(operateMsg, OperationState.failure, err)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCharts(charts: HelmChartItem[]) {
|
deleteCharts(charts: HelmChartItem[]) {
|
||||||
if (charts && charts.length < 1) { return; }
|
if (charts && charts.length < 1) { return; }
|
||||||
let chartsDelete$ = charts.map(chart => this.deleteChart(chart.name));
|
let chartsDelete$ = charts.map(chart => this.deleteChart(chart.name));
|
||||||
forkJoin(chartsDelete$)
|
forkJoin(chartsDelete$)
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => throwError(err)),
|
catchError(err => throwError(err)),
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
this.selectedRows = [];
|
this.selectedRows = [];
|
||||||
}))
|
}))
|
||||||
.subscribe(() => {});
|
.subscribe(() => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadLatestVersion(evt?: Event, item?: HelmChartItem) {
|
downloadLatestVersion(evt?: Event, item?: HelmChartItem) {
|
||||||
|
@ -38,18 +38,18 @@
|
|||||||
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-sm btn-secondary"
|
<button type="button" class="btn btn-sm btn-secondary"
|
||||||
[disabled]="!(selectedRows.length===1)"
|
[disabled]="!(selectedRows.length===1) || !hasDownloadHelmChartVersionPermission"
|
||||||
(click)="versionDownload()">
|
(click)="versionDownload()">
|
||||||
<clr-icon shape="download" size="16"></clr-icon> {{'HELM_CHART.DOWNLOAD' | translate}}
|
<clr-icon shape="download" size="16"></clr-icon> {{'HELM_CHART.DOWNLOAD' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary"
|
<button type="button" class="btn btn-sm btn-secondary"
|
||||||
[disabled]="selectedRows.length<=0 || !hasProjectAdminRole"
|
[disabled]="selectedRows.length<=0 || !hasDeleteHelmChartVersionPermission"
|
||||||
(click)="openVersionDeleteModal(selectedRows)">
|
(click)="openVersionDeleteModal(selectedRows)">
|
||||||
<clr-icon shape="times" size="16"></clr-icon> {{'BUTTON.DELETE' | translate}}
|
<clr-icon shape="times" size="16"></clr-icon> {{'BUTTON.DELETE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown>
|
<clr-dropdown>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger
|
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger
|
||||||
[disabled]="!(selectedRows.length===1 && developerRoleOrAbove)">
|
[disabled]="!(selectedRows.length===1)|| !hasAddRemoveHelmChartVersionPermission">
|
||||||
<clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}
|
<clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||||
@ -144,7 +144,7 @@
|
|||||||
<button type="button" class="btn btn-link"
|
<button type="button" class="btn btn-link"
|
||||||
(click)="versionDownload($event, item)">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
(click)="versionDownload($event, item)">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||||
<button type="button" class="btn btn-link"
|
<button type="button" class="btn btn-link"
|
||||||
[disabled]="selectedRows.length<=0 || !hasProjectAdminRole"
|
[disabled]="selectedRows.length<=0 || !hasDeleteHelmChartVersionPermission"
|
||||||
(click)="deleteVersionCard($event, item)">{{'BUTTON.DELETE' | translate}}</button>
|
(click)="deleteVersionCard($event, item)">{{'BUTTON.DELETE' | translate}}</button>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,6 +26,8 @@ import { ErrorHandler } from "./../../error-handler/error-handler";
|
|||||||
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../../utils";
|
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../../utils";
|
||||||
import { OperationService } from "./../../operation/operation.service";
|
import { OperationService } from "./../../operation/operation.service";
|
||||||
import { HelmChartService } from "./../../service/helm-chart.service";
|
import { HelmChartService } from "./../../service/helm-chart.service";
|
||||||
|
import { UserPermissionService } from "../../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../../service/permission-static";
|
||||||
import { ConfirmationAcknowledgement, ConfirmationDialogComponent, ConfirmationMessage } from "./../../confirmation-dialog";
|
import { ConfirmationAcknowledgement, ConfirmationDialogComponent, ConfirmationMessage } from "./../../confirmation-dialog";
|
||||||
import {
|
import {
|
||||||
OperateInfo,
|
OperateInfo,
|
||||||
@ -49,13 +51,11 @@ import {
|
|||||||
})
|
})
|
||||||
export class ChartVersionComponent implements OnInit {
|
export class ChartVersionComponent implements OnInit {
|
||||||
signedCon: { [key: string]: any | string[] } = {};
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
@Input() projectRoleID: number;
|
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() projectName: string;
|
@Input() projectName: string;
|
||||||
@Input() chartName: string;
|
@Input() chartName: string;
|
||||||
@Input() roleName: string;
|
@Input() roleName: string;
|
||||||
@Input() hasSignedIn: boolean;
|
@Input() hasSignedIn: boolean;
|
||||||
@Input() hasProjectAdminRole: boolean;
|
|
||||||
@Input() chartDefaultIcon: string = DefaultHelmIcon;
|
@Input() chartDefaultIcon: string = DefaultHelmIcon;
|
||||||
@Output() versionClickEvt = new EventEmitter<string>();
|
@Output() versionClickEvt = new EventEmitter<string>();
|
||||||
@Output() backEvt = new EventEmitter<any>();
|
@Output() backEvt = new EventEmitter<any>();
|
||||||
@ -85,12 +85,15 @@ export class ChartVersionComponent implements OnInit {
|
|||||||
|
|
||||||
@ViewChild("confirmationDialog")
|
@ViewChild("confirmationDialog")
|
||||||
confirmationDialog: ConfirmationDialogComponent;
|
confirmationDialog: ConfirmationDialogComponent;
|
||||||
|
hasAddRemoveHelmChartVersionPermission: boolean;
|
||||||
|
hasDownloadHelmChartVersionPermission: boolean;
|
||||||
|
hasDeleteHelmChartVersionPermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
private helmChartService: HelmChartService,
|
private helmChartService: HelmChartService,
|
||||||
private resrouceLabelService: LabelService,
|
private resrouceLabelService: LabelService,
|
||||||
|
public userPermissionService: UserPermissionService,
|
||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
) { }
|
) { }
|
||||||
@ -107,6 +110,7 @@ export class ChartVersionComponent implements OnInit {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
this.getLabels();
|
this.getLabels();
|
||||||
this.lastFilteredVersionName = "";
|
this.lastFilteredVersionName = "";
|
||||||
|
this.getHelmChartVersionPermission(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFilterValue(value: string) {
|
updateFilterValue(value: string) {
|
||||||
@ -326,7 +330,19 @@ export class ChartVersionComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get developerRoleOrAbove(): boolean {
|
getHelmChartVersionPermission(projectId: number): void {
|
||||||
return this.projectRoleID === Roles.DEVELOPER || this.hasProjectAdminRole;
|
|
||||||
|
let hasAddRemoveHelmChartVersionPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART_VERSION_LABEL.KEY, USERSTATICPERMISSION.HELM_CHART_VERSION_LABEL.VALUE.CREATE);
|
||||||
|
let hasDownloadHelmChartVersionPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART_VERSION.KEY, USERSTATICPERMISSION.HELM_CHART_VERSION.VALUE.READ);
|
||||||
|
let hasDeleteHelmChartVersionPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART_VERSION.KEY, USERSTATICPERMISSION.HELM_CHART_VERSION.VALUE.DELETE);
|
||||||
|
forkJoin(hasAddRemoveHelmChartVersionPermission, hasDownloadHelmChartVersionPermission, hasDeleteHelmChartVersionPermission)
|
||||||
|
.subscribe(permissions => {
|
||||||
|
this.hasAddRemoveHelmChartVersionPermission = permissions[0] as boolean;
|
||||||
|
this.hasDownloadHelmChartVersionPermission = permissions[1] as boolean;
|
||||||
|
this.hasDeleteHelmChartVersionPermission = permissions[2] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 btnGroup">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 btnGroup">
|
||||||
<button type="button" class="btn btn-sm btn-secondary" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon> {{'LABEL.NEW_LABEL' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasCreateLabelPermission" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon> {{'LABEL.NEW_LABEL' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length == 1)" (click)="editLabel(selectedRow)"><clr-icon shape="pencil" size="16"></clr-icon> {{'LABEL.EDIT' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length == 1) || !hasUpdateLabelPermission" (click)="editLabel(selectedRow)"><clr-icon shape="pencil" size="16"></clr-icon> {{'LABEL.EDIT' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow.length" (click)="deleteLabels(selectedRow)"><clr-icon shape="times" size="16"></clr-icon> {{'LABEL.DELETE' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow.length || !hasDeleteLabelPermission" (click)="deleteLabels(selectedRow)"><clr-icon shape="times" size="16"></clr-icon> {{'LABEL.DELETE' | translate}}</button>
|
||||||
<hbr-create-edit-label [scope]="scope" [projectId]="projectId" (reload)="reload()"></hbr-create-edit-label>
|
<hbr-create-edit-label [scope]="scope" [projectId]="projectId" (reload)="reload()"></hbr-create-edit-label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 content-mt">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 content-mt">
|
||||||
|
@ -19,22 +19,22 @@ import {
|
|||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Input
|
Input
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import {Label} from "../service/interface";
|
import { Label } from "../service/interface";
|
||||||
import {LabelService} from "../service/label.service";
|
import { LabelService } from "../service/label.service";
|
||||||
import {toPromise} from "../utils";
|
import { toPromise } from "../utils";
|
||||||
import {ErrorHandler} from "../error-handler/error-handler";
|
import { ErrorHandler } from "../error-handler/error-handler";
|
||||||
import {CreateEditLabelComponent} from "../create-edit-label/create-edit-label.component";
|
import { CreateEditLabelComponent } from "../create-edit-label/create-edit-label.component";
|
||||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||||
import {
|
import {
|
||||||
ConfirmationButtons,
|
ConfirmationButtons,
|
||||||
ConfirmationState,
|
ConfirmationState,
|
||||||
ConfirmationTargets
|
ConfirmationTargets
|
||||||
} from "../shared/shared.const";
|
} from "../shared/shared.const";
|
||||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||||
import {TranslateService} from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||||
import {OperationService} from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-label",
|
selector: "hbr-label",
|
||||||
@ -51,7 +51,9 @@ export class LabelComponent implements OnInit {
|
|||||||
|
|
||||||
@Input() scope: string;
|
@Input() scope: string;
|
||||||
@Input() projectId = 0;
|
@Input() projectId = 0;
|
||||||
@Input() hasProjectAdminRole: boolean;
|
@Input() hasCreateLabelPermission: boolean;
|
||||||
|
@Input() hasUpdateLabelPermission: boolean;
|
||||||
|
@Input() hasDeleteLabelPermission: boolean;
|
||||||
|
|
||||||
@ViewChild(CreateEditLabelComponent)
|
@ViewChild(CreateEditLabelComponent)
|
||||||
createEditLabel: CreateEditLabelComponent;
|
createEditLabel: CreateEditLabelComponent;
|
||||||
@ -59,10 +61,10 @@ export class LabelComponent implements OnInit {
|
|||||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||||
|
|
||||||
constructor(private labelService: LabelService,
|
constructor(private labelService: LabelService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private ref: ChangeDetectorRef) {
|
private ref: ChangeDetectorRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -162,11 +164,11 @@ export class LabelComponent implements OnInit {
|
|||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
});
|
||||||
}).catch(
|
}).catch(
|
||||||
error => {
|
error => {
|
||||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res);
|
operateChanges(operMessage, OperationState.failure, res);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forcely refresh the view
|
// Forcely refresh the view
|
||||||
@ -183,4 +185,5 @@ export class LabelComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}, duration);
|
}, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div class="list-rule">
|
<div class="list-rule">
|
||||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectRule($event)" [clrDgRowSelection]="true">
|
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectRule($event)" [clrDgRowSelection]="true">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon> {{'REPLICATION.NEW_REPLICATION_RULE' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasCreateReplicationPermission" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon> {{'REPLICATION.NEW_REPLICATION_RULE' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" [disabled]="!selectedRow" (click)="editRule(selectedRow)"><clr-icon shape="pencil" size="16"></clr-icon> {{'REPLICATION.EDIT_POLICY' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasUpdateReplicationPermission" [disabled]="!selectedRow" (click)="editRule(selectedRow)"><clr-icon shape="pencil" size="16"></clr-icon> {{'REPLICATION.EDIT_POLICY' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" [disabled]="!selectedRow" (click)="deleteRule(selectedRow)"><clr-icon shape="times" size="16"></clr-icon> {{'REPLICATION.DELETE_POLICY' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasDeleteReplicationPermission" [disabled]="!selectedRow" (click)="deleteRule(selectedRow)"><clr-icon shape="times" size="16"></clr-icon> {{'REPLICATION.DELETE_POLICY' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)"><clr-icon shape="export" size="16"></clr-icon> {{'REPLICATION.REPLICATE' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasExecuteReplicationPermission" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)"><clr-icon shape="export" size="16"></clr-icon> {{'REPLICATION.REPLICATE' | translate}}</button>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||||
|
@ -24,28 +24,29 @@ import {
|
|||||||
SimpleChange,
|
SimpleChange,
|
||||||
SimpleChanges
|
SimpleChanges
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { forkJoin} from "rxjs";
|
import { forkJoin } from "rxjs";
|
||||||
import { Comparator } from "../service/interface";
|
import { Comparator } from "../service/interface";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
|
||||||
import {ReplicationService} from "../service/replication.service";
|
import { ReplicationService } from "../service/replication.service";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ReplicationJob,
|
ReplicationJob,
|
||||||
ReplicationJobItem,
|
ReplicationJobItem,
|
||||||
ReplicationRule
|
ReplicationRule
|
||||||
} from "../service/interface";
|
} from "../service/interface";
|
||||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||||
import {
|
import {
|
||||||
ConfirmationState,
|
ConfirmationState,
|
||||||
ConfirmationTargets,
|
ConfirmationTargets,
|
||||||
ConfirmationButtons
|
ConfirmationButtons
|
||||||
} from "../shared/shared.const";
|
} from "../shared/shared.const";
|
||||||
import {ErrorHandler} from "../error-handler/error-handler";
|
import { ErrorHandler } from "../error-handler/error-handler";
|
||||||
import {toPromise, CustomComparator} from "../utils";
|
import { toPromise, CustomComparator } from "../utils";
|
||||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||||
import {OperationService} from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -58,12 +59,14 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
nullTime = "0001-01-01T00:00:00Z";
|
nullTime = "0001-01-01T00:00:00Z";
|
||||||
|
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() isSystemAdmin: boolean;
|
|
||||||
@Input() selectedId: number | string;
|
@Input() selectedId: number | string;
|
||||||
@Input() withReplicationJob: boolean;
|
@Input() withReplicationJob: boolean;
|
||||||
|
|
||||||
@Input() loading = false;
|
@Input() loading = false;
|
||||||
|
@Input() hasCreateReplicationPermission: boolean;
|
||||||
|
@Input() hasUpdateReplicationPermission: boolean;
|
||||||
|
@Input() hasDeleteReplicationPermission: boolean;
|
||||||
|
@Input() hasExecuteReplicationPermission: boolean;
|
||||||
@Output() reload = new EventEmitter<boolean>();
|
@Output() reload = new EventEmitter<boolean>();
|
||||||
@Output() selectOne = new EventEmitter<ReplicationRule>();
|
@Output() selectOne = new EventEmitter<ReplicationRule>();
|
||||||
@Output() editOne = new EventEmitter<ReplicationRule>();
|
@Output() editOne = new EventEmitter<ReplicationRule>();
|
||||||
@ -92,10 +95,10 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
enabledComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("enabled", "number");
|
enabledComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("enabled", "number");
|
||||||
|
|
||||||
constructor(private replicationService: ReplicationService,
|
constructor(private replicationService: ReplicationService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private ref: ChangeDetectorRef) {
|
private ref: ChangeDetectorRef) {
|
||||||
setInterval(() => ref.markForCheck(), 500);
|
setInterval(() => ref.markForCheck(), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +116,6 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
this.retrieveRules();
|
this.retrieveRules();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
let proIdChange: SimpleChange = changes["projectId"];
|
let proIdChange: SimpleChange = changes["projectId"];
|
||||||
if (proIdChange) {
|
if (proIdChange) {
|
||||||
@ -156,7 +158,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
let count = 0;
|
let count = 0;
|
||||||
rule.filters.forEach((data: any) => {
|
rule.filters.forEach((data: any) => {
|
||||||
if (data.kind === 'label' && data.value.deleted) {
|
if (data.kind === 'label' && data.value.deleted) {
|
||||||
count ++;
|
count++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
@ -258,8 +260,8 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
if (!this.canDeleteRule) {
|
if (!this.canDeleteRule) {
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
this.translateService.get('REPLICATION.DELETION_SUMMARY_FAILURE')).subscribe(res => {
|
this.translateService.get('REPLICATION.DELETION_SUMMARY_FAILURE')).subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,8 +275,8 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
if (error && error.status === 412) {
|
if (error && error.status === 412) {
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')).subscribe(res => {
|
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')).subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res);
|
operateChanges(operMessage, OperationState.failure, res);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<div class="form-content" id="clr-wrapper-public">
|
<div class="form-content" id="clr-wrapper-public">
|
||||||
<clr-checkbox-wrapper>
|
<clr-checkbox-wrapper>
|
||||||
<input type="checkbox" id="clr-checkbox-wrapper-public" clrCheckbox [(ngModel)]="projectPolicy.Public" name="public"
|
<input type="checkbox" id="clr-checkbox-wrapper-public" clrCheckbox [(ngModel)]="projectPolicy.Public" name="public"
|
||||||
[disabled]="!hasProjectAdminRole" />
|
[disabled]="!hasChangeConfigRole" />
|
||||||
<label for="clr-checkbox-wrapper-public">{{ 'PROJECT_CONFIG.PUBLIC_TOGGLE' | translate }}</label>
|
<label for="clr-checkbox-wrapper-public">{{ 'PROJECT_CONFIG.PUBLIC_TOGGLE' | translate }}</label>
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
|
|
||||||
@ -19,7 +19,7 @@
|
|||||||
<div class="form-content">
|
<div class="form-content">
|
||||||
<div *ngIf="withNotary">
|
<div *ngIf="withNotary">
|
||||||
<clr-checkbox-wrapper>
|
<clr-checkbox-wrapper>
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.ContentTrust" name="content-trust" [disabled]="!hasProjectAdminRole" />
|
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.ContentTrust" name="content-trust" [disabled]="!hasChangeConfigRole" />
|
||||||
<label>{{ 'PROJECT_CONFIG.CONTENT_TRUST_TOGGLE' | translate }}</label>
|
<label>{{ 'PROJECT_CONFIG.CONTENT_TRUST_TOGGLE' | translate }}</label>
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
|
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<div *ngIf="withClair" id="prevent-vulenrability-image">
|
<div *ngIf="withClair" id="prevent-vulenrability-image">
|
||||||
<clr-checkbox-wrapper>
|
<clr-checkbox-wrapper>
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.PreventVulImg" name="prevent-vulenrability-image-input"
|
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.PreventVulImg" name="prevent-vulenrability-image-input"
|
||||||
[disabled]="!hasProjectAdminRole" />
|
[disabled]="!hasChangeConfigRole" />
|
||||||
<label>{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_TOGGLE' | translate }}</label>
|
<label>{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_TOGGLE' | translate }}</label>
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
|
|
||||||
@ -52,16 +52,16 @@
|
|||||||
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SCAN' | translate }}</label>
|
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SCAN' | translate }}</label>
|
||||||
<div class="form-content">
|
<div class="form-content">
|
||||||
<clr-checkbox-wrapper id="scan-image-on-push-wrapper">
|
<clr-checkbox-wrapper id="scan-image-on-push-wrapper">
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.ScanImgOnPush" [disabled]="!hasProjectAdminRole"
|
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.ScanImgOnPush" [disabled]="!hasChangeConfigRole"
|
||||||
name="scan-image-on-push" />
|
name="scan-image-on-push" />
|
||||||
<label>{{ 'PROJECT_CONFIG.AUTOSCAN_TOGGLE' | translate }}</label>
|
<label>{{ 'PROJECT_CONFIG.AUTOSCAN_TOGGLE' | translate }}</label>
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.AUTOSCAN_POLICY' | translate }}</label></div>
|
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.AUTOSCAN_POLICY' | translate }}</label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges() || !hasProjectAdminRole">{{'BUTTON.SAVE'
|
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges() || !hasChangeConfigRole">{{'BUTTON.SAVE'
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges() || !hasProjectAdminRole">{{'BUTTON.CANCEL'
|
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges() || !hasChangeConfigRole">{{'BUTTON.CANCEL'
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
||||||
</section>
|
</section>
|
||||||
|
@ -8,7 +8,7 @@ import { ProjectService, ProjectDefaultService} from '../service/project.service
|
|||||||
import { SERVICE_CONFIG, IServiceConfig} from '../service.config';
|
import { SERVICE_CONFIG, IServiceConfig} from '../service.config';
|
||||||
import { SystemInfo } from '../service/interface';
|
import { SystemInfo } from '../service/interface';
|
||||||
import { Project } from './project';
|
import { Project } from './project';
|
||||||
|
import { UserPermissionService, UserPermissionDefaultService } from '../service/permission.service';
|
||||||
describe('ProjectPolicyConfigComponent', () => {
|
describe('ProjectPolicyConfigComponent', () => {
|
||||||
|
|
||||||
let systemInfoService: SystemInfoService;
|
let systemInfoService: SystemInfoService;
|
||||||
@ -102,7 +102,8 @@ describe('ProjectPolicyConfigComponent', () => {
|
|||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService}
|
{ provide: SystemInfoService, useClass: SystemInfoDefaultService},
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@ -13,6 +13,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { Project } from './project';
|
import { Project } from './project';
|
||||||
import {SystemInfo, SystemInfoService} from '../service/index';
|
import {SystemInfo, SystemInfoService} from '../service/index';
|
||||||
|
import { UserPermissionService } from '../service/permission.service';
|
||||||
|
import { USERSTATICPERMISSION } from '../service/permission-static';
|
||||||
|
|
||||||
export class ProjectPolicy {
|
export class ProjectPolicy {
|
||||||
Public: boolean;
|
Public: boolean;
|
||||||
@ -56,7 +58,7 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
|||||||
systemInfo: SystemInfo;
|
systemInfo: SystemInfo;
|
||||||
orgProjectPolicy = new ProjectPolicy();
|
orgProjectPolicy = new ProjectPolicy();
|
||||||
projectPolicy = new ProjectPolicy();
|
projectPolicy = new ProjectPolicy();
|
||||||
|
hasChangeConfigRole: boolean;
|
||||||
severityOptions = [
|
severityOptions = [
|
||||||
{severity: 'high', severityLevel: 'VULNERABILITY.SEVERITY.HIGH'},
|
{severity: 'high', severityLevel: 'VULNERABILITY.SEVERITY.HIGH'},
|
||||||
{severity: 'medium', severityLevel: 'VULNERABILITY.SEVERITY.MEDIUM'},
|
{severity: 'medium', severityLevel: 'VULNERABILITY.SEVERITY.MEDIUM'},
|
||||||
@ -69,6 +71,7 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
|||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
|
private userPermission: UserPermissionService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -85,8 +88,14 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
|||||||
|
|
||||||
// retrive project level policy data
|
// retrive project level policy data
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
|
this.getPermission();
|
||||||
|
}
|
||||||
|
private getPermission(): void {
|
||||||
|
this.userPermission.getPermission(this.projectId,
|
||||||
|
USERSTATICPERMISSION.CONFIGURATION.KEY, USERSTATICPERMISSION.CONFIGURATION.VALUE.UPDATE).subscribe(permissins => {
|
||||||
|
this.hasChangeConfigRole = permissins as boolean;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get withNotary(): boolean {
|
public get withNotary(): boolean {
|
||||||
return this.systemInfo ? this.systemInfo.with_notary : false;
|
return this.systemInfo ? this.systemInfo.with_notary : false;
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<hbr-list-replication-rule #listReplicationRule [projectId]="projectId" [isSystemAdmin]="isSystemAdmin" (replicateManual)=replicateManualRule($event)
|
<hbr-list-replication-rule #listReplicationRule [projectId]="projectId" (replicateManual)=replicateManualRule($event)
|
||||||
(selectOne)="selectOneRule($event)" (hideJobs)="hideJobs()" (openNewRule)="openModal()" (editOne)="openEditRule($event)"
|
(selectOne)="selectOneRule($event)" (hideJobs)="hideJobs()" (openNewRule)="openModal()" (editOne)="openEditRule($event)"
|
||||||
(reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"></hbr-list-replication-rule>
|
(reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"
|
||||||
|
[hasCreateReplicationPermission]="hasCreateReplicationPermission"
|
||||||
|
[hasUpdateReplicationPermission]="hasUpdateReplicationPermission"
|
||||||
|
[hasDeleteReplicationPermission]="hasDeleteReplicationPermission"
|
||||||
|
[hasExecuteReplicationPermission]="hasExecuteReplicationPermission"
|
||||||
|
></hbr-list-replication-rule>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 jobList" [hidden]='hiddenJobList'>
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 jobList" [hidden]='hiddenJobList'>
|
||||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
@ -104,6 +104,10 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
|||||||
@Input() isSystemAdmin: boolean;
|
@Input() isSystemAdmin: boolean;
|
||||||
@Input() withAdmiral: boolean;
|
@Input() withAdmiral: boolean;
|
||||||
@Input() withReplicationJob: boolean;
|
@Input() withReplicationJob: boolean;
|
||||||
|
@Input() hasCreateReplicationPermission: boolean;
|
||||||
|
@Input() hasUpdateReplicationPermission: boolean;
|
||||||
|
@Input() hasDeleteReplicationPermission: boolean;
|
||||||
|
@Input() hasExecuteReplicationPermission: boolean;
|
||||||
|
|
||||||
@Output() redirect = new EventEmitter<ReplicationRule>();
|
@Output() redirect = new EventEmitter<ReplicationRule>();
|
||||||
@Output() openCreateRule = new EventEmitter<any>();
|
@Output() openCreateRule = new EventEmitter<any>();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<clr-icon shape="download"></clr-icon>
|
<clr-icon shape="download"></clr-icon>
|
||||||
{{'CONFIG.REGISTRY_CERTIFICATE' | translate | uppercase}}
|
{{'CONFIG.REGISTRY_CERTIFICATE' | translate | uppercase}}
|
||||||
</a>
|
</a>
|
||||||
<hbr-push-image-button class="push-image-button" [registryUrl]="registryUrl" [projectName]="projectName"></hbr-push-image-button>
|
<hbr-push-image-button class="push-image-button" *ngIf="hasCreateRepositoryPermission" [registryUrl]="registryUrl" [projectName]="projectName"></hbr-push-image-button>
|
||||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filterEvt)="doSearchRepoNames($event)" [currentValue]="lastFilteredRepoName"></hbr-filter>
|
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filterEvt)="doSearchRepoNames($event)" [currentValue]="lastFilteredRepoName"></hbr-filter>
|
||||||
<span class="card-btn" (click)="showCard(true)" (mouseenter) ="mouseEnter('card') " (mouseleave) ="mouseLeave('card')">
|
<span class="card-btn" (click)="showCard(true)" (mouseenter) ="mouseEnter('card') " (mouseleave) ="mouseLeave('card')">
|
||||||
<clr-icon [ngClass]="{'is-highlight': isCardView || isHovering('card') }" shape="view-cards"></clr-icon>
|
<clr-icon [ngClass]="{'is-highlight': isCardView || isHovering('card') }" shape="view-cards"></clr-icon>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="provisionItemEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DEPLOY' | translate}}</button>
|
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="provisionItemEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DEPLOY' | translate}}</button>
|
||||||
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="itemAddInfoEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.ADDITIONAL_INFO' | translate}}</button>
|
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="itemAddInfoEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.ADDITIONAL_INFO' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" (click)="deleteRepos(selectedRow)" [disabled]="!(selectedRow.length && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" (click)="deleteRepos(selectedRow)" [disabled]="!(selectedRow.length)|| !hasDeleteRepositoryPermission"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
||||||
|
@ -24,13 +24,16 @@ import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
|
|||||||
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||||
import { OperationService } from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
import {ProjectDefaultService, ProjectService, RetagDefaultService, RetagService} from "../service";
|
import {ProjectDefaultService, ProjectService, RetagDefaultService, RetagService} from "../service";
|
||||||
|
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
|
import { of } from "rxjs";
|
||||||
describe('RepositoryComponentGridview (inline template)', () => {
|
describe('RepositoryComponentGridview (inline template)', () => {
|
||||||
|
|
||||||
let compRepo: RepositoryGridviewComponent;
|
let compRepo: RepositoryGridviewComponent;
|
||||||
let fixtureRepo: ComponentFixture<RepositoryGridviewComponent>;
|
let fixtureRepo: ComponentFixture<RepositoryGridviewComponent>;
|
||||||
let repositoryService: RepositoryService;
|
let repositoryService: RepositoryService;
|
||||||
let systemInfoService: SystemInfoService;
|
let systemInfoService: SystemInfoService;
|
||||||
|
let userPermissionService: UserPermissionService;
|
||||||
|
|
||||||
let spyRepos: jasmine.Spy;
|
let spyRepos: jasmine.Spy;
|
||||||
let spySystemInfo: jasmine.Spy;
|
let spySystemInfo: jasmine.Spy;
|
||||||
@ -72,7 +75,8 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
metadata: {xTotalCount: 2},
|
metadata: {xTotalCount: 2},
|
||||||
data: mockRepoData
|
data: mockRepoData
|
||||||
};
|
};
|
||||||
|
let mockHasCreateRepositoryPermission: boolean = true;
|
||||||
|
let mockHasDeleteRepositoryPermission: boolean = true;
|
||||||
// let mockTagData: Tag[] = [
|
// let mockTagData: Tag[] = [
|
||||||
// {
|
// {
|
||||||
// "digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
// "digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
||||||
@ -120,6 +124,7 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
{ provide: RetagService, useClass: RetagDefaultService },
|
{ provide: RetagService, useClass: RetagDefaultService },
|
||||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||||
{ provide: OperationService }
|
{ provide: OperationService }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@ -136,9 +141,17 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
|||||||
|
|
||||||
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepo));
|
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepo));
|
||||||
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
|
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
|
||||||
|
|
||||||
|
|
||||||
|
userPermissionService = fixtureRepo.debugElement.injector.get(UserPermissionService);
|
||||||
|
spyOn(userPermissionService, "getPermission")
|
||||||
|
.withArgs(compRepo.projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.CREATE )
|
||||||
|
.and.returnValue(of(mockHasCreateRepositoryPermission))
|
||||||
|
.withArgs(compRepo.projectId, USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE )
|
||||||
|
.and.returnValue(of(mockHasDeleteRepositoryPermission));
|
||||||
fixtureRepo.detectChanges();
|
fixtureRepo.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(compRepo).toBeTruthy();
|
expect(compRepo).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
@ -14,8 +14,8 @@ import {
|
|||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { forkJoin } from "rxjs";
|
import { forkJoin } from "rxjs";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import {TranslateService} from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import {Comparator, State} from "../service/interface";
|
import { Comparator, State } from "../service/interface";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Repository,
|
Repository,
|
||||||
@ -26,17 +26,19 @@ import {
|
|||||||
RepositoryItem,
|
RepositoryItem,
|
||||||
TagService
|
TagService
|
||||||
} from '../service/index';
|
} from '../service/index';
|
||||||
import {ErrorHandler} from '../error-handler/error-handler';
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
import {toPromise, CustomComparator, DEFAULT_PAGE_SIZE, calculatePage, doFiltering, doSorting, clone} from '../utils';
|
import { toPromise, CustomComparator, DEFAULT_PAGE_SIZE, calculatePage, doFiltering, doSorting, clone } from '../utils';
|
||||||
import {ConfirmationState, ConfirmationTargets, ConfirmationButtons} from '../shared/shared.const';
|
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||||
import {ConfirmationDialogComponent} from '../confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
import {ConfirmationMessage} from '../confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||||
import {ConfirmationAcknowledgement} from '../confirmation-dialog/confirmation-state-message';
|
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||||
import {Tag} from '../service/interface';
|
import { Tag } from '../service/interface';
|
||||||
import {GridViewComponent} from '../gridview/grid-view.component';
|
import { GridViewComponent } from '../gridview/grid-view.component';
|
||||||
import {OperationService} from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
import {OperateInfo, OperationState, operateChanges} from "../operation/operate";
|
import { UserPermissionService } from "../service/permission.service";
|
||||||
import {SERVICE_CONFIG, IServiceConfig, downloadUrl } from '../service.config';
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
|
import { OperateInfo, OperationState, operateChanges } from "../operation/operate";
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig, downloadUrl } from '../service.config';
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-repository-gridview",
|
selector: "hbr-repository-gridview",
|
||||||
templateUrl: "./repository-gridview.component.html",
|
templateUrl: "./repository-gridview.component.html",
|
||||||
@ -80,19 +82,21 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
confirmationDialog: ConfirmationDialogComponent;
|
confirmationDialog: ConfirmationDialogComponent;
|
||||||
|
|
||||||
@ViewChild("gridView") gridView: GridViewComponent;
|
@ViewChild("gridView") gridView: GridViewComponent;
|
||||||
|
hasCreateRepositoryPermission: boolean;
|
||||||
|
hasDeleteRepositoryPermission: boolean;
|
||||||
constructor(@Inject(SERVICE_CONFIG) private configInfo: IServiceConfig,
|
constructor(@Inject(SERVICE_CONFIG) private configInfo: IServiceConfig,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private repositoryService: RepositoryService,
|
private repositoryService: RepositoryService,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
private tagService: TagService,
|
private tagService: TagService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private ref: ChangeDetectorRef,
|
public userPermissionService: UserPermissionService,
|
||||||
private router: Router) {
|
private ref: ChangeDetectorRef,
|
||||||
if (this.configInfo && this.configInfo.systemInfoEndpoint) {
|
private router: Router) {
|
||||||
this.downloadLink = this.configInfo.systemInfoEndpoint + "/getcert";
|
if (this.configInfo && this.configInfo.systemInfoEndpoint) {
|
||||||
}
|
this.downloadLink = this.configInfo.systemInfoEndpoint + "/getcert";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get registryUrl(): string {
|
public get registryUrl(): string {
|
||||||
@ -142,6 +146,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.lastFilteredRepoName = "";
|
this.lastFilteredRepoName = "";
|
||||||
|
this.getHelmChartVersionPermission(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||||
@ -182,8 +187,8 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
if (this.signedCon[repo.name].length !== 0) {
|
if (this.signedCon[repo.name].length !== 0) {
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED')).subscribe(res => {
|
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED')).subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return toPromise<number>(this.repositoryService
|
return toPromise<number>(this.repositoryService
|
||||||
.deleteRepository(repo.name))
|
.deleteRepository(repo.name))
|
||||||
@ -193,24 +198,24 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.status === "412") {
|
if (error.status === "412") {
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
this.translateService.get('REPOSITORY.TAGS_SIGNED')).subscribe(res => {
|
this.translateService.get('REPOSITORY.TAGS_SIGNED')).subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (error.status === 503) {
|
||||||
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
|
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
||||||
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||||
|
operateChanges(operMessage, OperationState.failure, res);
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (error.status === 503) {
|
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
|
||||||
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
|
||||||
operateChanges(operMessage, OperationState.failure, res);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +224,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
let st: State = this.currentState;
|
let st: State = this.currentState;
|
||||||
if (!st) {
|
if (!st) {
|
||||||
st = {page: {}};
|
st = { page: {} };
|
||||||
}
|
}
|
||||||
st.page.size = this.pageSize;
|
st.page.size = this.pageSize;
|
||||||
st.page.from = 0;
|
st.page.from = 0;
|
||||||
@ -299,8 +304,8 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
return toPromise<Tag[]>(this.tagService.getTags(repo.name))
|
return toPromise<Tag[]>(this.tagService.getTags(repo.name))
|
||||||
.then(items => {
|
.then(items => {
|
||||||
if (items.some((t: Tag) => {
|
if (items.some((t: Tag) => {
|
||||||
return t.name === 'latest';
|
return t.name === 'latest';
|
||||||
})) {
|
})) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -449,7 +454,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
let st: State = this.currentState;
|
let st: State = this.currentState;
|
||||||
if (!st) {
|
if (!st) {
|
||||||
st = {page: {}};
|
st = { page: {} };
|
||||||
}
|
}
|
||||||
st.page.size = this.pageSize;
|
st.page.size = this.pageSize;
|
||||||
st.page.from = (targetPageNumber - 1) * this.pageSize;
|
st.page.from = (targetPageNumber - 1) * this.pageSize;
|
||||||
@ -497,4 +502,16 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
|||||||
return this.listHover;
|
return this.listHover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getHelmChartVersionPermission(projectId: number): void {
|
||||||
|
|
||||||
|
let hasCreateRepositoryPermission = this.userPermissionService.getPermission(this.projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.CREATE);
|
||||||
|
let hasDeleteRepositoryPermission = this.userPermissionService.getPermission(this.projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.DELETE);
|
||||||
|
forkJoin(hasCreateRepositoryPermission, hasDeleteRepositoryPermission).subscribe(permissions => {
|
||||||
|
this.hasCreateRepositoryPermission = permissions[0] as boolean;
|
||||||
|
this.hasDeleteRepositoryPermission = permissions[1] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
<div id=images-container>
|
<div id=images-container>
|
||||||
<hbr-tag ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)"
|
<hbr-tag ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)"
|
||||||
class="sub-grid-custom" [repoName]="repoName" [registryUrl]="registryUrl" [withNotary]="withNotary"
|
class="sub-grid-custom" [repoName]="repoName" [registryUrl]="registryUrl" [withNotary]="withNotary"
|
||||||
[withClair]="withClair" [withAdmiral]="withAdmiral" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole"
|
[withClair]="withClair" [withAdmiral]="withAdmiral" [hasSignedIn]="hasSignedIn"
|
||||||
[isGuest]="isGuest" [projectId]="projectId" [memberRoleID]="memberRoleID"></hbr-tag>
|
[isGuest]="isGuest" [projectId]="projectId" [memberRoleID]="memberRoleID"></hbr-tag>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -27,6 +27,7 @@ import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
|||||||
import { LabelDefaultService, LabelService } from "../service/label.service";
|
import { LabelDefaultService, LabelService } from "../service/label.service";
|
||||||
import { OperationService } from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
import { ProjectDefaultService, ProjectService, RetagDefaultService, RetagService } from "../service";
|
import { ProjectDefaultService, ProjectService, RetagDefaultService, RetagService } from "../service";
|
||||||
|
import { UserPermissionDefaultService, UserPermissionService } from "../service/permission.service";
|
||||||
|
|
||||||
|
|
||||||
class RouterStub {
|
class RouterStub {
|
||||||
@ -178,6 +179,7 @@ describe('RepositoryComponent (inline template)', () => {
|
|||||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
{ provide: RetagService, useClass: RetagDefaultService },
|
{ provide: RetagService, useClass: RetagDefaultService },
|
||||||
{ provide: LabelService, useClass: LabelDefaultService},
|
{ provide: LabelService, useClass: LabelDefaultService},
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService},
|
||||||
{ provide: ChannelService},
|
{ provide: ChannelService},
|
||||||
{ provide: OperationService }
|
{ provide: OperationService }
|
||||||
]
|
]
|
||||||
|
@ -13,3 +13,5 @@ export * from "./project.service";
|
|||||||
export * from "./label.service";
|
export * from "./label.service";
|
||||||
export * from "./helm-chart.service";
|
export * from "./helm-chart.service";
|
||||||
export * from "./retag.service";
|
export * from "./retag.service";
|
||||||
|
export * from "./permission.service";
|
||||||
|
export * from "./permission-static";
|
||||||
|
@ -119,8 +119,8 @@ export class Trigger {
|
|||||||
schedule_param:
|
schedule_param:
|
||||||
| any
|
| any
|
||||||
| {
|
| {
|
||||||
[key: string]: any | any[];
|
[key: string]: any | any[];
|
||||||
};
|
};
|
||||||
constructor(kind: string, param: any | { [key: string]: any | any[] }) {
|
constructor(kind: string, param: any | { [key: string]: any | any[] }) {
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.schedule_param = param;
|
this.schedule_param = param;
|
||||||
@ -395,8 +395,8 @@ export interface HelmChartSignature {
|
|||||||
* interface Manifest
|
* interface Manifest
|
||||||
*/
|
*/
|
||||||
export interface Manifest {
|
export interface Manifest {
|
||||||
manifset: Object;
|
manifset: Object;
|
||||||
config: string;
|
config: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RetagRequest {
|
export interface RetagRequest {
|
||||||
@ -426,10 +426,23 @@ export interface ClrDatagridFilterInterface<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated since 0.11 */
|
/** @deprecated since 0.11 */
|
||||||
export interface Comparator<T> extends ClrDatagridComparatorInterface<T> {}
|
export interface Comparator<T> extends ClrDatagridComparatorInterface<T> { }
|
||||||
/** @deprecated since 0.11 */
|
/** @deprecated since 0.11 */
|
||||||
export interface ClrFilter<T> extends ClrDatagridFilterInterface<T> {}
|
export interface ClrFilter<T> extends ClrDatagridFilterInterface<T> { }
|
||||||
/** @deprecated since 0.11 */
|
/** @deprecated since 0.11 */
|
||||||
export interface State extends ClrDatagridStateInterface {}
|
export interface State extends ClrDatagridStateInterface { }
|
||||||
export interface Modal extends ClrModal {}
|
export interface Modal extends ClrModal { }
|
||||||
export const Modal = ClrModal;
|
export const Modal = ClrModal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The access user privilege from serve.
|
||||||
|
*
|
||||||
|
**
|
||||||
|
* interface UserPrivilegeServe
|
||||||
|
*/
|
||||||
|
export interface UserPrivilegeServeItem {
|
||||||
|
[key: string]: any | any[];
|
||||||
|
resource: string;
|
||||||
|
action: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
134
src/portal/lib/src/service/permission-static.ts
Normal file
134
src/portal/lib/src/service/permission-static.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
export const USERSTATICPERMISSION = {
|
||||||
|
"PROJECT": {
|
||||||
|
'KEY': 'project',
|
||||||
|
'VALUE': {
|
||||||
|
"DELETE": "delete"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MEMBER": {
|
||||||
|
'KEY': 'member',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"UPDATE": "update",
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LOG": {
|
||||||
|
'KEY': 'log',
|
||||||
|
'VALUE': {
|
||||||
|
"LIST": "list"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPLICATION": {
|
||||||
|
'KEY': 'replication',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"UPDATE": "update",
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPLICATION_JOB": {
|
||||||
|
'KEY': 'replication-job',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LABEL": {
|
||||||
|
'KEY': 'label',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"UPDATE": "update",
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CONFIGURATION": {
|
||||||
|
'KEY': 'configuration',
|
||||||
|
'VALUE': {
|
||||||
|
"UPDATE": "update",
|
||||||
|
"READ": "read",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPOSITORY": {
|
||||||
|
'KEY': 'repository',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"UPDATE": "update",
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
"PUSH": "push",
|
||||||
|
"PULL": "pull",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPOSITORY_TAG": {
|
||||||
|
'KEY': 'repository-tag',
|
||||||
|
'VALUE': {
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPOSITORY_TAG_SCAN_JOB": {
|
||||||
|
'KEY': 'repository-tag-scan-job',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"READ": "read",
|
||||||
|
"LIST": "list",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPOSITORY_TAG_VULNERABILITY": {
|
||||||
|
'KEY': 'repository-tag-vulnerability',
|
||||||
|
'VALUE': {
|
||||||
|
"LIST": "list",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPOSITORY_TAG_LABEL": {
|
||||||
|
'KEY': 'repository-tag-label',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"DELETE": "delete",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPOSITORY_TAG_MANIFEST": {
|
||||||
|
'KEY': 'repository-tag-manifest',
|
||||||
|
'VALUE': {
|
||||||
|
"READ": "read",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HELM_CHART": {
|
||||||
|
'KEY': 'helm-chart',
|
||||||
|
'VALUE': {
|
||||||
|
"UPLOAD": "create",
|
||||||
|
"DOWNLOAD": "read",
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HELM_CHART_VERSION": {
|
||||||
|
'KEY': 'helm-chart-version',
|
||||||
|
'VALUE': {
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
"READ": "read",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HELM_CHART_VERSION_LABEL": {
|
||||||
|
'KEY': 'helm-chart-version-label',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"DELETE": "delete",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ROBOT": {
|
||||||
|
'KEY': 'robot',
|
||||||
|
'VALUE': {
|
||||||
|
"CREATE": "create",
|
||||||
|
"UPDATE": "update",
|
||||||
|
"DELETE": "delete",
|
||||||
|
"LIST": "list",
|
||||||
|
"READ": "read",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
76
src/portal/lib/src/service/permission.service.ts
Normal file
76
src/portal/lib/src/service/permission.service.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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 { Injectable } from '@angular/core';
|
||||||
|
import { Observable, throwError as observableThrowError } from "rxjs";
|
||||||
|
import { map, catchError, shareReplay } from "rxjs/operators";
|
||||||
|
import { UserPrivilegeServeItem } from './interface';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const CACHE_SIZE = 1;
|
||||||
|
/**
|
||||||
|
* Get System privilege about current backend server.
|
||||||
|
* @abstract
|
||||||
|
* class UserPermissionService
|
||||||
|
*/
|
||||||
|
|
||||||
|
export abstract class UserPermissionService {
|
||||||
|
/**
|
||||||
|
* Get user privilege information.
|
||||||
|
* @abstract
|
||||||
|
* returns
|
||||||
|
*/
|
||||||
|
abstract getPermission(projectId, resource, action);
|
||||||
|
abstract clearPermissionCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserPermissionDefaultService extends UserPermissionService {
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
private permissionCache: Observable<object>;
|
||||||
|
private getPermissionFromBackend(projectId): Observable<object> {
|
||||||
|
const userPermissionUrl = `/api/users/current/permissions?scope=/project/${projectId}&relative=true`;
|
||||||
|
return this.http.get(userPermissionUrl);
|
||||||
|
}
|
||||||
|
private processingPermissionResult(responsePermission, resource, action): boolean {
|
||||||
|
const permissionList = responsePermission as UserPrivilegeServeItem[];
|
||||||
|
for (const privilegeItem of permissionList) {
|
||||||
|
if (privilegeItem.resource === resource && privilegeItem.action === action) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public getPermission(projectId, resource, action): Observable<boolean> {
|
||||||
|
|
||||||
|
if (!this.permissionCache) {
|
||||||
|
this.permissionCache = this.getPermissionFromBackend(projectId).pipe(
|
||||||
|
shareReplay(CACHE_SIZE));
|
||||||
|
}
|
||||||
|
return this.permissionCache.pipe(map(response => {
|
||||||
|
return this.processingPermissionResult(response, resource, action);
|
||||||
|
}))
|
||||||
|
.pipe(catchError(error => observableThrowError(error)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
public clearPermissionCache() {
|
||||||
|
this.permissionCache = null;
|
||||||
|
}
|
||||||
|
}
|
@ -91,12 +91,14 @@ export const LabelColor = [
|
|||||||
{ 'color': '#F57600', 'textColor': 'black' }, { 'color': '#FFDC0B', 'textColor': 'black' },
|
{ 'color': '#F57600', 'textColor': 'black' }, { 'color': '#FFDC0B', 'textColor': 'black' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'master': 'MEMBER.PROJECT_MASTER',
|
||||||
|
'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
||||||
|
|
||||||
export const DefaultHelmIcon = '/images/helm-gray.svg';
|
export const DefaultHelmIcon = '/images/helm-gray.svg';
|
||||||
|
|
||||||
export enum Roles {
|
export enum Roles {
|
||||||
PROJECT_ADMIN = 1,
|
PROJECT_ADMIN = 1,
|
||||||
|
PROJECT_MASTER = 4,
|
||||||
DEVELOPER = 2,
|
DEVELOPER = 2,
|
||||||
GUEST = 3,
|
GUEST = 3,
|
||||||
OTHER = 0,
|
OTHER = 0,
|
||||||
|
@ -91,15 +91,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<clr-tabs>
|
<clr-tabs>
|
||||||
<clr-tab *ngIf="withClair">
|
<clr-tab *ngIf="hasVulnerabilitiesListPermission">
|
||||||
<button clrTabLink class="btn btn-link nav-link" id="tag-vulnerability" [class.active]='isCurrentTabLink("tag-vulnerability")'
|
<button clrTabLink class="btn btn-link nav-link" id="tag-vulnerability" [class.active]='isCurrentTabLink("tag-vulnerability")'
|
||||||
type="button" (click)='tabLinkClick("tag-vulnerability")'>{{'REPOSITORY.VULNERABILITY' | translate}}</button>
|
type="button" (click)='tabLinkClick("tag-vulnerability")'>{{'REPOSITORY.VULNERABILITY' | translate}}</button>
|
||||||
<clr-tab-content id="content1" *clrIfActive="true">
|
<clr-tab-content id="content1" *clrIfActive="true">
|
||||||
<hbr-vulnerabilities-grid [repositoryId]="repositoryId" [tagId]="tagId" [withAdminRole]="withAdminRole"></hbr-vulnerabilities-grid>
|
<hbr-vulnerabilities-grid [repositoryId]="repositoryId" [tagId]="tagId"></hbr-vulnerabilities-grid>
|
||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
</clr-tab>
|
</clr-tab>
|
||||||
<clr-tab>
|
<clr-tab>
|
||||||
<button id="tag-history" clrTabLink class="btn btn-link nav-link" [class.active]='isCurrentTabLink("tag-history")'
|
<button *ngIf="hasBuildHistoryPermission" id="tag-history" clrTabLink class="btn btn-link nav-link" [class.active]='isCurrentTabLink("tag-history")'
|
||||||
type="button" (click)='tabLinkClick("tag-history")'>{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
|
type="button" (click)='tabLinkClick("tag-history")'>{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
|
||||||
<clr-tab-content *clrIfActive>
|
<clr-tab-content *clrIfActive>
|
||||||
<hbr-tag-history [repositoryId]="repositoryId" [tagId]="tagId">{{ 'REPOSITORY.BUILD_HISTORY' |
|
<hbr-tag-history [repositoryId]="repositoryId" [tagId]="tagId">{{ 'REPOSITORY.BUILD_HISTORY' |
|
||||||
|
@ -25,15 +25,19 @@ import { VULNERABILITY_SCAN_STATUS } from "../utils";
|
|||||||
import { VULNERABILITY_DIRECTIVES } from "../vulnerability-scanning/index";
|
import { VULNERABILITY_DIRECTIVES } from "../vulnerability-scanning/index";
|
||||||
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||||
import { ChannelService } from "../channel/channel.service";
|
import { ChannelService } from "../channel/channel.service";
|
||||||
|
import { of } from "rxjs";
|
||||||
import {
|
import {
|
||||||
JobLogService,
|
JobLogService,
|
||||||
JobLogDefaultService
|
JobLogDefaultService
|
||||||
} from "../service/job-log.service";
|
} from "../service/job-log.service";
|
||||||
|
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
|
|
||||||
describe("TagDetailComponent (inline template)", () => {
|
describe("TagDetailComponent (inline template)", () => {
|
||||||
let comp: TagDetailComponent;
|
let comp: TagDetailComponent;
|
||||||
let fixture: ComponentFixture<TagDetailComponent>;
|
let fixture: ComponentFixture<TagDetailComponent>;
|
||||||
let tagService: TagService;
|
let tagService: TagService;
|
||||||
|
let userPermissionService: UserPermissionService;
|
||||||
let scanningService: ScanningResultService;
|
let scanningService: ScanningResultService;
|
||||||
let spy: jasmine.Spy;
|
let spy: jasmine.Spy;
|
||||||
let vulSpy: jasmine.Spy;
|
let vulSpy: jasmine.Spy;
|
||||||
@ -83,13 +87,13 @@ describe("TagDetailComponent (inline template)", () => {
|
|||||||
let config: IServiceConfig = {
|
let config: IServiceConfig = {
|
||||||
repositoryBaseEndpoint: "/api/repositories/testing"
|
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||||
};
|
};
|
||||||
|
let mockHasVulnerabilitiesListPermission: boolean = false;
|
||||||
|
let mockHasBuildHistoryPermission: boolean = true;
|
||||||
let mockManifest: Manifest = {
|
let mockManifest: Manifest = {
|
||||||
manifset: {},
|
manifset: {},
|
||||||
// tslint:disable-next-line:max-line-length
|
// tslint:disable-next-line:max-line-length
|
||||||
config: `{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"ArgsEscaped":true,"Image":"sha256:fbef17698ac8605733924d5662f0cbfc0b27a51e83ab7d7a4b8d8a9a9fe0d1c2","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"30e1a2427aa2325727a092488d304505780501585a6ccf5a6a53c4d83a826101","container_config":{"Hostname":"30e1a2427aa2","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\\"/bin/sh\\"]"],"ArgsEscaped":true,"Image":"sha256:fbef17698ac8605733924d5662f0cbfc0b27a51e83ab7d7a4b8d8a9a9fe0d1c2","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2018-01-09T21:10:58.579708634Z","docker_version":"17.06.2-ce","history":[{"created":"2018-01-09T21:10:58.365737589Z","created_by":"/bin/sh -c #(nop) ADD file:093f0723fa46f6cdbd6f7bd146448bb70ecce54254c35701feeceb956414622f in / "},{"created":"2018-01-09T21:10:58.579708634Z","created_by":"/bin/sh -c #(nop) CMD [\\"/bin/sh\\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:cd7100a72410606589a54b932cabd804a17f9ae5b42a1882bd56d263e02b6215"]}}`
|
config: `{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"ArgsEscaped":true,"Image":"sha256:fbef17698ac8605733924d5662f0cbfc0b27a51e83ab7d7a4b8d8a9a9fe0d1c2","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"30e1a2427aa2325727a092488d304505780501585a6ccf5a6a53c4d83a826101","container_config":{"Hostname":"30e1a2427aa2","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\\"/bin/sh\\"]"],"ArgsEscaped":true,"Image":"sha256:fbef17698ac8605733924d5662f0cbfc0b27a51e83ab7d7a4b8d8a9a9fe0d1c2","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2018-01-09T21:10:58.579708634Z","docker_version":"17.06.2-ce","history":[{"created":"2018-01-09T21:10:58.365737589Z","created_by":"/bin/sh -c #(nop) ADD file:093f0723fa46f6cdbd6f7bd146448bb70ecce54254c35701feeceb956414622f in / "},{"created":"2018-01-09T21:10:58.579708634Z","created_by":"/bin/sh -c #(nop) CMD [\\"/bin/sh\\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:cd7100a72410606589a54b932cabd804a17f9ae5b42a1882bd56d263e02b6215"]}}`
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [SharedModule],
|
imports: [SharedModule],
|
||||||
@ -108,6 +112,7 @@ describe("TagDetailComponent (inline template)", () => {
|
|||||||
{ provide: JobLogService, useClass: JobLogDefaultService },
|
{ provide: JobLogService, useClass: JobLogDefaultService },
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: TagService, useClass: TagDefaultService },
|
{ provide: TagService, useClass: TagDefaultService },
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||||
{
|
{
|
||||||
provide: ScanningResultService,
|
provide: ScanningResultService,
|
||||||
useClass: ScanningResultDefaultService
|
useClass: ScanningResultDefaultService
|
||||||
@ -122,6 +127,8 @@ describe("TagDetailComponent (inline template)", () => {
|
|||||||
|
|
||||||
comp.tagId = "mock_tag";
|
comp.tagId = "mock_tag";
|
||||||
comp.repositoryId = "mock_repo";
|
comp.repositoryId = "mock_repo";
|
||||||
|
comp.projectId = 1;
|
||||||
|
|
||||||
|
|
||||||
tagService = fixture.debugElement.injector.get(TagService);
|
tagService = fixture.debugElement.injector.get(TagService);
|
||||||
spy = spyOn(tagService, "getTag").and.returnValues(
|
spy = spyOn(tagService, "getTag").and.returnValues(
|
||||||
@ -153,7 +160,14 @@ describe("TagDetailComponent (inline template)", () => {
|
|||||||
manifestSpy = spyOn(tagService, "getManifest").and.returnValues(
|
manifestSpy = spyOn(tagService, "getManifest").and.returnValues(
|
||||||
Promise.resolve(mockManifest)
|
Promise.resolve(mockManifest)
|
||||||
);
|
);
|
||||||
|
userPermissionService = fixture.debugElement.injector.get(UserPermissionService);
|
||||||
|
|
||||||
|
spyOn(userPermissionService, "getPermission")
|
||||||
|
.withArgs(comp.projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG_VULNERABILITY.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_VULNERABILITY.VALUE.LIST )
|
||||||
|
.and.returnValue(of(mockHasVulnerabilitiesListPermission))
|
||||||
|
.withArgs(comp.projectId, USERSTATICPERMISSION.REPOSITORY_TAG_MANIFEST.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_MANIFEST.VALUE.READ )
|
||||||
|
.and.returnValue(of(mockHasBuildHistoryPermission));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,6 +4,9 @@ import { TagService, Tag, VulnerabilitySeverity } from "../service/index";
|
|||||||
import { toPromise } from "../utils";
|
import { toPromise } from "../utils";
|
||||||
import { ErrorHandler } from "../error-handler/index";
|
import { ErrorHandler } from "../error-handler/index";
|
||||||
import { Label } from "../service/interface";
|
import { Label } from "../service/interface";
|
||||||
|
import { forkJoin } from "rxjs";
|
||||||
|
import { UserPermissionService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
|
|
||||||
const TabLinkContentMap: { [index: string]: string } = {
|
const TabLinkContentMap: { [index: string]: string } = {
|
||||||
"tag-history": "history",
|
"tag-history": "history",
|
||||||
@ -32,8 +35,6 @@ export class TagDetailComponent implements OnInit {
|
|||||||
withAdmiral: boolean;
|
withAdmiral: boolean;
|
||||||
@Input()
|
@Input()
|
||||||
withClair: boolean;
|
withClair: boolean;
|
||||||
@Input()
|
|
||||||
withAdminRole: boolean;
|
|
||||||
tagDetails: Tag = {
|
tagDetails: Tag = {
|
||||||
name: "--",
|
name: "--",
|
||||||
size: "--",
|
size: "--",
|
||||||
@ -51,11 +52,14 @@ export class TagDetailComponent implements OnInit {
|
|||||||
backEvt: EventEmitter<any> = new EventEmitter<any>();
|
backEvt: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
currentTabID = "tag-vulnerability";
|
currentTabID = "tag-vulnerability";
|
||||||
|
hasVulnerabilitiesListPermission: boolean;
|
||||||
|
hasBuildHistoryPermission: boolean;
|
||||||
|
@Input() projectId: number;
|
||||||
constructor(
|
constructor(
|
||||||
private tagService: TagService,
|
private tagService: TagService,
|
||||||
private errorHandler: ErrorHandler
|
private errorHandler: ErrorHandler,
|
||||||
) {}
|
private userPermissionService: UserPermissionService,
|
||||||
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.repositoryId && this.tagId) {
|
if (this.repositoryId && this.tagId) {
|
||||||
@ -90,6 +94,7 @@ export class TagDetailComponent implements OnInit {
|
|||||||
})
|
})
|
||||||
.catch(error => this.errorHandler.error(error));
|
.catch(error => this.errorHandler.error(error));
|
||||||
}
|
}
|
||||||
|
this.getTagPermissions(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBack(): void {
|
onBack(): void {
|
||||||
@ -173,4 +178,16 @@ export class TagDetailComponent implements OnInit {
|
|||||||
tabLinkClick(tabID: string) {
|
tabLinkClick(tabID: string) {
|
||||||
this.currentTabID = tabID;
|
this.currentTabID = tabID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTagPermissions(projectId: number): void {
|
||||||
|
|
||||||
|
const hasVulnerabilitiesListPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG_VULNERABILITY.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_VULNERABILITY.VALUE.LIST);
|
||||||
|
const hasBuildHistoryPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG_MANIFEST.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_MANIFEST.VALUE.READ);
|
||||||
|
forkJoin(hasVulnerabilitiesListPermission, hasBuildHistoryPermission).subscribe(permissions => {
|
||||||
|
this.hasVulnerabilitiesListPermission = permissions[0] as boolean;
|
||||||
|
this.hasBuildHistoryPermission = permissions[1] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(canScanNow(selectedRow) && selectedRow.length==1)" (click)="scanNow(selectedRow)"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(canScanNow(selectedRow) && selectedRow.length==1)" (click)="scanNow(selectedRow)"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" ><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" ><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||||
<clr-dropdown *ngIf="!withAdmiral">
|
<clr-dropdown *ngIf="!withAdmiral">
|
||||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1 && developerRoleOrAbove)" (click)="addLabels(selectedRow)" ><clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1)||!hasAddLabelImagePermission" (click)="addLabels(selectedRow)" ><clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}</button>
|
||||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||||
<div class="filter-grid">
|
<div class="filter-grid">
|
||||||
<label class="dropdown-header">{{'REPOSITORY.ADD_LABEL_TO_IMAGE' | translate}}</label>
|
<label class="dropdown-header">{{'REPOSITORY.ADD_LABEL_TO_IMAGE' | translate}}</label>
|
||||||
@ -75,8 +75,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="!withAdmiral" [disabled]="!(selectedRow.length===1 && guestRoleOrAbove)" (click)="retag(selectedRow)"><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.RETAG' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" *ngIf="!withAdmiral" [disabled]="!(selectedRow.length===1)|| !hasRetagImagePermission" (click)="retag(selectedRow)"><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.RETAG' | translate}}</button>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasProjectAdminRole" (click)="deleteTags(selectedRow)" [disabled]="!selectedRow.length"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasDeleteImagePermission" (click)="deleteTags(selectedRow)" [disabled]="!hasDeleteImagePermission||!selectedRow.length"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column class="flex-max-width" [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
<clr-dg-column class="flex-max-width" [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'size'">{{'REPOSITORY.SIZE' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'size'">{{'REPOSITORY.SIZE' | translate}}</clr-dg-column>
|
||||||
|
@ -20,16 +20,21 @@ import { ChannelService } from "../channel/index";
|
|||||||
import { CopyInputComponent } from "../push-image/copy-input.component";
|
import { CopyInputComponent } from "../push-image/copy-input.component";
|
||||||
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||||
import { LabelDefaultService, LabelService } from "../service/label.service";
|
import { LabelDefaultService, LabelService } from "../service/label.service";
|
||||||
|
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
import { OperationService } from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
|
import { Observable, of } from "rxjs";
|
||||||
|
|
||||||
describe("TagComponent (inline template)", () => {
|
describe("TagComponent (inline template)", () => {
|
||||||
|
|
||||||
let comp: TagComponent;
|
let comp: TagComponent;
|
||||||
let fixture: ComponentFixture<TagComponent>;
|
let fixture: ComponentFixture<TagComponent>;
|
||||||
let tagService: TagService;
|
let tagService: TagService;
|
||||||
|
let userPermissionService: UserPermissionService;
|
||||||
let spy: jasmine.Spy;
|
let spy: jasmine.Spy;
|
||||||
let spyLabels: jasmine.Spy;
|
let spyLabels: jasmine.Spy;
|
||||||
let spyLabels1: jasmine.Spy;
|
let spyLabels1: jasmine.Spy;
|
||||||
|
|
||||||
let mockTags: Tag[] = [
|
let mockTags: Tag[] = [
|
||||||
{
|
{
|
||||||
"digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
"digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
||||||
@ -95,7 +100,10 @@ describe("TagComponent (inline template)", () => {
|
|||||||
let config: IServiceConfig = {
|
let config: IServiceConfig = {
|
||||||
repositoryBaseEndpoint: "/api/repositories/testing"
|
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||||
};
|
};
|
||||||
|
let mockHasAddLabelImagePermission: boolean = true;
|
||||||
|
let mockHasRetagImagePermission: boolean = true;
|
||||||
|
let mockHasDeleteImagePermission: boolean = true;
|
||||||
|
let mockHasScanImagePermission: boolean = true;
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -119,6 +127,7 @@ describe("TagComponent (inline template)", () => {
|
|||||||
{ provide: RetagService, useClass: RetagDefaultService },
|
{ provide: RetagService, useClass: RetagDefaultService },
|
||||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
{ provide: LabelService, useClass: LabelDefaultService },
|
{ provide: LabelService, useClass: LabelDefaultService },
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||||
{ provide: OperationService }
|
{ provide: OperationService }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@ -130,10 +139,12 @@ describe("TagComponent (inline template)", () => {
|
|||||||
|
|
||||||
comp.projectId = 1;
|
comp.projectId = 1;
|
||||||
comp.repoName = "library/nginx";
|
comp.repoName = "library/nginx";
|
||||||
comp.hasProjectAdminRole = true;
|
comp.hasDeleteImagePermission = true;
|
||||||
|
comp.hasScanImagePermission = true;
|
||||||
comp.hasSignedIn = true;
|
comp.hasSignedIn = true;
|
||||||
comp.registryUrl = "http://registry.testing.com";
|
comp.registryUrl = "http://registry.testing.com";
|
||||||
comp.withNotary = false;
|
comp.withNotary = false;
|
||||||
|
comp.withAdmiral = false;
|
||||||
|
|
||||||
|
|
||||||
let labelService: LabelService;
|
let labelService: LabelService;
|
||||||
@ -141,6 +152,17 @@ describe("TagComponent (inline template)", () => {
|
|||||||
|
|
||||||
tagService = fixture.debugElement.injector.get(TagService);
|
tagService = fixture.debugElement.injector.get(TagService);
|
||||||
spy = spyOn(tagService, "getTags").and.returnValues(Promise.resolve(mockTags));
|
spy = spyOn(tagService, "getTags").and.returnValues(Promise.resolve(mockTags));
|
||||||
|
userPermissionService = fixture.debugElement.injector.get(UserPermissionService);
|
||||||
|
|
||||||
|
spyOn(userPermissionService, "getPermission")
|
||||||
|
.withArgs(comp.projectId, USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.VALUE.CREATE )
|
||||||
|
.and.returnValue(of(mockHasAddLabelImagePermission))
|
||||||
|
.withArgs(comp.projectId, USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.PULL )
|
||||||
|
.and.returnValue(of(mockHasRetagImagePermission))
|
||||||
|
.withArgs(comp.projectId, USERSTATICPERMISSION.REPOSITORY_TAG.KEY, USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE )
|
||||||
|
.and.returnValue(of(mockHasDeleteImagePermission))
|
||||||
|
.withArgs(comp.projectId, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE)
|
||||||
|
.and.returnValue(of(mockHasScanImagePermission));
|
||||||
|
|
||||||
labelService = fixture.debugElement.injector.get(LabelService);
|
labelService = fixture.debugElement.injector.get(LabelService);
|
||||||
|
|
||||||
@ -149,7 +171,6 @@ describe("TagComponent (inline template)", () => {
|
|||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should load data", async(() => {
|
it("should load data", async(() => {
|
||||||
expect(spy.calls.any).toBeTruthy();
|
expect(spy.calls.any).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
@ -169,3 +190,5 @@ describe("TagComponent (inline template)", () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@ import {
|
|||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
ElementRef, AfterViewInit
|
ElementRef, AfterViewInit
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import {Subject, forkJoin} from "rxjs";
|
import { Subject, forkJoin } from "rxjs";
|
||||||
import { debounceTime , distinctUntilChanged, finalize} from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, finalize } from 'rxjs/operators';
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { State, Comparator } from "../service/interface";
|
import { State, Comparator } from "../service/interface";
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ import { TagService, RetagService, VulnerabilitySeverity, RequestQueryParams } f
|
|||||||
import { ErrorHandler } from "../error-handler/error-handler";
|
import { ErrorHandler } from "../error-handler/error-handler";
|
||||||
import { ChannelService } from "../channel/index";
|
import { ChannelService } from "../channel/index";
|
||||||
import {
|
import {
|
||||||
ConfirmationTargets,
|
ConfirmationTargets,
|
||||||
ConfirmationState,
|
ConfirmationState,
|
||||||
ConfirmationButtons, Roles
|
ConfirmationButtons, Roles
|
||||||
} from "../shared/shared.const";
|
} from "../shared/shared.const";
|
||||||
|
|
||||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||||
@ -54,6 +54,8 @@ import {
|
|||||||
|
|
||||||
import { CopyInputComponent } from "../push-image/copy-input.component";
|
import { CopyInputComponent } from "../push-image/copy-input.component";
|
||||||
import { LabelService } from "../service/label.service";
|
import { LabelService } from "../service/label.service";
|
||||||
|
import { UserPermissionService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||||
import { OperationService } from "../operation/operation.service";
|
import { OperationService } from "../operation/operation.service";
|
||||||
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
||||||
@ -71,14 +73,13 @@ export interface LabelState {
|
|||||||
})
|
})
|
||||||
export class TagComponent implements OnInit, AfterViewInit {
|
export class TagComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
signedCon: {[key: string]: any | string[]} = {};
|
signedCon: { [key: string]: any | string[] } = {};
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() memberRoleID: number;
|
@Input() memberRoleID: number;
|
||||||
@Input() repoName: string;
|
@Input() repoName: string;
|
||||||
@Input() isEmbedded: boolean;
|
@Input() isEmbedded: boolean;
|
||||||
|
|
||||||
@Input() hasSignedIn: boolean;
|
@Input() hasSignedIn: boolean;
|
||||||
@Input() hasProjectAdminRole: boolean;
|
|
||||||
@Input() isGuest: boolean;
|
@Input() isGuest: boolean;
|
||||||
@Input() registryUrl: string;
|
@Input() registryUrl: string;
|
||||||
@Input() withNotary: boolean;
|
@Input() withNotary: boolean;
|
||||||
@ -116,8 +117,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
labelListOpen = false;
|
labelListOpen = false;
|
||||||
selectedTag: Tag[];
|
selectedTag: Tag[];
|
||||||
labelNameFilter: Subject<string> = new Subject<string> ();
|
labelNameFilter: Subject<string> = new Subject<string>();
|
||||||
stickLabelNameFilter: Subject<string> = new Subject<string> ();
|
stickLabelNameFilter: Subject<string> = new Subject<string>();
|
||||||
filterOnGoing: boolean;
|
filterOnGoing: boolean;
|
||||||
stickName = '';
|
stickName = '';
|
||||||
filterName = '';
|
filterName = '';
|
||||||
@ -144,10 +145,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
totalCount = 0;
|
totalCount = 0;
|
||||||
currentState: State;
|
currentState: State;
|
||||||
|
|
||||||
|
hasAddLabelImagePermission: boolean;
|
||||||
|
hasRetagImagePermission: boolean;
|
||||||
|
hasDeleteImagePermission: boolean;
|
||||||
|
hasScanImagePermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private tagService: TagService,
|
private tagService: TagService,
|
||||||
private retagService: RetagService,
|
private retagService: RetagService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
private labelService: LabelService,
|
private labelService: LabelService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private ref: ChangeDetectorRef,
|
private ref: ChangeDetectorRef,
|
||||||
@ -164,50 +170,50 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
this.errorHandler.error("Repo name cannot be unset.");
|
this.errorHandler.error("Repo name cannot be unset.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
this.lastFilteredTagName = '';
|
this.lastFilteredTagName = '';
|
||||||
|
|
||||||
this.labelNameFilter
|
this.labelNameFilter
|
||||||
.pipe(debounceTime(500))
|
.pipe(debounceTime(500))
|
||||||
.pipe(distinctUntilChanged())
|
.pipe(distinctUntilChanged())
|
||||||
.subscribe((name: string) => {
|
.subscribe((name: string) => {
|
||||||
if (this.filterName.length) {
|
if (this.filterName.length) {
|
||||||
this.filterOnGoing = true;
|
this.filterOnGoing = true;
|
||||||
|
|
||||||
this.imageFilterLabels.forEach(data => {
|
this.imageFilterLabels.forEach(data => {
|
||||||
if (data.label.name.indexOf(this.filterName) !== -1) {
|
if (data.label.name.indexOf(this.filterName) !== -1) {
|
||||||
data.show = true;
|
data.show = true;
|
||||||
} else {
|
} else {
|
||||||
data.show = false;
|
data.show = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setInterval(() => this.ref.markForCheck(), 200);
|
setInterval(() => this.ref.markForCheck(), 200);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stickLabelNameFilter
|
this.stickLabelNameFilter
|
||||||
.pipe(debounceTime(500))
|
.pipe(debounceTime(500))
|
||||||
.pipe(distinctUntilChanged())
|
.pipe(distinctUntilChanged())
|
||||||
.subscribe((name: string) => {
|
.subscribe((name: string) => {
|
||||||
if (this.stickName.length) {
|
if (this.stickName.length) {
|
||||||
this.filterOnGoing = true;
|
this.filterOnGoing = true;
|
||||||
|
|
||||||
this.imageStickLabels.forEach(data => {
|
this.imageStickLabels.forEach(data => {
|
||||||
if (data.label.name.indexOf(this.stickName) !== -1) {
|
if (data.label.name.indexOf(this.stickName) !== -1) {
|
||||||
data.show = true;
|
data.show = true;
|
||||||
} else {
|
} else {
|
||||||
data.show = false;
|
data.show = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setInterval(() => this.ref.markForCheck(), 200);
|
setInterval(() => this.ref.markForCheck(), 200);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.getImagePermissionRule(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
@ -219,7 +225,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
public get filterLabelPieceWidth() {
|
public get filterLabelPieceWidth() {
|
||||||
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
||||||
return len > 210 ? 210 : len;
|
return len > 210 ? 210 : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
doSearchTagNames(tagName: string) {
|
doSearchTagNames(tagName: string) {
|
||||||
this.lastFilteredTagName = tagName;
|
this.lastFilteredTagName = tagName;
|
||||||
@ -234,9 +240,9 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
st.page.to = this.pageSize - 1;
|
st.page.to = this.pageSize - 1;
|
||||||
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
|
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
|
||||||
if (selectedLab) {
|
if (selectedLab) {
|
||||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}, {property: 'labels.id', value: selectedLab.label.id}];
|
st.filters = [{ property: 'name', value: this.lastFilteredTagName }, { property: 'labels.id', value: selectedLab.label.id }];
|
||||||
} else {
|
} else {
|
||||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}];
|
st.filters = [{ property: 'name', value: this.lastFilteredTagName }];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clrLoad(st);
|
this.clrLoad(st);
|
||||||
@ -286,14 +292,14 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
toPromise<Label[]>(this.labelService.getGLabels()).then((res: Label[]) => {
|
toPromise<Label[]>(this.labelService.getGLabels()).then((res: Label[]) => {
|
||||||
if (res.length) {
|
if (res.length) {
|
||||||
res.forEach(data => {
|
res.forEach(data => {
|
||||||
this.imageLabels.push({'iconsShow': false, 'label': data, 'show': true});
|
this.imageLabels.push({ 'iconsShow': false, 'label': data, 'show': true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toPromise<Label[]>(this.labelService.getPLabels(this.projectId)).then((res1: Label[]) => {
|
toPromise<Label[]>(this.labelService.getPLabels(this.projectId)).then((res1: Label[]) => {
|
||||||
if (res1.length) {
|
if (res1.length) {
|
||||||
res1.forEach(data => {
|
res1.forEach(data => {
|
||||||
this.imageLabels.push({'iconsShow': false, 'label': data, 'show': true});
|
this.imageLabels.push({ 'iconsShow': false, 'label': data, 'show': true });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.imageFilterLabels = clone(this.imageLabels);
|
this.imageFilterLabels = clone(this.imageLabels);
|
||||||
@ -372,15 +378,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unSelectLabel(labelInfo: LabelState): void {
|
unSelectLabel(labelInfo: LabelState): void {
|
||||||
if (!this.inprogress) {
|
if (!this.inprogress) {
|
||||||
this.inprogress = true;
|
this.inprogress = true;
|
||||||
let labelId = labelInfo.label.id;
|
let labelId = labelInfo.label.id;
|
||||||
this.selectedRow = this.selectedTag;
|
this.selectedRow = this.selectedTag;
|
||||||
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
// insert the unselected label to groups with the same icons
|
// insert the unselected label to groups with the same icons
|
||||||
this.sortOperation(this.imageStickLabels, labelInfo);
|
this.sortOperation(this.imageStickLabels, labelInfo);
|
||||||
labelInfo.iconsShow = false;
|
labelInfo.iconsShow = false;
|
||||||
this.inprogress = false;
|
this.inprogress = false;
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@ -417,26 +423,26 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
data.iconsShow = true;
|
data.iconsShow = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.imageFilterLabels.splice(this.imageFilterLabels.indexOf(labelInfo), 1);
|
this.imageFilterLabels.splice(this.imageFilterLabels.indexOf(labelInfo), 1);
|
||||||
this.imageFilterLabels.unshift(labelInfo);
|
this.imageFilterLabels.unshift(labelInfo);
|
||||||
this.filterOneLabel = labelInfo.label;
|
this.filterOneLabel = labelInfo.label;
|
||||||
|
|
||||||
// reload data
|
// reload data
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
let st: State = this.currentState;
|
let st: State = this.currentState;
|
||||||
if (!st) {
|
if (!st) {
|
||||||
st = { page: {} };
|
st = { page: {} };
|
||||||
}
|
}
|
||||||
st.page.size = this.pageSize;
|
st.page.size = this.pageSize;
|
||||||
st.page.from = 0;
|
st.page.from = 0;
|
||||||
st.page.to = this.pageSize - 1;
|
st.page.to = this.pageSize - 1;
|
||||||
if (this.lastFilteredTagName) {
|
if (this.lastFilteredTagName) {
|
||||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}, {property: 'labels.id', value: labelId}];
|
st.filters = [{ property: 'name', value: this.lastFilteredTagName }, { property: 'labels.id', value: labelId }];
|
||||||
} else {
|
} else {
|
||||||
st.filters = [{property: 'labels.id', value: labelId}];
|
st.filters = [{ property: 'labels.id', value: labelId }];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clrLoad(st);
|
this.clrLoad(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
unFilterLabel(labelInfo: LabelState): void {
|
unFilterLabel(labelInfo: LabelState): void {
|
||||||
@ -456,7 +462,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
st.page.from = 0;
|
st.page.from = 0;
|
||||||
st.page.to = this.pageSize - 1;
|
st.page.to = this.pageSize - 1;
|
||||||
if (this.lastFilteredTagName) {
|
if (this.lastFilteredTagName) {
|
||||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}];
|
st.filters = [{ property: 'name', value: this.lastFilteredTagName }];
|
||||||
} else {
|
} else {
|
||||||
st.filters = [];
|
st.filters = [];
|
||||||
}
|
}
|
||||||
@ -480,7 +486,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
data.show = false;
|
data.show = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.openLabelFilterPanel = false;
|
this.openLabelFilterPanel = false;
|
||||||
this.openLabelFilterPiece = false;
|
this.openLabelFilterPiece = false;
|
||||||
}
|
}
|
||||||
@ -523,7 +529,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
retrieve() {
|
retrieve() {
|
||||||
this.tags = [];
|
this.tags = [];
|
||||||
let signatures: string[] = [] ;
|
let signatures: string[] = [];
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
toPromise<Tag[]>(this.tagService
|
toPromise<Tag[]>(this.tagService
|
||||||
@ -539,15 +545,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
components: {
|
components: {
|
||||||
total: 0,
|
total: 0,
|
||||||
summary: []
|
summary: []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (t.signature !== null) {
|
if (t.signature !== null) {
|
||||||
signatures.push(t.name);
|
signatures.push(t.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.tags = items;
|
this.tags = items;
|
||||||
let signedName: {[key: string]: string[]} = {};
|
let signedName: { [key: string]: string[] } = {};
|
||||||
signedName[this.repoName] = signatures;
|
signedName[this.repoName] = signatures;
|
||||||
this.signatureOutput.emit(signedName);
|
this.signatureOutput.emit(signedName);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@ -568,9 +574,9 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
if (Math.pow(1024, 1) <= size && size < Math.pow(1024, 2)) {
|
if (Math.pow(1024, 1) <= size && size < Math.pow(1024, 2)) {
|
||||||
return (size / Math.pow(1024, 1)).toFixed(2) + "KB";
|
return (size / Math.pow(1024, 1)).toFixed(2) + "KB";
|
||||||
} else if (Math.pow(1024, 2) <= size && size < Math.pow(1024, 3)) {
|
} else if (Math.pow(1024, 2) <= size && size < Math.pow(1024, 3)) {
|
||||||
return (size / Math.pow(1024, 2)).toFixed(2) + "MB";
|
return (size / Math.pow(1024, 2)).toFixed(2) + "MB";
|
||||||
} else if (Math.pow(1024, 3) <= size && size < Math.pow(1024, 4)) {
|
} else if (Math.pow(1024, 3) <= size && size < Math.pow(1024, 4)) {
|
||||||
return (size / Math.pow(1024, 3)).toFixed(2) + "GB";
|
return (size / Math.pow(1024, 3)).toFixed(2) + "GB";
|
||||||
} else {
|
} else {
|
||||||
return size + "B";
|
return size + "B";
|
||||||
}
|
}
|
||||||
@ -578,8 +584,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
retag(tags: Tag[]) {
|
retag(tags: Tag[]) {
|
||||||
if (tags && tags.length) {
|
if (tags && tags.length) {
|
||||||
this.retagDialogOpened = true;
|
this.retagDialogOpened = true;
|
||||||
this.retagSrcImage = this.repoName + ":" + tags[0].digest;
|
this.retagSrcImage = this.repoName + ":" + tags[0].digest;
|
||||||
} else {
|
} else {
|
||||||
this.errorHandler.error("One tag should be selected before retag.");
|
this.errorHandler.error("One tag should be selected before retag.");
|
||||||
}
|
}
|
||||||
@ -587,23 +593,23 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
onRetag() {
|
onRetag() {
|
||||||
this.retagService.retag({
|
this.retagService.retag({
|
||||||
targetProject: this.imageNameInput.projectName.value,
|
targetProject: this.imageNameInput.projectName.value,
|
||||||
targetRepo: this.imageNameInput.repoName.value,
|
targetRepo: this.imageNameInput.repoName.value,
|
||||||
targetTag: this.imageNameInput.tagName.value,
|
targetTag: this.imageNameInput.tagName.value,
|
||||||
srcImage: this.retagSrcImage,
|
srcImage: this.retagSrcImage,
|
||||||
override: true
|
override: true
|
||||||
})
|
})
|
||||||
.pipe(finalize(() => {
|
.pipe(finalize(() => {
|
||||||
this.retagDialogOpened = false;
|
this.retagDialogOpened = false;
|
||||||
this.imageNameInput.form.reset();
|
this.imageNameInput.form.reset();
|
||||||
}))
|
}))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
||||||
this.errorHandler.info(res);
|
this.errorHandler.info(res);
|
||||||
});
|
});
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTags(tags: Tag[]) {
|
deleteTags(tags: Tag[]) {
|
||||||
@ -631,8 +637,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||||
if (message &&
|
if (message &&
|
||||||
message.source === ConfirmationTargets.TAG
|
message.source === ConfirmationTargets.TAG
|
||||||
&& message.state === ConfirmationState.CONFIRMED) {
|
&& message.state === ConfirmationState.CONFIRMED) {
|
||||||
let tags: Tag[] = message.data;
|
let tags: Tag[] = message.data;
|
||||||
if (tags && tags.length) {
|
if (tags && tags.length) {
|
||||||
let promiseLists: any[] = [];
|
let promiseLists: any[] = [];
|
||||||
@ -660,27 +666,27 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
if (tag.signature) {
|
if (tag.signature) {
|
||||||
forkJoin(this.translateService.get("BATCH.DELETED_FAILURE"),
|
forkJoin(this.translateService.get("BATCH.DELETED_FAILURE"),
|
||||||
this.translateService.get("REPOSITORY.DELETION_SUMMARY_TAG_DENIED")).subscribe(res => {
|
this.translateService.get("REPOSITORY.DELETION_SUMMARY_TAG_DENIED")).subscribe(res => {
|
||||||
let wrongInfo: string = res[1] + "notary -s https://" + this.registryUrl +
|
let wrongInfo: string = res[1] + "notary -s https://" + this.registryUrl +
|
||||||
":4443 -d ~/.docker/trust remove -p " +
|
":4443 -d ~/.docker/trust remove -p " +
|
||||||
this.registryUrl + "/" + this.repoName +
|
this.registryUrl + "/" + this.repoName +
|
||||||
" " + name;
|
" " + name;
|
||||||
operateChanges(operMessage, OperationState.failure, wrongInfo);
|
operateChanges(operMessage, OperationState.failure, wrongInfo);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return toPromise<number>(this.tagService
|
return toPromise<number>(this.tagService
|
||||||
.deleteTag(this.repoName, tag.name))
|
.deleteTag(this.repoName, tag.name))
|
||||||
.then(
|
.then(
|
||||||
response => {
|
response => {
|
||||||
this.translateService.get("BATCH.DELETED_SUCCESS")
|
this.translateService.get("BATCH.DELETED_SUCCESS")
|
||||||
.subscribe(res => {
|
.subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.status === 503) {
|
if (error.status === 503) {
|
||||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||||
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||||
@ -744,13 +750,29 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
// Whether show the 'scan now' menu
|
// Whether show the 'scan now' menu
|
||||||
canScanNow(t: Tag[]): boolean {
|
canScanNow(t: Tag[]): boolean {
|
||||||
if (!this.withClair) { return false; }
|
if (!this.withClair) { return false; }
|
||||||
if (!this.hasProjectAdminRole) { return false; }
|
if (!this.hasScanImagePermission) { return false; }
|
||||||
let st: string = this.scanStatus(t[0]);
|
let st: string = this.scanStatus(t[0]);
|
||||||
|
|
||||||
return st !== VULNERABILITY_SCAN_STATUS.pending &&
|
return st !== VULNERABILITY_SCAN_STATUS.pending &&
|
||||||
st !== VULNERABILITY_SCAN_STATUS.running;
|
st !== VULNERABILITY_SCAN_STATUS.running;
|
||||||
}
|
}
|
||||||
|
getImagePermissionRule(projectId: number): void {
|
||||||
|
let hasAddLabelImagePermission = this.userPermissionService.getPermission(projectId, USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.KEY,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.VALUE.CREATE);
|
||||||
|
let hasRetagImagePermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.PULL);
|
||||||
|
let hasDeleteImagePermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG.KEY, USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE);
|
||||||
|
let hasScanImagePermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE);
|
||||||
|
forkJoin(hasAddLabelImagePermission, hasRetagImagePermission, hasDeleteImagePermission, hasScanImagePermission)
|
||||||
|
.subscribe(permissions => {
|
||||||
|
this.hasAddLabelImagePermission = permissions[0] as boolean;
|
||||||
|
this.hasRetagImagePermission = permissions[1] as boolean;
|
||||||
|
this.hasDeleteImagePermission = permissions[2] as boolean;
|
||||||
|
this.hasScanImagePermission = permissions[3] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error) );
|
||||||
|
}
|
||||||
// Trigger scan
|
// Trigger scan
|
||||||
scanNow(t: Tag[]): void {
|
scanNow(t: Tag[]): void {
|
||||||
if (t && t.length) {
|
if (t && t.length) {
|
||||||
@ -763,14 +785,6 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
// pull command
|
// pull command
|
||||||
onCpError($event: any): void {
|
onCpError($event: any): void {
|
||||||
this.copyInput.setPullCommendShow();
|
this.copyInput.setPullCommendShow();
|
||||||
}
|
|
||||||
|
|
||||||
public get developerRoleOrAbove(): boolean {
|
|
||||||
return this.memberRoleID === Roles.DEVELOPER || this.hasProjectAdminRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get guestRoleOrAbove(): boolean {
|
|
||||||
return this.memberRoleID === Roles.GUEST || this.memberRoleID === Roles.DEVELOPER || this.hasProjectAdminRole;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,14 @@ export const HTTP_GET_OPTIONS: RequestOptions = new RequestOptions({
|
|||||||
"Pragma": 'no-cache'
|
"Pragma": 'no-cache'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
export const HTTP_GET_OPTIONS_CACHE: RequestOptions = new RequestOptions({
|
||||||
|
headers: new Headers({
|
||||||
|
"Content-Type": 'application/json',
|
||||||
|
"Accept": 'application/json',
|
||||||
|
"Cache-Control": 'no-cache',
|
||||||
|
"Pragma": 'no-cache',
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
export const FILE_UPLOAD_OPTION: RequestOptions = new RequestOptions({
|
export const FILE_UPLOAD_OPTION: RequestOptions = new RequestOptions({
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-datagrid>
|
<clr-datagrid>
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!withAdminRole" (click)="scanNow()"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasScanImagePermission" (click)="scanNow()"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'severity'">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'severity'">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
|
||||||
|
@ -8,6 +8,7 @@ import { ErrorHandler } from '../error-handler/index';
|
|||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { FilterComponent } from '../filter/index';
|
import { FilterComponent } from '../filter/index';
|
||||||
import {ChannelService} from "../channel/channel.service";
|
import {ChannelService} from "../channel/channel.service";
|
||||||
|
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||||
|
|
||||||
describe('ResultGridComponent (inline template)', () => {
|
describe('ResultGridComponent (inline template)', () => {
|
||||||
let component: ResultGridComponent;
|
let component: ResultGridComponent;
|
||||||
@ -29,7 +30,8 @@ describe('ResultGridComponent (inline template)', () => {
|
|||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
ChannelService,
|
ChannelService,
|
||||||
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,10 +5,12 @@ import {
|
|||||||
VulnerabilitySeverity
|
VulnerabilitySeverity
|
||||||
} from '../service/index';
|
} from '../service/index';
|
||||||
import { ErrorHandler } from '../error-handler/index';
|
import { ErrorHandler } from '../error-handler/index';
|
||||||
|
import { forkJoin } from "rxjs";
|
||||||
|
|
||||||
import { toPromise } from '../utils';
|
import { toPromise } from '../utils';
|
||||||
import {ChannelService} from "../channel/channel.service";
|
import { ChannelService } from "../channel/channel.service";
|
||||||
|
import { UserPermissionService } from "../service/permission.service";
|
||||||
|
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-vulnerabilities-grid',
|
selector: 'hbr-vulnerabilities-grid',
|
||||||
templateUrl: './result-grid.component.html',
|
templateUrl: './result-grid.component.html',
|
||||||
@ -20,12 +22,12 @@ export class ResultGridComponent implements OnInit {
|
|||||||
|
|
||||||
@Input() tagId: string;
|
@Input() tagId: string;
|
||||||
@Input() repositoryId: string;
|
@Input() repositoryId: string;
|
||||||
@Input() withAdminRole: boolean;
|
hasScanImagePermission: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private channel: ChannelService,
|
private channel: ChannelService,
|
||||||
private errorHandler: ErrorHandler
|
private userPermissionService: UserPermissionService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -79,4 +81,14 @@ export class ResultGridComponent implements OnInit {
|
|||||||
scanNow(): void {
|
scanNow(): void {
|
||||||
this.channel.publishScanEvent(this.repositoryId + "/" + this.tagId);
|
this.channel.publishScanEvent(this.repositoryId + "/" + this.tagId);
|
||||||
}
|
}
|
||||||
|
getScanPermissions(projectId: number): void {
|
||||||
|
|
||||||
|
const hasScanImagePermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE);
|
||||||
|
forkJoin(hasScanImagePermission).subscribe(permissions => {
|
||||||
|
this.hasScanImagePermission = permissions[0] as boolean;
|
||||||
|
}, error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { SharedModule } from '../shared/shared.module';
|
|||||||
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../utils';
|
import { VULNERABILITY_SCAN_STATUS } from '../utils';
|
||||||
|
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||||
|
|
||||||
describe('ResultTipComponent (inline template)', () => {
|
describe('ResultTipComponent (inline template)', () => {
|
||||||
let component: ResultTipComponent;
|
let component: ResultTipComponent;
|
||||||
@ -41,7 +42,8 @@ describe('ResultTipComponent (inline template)', () => {
|
|||||||
SharedModule
|
SharedModule
|
||||||
],
|
],
|
||||||
declarations: [ResultTipComponent],
|
declarations: [ResultTipComponent],
|
||||||
providers: [{ provide: SERVICE_CONFIG, useValue: testConfig }]
|
providers: [{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||||
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService }]
|
||||||
});
|
});
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
60
src/portal/package-lock.json
generated
60
src/portal/package-lock.json
generated
@ -134,13 +134,15 @@
|
|||||||
"version": "1.37.0",
|
"version": "1.37.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
|
||||||
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
|
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
"version": "2.1.21",
|
"version": "2.1.21",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
|
||||||
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
|
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "~1.37.0"
|
"mime-db": "~1.37.0"
|
||||||
}
|
}
|
||||||
@ -2757,9 +2759,9 @@
|
|||||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
|
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
|
||||||
},
|
},
|
||||||
"@types/jasmine": {
|
"@types/jasmine": {
|
||||||
"version": "2.8.8",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.8.tgz",
|
"resolved": "http://registry.npm.taobao.org/@types/jasmine/download/@types/jasmine-3.3.8.tgz",
|
||||||
"integrity": "sha512-OJSUxLaxXsjjhob2DBzqzgrkLmukM3+JMpRp0r0E4HTdT1nwDCWhaswjYxazPij6uOdzHCJfNbDjmQ1/rnNbCg==",
|
"integrity": "sha1-/Gq9kvcSFDFoXsmG8OyPd7Q5Cj4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/jasminewd2": {
|
"@types/jasminewd2": {
|
||||||
@ -6284,7 +6286,8 @@
|
|||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
@ -6305,12 +6308,14 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -6325,17 +6330,20 @@
|
|||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@ -6452,7 +6460,8 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
@ -6464,6 +6473,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@ -6478,6 +6488,7 @@
|
|||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@ -6485,12 +6496,14 @@
|
|||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
|
||||||
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
|
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
@ -6509,6 +6522,7 @@
|
|||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@ -6589,7 +6603,8 @@
|
|||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@ -6601,6 +6616,7 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@ -6686,7 +6702,8 @@
|
|||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@ -6722,6 +6739,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
@ -6741,6 +6759,7 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
@ -6784,12 +6803,14 @@
|
|||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
|
||||||
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
|
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -8703,10 +8724,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jasmine-core": {
|
"jasmine-core": {
|
||||||
"version": "2.99.1",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
|
"resolved": "http://registry.npm.taobao.org/jasmine-core/download/jasmine-core-3.3.0.tgz",
|
||||||
"integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
|
"integrity": "sha1-3qHNxjS8k8fg1K0nGF3zD6lxsQ4="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"jasmine-diff": {
|
"jasmine-diff": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
@ -8965,7 +8985,7 @@
|
|||||||
},
|
},
|
||||||
"karma-jasmine": {
|
"karma-jasmine": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz",
|
"resolved": "http://registry.npm.taobao.org/karma-jasmine/download/karma-jasmine-1.1.2.tgz",
|
||||||
"integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=",
|
"integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
"buffer": "^5.2.1",
|
"buffer": "^5.2.1",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
|
"jasmine-core": "^3.3.0",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ng-packagr": "^4.1.1",
|
"ng-packagr": "^4.1.1",
|
||||||
@ -65,18 +66,17 @@
|
|||||||
"@angular/compiler-cli": "^7.1.3",
|
"@angular/compiler-cli": "^7.1.3",
|
||||||
"@angular/language-service": "^7.1.3",
|
"@angular/language-service": "^7.1.3",
|
||||||
"@types/core-js": "^0.9.41",
|
"@types/core-js": "^0.9.41",
|
||||||
"@types/jasmine": "~2.8.6",
|
"@types/jasmine": "^3.3.1",
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"@types/node": "~8.9.4",
|
"@types/node": "~8.9.4",
|
||||||
"codelyzer": "~4.2.1",
|
"codelyzer": "~4.2.1",
|
||||||
"enhanced-resolve": "^3.0.0",
|
"enhanced-resolve": "^3.0.0",
|
||||||
"jasmine-core": "~2.99.1",
|
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma": "~1.7.1",
|
"karma": "~1.7.1",
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
"karma-cli": "^1.0.1",
|
"karma-cli": "^1.0.1",
|
||||||
"karma-coverage-istanbul-reporter": "~2.0.0",
|
"karma-coverage-istanbul-reporter": "~2.0.0",
|
||||||
"karma-jasmine": "~1.1.1",
|
"karma-jasmine": "^1.1.2",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"karma-mocha-reporter": "^2.2.4",
|
"karma-mocha-reporter": "^2.2.4",
|
||||||
"karma-remap-istanbul": "^0.6.0",
|
"karma-remap-istanbul": "^0.6.0",
|
||||||
|
@ -25,7 +25,10 @@
|
|||||||
<button id="config-label" clrTabLink>{{'CONFIG.LABEL' | translate }}</button>
|
<button id="config-label" clrTabLink>{{'CONFIG.LABEL' | translate }}</button>
|
||||||
<ng-template [(clrIfActive)]="labelActive">
|
<ng-template [(clrIfActive)]="labelActive">
|
||||||
<clr-tab-content id="system_label" *ngIf="!withAdmiral">
|
<clr-tab-content id="system_label" *ngIf="!withAdmiral">
|
||||||
<hbr-label [scope]="'g'"></hbr-label>
|
<hbr-label [scope]="'g'"
|
||||||
|
[hasCreateLabelPermission]="true"
|
||||||
|
[hasUpdateLabelPermission]="true"
|
||||||
|
[hasDeleteLabelPermission]="true"></hbr-label>
|
||||||
</clr-tab-content>
|
</clr-tab-content>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</clr-tab>
|
</clr-tab>
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
[chartName]='chartName'
|
[chartName]='chartName'
|
||||||
[roleName]='roleName'
|
[roleName]='roleName'
|
||||||
[hasSignedIn]='hasSignedIn'
|
[hasSignedIn]='hasSignedIn'
|
||||||
[projectRoleID]='project_member_role_id'
|
|
||||||
[hasProjectAdminRole]='hasProjectAdminRole'
|
|
||||||
(versionClickEvt)='onVersionClick($event)'
|
(versionClickEvt)='onVersionClick($event)'
|
||||||
(backEvt)='gotoChartList()'>
|
(backEvt)='gotoChartList()'>
|
||||||
</hbr-helm-chart-version>
|
</hbr-helm-chart-version>
|
||||||
|
@ -21,9 +21,7 @@ export class ListChartVersionsComponent implements OnInit {
|
|||||||
roleName: string;
|
roleName: string;
|
||||||
|
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
currentUser: SessionUser;
|
currentUser: SessionUser;
|
||||||
project_member_role_id: number;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -39,10 +37,8 @@ export class ListChartVersionsComponent implements OnInit {
|
|||||||
let resolverData = this.route.snapshot.data;
|
let resolverData = this.route.snapshot.data;
|
||||||
if (resolverData) {
|
if (resolverData) {
|
||||||
let project = <Project>(resolverData["projectResolver"]);
|
let project = <Project>(resolverData["projectResolver"]);
|
||||||
this.hasProjectAdminRole = project.has_project_admin_role;
|
|
||||||
this.roleName = project.role_name;
|
this.roleName = project.role_name;
|
||||||
this.projectName = project.name;
|
this.projectName = project.name;
|
||||||
this.project_member_role_id = project.current_user_role_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,5 @@
|
|||||||
[urlPrefix]='urlPrefix'
|
[urlPrefix]='urlPrefix'
|
||||||
[hasSignedIn]='hasSignedIn'
|
[hasSignedIn]='hasSignedIn'
|
||||||
[projectRoleID]='project_member_role_id'
|
[projectRoleID]='project_member_role_id'
|
||||||
[hasProjectAdminRole]='hasProjectAdminRole'
|
|
||||||
(chartClickEvt)='onChartClick($event)'>
|
(chartClickEvt)='onChartClick($event)'>
|
||||||
</hbr-helm-chart>
|
</hbr-helm-chart>
|
||||||
|
@ -17,7 +17,6 @@ export class ListChartsComponent implements OnInit {
|
|||||||
projectName: string;
|
projectName: string;
|
||||||
urlPrefix: string;
|
urlPrefix: string;
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
project_member_role_id: number;
|
project_member_role_id: number;
|
||||||
currentUser: SessionUser;
|
currentUser: SessionUser;
|
||||||
|
|
||||||
@ -35,7 +34,6 @@ export class ListChartsComponent implements OnInit {
|
|||||||
if (resolverData) {
|
if (resolverData) {
|
||||||
let project = <Project>(resolverData["projectResolver"]);
|
let project = <Project>(resolverData["projectResolver"]);
|
||||||
this.projectName = project.name;
|
this.projectName = project.name;
|
||||||
this.hasProjectAdminRole = project.has_project_admin_role;
|
|
||||||
this.project_member_role_id = project.current_user_role_id;
|
this.project_member_role_id = project.current_user_role_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
<input type="radio" name="member_role" id="checkrads_project_admin" [value]=1 [(ngModel)]="member.role_id">
|
<input type="radio" name="member_role" id="checkrads_project_admin" [value]=1 [(ngModel)]="member.role_id">
|
||||||
<label for="checkrads_project_admin">{{'MEMBER.PROJECT_ADMIN' | translate}}</label>
|
<label for="checkrads_project_admin">{{'MEMBER.PROJECT_ADMIN' | translate}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<input type="radio" name="member_role" id="checkrads_project_master" [value]=4 [(ngModel)]="member.role_id">
|
||||||
|
<label for="checkrads_project_master">{{'MEMBER.PROJECT_MASTER' | translate}}</label>
|
||||||
|
</div>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" name="member_role" id="checkrads_developer" [value]=2 [(ngModel)]="member.role_id">
|
<input type="radio" name="member_role" id="checkrads_developer" [value]=2 [(ngModel)]="member.role_id">
|
||||||
<label for="checkrads_developer">{{'MEMBER.DEVELOPER' | translate}}</label>
|
<label for="checkrads_developer">{{'MEMBER.DEVELOPER' | translate}}</label>
|
||||||
|
@ -13,21 +13,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button class="btn btn-sm btn-secondary" (click)="openAddMemberModal()" [disabled]="!hasProjectAdminRole">
|
<button class="btn btn-sm btn-secondary" (click)="openAddMemberModal()" [disabled]="!hasCreateMemberPermission">
|
||||||
<span><clr-icon shape="plus" size="16"></clr-icon> {{'MEMBER.USER' | translate }}</span>
|
<span><clr-icon shape="plus" size="16"></clr-icon> {{'MEMBER.USER' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm btn-secondary" (click)="openAddGroupModal()" [disabled]="!hasProjectAdminRole || !isLdapMode">
|
<button class="btn btn-sm btn-secondary" (click)="openAddGroupModal()" [disabled]="!hasCreateMemberPermission || !isLdapMode">
|
||||||
<span><clr-icon shape="plus" size="16"></clr-icon> {{'MEMBER.LDAP_GROUP' | translate}}</span>
|
<span><clr-icon shape="plus" size="16"></clr-icon> {{'MEMBER.LDAP_GROUP' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown id='member-action' [clrCloseMenuOnItemClick]="false" class="btn btn-sm btn-link" clrDropdownTrigger>
|
<clr-dropdown id='member-action' [clrCloseMenuOnItemClick]="false" class="btn btn-sm btn-link" clrDropdownTrigger>
|
||||||
<span>{{'MEMBER.ACTION' | translate}}<clr-icon shape="caret down"></clr-icon></span>
|
<span>{{'MEMBER.ACTION' | translate}}<clr-icon shape="caret down"></clr-icon></span>
|
||||||
<clr-dropdown-menu *clrIfOpen>
|
<clr-dropdown-menu *clrIfOpen>
|
||||||
<label class="dropdown-header">{{'MEMBER.SET_ROLE' | translate}}</label>
|
<label class="dropdown-header">{{'MEMBER.SET_ROLE' | translate}}</label>
|
||||||
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 1)" [disabled]="!(selectedRow.length && hasProjectAdminRole) || onlySelf">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 1)" [disabled]="!(selectedRow.length && hasUpdateMemberPermission) || onlySelf">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||||
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 2)" [disabled]="!(selectedRow.length && hasProjectAdminRole) || onlySelf">{{'MEMBER.DEVELOPER' | translate}}</button>
|
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 4)" [disabled]="!(selectedRow.length && hasUpdateMemberPermission) || onlySelf">{{'MEMBER.PROJECT_MASTER' | translate}}</button>
|
||||||
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 3)" [disabled]="!(selectedRow.length && hasProjectAdminRole) || onlySelf">{{'MEMBER.GUEST' | translate}}</button>
|
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 2)" [disabled]="!(selectedRow.length && hasUpdateMemberPermission) || onlySelf">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||||
|
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 3)" [disabled]="!(selectedRow.length && hasUpdateMemberPermission) || onlySelf">{{'MEMBER.GUEST' | translate}}</button>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<button clrDropdownItem (click)="openDeleteMembersDialog(selectedRow)" [disabled]="!(selectedRow.length && hasProjectAdminRole) || onlySelf">{{'MEMBER.REMOVE' | translate}}</button>
|
<button clrDropdownItem (click)="openDeleteMembersDialog(selectedRow)" [disabled]="!(selectedRow.length && hasDeleteMemberPermission) || onlySelf">{{'MEMBER.REMOVE' | translate}}</button>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import {finalize} from 'rxjs/operators';
|
import { finalize } from 'rxjs/operators';
|
||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -15,9 +15,9 @@ import {finalize} from 'rxjs/operators';
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core";
|
import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription, forkJoin } from "rxjs";
|
||||||
import {TranslateService} from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "@harbor/ui";
|
import { operateChanges, OperateInfo, OperationService, OperationState } from "@harbor/ui";
|
||||||
|
|
||||||
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
|
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
|
||||||
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from "../../shared/shared.const";
|
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from "../../shared/shared.const";
|
||||||
@ -31,7 +31,8 @@ import { SessionUser } from "../../shared/session-user";
|
|||||||
import { AddGroupComponent } from './add-group/add-group.component';
|
import { AddGroupComponent } from './add-group/add-group.component';
|
||||||
import { MemberService } from "./member.service";
|
import { MemberService } from "./member.service";
|
||||||
import { AddMemberComponent } from "./add-member/add-member.component";
|
import { AddMemberComponent } from "./add-member/add-member.component";
|
||||||
import {AppConfigService} from "../../app-config.service";
|
import { AppConfigService } from "../../app-config.service";
|
||||||
|
import { UserPermissionService, USERSTATICPERMISSION, ErrorHandler } from "@harbor/ui";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "member.component.html",
|
templateUrl: "member.component.html",
|
||||||
@ -46,7 +47,6 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
delSub: Subscription;
|
delSub: Subscription;
|
||||||
|
|
||||||
currentUser: SessionUser;
|
currentUser: SessionUser;
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
|
|
||||||
batchOps = 'delete';
|
batchOps = 'delete';
|
||||||
searchMember: string;
|
searchMember: string;
|
||||||
@ -65,7 +65,9 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
@ViewChild(AddGroupComponent)
|
@ViewChild(AddGroupComponent)
|
||||||
addGroupComponent: AddGroupComponent;
|
addGroupComponent: AddGroupComponent;
|
||||||
|
hasCreateMemberPermission: boolean;
|
||||||
|
hasUpdateMemberPermission: boolean;
|
||||||
|
hasDeleteMemberPermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -76,6 +78,8 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
private ref: ChangeDetectorRef) {
|
private ref: ChangeDetectorRef) {
|
||||||
|
|
||||||
this.delSub = OperateDialogService.confirmationConfirm$.subscribe(message => {
|
this.delSub = OperateDialogService.confirmationConfirm$.subscribe(message => {
|
||||||
@ -102,14 +106,12 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
this.projectId = +this.route.snapshot.parent.params["id"];
|
this.projectId = +this.route.snapshot.parent.params["id"];
|
||||||
// Get current user from registered resolver.
|
// Get current user from registered resolver.
|
||||||
this.currentUser = this.session.getCurrentUser();
|
this.currentUser = this.session.getCurrentUser();
|
||||||
let resolverData = this.route.snapshot.parent.data;
|
|
||||||
if (resolverData) {
|
|
||||||
this.hasProjectAdminRole = (<Project>resolverData["projectResolver"]).has_project_admin_role;
|
|
||||||
}
|
|
||||||
this.retrieve(this.projectId, "");
|
this.retrieve(this.projectId, "");
|
||||||
if (this.appConfigService.isLdapMode()) {
|
if (this.appConfigService.isLdapMode()) {
|
||||||
this.isLdapMode = true;
|
this.isLdapMode = true;
|
||||||
}
|
}
|
||||||
|
// get member permission rule
|
||||||
|
this.getMemberPermissionRule(this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
doSearch(searchMember: string) {
|
doSearch(searchMember: string) {
|
||||||
@ -126,23 +128,23 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
this.selectedRow = [];
|
this.selectedRow = [];
|
||||||
this.memberService
|
this.memberService
|
||||||
.listMembers(projectId, username).pipe(
|
.listMembers(projectId, username).pipe(
|
||||||
finalize(() => this.loading = false))
|
finalize(() => this.loading = false))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.members = response;
|
this.members = response;
|
||||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||||
setTimeout(() => clearInterval(hnd), 1000);
|
setTimeout(() => clearInterval(hnd), 1000);
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.router.navigate(["/harbor", "projects"]);
|
this.router.navigate(["/harbor", "projects"]);
|
||||||
this.messageHandlerService.handleError(error);
|
this.messageHandlerService.handleError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get onlySelf(): boolean {
|
get onlySelf(): boolean {
|
||||||
if (this.selectedRow.length === 1 &&
|
if (this.selectedRow.length === 1 &&
|
||||||
this.selectedRow[0].entity_type === 'u' &&
|
this.selectedRow[0].entity_type === 'u' &&
|
||||||
this.selectedRow[0].entity_id === this.currentUser.user_id) {
|
this.selectedRow[0].entity_id === this.currentUser.user_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -173,7 +175,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
addedGroup(result: boolean) {
|
addedGroup(result: boolean) {
|
||||||
this.searchMember = "";
|
this.searchMember = "";
|
||||||
this.retrieve(this.projectId, "");
|
this.retrieve(this.projectId, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
changeMembersRole(members: Member[], roleId: number) {
|
changeMembersRole(members: Member[], roleId: number) {
|
||||||
if (!members) {
|
if (!members) {
|
||||||
@ -182,9 +184,9 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
let changeOperate = (projectId: number, member: Member, ) => {
|
let changeOperate = (projectId: number, member: Member, ) => {
|
||||||
return this.memberService
|
return this.memberService
|
||||||
.changeMemberRole(projectId, member.id, roleId)
|
.changeMemberRole(projectId, member.id, roleId)
|
||||||
.then( () => this.batchChangeRoleInfos[member.id] = 'done')
|
.then(() => this.batchChangeRoleInfos[member.id] = 'done')
|
||||||
.catch(error => this.messageHandlerService.handleError(error + ": " + member.entity_name));
|
.catch(error => this.messageHandlerService.handleError(error + ": " + member.entity_name));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Preparation for members role change
|
// Preparation for members role change
|
||||||
@ -223,7 +225,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
ConfirmationTargets.PROJECT_MEMBER,
|
ConfirmationTargets.PROJECT_MEMBER,
|
||||||
ConfirmationButtons.DELETE_CANCEL
|
ConfirmationButtons.DELETE_CANCEL
|
||||||
);
|
);
|
||||||
this.OperateDialogService.openComfirmDialog(deletionMessage);
|
this.OperateDialogService.openComfirmDialog(deletionMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,15 +252,15 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
return this.memberService
|
return this.memberService
|
||||||
.deleteMember(projectId, member.id)
|
.deleteMember(projectId, member.id)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.translate.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
this.translate.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.success);
|
operateChanges(operMessage, OperationState.success);
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
|
||||||
operateChanges(operMessage, OperationState.failure, res);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||||
|
operateChanges(operMessage, OperationState.failure, res);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deleting member then wating for results
|
// Deleting member then wating for results
|
||||||
@ -270,4 +272,17 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
this.retrieve(this.projectId, "");
|
this.retrieve(this.projectId, "");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
getMemberPermissionRule(projectId: number): void {
|
||||||
|
let hasCreateMemberPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.MEMBER.KEY, USERSTATICPERMISSION.MEMBER.VALUE.CREATE);
|
||||||
|
let hasUpdateMemberPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.MEMBER.KEY, USERSTATICPERMISSION.MEMBER.VALUE.UPDATE);
|
||||||
|
let hasDeleteMemberPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.MEMBER.KEY, USERSTATICPERMISSION.MEMBER.VALUE.DELETE);
|
||||||
|
forkJoin(hasCreateMemberPermission, hasUpdateMemberPermission, hasDeleteMemberPermission).subscribe(MemberRule => {
|
||||||
|
this.hasCreateMemberPermission = MemberRule[0] as boolean;
|
||||||
|
this.hasUpdateMemberPermission = MemberRule[1] as boolean;
|
||||||
|
this.hasDeleteMemberPermission = MemberRule[2] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 config-top ">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 config-top ">
|
||||||
<hbr-project-policy-config [projectId]="projectId" [projectName]="projectName" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole"></hbr-project-policy-config>
|
<hbr-project-policy-config [projectId]="projectId" [projectName]="projectName" [hasSignedIn]="hasSignedIn"></hbr-project-policy-config>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -28,7 +28,6 @@ export class ProjectConfigComponent implements OnInit {
|
|||||||
projectName: string;
|
projectName: string;
|
||||||
currentUser: SessionUser;
|
currentUser: SessionUser;
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -42,7 +41,6 @@ export class ProjectConfigComponent implements OnInit {
|
|||||||
let resolverData = this.route.snapshot.parent.data;
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
if (resolverData) {
|
if (resolverData) {
|
||||||
let pro: Project = <Project>resolverData['projectResolver'];
|
let pro: Project = <Project>resolverData['projectResolver'];
|
||||||
this.hasProjectAdminRole = pro.has_project_admin_role;
|
|
||||||
this.projectName = pro.name;
|
this.projectName = pro.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,28 +4,28 @@
|
|||||||
<h1 class="custom-h2" sub-header-title>{{currentProject.name}} <span class="role-label" *ngIf="isMember">{{roleName | translate}}</span></h1>
|
<h1 class="custom-h2" sub-header-title>{{currentProject.name}} <span class="role-label" *ngIf="isMember">{{roleName | translate}}</span></h1>
|
||||||
<nav class="subnav sub-nav-bg-color">
|
<nav class="subnav sub-nav-bg-color">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item" *ngIf="hasRepositoryListPermission">
|
||||||
<a class="nav-link" routerLink="repositories" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
<a class="nav-link" routerLink="repositories" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="withHelmChart" class="nav-item">
|
<li *ngIf="withHelmChart && hasHelmChartsListPermission" class="nav-item">
|
||||||
<a class="nav-link" routerLink="helm-charts" routerLinkActive="active">{{'PROJECT_DETAIL.HELMCHART' | translate}}</a>
|
<a class="nav-link" routerLink="helm-charts" routerLinkActive="active">{{'PROJECT_DETAIL.HELMCHART' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
<li class="nav-item" *ngIf="hasMemberListPermission">
|
||||||
<a class="nav-link" routerLink="members" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
<a class="nav-link" routerLink="members" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSProjectAdmin || isSystemAdmin">
|
<li class="nav-item" *ngIf="hasReplicationListPermission">
|
||||||
<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="(isSProjectAdmin || isSystemAdmin) && !withAdmiral">
|
<li class="nav-item" *ngIf="(hasLabelListPermission && hasLabelCreatePermission) && !withAdmiral">
|
||||||
<a class="nav-link" routerLink="labels" routerLinkActive="active">{{'PROJECT_DETAIL.LABELS' | translate}}</a>
|
<a class="nav-link" routerLink="labels" routerLinkActive="active">{{'PROJECT_DETAIL.LABELS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
<li class="nav-item" *ngIf="hasLogListPermission">
|
||||||
<a class="nav-link" routerLink="logs" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
<a class="nav-link" routerLink="logs" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSProjectAdmin || isSystemAdmin">
|
<li class="nav-item" *ngIf="hasRobotListPermission">
|
||||||
<a class="nav-link" routerLink="robot-account" routerLinkActive="active">{{'PROJECT_DETAIL.ROBOT_ACCOUNTS' | translate}}</a>
|
<a class="nav-link" routerLink="robot-account" routerLinkActive="active">{{'PROJECT_DETAIL.ROBOT_ACCOUNTS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSessionValid && (isSystemAdmin || isMember)">
|
<li class="nav-item" *ngIf="isSessionValid && (hasConfigurationListPermission)">
|
||||||
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
|
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
import { Project } from '../project';
|
import { Project } from '../project';
|
||||||
@ -19,26 +19,38 @@ import { Project } from '../project';
|
|||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { ProjectService } from '../../project/project.service';
|
import { ProjectService } from '../../project/project.service';
|
||||||
|
|
||||||
import {AppConfigService} from "../../app-config.service";
|
import { AppConfigService } from "../../app-config.service";
|
||||||
|
import { UserPermissionService, USERSTATICPERMISSION, ErrorHandler } from "@harbor/ui";
|
||||||
|
import { forkJoin } from "rxjs";
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'project-detail',
|
selector: 'project-detail',
|
||||||
templateUrl: 'project-detail.component.html',
|
templateUrl: 'project-detail.component.html',
|
||||||
styleUrls: [ 'project-detail.component.scss' ]
|
styleUrls: ['project-detail.component.scss']
|
||||||
})
|
})
|
||||||
export class ProjectDetailComponent {
|
export class ProjectDetailComponent implements OnInit {
|
||||||
|
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
currentProject: Project;
|
currentProject: Project;
|
||||||
|
|
||||||
isMember: boolean;
|
isMember: boolean;
|
||||||
roleName: string;
|
roleName: string;
|
||||||
|
projectId: number;
|
||||||
|
hasHelmChartsListPermission: boolean;
|
||||||
|
hasRepositoryListPermission: boolean;
|
||||||
|
hasMemberListPermission: boolean;
|
||||||
|
hasReplicationListPermission: boolean;
|
||||||
|
hasLabelListPermission: boolean;
|
||||||
|
hasLabelCreatePermission: boolean;
|
||||||
|
hasLogListPermission: boolean;
|
||||||
|
hasConfigurationListPermission: boolean;
|
||||||
|
hasRobotListPermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private sessionService: SessionService,
|
private sessionService: SessionService,
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
private projectService: ProjectService) {
|
private projectService: ProjectService) {
|
||||||
|
|
||||||
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
||||||
@ -48,14 +60,42 @@ export class ProjectDetailComponent {
|
|||||||
this.roleName = this.currentProject.role_name;
|
this.roleName = this.currentProject.role_name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
ngOnInit() {
|
||||||
public get isSystemAdmin(): boolean {
|
this.projectId = this.route.snapshot.params['id'];
|
||||||
let account = this.sessionService.getCurrentUser();
|
this.getPermissionsList(this.projectId);
|
||||||
return account && account.has_admin_role;
|
|
||||||
}
|
}
|
||||||
|
getPermissionsList(projectId: number): void {
|
||||||
|
let permissionsList = [];
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.LOG.KEY, USERSTATICPERMISSION.LOG.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.CONFIGURATION.KEY, USERSTATICPERMISSION.CONFIGURATION.VALUE.READ));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.MEMBER.KEY, USERSTATICPERMISSION.MEMBER.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPLICATION.KEY, USERSTATICPERMISSION.REPLICATION.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPOSITORY.KEY, USERSTATICPERMISSION.REPOSITORY.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.HELM_CHART.KEY, USERSTATICPERMISSION.HELM_CHART.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.ROBOT.KEY, USERSTATICPERMISSION.ROBOT.VALUE.LIST));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.CREATE));
|
||||||
|
forkJoin(...permissionsList).subscribe(Rules => {
|
||||||
|
this.hasLogListPermission = Rules[0] as boolean;
|
||||||
|
this.hasConfigurationListPermission = Rules[1] as boolean;
|
||||||
|
this.hasMemberListPermission = Rules[2] as boolean;
|
||||||
|
this.hasReplicationListPermission = Rules[3] as boolean;
|
||||||
|
this.hasLabelListPermission = Rules[4] as boolean;
|
||||||
|
this.hasRepositoryListPermission = Rules[5] as boolean;
|
||||||
|
this.hasHelmChartsListPermission = Rules[6] as boolean;
|
||||||
|
this.hasRobotListPermission = Rules[7] as boolean;
|
||||||
|
this.hasLabelCreatePermission = Rules[8] as boolean;
|
||||||
|
|
||||||
public get isSProjectAdmin(): boolean {
|
}, error => this.errorHandler.error(error));
|
||||||
return this.currentProject.has_project_admin_role;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isSessionValid(): boolean {
|
public get isSessionValid(): boolean {
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 label-top">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 label-top">
|
||||||
<hbr-label [projectId]="projectId" [scope]="'p'" [hasProjectAdminRole]="hasProjectAdminRole"></hbr-label>
|
<hbr-label
|
||||||
|
[projectId]="projectId"
|
||||||
|
[scope]="'p'"
|
||||||
|
[hasCreateLabelPermission]="hasCreateLabelPermission"
|
||||||
|
[hasUpdateLabelPermission]="hasUpdateLabelPermission"
|
||||||
|
[hasDeleteLabelPermission]="hasDeleteLabelPermission"></hbr-label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -16,6 +16,8 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { SessionUser } from '../../shared/session-user';
|
import { SessionUser } from '../../shared/session-user';
|
||||||
import { Project } from '../project';
|
import { Project } from '../project';
|
||||||
|
import { forkJoin } from 'rxjs';
|
||||||
|
import { UserPermissionService, USERSTATICPERMISSION, ErrorHandler } from "@harbor/ui";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-config',
|
selector: 'app-project-config',
|
||||||
@ -29,21 +31,34 @@ export class ProjectLabelComponent implements OnInit {
|
|||||||
currentUser: SessionUser;
|
currentUser: SessionUser;
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
hasProjectAdminRole: boolean;
|
hasProjectAdminRole: boolean;
|
||||||
|
hasCreateLabelPermission: boolean;
|
||||||
|
hasUpdateLabelPermission: boolean;
|
||||||
|
hasDeleteLabelPermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
private session: SessionService) {}
|
private session: SessionService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||||
this.currentUser = this.session.getCurrentUser();
|
this.currentUser = this.session.getCurrentUser();
|
||||||
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
||||||
let resolverData = this.route.snapshot.parent.data;
|
this.getLabelPermissionRule(this.projectId);
|
||||||
if (resolverData) {
|
|
||||||
let pro: Project = <Project>resolverData['projectResolver'];
|
|
||||||
this.hasProjectAdminRole = pro.has_project_admin_role;
|
|
||||||
this.projectName = pro.name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLabelPermissionRule(projectId: number): void {
|
||||||
|
const hasCreateLabelPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.CREATE);
|
||||||
|
const hasUpdateLabelPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.UPDATE);
|
||||||
|
const hasDeleteLabelPermission = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.DELETE);
|
||||||
|
forkJoin(hasCreateLabelPermission, hasUpdateLabelPermission, hasDeleteLabelPermission).subscribe(permissions => {
|
||||||
|
this.hasCreateLabelPermission = permissions[0] as boolean;
|
||||||
|
this.hasUpdateLabelPermission = permissions[1] as boolean;
|
||||||
|
this.hasDeleteLabelPermission = permissions[2] as boolean;
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button class="btn btn-sm btn-secondary"
|
<button class="btn btn-sm btn-secondary" [disabled]="!hasRobotCreatePermission" (click)="openAddRobotModal()">
|
||||||
(click)="openAddRobotModal()">
|
<span>
|
||||||
<span><clr-icon shape="plus" size="16"></clr-icon> {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
|
<clr-icon shape="plus" size="16"></clr-icon> {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
|
||||||
| translate }}</span>
|
| translate }}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="btn btn-sm
|
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="btn btn-sm
|
||||||
btn-link"
|
btn-link" clrDropdownTrigger>
|
||||||
clrDropdownTrigger>
|
|
||||||
<span>{{'MEMBER.ACTION' | translate}}<clr-icon shape="caret
|
<span>{{'MEMBER.ACTION' | translate}}<clr-icon shape="caret
|
||||||
down"></clr-icon></span>
|
down"></clr-icon></span>
|
||||||
<clr-dropdown-menu *clrIfOpen>
|
<clr-dropdown-menu *clrIfOpen>
|
||||||
<button clrDropdownItem [disabled]="!(selectedRow.length ==
|
<button clrDropdownItem [disabled]="!hasRobotUpdatePermission||!(selectedRow.length ==
|
||||||
1)" (click)="changeAccountStatus(selectedRow)">
|
1)" (click)="changeAccountStatus(selectedRow)">
|
||||||
<span *ngIf="selectedRow[0] && !selectedRow[0].disabled
|
<span *ngIf="selectedRow[0] && !selectedRow[0].disabled
|
||||||
|| selectedRow.length!==1">{{'ROBOT_ACCOUNT.DISABLE_ACCOUNT'
|
|| selectedRow.length!==1">{{'ROBOT_ACCOUNT.DISABLE_ACCOUNT'
|
||||||
@ -35,9 +35,7 @@
|
|||||||
| translate}}</span>
|
| translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<button clrDropdownItem
|
<button clrDropdownItem (click)="openDeleteRobotsDialog(selectedRow)" [disabled]="!hasRobotDeletePermission || !selectedRow.length">{{'ROBOT_ACCOUNT.DELETE'
|
||||||
(click)="openDeleteRobotsDialog(selectedRow)"
|
|
||||||
[disabled]="!selectedRow.length">{{'ROBOT_ACCOUNT.DELETE'
|
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
@ -49,12 +47,8 @@
|
|||||||
<clr-dg-row *clrDgItems="let r of robots" [clrDgItem]="r">
|
<clr-dg-row *clrDgItems="let r of robots" [clrDgItem]="r">
|
||||||
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
||||||
<clr-dg-cell [ngSwitch]="r.disabled">
|
<clr-dg-cell [ngSwitch]="r.disabled">
|
||||||
<clr-icon shape="check-circle" *ngSwitchCase="false"
|
<clr-icon shape="check-circle" *ngSwitchCase="false" size="20" class="color-green"></clr-icon>
|
||||||
size="20"
|
<clr-icon shape="times-circle" *ngSwitchCase="true" size="16" class="color-red red-position"></clr-icon>
|
||||||
class="color-green"></clr-icon>
|
|
||||||
<clr-icon shape="times-circle" *ngSwitchCase="true"
|
|
||||||
size="16"
|
|
||||||
class="color-red red-position"></clr-icon>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{r.description}}</clr-dg-cell>
|
<clr-dg-cell>{{r.description}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
@ -68,6 +62,5 @@
|
|||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
</div>
|
</div>
|
||||||
<add-robot [projectId]="projectId" [projectName]="projectName"
|
<add-robot [projectId]="projectId" [projectName]="projectName" (create)="createAccount($event)"></add-robot>
|
||||||
(create)="createAccount($event)"></add-robot>
|
</div>
|
||||||
</div>
|
|
@ -25,7 +25,10 @@ import {
|
|||||||
operateChanges,
|
operateChanges,
|
||||||
OperateInfo,
|
OperateInfo,
|
||||||
OperationService,
|
OperationService,
|
||||||
OperationState
|
OperationState,
|
||||||
|
UserPermissionService,
|
||||||
|
USERSTATICPERMISSION,
|
||||||
|
ErrorHandler
|
||||||
} from "@harbor/ui";
|
} from "@harbor/ui";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -48,12 +51,17 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
|||||||
robots: Robot[];
|
robots: Robot[];
|
||||||
projectId: number;
|
projectId: number;
|
||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
|
hasRobotCreatePermission: boolean;
|
||||||
|
hasRobotUpdatePermission: boolean;
|
||||||
|
hasRobotDeletePermission: boolean;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private robotService: RobotService,
|
private robotService: RobotService,
|
||||||
private OperateDialogService: ConfirmationDialogService,
|
private OperateDialogService: ConfirmationDialogService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
private ref: ChangeDetectorRef,
|
private ref: ChangeDetectorRef,
|
||||||
private messageHandlerService: MessageHandlerService
|
private messageHandlerService: MessageHandlerService
|
||||||
) {
|
) {
|
||||||
@ -80,8 +88,24 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.searchRobot = "";
|
this.searchRobot = "";
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
|
this.getPermissionsList(this.projectId);
|
||||||
}
|
}
|
||||||
|
getPermissionsList(projectId: number): void {
|
||||||
|
let permissionsList = [];
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.ROBOT.KEY, USERSTATICPERMISSION.ROBOT.VALUE.CREATE));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.ROBOT.KEY, USERSTATICPERMISSION.ROBOT.VALUE.UPDATE));
|
||||||
|
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.ROBOT.KEY, USERSTATICPERMISSION.ROBOT.VALUE.DELETE));
|
||||||
|
|
||||||
|
forkJoin(...permissionsList).subscribe(Rules => {
|
||||||
|
this.hasRobotCreatePermission = Rules[0] as boolean;
|
||||||
|
this.hasRobotUpdatePermission = Rules[1] as boolean;
|
||||||
|
this.hasRobotDeletePermission = Rules[2] as boolean;
|
||||||
|
|
||||||
|
}, error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.subscription) {
|
if (this.subscription) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
@ -122,7 +146,7 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
|||||||
this.selectedRow = [];
|
this.selectedRow = [];
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe(() => {});
|
.subscribe(() => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
delOperate(robot: Robot) {
|
delOperate(robot: Robot) {
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
<div>
|
<div>
|
||||||
<hbr-replication #replicationView [projectId]="projectIdentify" [projectName]="projectName" [isSystemAdmin]="isSystemAdmin" [withReplicationJob]='true' (goToRegistry)="goRegistry()"></hbr-replication>
|
<hbr-replication #replicationView
|
||||||
|
[projectId]="projectIdentify"
|
||||||
|
[projectName]="projectName"
|
||||||
|
[isSystemAdmin]="isSystemAdmin"
|
||||||
|
[withReplicationJob]='true'
|
||||||
|
(goToRegistry)="goRegistry()"
|
||||||
|
[hasCreateReplicationPermission]="hasCreateReplicationPermission"
|
||||||
|
[hasUpdateReplicationPermission]="hasUpdateReplicationPermission"
|
||||||
|
[hasDeleteReplicationPermission]="hasDeleteReplicationPermission"
|
||||||
|
[hasExecuteReplicationPermission]="hasExecuteReplicationPermission"></hbr-replication>
|
||||||
</div>
|
</div>
|
@ -12,13 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
|
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
import { ReplicationComponent } from '@harbor/ui';
|
import { SessionService } from "../shared/session.service";
|
||||||
|
import { Project } from "../project/project";
|
||||||
import {SessionService} from "../shared/session.service";
|
import { ProjectService } from "../project/project.service";
|
||||||
import {Project} from "../project/project";
|
import { ReplicationComponent, UserPermissionService, USERSTATICPERMISSION, ErrorHandler } from "@harbor/ui";
|
||||||
import {ProjectService} from "../project/project.service";
|
import { forkJoin } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'replication',
|
selector: 'replication',
|
||||||
@ -28,25 +28,30 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
|
|||||||
projectIdentify: string | number;
|
projectIdentify: string | number;
|
||||||
@ViewChild("replicationView") replicationView: ReplicationComponent;
|
@ViewChild("replicationView") replicationView: ReplicationComponent;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
|
hasCreateReplicationPermission: boolean;
|
||||||
|
hasUpdateReplicationPermission: boolean;
|
||||||
|
hasDeleteReplicationPermission: boolean;
|
||||||
|
hasExecuteReplicationPermission: boolean;
|
||||||
constructor(private route: ActivatedRoute,
|
constructor(private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private proService: ProjectService,
|
private proService: ProjectService,
|
||||||
private session: SessionService) { }
|
private userPermissionService: UserPermissionService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private session: SessionService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.projectIdentify = +this.route.snapshot.parent.params['id'];
|
this.projectIdentify = +this.route.snapshot.parent.params['id'];
|
||||||
|
this.getReplicationPermissions(this.projectIdentify);
|
||||||
this.proService.listProjects("", undefined).toPromise()
|
this.proService.listProjects("", undefined).toPromise()
|
||||||
.then(response => {
|
.then(response => {
|
||||||
let projects = response.json() as Project[];
|
let projects = response.json() as Project[];
|
||||||
if (projects.length) {
|
if (projects.length) {
|
||||||
let project = projects.find(data => data.project_id === this.projectIdentify);
|
let project = projects.find(data => data.project_id === this.projectIdentify);
|
||||||
if (project) {
|
if (project) {
|
||||||
this.projectName = project.name;
|
this.projectName = project.name;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isSystemAdmin(): boolean {
|
public get isSystemAdmin(): boolean {
|
||||||
@ -66,4 +71,24 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
|
|||||||
goRegistry(): void {
|
goRegistry(): void {
|
||||||
this.router.navigate(['/harbor', 'registries']);
|
this.router.navigate(['/harbor', 'registries']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getReplicationPermissions(projectId: number): void {
|
||||||
|
|
||||||
|
let permissionsCreate = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPLICATION.KEY, USERSTATICPERMISSION.REPLICATION.VALUE.CREATE);
|
||||||
|
let permissionsUpdate = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPLICATION.KEY, USERSTATICPERMISSION.REPLICATION.VALUE.UPDATE);
|
||||||
|
let permissionsDelete = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPLICATION.KEY, USERSTATICPERMISSION.REPLICATION.VALUE.DELETE);
|
||||||
|
let permissionsExecute = this.userPermissionService.getPermission(projectId,
|
||||||
|
USERSTATICPERMISSION.REPLICATION_JOB.KEY, USERSTATICPERMISSION.REPLICATION_JOB.VALUE.CREATE);
|
||||||
|
forkJoin(permissionsCreate, permissionsUpdate, permissionsDelete, permissionsExecute).subscribe(permissions => {
|
||||||
|
this.hasCreateReplicationPermission = permissions[0] as boolean;
|
||||||
|
this.hasUpdateReplicationPermission = permissions[1] as boolean;
|
||||||
|
this.hasDeleteReplicationPermission = permissions[2] as boolean;
|
||||||
|
this.hasExecuteReplicationPermission = permissions[3] as boolean;
|
||||||
|
}, error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
<h2 class="custom-h2">{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</h2>
|
<h2 class="custom-h2">{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</h2>
|
||||||
<div class="content-top">
|
<div class="content-top">
|
||||||
<hbr-replication [withReplicationJob]='true' [isSystemAdmin]="isSystemAdmin" [withAdmiral]="withAdmiral" (goToRegistry)="goRegistry()" (redirect)="customRedirect($event)"></hbr-replication>
|
<hbr-replication
|
||||||
|
[withReplicationJob]='true'
|
||||||
|
[isSystemAdmin]="isSystemAdmin"
|
||||||
|
[withAdmiral]="withAdmiral"
|
||||||
|
(goToRegistry)="goRegistry()"
|
||||||
|
(redirect)="customRedirect($event)"
|
||||||
|
[hasCreateReplicationPermission]="true"
|
||||||
|
[hasUpdateReplicationPermission]="true"
|
||||||
|
[hasDeleteReplicationPermission]="true"
|
||||||
|
[hasExecuteReplicationPermission]="true"></hbr-replication>
|
||||||
</div>
|
</div>
|
@ -5,9 +5,9 @@
|
|||||||
<a (click)="goBack(repositoryId)">< {{repositoryId}}</a>
|
<a (click)="goBack(repositoryId)">< {{repositoryId}}</a>
|
||||||
</div>
|
</div>
|
||||||
<hbr-tag-detail (backEvt)="goBack($event)"
|
<hbr-tag-detail (backEvt)="goBack($event)"
|
||||||
[tagId]="tagId"
|
[tagId]="tagId"
|
||||||
[withClair]="withClair"
|
[withClair]="withClair"
|
||||||
[withAdmiral]="withAdmiral"
|
[withAdmiral]="withAdmiral"
|
||||||
[repositoryId]="repositoryId"
|
[projectId]="projectId"
|
||||||
[withAdminRole]="withAdminRole"></hbr-tag-detail>
|
[repositoryId]="repositoryId"></hbr-tag-detail>
|
||||||
</div>
|
</div>
|
@ -48,10 +48,6 @@ export class TagDetailPageComponent implements OnInit {
|
|||||||
return this.appConfigService.getConfig().with_clair;
|
return this.appConfigService.getConfig().with_clair;
|
||||||
}
|
}
|
||||||
|
|
||||||
get withAdminRole(): boolean {
|
|
||||||
return this.session.getCurrentUser().has_admin_role;
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(tag: string): void {
|
goBack(tag: string): void {
|
||||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]);
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { ErrorHandler } from '@harbor/ui';
|
import { ErrorHandler, UserPermissionService } from '@harbor/ui';
|
||||||
|
|
||||||
import { MessageService } from '../../global-message/message.service';
|
import { MessageService } from '../../global-message/message.service';
|
||||||
import { AlertType, httpStatusCode } from '../../shared/shared.const';
|
import { AlertType, httpStatusCode } from '../../shared/shared.const';
|
||||||
@ -27,6 +27,7 @@ export class MessageHandlerService implements ErrorHandler {
|
|||||||
constructor(
|
constructor(
|
||||||
private msgService: MessageService,
|
private msgService: MessageService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
private session: SessionService) { }
|
private session: SessionService) { }
|
||||||
|
|
||||||
// Handle the error and map it to the suitable message
|
// Handle the error and map it to the suitable message
|
||||||
@ -46,6 +47,7 @@ export class MessageHandlerService implements ErrorHandler {
|
|||||||
this.msgService.announceAppLevelMessage(code, msg, AlertType.DANGER);
|
this.msgService.announceAppLevelMessage(code, msg, AlertType.DANGER);
|
||||||
// Session is invalid now, clare session cache
|
// Session is invalid now, clare session cache
|
||||||
this.session.clear();
|
this.session.clear();
|
||||||
|
this.userPermissionService.clearPermissionCache();
|
||||||
} else {
|
} else {
|
||||||
this.msgService.announceMessage(code, msg, AlertType.DANGER);
|
this.msgService.announceMessage(code, msg, AlertType.DANGER);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
|||||||
() => {
|
() => {
|
||||||
// Add exception for repository in project detail router activation.
|
// Add exception for repository in project detail router activation.
|
||||||
this.projectService.getProject(projectId).subscribe(project => {
|
this.projectService.getProject(projectId).subscribe(project => {
|
||||||
if (project.public === 1) {
|
if (project.metadata && project.metadata.public === 'true') {
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
}
|
}
|
||||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
||||||
|
@ -20,10 +20,11 @@ import {
|
|||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { CommonRoutes } from '../../shared/shared.const';
|
import { CommonRoutes } from '../../shared/shared.const';
|
||||||
|
import { UserPermissionService } from "@harbor/ui";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SignInGuard implements CanActivate, CanActivateChild {
|
export class SignInGuard implements CanActivate, CanActivateChild {
|
||||||
constructor(private authService: SessionService, private router: Router) { }
|
constructor(private authService: SessionService, private router: Router, private userPermission: UserPermissionService) { }
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
|
||||||
// If user has logged in, should not login again
|
// If user has logged in, should not login again
|
||||||
@ -34,6 +35,8 @@ export class SignInGuard implements CanActivate, CanActivateChild {
|
|||||||
this.authService.signOff()
|
this.authService.signOff()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.authService.clear(); // Destroy session cache
|
this.authService.clear(); // Destroy session cache
|
||||||
|
this.userPermission.clearPermissionCache();
|
||||||
|
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -20,7 +20,7 @@ import { Member } from '../project/member/member';
|
|||||||
|
|
||||||
import { SignInCredential } from './sign-in-credential';
|
import { SignInCredential } from './sign-in-credential';
|
||||||
import { enLang } from '../shared/shared.const';
|
import { enLang } from '../shared/shared.const';
|
||||||
import {HTTP_FORM_OPTIONS, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "./shared.utils";
|
import { HTTP_FORM_OPTIONS, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS } from "./shared.utils";
|
||||||
|
|
||||||
const signInUrl = '/c/login';
|
const signInUrl = '/c/login';
|
||||||
const currentUserEndpoint = "/api/users/current";
|
const currentUserEndpoint = "/api/users/current";
|
||||||
@ -67,7 +67,7 @@ export class SessionService {
|
|||||||
signIn(signInCredential: SignInCredential): Promise<any> {
|
signIn(signInCredential: SignInCredential): Promise<any> {
|
||||||
// Build the form package
|
// Build the form package
|
||||||
let queryParam: string = 'principal=' + encodeURIComponent(signInCredential.principal) +
|
let queryParam: string = 'principal=' + encodeURIComponent(signInCredential.principal) +
|
||||||
'&password=' + encodeURIComponent(signInCredential.password);
|
'&password=' + encodeURIComponent(signInCredential.password);
|
||||||
|
|
||||||
// Trigger Http
|
// Trigger Http
|
||||||
return this.http.post(signInUrl, queryParam, HTTP_FORM_OPTIONS)
|
return this.http.post(signInUrl, queryParam, HTTP_FORM_OPTIONS)
|
||||||
@ -144,9 +144,9 @@ export class SessionService {
|
|||||||
return Promise.reject("Invalid account settings");
|
return Promise.reject("Invalid account settings");
|
||||||
}
|
}
|
||||||
return this.http.post(renameAdminEndpoint, JSON.stringify({}), HTTP_JSON_OPTIONS)
|
return this.http.post(renameAdminEndpoint, JSON.stringify({}), HTTP_JSON_OPTIONS)
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then(() => null)
|
.then(() => null)
|
||||||
.catch(error => this.handleError(error));
|
.catch(error => this.handleError(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,16 +78,19 @@ export const enum ConfirmationButtons {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectTypes = { 0: 'PROJECT.ALL_PROJECTS', 1: 'PROJECT.PRIVATE_PROJECTS', 2: 'PROJECT.PUBLIC_PROJECTS' };
|
export const ProjectTypes = { 0: 'PROJECT.ALL_PROJECTS', 1: 'PROJECT.PRIVATE_PROJECTS', 2: 'PROJECT.PUBLIC_PROJECTS' };
|
||||||
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST', 4: 'MEMBER.PROJECT_MASTER' };
|
||||||
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN',
|
||||||
|
'master': 'MEMBER.PROJECT_MASTER', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
||||||
export const ProjectRoles = [
|
export const ProjectRoles = [
|
||||||
{ id: 1, value: "MEMBER.PROJECT_ADMIN" },
|
{ id: 1, value: "MEMBER.PROJECT_ADMIN" },
|
||||||
{ id: 2, value: "MEMBER.DEVELOPER" },
|
{ id: 2, value: "MEMBER.DEVELOPER" },
|
||||||
{ id: 3, value: "MEMBER.GUEST" }
|
{ id: 3, value: "MEMBER.GUEST" },
|
||||||
|
{ id: 4, value: "MEMBER.PROJECT_MASTER" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export enum Roles {
|
export enum Roles {
|
||||||
PROJECT_ADMIN = 1,
|
PROJECT_ADMIN = 1,
|
||||||
|
PROJECT_MASTER = 4,
|
||||||
DEVELOPER = 2,
|
DEVELOPER = 2,
|
||||||
GUEST = 3,
|
GUEST = 3,
|
||||||
OTHER = 0,
|
OTHER = 0,
|
||||||
|
@ -26,8 +26,8 @@ import { AppConfigService } from '../app-config.service';
|
|||||||
import { NewUserModalComponent } from './new-user-modal.component';
|
import { NewUserModalComponent } from './new-user-modal.component';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import { User } from './user';
|
import { User } from './user';
|
||||||
import {ChangePasswordComponent} from "./change-password/change-password.component";
|
import { ChangePasswordComponent } from "./change-password/change-password.component";
|
||||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "@harbor/ui";
|
import { operateChanges, OperateInfo, OperationService, OperationState } from "@harbor/ui";
|
||||||
/**
|
/**
|
||||||
* NOTES:
|
* NOTES:
|
||||||
* Pagination for this component is a temporary workaround solution. It will be replaced in future release.
|
* Pagination for this component is a temporary workaround solution. It will be replaced in future release.
|
||||||
@ -153,7 +153,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
return this.onGoing;
|
return this.onGoing;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {}
|
ngOnInit(): void { }
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.deletionSubscription) {
|
if (this.deletionSubscription) {
|
||||||
@ -223,15 +223,15 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(promiseLists).then(() => {
|
Promise.all(promiseLists).then(() => {
|
||||||
this.selectedRow = [];
|
this.selectedRow = [];
|
||||||
this.refresh();
|
this.refresh();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.selectedRow = [];
|
this.selectedRow = [];
|
||||||
this.msgHandler.handleError(error);
|
this.msgHandler.handleError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the specified user
|
// Delete the specified user
|
||||||
@ -298,7 +298,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
this.translate.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
this.translate.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||||
operateChanges(operMessage, OperationState.failure, res);
|
operateChanges(operMessage, OperationState.failure, res);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh the user list
|
// Refresh the user list
|
||||||
@ -310,15 +310,15 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.originalUsers = this.userService.getUsers();
|
this.originalUsers = this.userService.getUsers();
|
||||||
this.originalUsers.then(users => {
|
this.originalUsers.then(users => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
|
|
||||||
this.totalCount = users.length;
|
this.totalCount = users.length;
|
||||||
this.users = users.slice(from, to); // First page
|
this.users = users.slice(from, to); // First page
|
||||||
|
|
||||||
this.forceRefreshView(5000);
|
this.forceRefreshView(5000);
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
this.msgHandler.handleError(error);
|
this.msgHandler.handleError(error);
|
||||||
|
@ -219,6 +219,7 @@
|
|||||||
"ROLE": "Role",
|
"ROLE": "Role",
|
||||||
"SYS_ADMIN": "System Admin",
|
"SYS_ADMIN": "System Admin",
|
||||||
"PROJECT_ADMIN": "Project Admin",
|
"PROJECT_ADMIN": "Project Admin",
|
||||||
|
"PROJECT_MASTER": "Master",
|
||||||
"DEVELOPER": "Developer",
|
"DEVELOPER": "Developer",
|
||||||
"GUEST": "Guest",
|
"GUEST": "Guest",
|
||||||
"DELETE": "Delete",
|
"DELETE": "Delete",
|
||||||
|
@ -219,6 +219,7 @@
|
|||||||
"ROLE": "Rol",
|
"ROLE": "Rol",
|
||||||
"SYS_ADMIN": "Administrador del sistema",
|
"SYS_ADMIN": "Administrador del sistema",
|
||||||
"PROJECT_ADMIN": "Administrador del proyecto",
|
"PROJECT_ADMIN": "Administrador del proyecto",
|
||||||
|
"PROJECT_MASTER": "Mantenedor",
|
||||||
"DEVELOPER": "Desarrollador",
|
"DEVELOPER": "Desarrollador",
|
||||||
"GUEST": "Invitado",
|
"GUEST": "Invitado",
|
||||||
"DELETE": "Eliminar",
|
"DELETE": "Eliminar",
|
||||||
|
@ -224,6 +224,7 @@
|
|||||||
"USER_TYPE": "User",
|
"USER_TYPE": "User",
|
||||||
"SYS_ADMIN": "System Admin",
|
"SYS_ADMIN": "System Admin",
|
||||||
"PROJECT_ADMIN": "Project Admin",
|
"PROJECT_ADMIN": "Project Admin",
|
||||||
|
"PROJECT_MASTER": "préposé à la maintenance",
|
||||||
"DEVELOPER": "Développeur",
|
"DEVELOPER": "Développeur",
|
||||||
"GUEST": "Invité",
|
"GUEST": "Invité",
|
||||||
"DELETE": "Supprimer",
|
"DELETE": "Supprimer",
|
||||||
|
@ -217,6 +217,7 @@
|
|||||||
"ROLE": "Função",
|
"ROLE": "Função",
|
||||||
"SYS_ADMIN": "Administrador do Sistema",
|
"SYS_ADMIN": "Administrador do Sistema",
|
||||||
"PROJECT_ADMIN": "Administrador do Projeto",
|
"PROJECT_ADMIN": "Administrador do Projeto",
|
||||||
|
"PROJECT_MASTER": "Mantenedor",
|
||||||
"DEVELOPER": "Desenvolvedor",
|
"DEVELOPER": "Desenvolvedor",
|
||||||
"GUEST": "Visitante",
|
"GUEST": "Visitante",
|
||||||
"DELETE": "Remover",
|
"DELETE": "Remover",
|
||||||
|
@ -219,6 +219,7 @@
|
|||||||
"ROLE": "角色",
|
"ROLE": "角色",
|
||||||
"SYS_ADMIN": "系统管理员",
|
"SYS_ADMIN": "系统管理员",
|
||||||
"PROJECT_ADMIN": "项目管理员",
|
"PROJECT_ADMIN": "项目管理员",
|
||||||
|
"PROJECT_MASTER": "维护人员",
|
||||||
"DEVELOPER": "开发人员",
|
"DEVELOPER": "开发人员",
|
||||||
"GUEST": "访客",
|
"GUEST": "访客",
|
||||||
"DELETE": "删除",
|
"DELETE": "删除",
|
||||||
|
@ -8,6 +8,7 @@ ${HARBOR_VERSION} V1.1.1
|
|||||||
*** Keywords ***
|
*** Keywords ***
|
||||||
|
|
||||||
Goto Project Config
|
Goto Project Config
|
||||||
|
Sleep 3
|
||||||
Click Element //project-detail//ul/li[contains(.,'Configuration')]
|
Click Element //project-detail//ul/li[contains(.,'Configuration')]
|
||||||
Sleep 2
|
Sleep 2
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ Go To Project Log
|
|||||||
Sleep 2
|
Sleep 2
|
||||||
|
|
||||||
Switch To Member
|
Switch To Member
|
||||||
|
Sleep 3
|
||||||
Click Element xpath=${project_member_xpath}
|
Click Element xpath=${project_member_xpath}
|
||||||
Sleep 1
|
Sleep 1
|
||||||
|
|
||||||
@ -89,22 +90,22 @@ Search Private Projects
|
|||||||
Make Project Private
|
Make Project Private
|
||||||
[Arguments] ${projectname}
|
[Arguments] ${projectname}
|
||||||
Go Into Project ${project name}
|
Go Into Project ${project name}
|
||||||
Sleep 1
|
Sleep 2
|
||||||
Click Element xpath=//project-detail//a[contains(.,'Configuration')]
|
Click Element xpath=//project-detail//a[contains(.,'Configuration')]
|
||||||
Sleep 1
|
Sleep 1
|
||||||
Checkbox Should Be Selected xpath=//input[@name='public']
|
Checkbox Should Be Selected xpath=//input[@name='public']
|
||||||
Click Element //div[@id='clr-wrapper-public']//label
|
Click Element //div[@id="clr-wrapper-public"]//label[1]
|
||||||
Wait Until Element Is Enabled //button[contains(.,'SAVE')]
|
Wait Until Element Is Enabled //button[contains(.,'SAVE')]
|
||||||
Click Element //button[contains(.,'SAVE')]
|
Click Element //button[contains(.,'SAVE')]
|
||||||
Wait Until Page Contains Configuration has been successfully saved
|
Wait Until Page Contains Configuration has been successfully saved
|
||||||
|
|
||||||
Make Project Public
|
Make Project Public
|
||||||
[Arguments] ${projectname}
|
[Arguments] ${projectname}
|
||||||
Go Into Project ${project name}
|
Go Into Project ${project name}
|
||||||
Sleep 1
|
Sleep 2
|
||||||
Click Element xpath=//project-detail//a[contains(.,'Configuration')]
|
Click Element xpath=//project-detail//a[contains(.,'Configuration')]
|
||||||
Checkbox Should Not Be Selected xpath=//input[@name='public']
|
Checkbox Should Not Be Selected xpath=//input[@name='public']
|
||||||
Click Element //div[@id='clr-wrapper-public']//label
|
Click Element //div[@id="clr-wrapper-public"]//label[1]
|
||||||
Wait Until Element Is Enabled //button[contains(.,'SAVE')]
|
Wait Until Element Is Enabled //button[contains(.,'SAVE')]
|
||||||
Click Element //button[contains(.,'SAVE')]
|
Click Element //button[contains(.,'SAVE')]
|
||||||
Wait Until Page Contains Configuration has been successfully saved
|
Wait Until Page Contains Configuration has been successfully saved
|
||||||
|
@ -16,5 +16,5 @@
|
|||||||
Documentation This resource provides any keywords related to the Harbor private registry appliance
|
Documentation This resource provides any keywords related to the Harbor private registry appliance
|
||||||
|
|
||||||
*** Variables ***
|
*** Variables ***
|
||||||
${member_action_xpath} //*[@id='member-action']
|
${member_action_xpath} //*[@id="member-action"]
|
||||||
${delete_action_xpath} //clr-dropdown/clr-dropdown-menu/button[4]
|
${delete_action_xpath} //clr-dropdown/clr-dropdown-menu/button[5]
|
||||||
|
@ -63,6 +63,7 @@ Enable Scan On Push
|
|||||||
Sleep 10
|
Sleep 10
|
||||||
|
|
||||||
Vulnerability Not Ready Project Hint
|
Vulnerability Not Ready Project Hint
|
||||||
|
Sleep 2
|
||||||
${element}= Set Variable xpath=//span[contains(@class, 'db-status-warning')]
|
${element}= Set Variable xpath=//span[contains(@class, 'db-status-warning')]
|
||||||
Wait Until Element Is Visible And Enabled ${element}
|
Wait Until Element Is Visible And Enabled ${element}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user