mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 04:05:40 +01:00
Merge pull request #6758 from jwangyangls/privilege_escalation
user permission change and add master role
This commit is contained in:
commit
3d726219e5
@ -19,8 +19,8 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef
|
||||
} from "@angular/core";
|
||||
import { Subscription} from "rxjs";
|
||||
import {forkJoin} from "rxjs";
|
||||
import { Subscription } from "rxjs";
|
||||
import { forkJoin } from "rxjs";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { Comparator } from "../service/interface";
|
||||
|
||||
@ -29,9 +29,9 @@ import { EndpointService } from "../service/endpoint.service";
|
||||
|
||||
import { ErrorHandler } from "../error-handler/index";
|
||||
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
|
||||
import {
|
||||
ConfirmationTargets,
|
||||
@ -42,8 +42,9 @@ import {
|
||||
import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component";
|
||||
import { toPromise, CustomComparator } from "../utils";
|
||||
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: "hbr-endpoint",
|
||||
@ -86,10 +87,10 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
constructor(private endpointService: EndpointService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
this.forceRefreshView(1000);
|
||||
}
|
||||
|
||||
@ -208,18 +209,18 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(
|
||||
error => {
|
||||
if (error && error.status === 412) {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
error => {
|
||||
if (error && error.status === 412) {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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 { HELMCHART_DIRECTIVE } from "./helm-chart/index";
|
||||
import { IMAGE_NAME_INPUT_DIRECTIVES } from "./image-name-input/index";
|
||||
|
||||
import {
|
||||
SystemInfoService,
|
||||
SystemInfoDefaultService,
|
||||
@ -56,7 +55,9 @@ import {
|
||||
HelmChartService,
|
||||
HelmChartDefaultService,
|
||||
RetagService,
|
||||
RetagDefaultService
|
||||
RetagDefaultService,
|
||||
UserPermissionService,
|
||||
UserPermissionDefaultService
|
||||
} from './service/index';
|
||||
import {
|
||||
ErrorHandler,
|
||||
@ -68,7 +69,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateServiceInitializer } from './i18n/index';
|
||||
import { DEFAULT_LANG_COOKIE_KEY, DEFAULT_SUPPORTING_LANGS, DEFAULT_LANG } from './utils';
|
||||
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
|
||||
@ -151,6 +152,8 @@ export interface HarborModuleConfig {
|
||||
|
||||
// Service implementation for helmchart
|
||||
helmChartService?: Provider;
|
||||
// Service implementation for userPermission
|
||||
userPermissionService?: Provider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,8 +251,9 @@ export class HarborLibraryModule {
|
||||
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
||||
config.helmChartService || {provide: HelmChartService, useClass: HelmChartDefaultService},
|
||||
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
|
||||
config.helmChartService || { provide: HelmChartService, useClass: HelmChartDefaultService },
|
||||
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||
// Do initializing
|
||||
TranslateServiceInitializer,
|
||||
{
|
||||
@ -281,8 +285,9 @@ export class HarborLibraryModule {
|
||||
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService },
|
||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
||||
config.helmChartService || {provide: HelmChartService, useClass: HelmChartDefaultService},
|
||||
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
|
||||
config.helmChartService || { provide: HelmChartService, useClass: HelmChartDefaultService },
|
||||
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||
ChannelService,
|
||||
OperationService
|
||||
]
|
||||
|
@ -23,14 +23,14 @@
|
||||
<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-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}}
|
||||
</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)">
|
||||
<clr-icon shape="trash" size="16"></clr-icon>{{'BUTTON.DELETE' | translate}}
|
||||
</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}}
|
||||
</button>
|
||||
</clr-dg-action-bar>
|
||||
|
@ -17,9 +17,11 @@ import { SystemInfo, SystemInfoService, HelmChartItem } from "../service/index";
|
||||
import { ErrorHandler } from "../error-handler/error-handler";
|
||||
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../utils";
|
||||
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 { OperationService } from "./../operation/operation.service";
|
||||
import { UserPermissionService } from "../service/permission.service";
|
||||
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||
import {
|
||||
OperateInfo,
|
||||
OperationState,
|
||||
@ -45,7 +47,6 @@ export class HelmChartComponent implements OnInit {
|
||||
@Input() urlPrefix: string;
|
||||
@Input() hasSignedIn: boolean;
|
||||
@Input() projectRoleID = Roles.OTHER;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Output() chartClickEvt = new EventEmitter<any>();
|
||||
@Output() chartDownloadEve = new EventEmitter<string>();
|
||||
@Input() chartDefaultIcon: string = DefaultHelmIcon;
|
||||
@ -76,24 +77,23 @@ export class HelmChartComponent implements OnInit {
|
||||
@ViewChild('chartUploadForm') uploadForm: NgForm;
|
||||
|
||||
@ViewChild("confirmationDialog") confirmationDialog: ConfirmationDialogComponent;
|
||||
|
||||
hasUploadHelmChartsPermission: boolean;
|
||||
hasDownloadHelmChartsPermission: boolean;
|
||||
hasDeleteHelmChartsPermission: boolean;
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private helmChartService: HelmChartService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private operationService: OperationService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
public get registryUrl(): string {
|
||||
return this.systemInfo ? this.systemInfo.registry_url : "";
|
||||
}
|
||||
|
||||
public get developerRoleOrAbove(): boolean {
|
||||
return this.projectRoleID === Roles.DEVELOPER || this.hasProjectAdminRole;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Get system info for tag views
|
||||
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||
@ -101,8 +101,21 @@ export class HelmChartComponent implements OnInit {
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
this.lastFilteredChartName = "";
|
||||
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) {
|
||||
this.lastFilteredChartName = value;
|
||||
this.refresh();
|
||||
@ -111,22 +124,22 @@ export class HelmChartComponent implements OnInit {
|
||||
refresh() {
|
||||
this.loading = true;
|
||||
this.helmChartService
|
||||
.getHelmCharts(this.projectName)
|
||||
.pipe(finalize(() => {
|
||||
.getHelmCharts(this.projectName)
|
||||
.pipe(finalize(() => {
|
||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 3000);
|
||||
this.loading = false;
|
||||
}))
|
||||
.subscribe(
|
||||
charts => {
|
||||
this.charts = charts.filter(x => x.name.includes(this.lastFilteredChartName));
|
||||
this.chartsCopy = charts.map(x => Object.assign({}, x));
|
||||
this.totalCount = charts.length;
|
||||
},
|
||||
err => {
|
||||
this.errorHandler.error(err);
|
||||
}
|
||||
);
|
||||
}))
|
||||
.subscribe(
|
||||
charts => {
|
||||
this.charts = charts.filter(x => x.name.includes(this.lastFilteredChartName));
|
||||
this.chartsCopy = charts.map(x => Object.assign({}, x));
|
||||
this.totalCount = charts.length;
|
||||
},
|
||||
err => {
|
||||
this.errorHandler.error(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onChartClick(item: HelmChartItem) {
|
||||
@ -163,10 +176,10 @@ export class HelmChartComponent implements OnInit {
|
||||
this.refresh();
|
||||
}))
|
||||
.subscribe(() => {
|
||||
this.translateService
|
||||
.get("HELM_CHART.FILE_UPLOADED")
|
||||
.subscribe(res => this.errorHandler.info(res));
|
||||
},
|
||||
this.translateService
|
||||
.get("HELM_CHART.FILE_UPLOADED")
|
||||
.subscribe(res => this.errorHandler.info(res));
|
||||
},
|
||||
err => this.errorHandler.error(err)
|
||||
);
|
||||
}
|
||||
@ -192,23 +205,23 @@ export class HelmChartComponent implements OnInit {
|
||||
this.operationService.publishInfo(operateMsg);
|
||||
|
||||
return this.helmChartService.deleteHelmChart(this.projectName, chartName)
|
||||
.pipe(map(
|
||||
() => operateChanges(operateMsg, OperationState.success),
|
||||
err => operateChanges(operateMsg, OperationState.failure, err)
|
||||
));
|
||||
.pipe(map(
|
||||
() => operateChanges(operateMsg, OperationState.success),
|
||||
err => operateChanges(operateMsg, OperationState.failure, err)
|
||||
));
|
||||
}
|
||||
|
||||
deleteCharts(charts: HelmChartItem[]) {
|
||||
if (charts && charts.length < 1) { return; }
|
||||
let chartsDelete$ = charts.map(chart => this.deleteChart(chart.name));
|
||||
forkJoin(chartsDelete$)
|
||||
.pipe(
|
||||
catchError(err => throwError(err)),
|
||||
finalize(() => {
|
||||
this.refresh();
|
||||
this.selectedRows = [];
|
||||
}))
|
||||
.subscribe(() => {});
|
||||
.pipe(
|
||||
catchError(err => throwError(err)),
|
||||
finalize(() => {
|
||||
this.refresh();
|
||||
this.selectedRows = [];
|
||||
}))
|
||||
.subscribe(() => { });
|
||||
}
|
||||
|
||||
downloadLatestVersion(evt?: Event, item?: HelmChartItem) {
|
||||
|
@ -38,18 +38,18 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-sm btn-secondary"
|
||||
[disabled]="!(selectedRows.length===1)"
|
||||
[disabled]="!(selectedRows.length===1) || !hasDownloadHelmChartVersionPermission"
|
||||
(click)="versionDownload()">
|
||||
<clr-icon shape="download" size="16"></clr-icon> {{'HELM_CHART.DOWNLOAD' | translate}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary"
|
||||
[disabled]="selectedRows.length<=0 || !hasProjectAdminRole"
|
||||
[disabled]="selectedRows.length<=0 || !hasDeleteHelmChartVersionPermission"
|
||||
(click)="openVersionDeleteModal(selectedRows)">
|
||||
<clr-icon shape="times" size="16"></clr-icon> {{'BUTTON.DELETE' | translate}}
|
||||
</button>
|
||||
<clr-dropdown>
|
||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger
|
||||
[disabled]="!(selectedRows.length===1 && developerRoleOrAbove)">
|
||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger
|
||||
[disabled]="!(selectedRows.length===1)|| !hasAddRemoveHelmChartVersionPermission">
|
||||
<clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}
|
||||
</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||
@ -144,7 +144,7 @@
|
||||
<button type="button" class="btn btn-link"
|
||||
(click)="versionDownload($event, item)">{{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||
<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>
|
||||
</clr-dropdown>
|
||||
</div>
|
||||
|
@ -26,6 +26,8 @@ import { ErrorHandler } from "./../../error-handler/error-handler";
|
||||
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../../utils";
|
||||
import { OperationService } from "./../../operation/operation.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 {
|
||||
OperateInfo,
|
||||
@ -49,13 +51,11 @@ import {
|
||||
})
|
||||
export class ChartVersionComponent implements OnInit {
|
||||
signedCon: { [key: string]: any | string[] } = {};
|
||||
@Input() projectRoleID: number;
|
||||
@Input() projectId: number;
|
||||
@Input() projectName: string;
|
||||
@Input() chartName: string;
|
||||
@Input() roleName: string;
|
||||
@Input() hasSignedIn: boolean;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Input() chartDefaultIcon: string = DefaultHelmIcon;
|
||||
@Output() versionClickEvt = new EventEmitter<string>();
|
||||
@Output() backEvt = new EventEmitter<any>();
|
||||
@ -85,12 +85,15 @@ export class ChartVersionComponent implements OnInit {
|
||||
|
||||
@ViewChild("confirmationDialog")
|
||||
confirmationDialog: ConfirmationDialogComponent;
|
||||
|
||||
hasAddRemoveHelmChartVersionPermission: boolean;
|
||||
hasDownloadHelmChartVersionPermission: boolean;
|
||||
hasDeleteHelmChartVersionPermission: boolean;
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private helmChartService: HelmChartService,
|
||||
private resrouceLabelService: LabelService,
|
||||
public userPermissionService: UserPermissionService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private operationService: OperationService,
|
||||
) { }
|
||||
@ -107,6 +110,7 @@ export class ChartVersionComponent implements OnInit {
|
||||
this.refresh();
|
||||
this.getLabels();
|
||||
this.lastFilteredVersionName = "";
|
||||
this.getHelmChartVersionPermission(this.projectId);
|
||||
}
|
||||
|
||||
updateFilterValue(value: string) {
|
||||
@ -326,7 +330,19 @@ export class ChartVersionComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public get developerRoleOrAbove(): boolean {
|
||||
return this.projectRoleID === Roles.DEVELOPER || this.hasProjectAdminRole;
|
||||
getHelmChartVersionPermission(projectId: number): void {
|
||||
|
||||
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 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]="!(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" (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]="!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) || !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 || !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>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 content-mt">
|
||||
|
@ -19,22 +19,22 @@ import {
|
||||
ChangeDetectorRef,
|
||||
Input
|
||||
} from "@angular/core";
|
||||
import {Label} from "../service/interface";
|
||||
import {LabelService} from "../service/label.service";
|
||||
import {toPromise} from "../utils";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
import {CreateEditLabelComponent} from "../create-edit-label/create-edit-label.component";
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import { Label } from "../service/interface";
|
||||
import { LabelService } from "../service/label.service";
|
||||
import { toPromise } from "../utils";
|
||||
import { ErrorHandler } from "../error-handler/error-handler";
|
||||
import { CreateEditLabelComponent } from "../create-edit-label/create-edit-label.component";
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets
|
||||
} from "../shared/shared.const";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
@Component({
|
||||
selector: "hbr-label",
|
||||
@ -51,7 +51,9 @@ export class LabelComponent implements OnInit {
|
||||
|
||||
@Input() scope: string;
|
||||
@Input() projectId = 0;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Input() hasCreateLabelPermission: boolean;
|
||||
@Input() hasUpdateLabelPermission: boolean;
|
||||
@Input() hasDeleteLabelPermission: boolean;
|
||||
|
||||
@ViewChild(CreateEditLabelComponent)
|
||||
createEditLabel: CreateEditLabelComponent;
|
||||
@ -59,10 +61,10 @@ export class LabelComponent implements OnInit {
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
|
||||
constructor(private labelService: LabelService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -162,11 +164,11 @@ export class LabelComponent implements OnInit {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(
|
||||
error => {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
error => {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Forcely refresh the view
|
||||
@ -183,4 +185,5 @@ export class LabelComponent implements OnInit {
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<div class="list-rule">
|
||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectRule($event)" [clrDgRowSelection]="true">
|
||||
<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="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="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="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="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="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="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="hasExecuteReplicationPermission" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)"><clr-icon shape="export" size="16"></clr-icon> {{'REPLICATION.REPLICATE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
|
@ -24,28 +24,29 @@ import {
|
||||
SimpleChange,
|
||||
SimpleChanges
|
||||
} from "@angular/core";
|
||||
import { forkJoin} from "rxjs";
|
||||
import { forkJoin } from "rxjs";
|
||||
import { Comparator } from "../service/interface";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
import {ReplicationService} from "../service/replication.service";
|
||||
import { ReplicationService } from "../service/replication.service";
|
||||
|
||||
import {
|
||||
ReplicationJob,
|
||||
ReplicationJobItem,
|
||||
ReplicationRule
|
||||
} from "../service/interface";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import {
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
ConfirmationButtons
|
||||
} from "../shared/shared.const";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
import {toPromise, CustomComparator} from "../utils";
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
import { ErrorHandler } from "../error-handler/error-handler";
|
||||
import { toPromise, CustomComparator } from "../utils";
|
||||
import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -58,12 +59,14 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
nullTime = "0001-01-01T00:00:00Z";
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() isSystemAdmin: boolean;
|
||||
@Input() selectedId: number | string;
|
||||
@Input() withReplicationJob: boolean;
|
||||
|
||||
@Input() loading = false;
|
||||
|
||||
@Input() hasCreateReplicationPermission: boolean;
|
||||
@Input() hasUpdateReplicationPermission: boolean;
|
||||
@Input() hasDeleteReplicationPermission: boolean;
|
||||
@Input() hasExecuteReplicationPermission: boolean;
|
||||
@Output() reload = new EventEmitter<boolean>();
|
||||
@Output() selectOne = 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");
|
||||
|
||||
constructor(private replicationService: ReplicationService,
|
||||
private translateService: TranslateService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
private translateService: TranslateService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
setInterval(() => ref.markForCheck(), 500);
|
||||
}
|
||||
|
||||
@ -113,7 +116,6 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
this.retrieveRules();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
let proIdChange: SimpleChange = changes["projectId"];
|
||||
if (proIdChange) {
|
||||
@ -156,7 +158,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
let count = 0;
|
||||
rule.filters.forEach((data: any) => {
|
||||
if (data.kind === 'label' && data.value.deleted) {
|
||||
count ++;
|
||||
count++;
|
||||
}
|
||||
});
|
||||
if (count === 0) {
|
||||
@ -258,8 +260,8 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
if (!this.canDeleteRule) {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.DELETION_SUMMARY_FAILURE')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -273,8 +275,8 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
if (error && error.status === 412) {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="form-content" id="clr-wrapper-public">
|
||||
<clr-checkbox-wrapper>
|
||||
<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>
|
||||
</clr-checkbox-wrapper>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<div class="form-content">
|
||||
<div *ngIf="withNotary">
|
||||
<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>
|
||||
</clr-checkbox-wrapper>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<div *ngIf="withClair" id="prevent-vulenrability-image">
|
||||
<clr-checkbox-wrapper>
|
||||
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.PreventVulImg" name="prevent-vulenrability-image-input"
|
||||
[disabled]="!hasProjectAdminRole" />
|
||||
[disabled]="!hasChangeConfigRole" />
|
||||
<label>{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_TOGGLE' | translate }}</label>
|
||||
</clr-checkbox-wrapper>
|
||||
|
||||
@ -52,16 +52,16 @@
|
||||
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SCAN' | translate }}</label>
|
||||
<div class="form-content">
|
||||
<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" />
|
||||
<label>{{ 'PROJECT_CONFIG.AUTOSCAN_TOGGLE' | translate }}</label>
|
||||
</clr-checkbox-wrapper>
|
||||
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.AUTOSCAN_POLICY' | translate }}</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges() || !hasProjectAdminRole">{{'BUTTON.SAVE'
|
||||
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges() || !hasChangeConfigRole">{{'BUTTON.SAVE'
|
||||
| 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>
|
||||
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
||||
</section>
|
||||
|
@ -8,7 +8,7 @@ import { ProjectService, ProjectDefaultService} from '../service/project.service
|
||||
import { SERVICE_CONFIG, IServiceConfig} from '../service.config';
|
||||
import { SystemInfo } from '../service/interface';
|
||||
import { Project } from './project';
|
||||
|
||||
import { UserPermissionService, UserPermissionDefaultService } from '../service/permission.service';
|
||||
describe('ProjectPolicyConfigComponent', () => {
|
||||
|
||||
let systemInfoService: SystemInfoService;
|
||||
@ -102,7 +102,8 @@ describe('ProjectPolicyConfigComponent', () => {
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService}
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService},
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
@ -13,6 +13,8 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Project } from './project';
|
||||
import {SystemInfo, SystemInfoService} from '../service/index';
|
||||
import { UserPermissionService } from '../service/permission.service';
|
||||
import { USERSTATICPERMISSION } from '../service/permission-static';
|
||||
|
||||
export class ProjectPolicy {
|
||||
Public: boolean;
|
||||
@ -56,7 +58,7 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
systemInfo: SystemInfo;
|
||||
orgProjectPolicy = new ProjectPolicy();
|
||||
projectPolicy = new ProjectPolicy();
|
||||
|
||||
hasChangeConfigRole: boolean;
|
||||
severityOptions = [
|
||||
{severity: 'high', severityLevel: 'VULNERABILITY.SEVERITY.HIGH'},
|
||||
{severity: 'medium', severityLevel: 'VULNERABILITY.SEVERITY.MEDIUM'},
|
||||
@ -69,6 +71,7 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
private translate: TranslateService,
|
||||
private projectService: ProjectService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private userPermission: UserPermissionService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -85,8 +88,14 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
|
||||
// retrive project level policy data
|
||||
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 {
|
||||
return this.systemInfo ? this.systemInfo.with_notary : false;
|
||||
}
|
||||
|
@ -11,9 +11,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<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)"
|
||||
(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 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">
|
||||
|
@ -104,6 +104,10 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
@Input() isSystemAdmin: boolean;
|
||||
@Input() withAdmiral: boolean;
|
||||
@Input() withReplicationJob: boolean;
|
||||
@Input() hasCreateReplicationPermission: boolean;
|
||||
@Input() hasUpdateReplicationPermission: boolean;
|
||||
@Input() hasDeleteReplicationPermission: boolean;
|
||||
@Input() hasExecuteReplicationPermission: boolean;
|
||||
|
||||
@Output() redirect = new EventEmitter<ReplicationRule>();
|
||||
@Output() openCreateRule = new EventEmitter<any>();
|
||||
|
@ -7,7 +7,7 @@
|
||||
<clr-icon shape="download"></clr-icon>
|
||||
{{'CONFIG.REGISTRY_CERTIFICATE' | translate | uppercase}}
|
||||
</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>
|
||||
<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>
|
||||
@ -27,7 +27,7 @@
|
||||
<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)="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-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | 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 { OperationService } from "../operation/operation.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)', () => {
|
||||
|
||||
let compRepo: RepositoryGridviewComponent;
|
||||
let fixtureRepo: ComponentFixture<RepositoryGridviewComponent>;
|
||||
let repositoryService: RepositoryService;
|
||||
let systemInfoService: SystemInfoService;
|
||||
let userPermissionService: UserPermissionService;
|
||||
|
||||
let spyRepos: jasmine.Spy;
|
||||
let spySystemInfo: jasmine.Spy;
|
||||
@ -72,7 +75,8 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
||||
metadata: {xTotalCount: 2},
|
||||
data: mockRepoData
|
||||
};
|
||||
|
||||
let mockHasCreateRepositoryPermission: boolean = true;
|
||||
let mockHasDeleteRepositoryPermission: boolean = true;
|
||||
// let mockTagData: Tag[] = [
|
||||
// {
|
||||
// "digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
||||
@ -120,6 +124,7 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||
{ provide: RetagService, useClass: RetagDefaultService },
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
@ -136,9 +141,17 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
||||
|
||||
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepo));
|
||||
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();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(compRepo).toBeTruthy();
|
||||
});
|
||||
|
@ -14,8 +14,8 @@ import {
|
||||
import { Router } from "@angular/router";
|
||||
import { forkJoin } from "rxjs";
|
||||
import { finalize } from "rxjs/operators";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {Comparator, State} from "../service/interface";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { Comparator, State } from "../service/interface";
|
||||
|
||||
import {
|
||||
Repository,
|
||||
@ -26,17 +26,19 @@ import {
|
||||
RepositoryItem,
|
||||
TagService
|
||||
} from '../service/index';
|
||||
import {ErrorHandler} from '../error-handler/error-handler';
|
||||
import {toPromise, CustomComparator, DEFAULT_PAGE_SIZE, calculatePage, doFiltering, doSorting, clone} from '../utils';
|
||||
import {ConfirmationState, ConfirmationTargets, ConfirmationButtons} from '../shared/shared.const';
|
||||
import {ConfirmationDialogComponent} from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import {ConfirmationMessage} from '../confirmation-dialog/confirmation-message';
|
||||
import {ConfirmationAcknowledgement} from '../confirmation-dialog/confirmation-state-message';
|
||||
import {Tag} from '../service/interface';
|
||||
import {GridViewComponent} from '../gridview/grid-view.component';
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
import {OperateInfo, OperationState, operateChanges} from "../operation/operate";
|
||||
import {SERVICE_CONFIG, IServiceConfig, downloadUrl } from '../service.config';
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { toPromise, CustomComparator, DEFAULT_PAGE_SIZE, calculatePage, doFiltering, doSorting, clone } from '../utils';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||
import { Tag } from '../service/interface';
|
||||
import { GridViewComponent } from '../gridview/grid-view.component';
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
import { UserPermissionService } from "../service/permission.service";
|
||||
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||
import { OperateInfo, OperationState, operateChanges } from "../operation/operate";
|
||||
import { SERVICE_CONFIG, IServiceConfig, downloadUrl } from '../service.config';
|
||||
@Component({
|
||||
selector: "hbr-repository-gridview",
|
||||
templateUrl: "./repository-gridview.component.html",
|
||||
@ -80,19 +82,21 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
confirmationDialog: ConfirmationDialogComponent;
|
||||
|
||||
@ViewChild("gridView") gridView: GridViewComponent;
|
||||
|
||||
hasCreateRepositoryPermission: boolean;
|
||||
hasDeleteRepositoryPermission: boolean;
|
||||
constructor(@Inject(SERVICE_CONFIG) private configInfo: IServiceConfig,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private repositoryService: RepositoryService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private tagService: TagService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private router: Router) {
|
||||
if (this.configInfo && this.configInfo.systemInfoEndpoint) {
|
||||
this.downloadLink = this.configInfo.systemInfoEndpoint + "/getcert";
|
||||
}
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private repositoryService: RepositoryService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private tagService: TagService,
|
||||
private operationService: OperationService,
|
||||
public userPermissionService: UserPermissionService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private router: Router) {
|
||||
if (this.configInfo && this.configInfo.systemInfoEndpoint) {
|
||||
this.downloadLink = this.configInfo.systemInfoEndpoint + "/getcert";
|
||||
}
|
||||
}
|
||||
|
||||
public get registryUrl(): string {
|
||||
@ -142,6 +146,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
}
|
||||
|
||||
this.lastFilteredRepoName = "";
|
||||
this.getHelmChartVersionPermission(this.projectId);
|
||||
}
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
@ -182,8 +187,8 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
if (this.signedCon[repo.name].length !== 0) {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
return toPromise<number>(this.repositoryService
|
||||
.deleteRepository(repo.name))
|
||||
@ -193,24 +198,24 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(error => {
|
||||
if (error.status === "412") {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.TAGS_SIGNED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
if (error.status === "412") {
|
||||
forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.TAGS_SIGNED')).subscribe(res => {
|
||||
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;
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = {page: {}};
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
@ -299,8 +304,8 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
return toPromise<Tag[]>(this.tagService.getTags(repo.name))
|
||||
.then(items => {
|
||||
if (items.some((t: Tag) => {
|
||||
return t.name === 'latest';
|
||||
})) {
|
||||
return t.name === 'latest';
|
||||
})) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -449,7 +454,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = {page: {}};
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = (targetPageNumber - 1) * this.pageSize;
|
||||
@ -497,4 +502,16 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
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>
|
||||
<hbr-tag ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)"
|
||||
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>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -27,6 +27,7 @@ import { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||
import { LabelDefaultService, LabelService } from "../service/label.service";
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
import { ProjectDefaultService, ProjectService, RetagDefaultService, RetagService } from "../service";
|
||||
import { UserPermissionDefaultService, UserPermissionService } from "../service/permission.service";
|
||||
|
||||
|
||||
class RouterStub {
|
||||
@ -178,6 +179,7 @@ describe('RepositoryComponent (inline template)', () => {
|
||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||
{ provide: RetagService, useClass: RetagDefaultService },
|
||||
{ provide: LabelService, useClass: LabelDefaultService},
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService},
|
||||
{ provide: ChannelService},
|
||||
{ provide: OperationService }
|
||||
]
|
||||
|
@ -13,3 +13,5 @@ export * from "./project.service";
|
||||
export * from "./label.service";
|
||||
export * from "./helm-chart.service";
|
||||
export * from "./retag.service";
|
||||
export * from "./permission.service";
|
||||
export * from "./permission-static";
|
||||
|
@ -119,8 +119,8 @@ export class Trigger {
|
||||
schedule_param:
|
||||
| any
|
||||
| {
|
||||
[key: string]: any | any[];
|
||||
};
|
||||
[key: string]: any | any[];
|
||||
};
|
||||
constructor(kind: string, param: any | { [key: string]: any | any[] }) {
|
||||
this.kind = kind;
|
||||
this.schedule_param = param;
|
||||
@ -395,8 +395,8 @@ export interface HelmChartSignature {
|
||||
* interface Manifest
|
||||
*/
|
||||
export interface Manifest {
|
||||
manifset: Object;
|
||||
config: string;
|
||||
manifset: Object;
|
||||
config: string;
|
||||
}
|
||||
|
||||
export interface RetagRequest {
|
||||
@ -426,10 +426,23 @@ export interface ClrDatagridFilterInterface<T> {
|
||||
}
|
||||
|
||||
/** @deprecated since 0.11 */
|
||||
export interface Comparator<T> extends ClrDatagridComparatorInterface<T> {}
|
||||
export interface Comparator<T> extends ClrDatagridComparatorInterface<T> { }
|
||||
/** @deprecated since 0.11 */
|
||||
export interface ClrFilter<T> extends ClrDatagridFilterInterface<T> {}
|
||||
export interface ClrFilter<T> extends ClrDatagridFilterInterface<T> { }
|
||||
/** @deprecated since 0.11 */
|
||||
export interface State extends ClrDatagridStateInterface {}
|
||||
export interface Modal extends ClrModal {}
|
||||
export interface State extends ClrDatagridStateInterface { }
|
||||
export interface Modal extends 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' },
|
||||
];
|
||||
|
||||
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 enum Roles {
|
||||
PROJECT_ADMIN = 1,
|
||||
PROJECT_MASTER = 4,
|
||||
DEVELOPER = 2,
|
||||
GUEST = 3,
|
||||
OTHER = 0,
|
||||
|
@ -91,15 +91,15 @@
|
||||
</div>
|
||||
</section>
|
||||
<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")'
|
||||
type="button" (click)='tabLinkClick("tag-vulnerability")'>{{'REPOSITORY.VULNERABILITY' | translate}}</button>
|
||||
<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>
|
||||
<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>
|
||||
<clr-tab-content *clrIfActive>
|
||||
<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 { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||
import { ChannelService } from "../channel/channel.service";
|
||||
import { of } from "rxjs";
|
||||
import {
|
||||
JobLogService,
|
||||
JobLogDefaultService
|
||||
} from "../service/job-log.service";
|
||||
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||
import { USERSTATICPERMISSION } from "../service/permission-static";
|
||||
|
||||
describe("TagDetailComponent (inline template)", () => {
|
||||
let comp: TagDetailComponent;
|
||||
let fixture: ComponentFixture<TagDetailComponent>;
|
||||
let tagService: TagService;
|
||||
let userPermissionService: UserPermissionService;
|
||||
let scanningService: ScanningResultService;
|
||||
let spy: jasmine.Spy;
|
||||
let vulSpy: jasmine.Spy;
|
||||
@ -83,13 +87,13 @@ describe("TagDetailComponent (inline template)", () => {
|
||||
let config: IServiceConfig = {
|
||||
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||
};
|
||||
|
||||
let mockHasVulnerabilitiesListPermission: boolean = false;
|
||||
let mockHasBuildHistoryPermission: boolean = true;
|
||||
let mockManifest: Manifest = {
|
||||
manifset: {},
|
||||
// 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"]}}`
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [SharedModule],
|
||||
@ -108,6 +112,7 @@ describe("TagDetailComponent (inline template)", () => {
|
||||
{ provide: JobLogService, useClass: JobLogDefaultService },
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||
{
|
||||
provide: ScanningResultService,
|
||||
useClass: ScanningResultDefaultService
|
||||
@ -122,6 +127,8 @@ describe("TagDetailComponent (inline template)", () => {
|
||||
|
||||
comp.tagId = "mock_tag";
|
||||
comp.repositoryId = "mock_repo";
|
||||
comp.projectId = 1;
|
||||
|
||||
|
||||
tagService = fixture.debugElement.injector.get(TagService);
|
||||
spy = spyOn(tagService, "getTag").and.returnValues(
|
||||
@ -153,7 +160,14 @@ describe("TagDetailComponent (inline template)", () => {
|
||||
manifestSpy = spyOn(tagService, "getManifest").and.returnValues(
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -4,6 +4,9 @@ import { TagService, Tag, VulnerabilitySeverity } from "../service/index";
|
||||
import { toPromise } from "../utils";
|
||||
import { ErrorHandler } from "../error-handler/index";
|
||||
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 } = {
|
||||
"tag-history": "history",
|
||||
@ -32,8 +35,6 @@ export class TagDetailComponent implements OnInit {
|
||||
withAdmiral: boolean;
|
||||
@Input()
|
||||
withClair: boolean;
|
||||
@Input()
|
||||
withAdminRole: boolean;
|
||||
tagDetails: Tag = {
|
||||
name: "--",
|
||||
size: "--",
|
||||
@ -51,11 +52,14 @@ export class TagDetailComponent implements OnInit {
|
||||
backEvt: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
currentTabID = "tag-vulnerability";
|
||||
|
||||
hasVulnerabilitiesListPermission: boolean;
|
||||
hasBuildHistoryPermission: boolean;
|
||||
@Input() projectId: number;
|
||||
constructor(
|
||||
private tagService: TagService,
|
||||
private errorHandler: ErrorHandler
|
||||
) {}
|
||||
private errorHandler: ErrorHandler,
|
||||
private userPermissionService: UserPermissionService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.repositoryId && this.tagId) {
|
||||
@ -90,6 +94,7 @@ export class TagDetailComponent implements OnInit {
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
this.getTagPermissions(this.projectId);
|
||||
}
|
||||
|
||||
onBack(): void {
|
||||
@ -173,4 +178,16 @@ export class TagDetailComponent implements OnInit {
|
||||
tabLinkClick(tabID: string) {
|
||||
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]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" ><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||
<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>
|
||||
<div class="filter-grid">
|
||||
<label class="dropdown-header">{{'REPOSITORY.ADD_LABEL_TO_IMAGE' | translate}}</label>
|
||||
@ -75,8 +75,8 @@
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</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="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="!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="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-column class="flex-max-width" [clrDgField]="'name'">{{'REPOSITORY.TAG' | 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 { LabelPieceComponent } from "../label-piece/label-piece.component";
|
||||
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 { Observable, of } from "rxjs";
|
||||
|
||||
describe("TagComponent (inline template)", () => {
|
||||
|
||||
let comp: TagComponent;
|
||||
let fixture: ComponentFixture<TagComponent>;
|
||||
let tagService: TagService;
|
||||
let userPermissionService: UserPermissionService;
|
||||
let spy: jasmine.Spy;
|
||||
let spyLabels: jasmine.Spy;
|
||||
let spyLabels1: jasmine.Spy;
|
||||
|
||||
let mockTags: Tag[] = [
|
||||
{
|
||||
"digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
||||
@ -95,7 +100,10 @@ describe("TagComponent (inline template)", () => {
|
||||
let config: IServiceConfig = {
|
||||
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||
};
|
||||
|
||||
let mockHasAddLabelImagePermission: boolean = true;
|
||||
let mockHasRetagImagePermission: boolean = true;
|
||||
let mockHasDeleteImagePermission: boolean = true;
|
||||
let mockHasScanImagePermission: boolean = true;
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@ -119,6 +127,7 @@ describe("TagComponent (inline template)", () => {
|
||||
{ provide: RetagService, useClass: RetagDefaultService },
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||
{ provide: LabelService, useClass: LabelDefaultService },
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
@ -130,10 +139,12 @@ describe("TagComponent (inline template)", () => {
|
||||
|
||||
comp.projectId = 1;
|
||||
comp.repoName = "library/nginx";
|
||||
comp.hasProjectAdminRole = true;
|
||||
comp.hasDeleteImagePermission = true;
|
||||
comp.hasScanImagePermission = true;
|
||||
comp.hasSignedIn = true;
|
||||
comp.registryUrl = "http://registry.testing.com";
|
||||
comp.withNotary = false;
|
||||
comp.withAdmiral = false;
|
||||
|
||||
|
||||
let labelService: LabelService;
|
||||
@ -141,6 +152,17 @@ describe("TagComponent (inline template)", () => {
|
||||
|
||||
tagService = fixture.debugElement.injector.get(TagService);
|
||||
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);
|
||||
|
||||
@ -149,7 +171,6 @@ describe("TagComponent (inline template)", () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should load data", async(() => {
|
||||
expect(spy.calls.any).toBeTruthy();
|
||||
}));
|
||||
@ -169,3 +190,5 @@ describe("TagComponent (inline template)", () => {
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -21,8 +21,8 @@ import {
|
||||
ChangeDetectorRef,
|
||||
ElementRef, AfterViewInit
|
||||
} from "@angular/core";
|
||||
import {Subject, forkJoin} from "rxjs";
|
||||
import { debounceTime , distinctUntilChanged, finalize} from 'rxjs/operators';
|
||||
import { Subject, forkJoin } from "rxjs";
|
||||
import { debounceTime, distinctUntilChanged, finalize } from 'rxjs/operators';
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { State, Comparator } from "../service/interface";
|
||||
|
||||
@ -30,9 +30,9 @@ import { TagService, RetagService, VulnerabilitySeverity, RequestQueryParams } f
|
||||
import { ErrorHandler } from "../error-handler/error-handler";
|
||||
import { ChannelService } from "../channel/index";
|
||||
import {
|
||||
ConfirmationTargets,
|
||||
ConfirmationState,
|
||||
ConfirmationButtons, Roles
|
||||
ConfirmationTargets,
|
||||
ConfirmationState,
|
||||
ConfirmationButtons, Roles
|
||||
} from "../shared/shared.const";
|
||||
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
@ -54,6 +54,8 @@ import {
|
||||
|
||||
import { CopyInputComponent } from "../push-image/copy-input.component";
|
||||
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 { OperationService } from "../operation/operation.service";
|
||||
import { ImageNameInputComponent } from "../image-name-input/image-name-input.component";
|
||||
@ -71,14 +73,13 @@ export interface LabelState {
|
||||
})
|
||||
export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
signedCon: {[key: string]: any | string[]} = {};
|
||||
signedCon: { [key: string]: any | string[] } = {};
|
||||
@Input() projectId: number;
|
||||
@Input() memberRoleID: number;
|
||||
@Input() repoName: string;
|
||||
@Input() isEmbedded: boolean;
|
||||
|
||||
@Input() hasSignedIn: boolean;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Input() isGuest: boolean;
|
||||
@Input() registryUrl: string;
|
||||
@Input() withNotary: boolean;
|
||||
@ -116,8 +117,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
labelListOpen = false;
|
||||
selectedTag: Tag[];
|
||||
labelNameFilter: Subject<string> = new Subject<string> ();
|
||||
stickLabelNameFilter: Subject<string> = new Subject<string> ();
|
||||
labelNameFilter: Subject<string> = new Subject<string>();
|
||||
stickLabelNameFilter: Subject<string> = new Subject<string>();
|
||||
filterOnGoing: boolean;
|
||||
stickName = '';
|
||||
filterName = '';
|
||||
@ -144,10 +145,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
totalCount = 0;
|
||||
currentState: State;
|
||||
|
||||
hasAddLabelImagePermission: boolean;
|
||||
hasRetagImagePermission: boolean;
|
||||
hasDeleteImagePermission: boolean;
|
||||
hasScanImagePermission: boolean;
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private tagService: TagService,
|
||||
private retagService: RetagService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private labelService: LabelService,
|
||||
private translateService: TranslateService,
|
||||
private ref: ChangeDetectorRef,
|
||||
@ -164,50 +170,50 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
this.errorHandler.error("Repo name cannot be unset.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.retrieve();
|
||||
this.lastFilteredTagName = '';
|
||||
|
||||
this.labelNameFilter
|
||||
.pipe(debounceTime(500))
|
||||
.pipe(distinctUntilChanged())
|
||||
.subscribe((name: string) => {
|
||||
if (this.filterName.length) {
|
||||
this.filterOnGoing = true;
|
||||
.pipe(debounceTime(500))
|
||||
.pipe(distinctUntilChanged())
|
||||
.subscribe((name: string) => {
|
||||
if (this.filterName.length) {
|
||||
this.filterOnGoing = true;
|
||||
|
||||
this.imageFilterLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(this.filterName) !== -1) {
|
||||
data.show = true;
|
||||
} else {
|
||||
data.show = false;
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
setInterval(() => this.ref.markForCheck(), 200);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
this.imageFilterLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(this.filterName) !== -1) {
|
||||
data.show = true;
|
||||
} else {
|
||||
data.show = false;
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
setInterval(() => this.ref.markForCheck(), 200);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
this.stickLabelNameFilter
|
||||
.pipe(debounceTime(500))
|
||||
.pipe(distinctUntilChanged())
|
||||
.subscribe((name: string) => {
|
||||
if (this.stickName.length) {
|
||||
this.filterOnGoing = true;
|
||||
.pipe(debounceTime(500))
|
||||
.pipe(distinctUntilChanged())
|
||||
.subscribe((name: string) => {
|
||||
if (this.stickName.length) {
|
||||
this.filterOnGoing = true;
|
||||
|
||||
this.imageStickLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(this.stickName) !== -1) {
|
||||
data.show = true;
|
||||
} else {
|
||||
data.show = false;
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
setInterval(() => this.ref.markForCheck(), 200);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
this.imageStickLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(this.stickName) !== -1) {
|
||||
data.show = true;
|
||||
} else {
|
||||
data.show = false;
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
setInterval(() => this.ref.markForCheck(), 200);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
this.getImagePermissionRule(this.projectId);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@ -219,7 +225,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
public get filterLabelPieceWidth() {
|
||||
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
||||
return len > 210 ? 210 : len;
|
||||
}
|
||||
}
|
||||
|
||||
doSearchTagNames(tagName: string) {
|
||||
this.lastFilteredTagName = tagName;
|
||||
@ -234,9 +240,9 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
st.page.to = this.pageSize - 1;
|
||||
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
|
||||
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 {
|
||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}];
|
||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }];
|
||||
}
|
||||
|
||||
this.clrLoad(st);
|
||||
@ -286,14 +292,14 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
toPromise<Label[]>(this.labelService.getGLabels()).then((res: Label[]) => {
|
||||
if (res.length) {
|
||||
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[]) => {
|
||||
if (res1.length) {
|
||||
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);
|
||||
@ -372,15 +378,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
unSelectLabel(labelInfo: LabelState): void {
|
||||
if (!this.inprogress) {
|
||||
this.inprogress = true;
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||
this.refresh();
|
||||
if (!this.inprogress) {
|
||||
this.inprogress = true;
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||
this.refresh();
|
||||
|
||||
// insert the unselected label to groups with the same icons
|
||||
this.sortOperation(this.imageStickLabels, labelInfo);
|
||||
// insert the unselected label to groups with the same icons
|
||||
this.sortOperation(this.imageStickLabels, labelInfo);
|
||||
labelInfo.iconsShow = false;
|
||||
this.inprogress = false;
|
||||
}).catch(err => {
|
||||
@ -417,26 +423,26 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
data.iconsShow = true;
|
||||
}
|
||||
});
|
||||
this.imageFilterLabels.splice(this.imageFilterLabels.indexOf(labelInfo), 1);
|
||||
this.imageFilterLabels.unshift(labelInfo);
|
||||
this.filterOneLabel = labelInfo.label;
|
||||
this.imageFilterLabels.splice(this.imageFilterLabels.indexOf(labelInfo), 1);
|
||||
this.imageFilterLabels.unshift(labelInfo);
|
||||
this.filterOneLabel = labelInfo.label;
|
||||
|
||||
// reload data
|
||||
this.currentPage = 1;
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
if (this.lastFilteredTagName) {
|
||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}, {property: 'labels.id', value: labelId}];
|
||||
} else {
|
||||
st.filters = [{property: 'labels.id', value: labelId}];
|
||||
}
|
||||
// reload data
|
||||
this.currentPage = 1;
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
if (this.lastFilteredTagName) {
|
||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }, { property: 'labels.id', value: labelId }];
|
||||
} else {
|
||||
st.filters = [{ property: 'labels.id', value: labelId }];
|
||||
}
|
||||
|
||||
this.clrLoad(st);
|
||||
this.clrLoad(st);
|
||||
}
|
||||
|
||||
unFilterLabel(labelInfo: LabelState): void {
|
||||
@ -456,7 +462,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
if (this.lastFilteredTagName) {
|
||||
st.filters = [{property: 'name', value: this.lastFilteredTagName}];
|
||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }];
|
||||
} else {
|
||||
st.filters = [];
|
||||
}
|
||||
@ -480,7 +486,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
data.show = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
} else {
|
||||
this.openLabelFilterPanel = false;
|
||||
this.openLabelFilterPiece = false;
|
||||
}
|
||||
@ -523,7 +529,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
retrieve() {
|
||||
this.tags = [];
|
||||
let signatures: string[] = [] ;
|
||||
let signatures: string[] = [];
|
||||
this.loading = true;
|
||||
|
||||
toPromise<Tag[]>(this.tagService
|
||||
@ -539,15 +545,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
components: {
|
||||
total: 0,
|
||||
summary: []
|
||||
}
|
||||
};
|
||||
}
|
||||
if (t.signature !== null) {
|
||||
signatures.push(t.name);
|
||||
}
|
||||
});
|
||||
this.tags = items;
|
||||
let signedName: {[key: string]: string[]} = {};
|
||||
}
|
||||
};
|
||||
}
|
||||
if (t.signature !== null) {
|
||||
signatures.push(t.name);
|
||||
}
|
||||
});
|
||||
this.tags = items;
|
||||
let signedName: { [key: string]: string[] } = {};
|
||||
signedName[this.repoName] = signatures;
|
||||
this.signatureOutput.emit(signedName);
|
||||
this.loading = false;
|
||||
@ -568,9 +574,9 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
if (Math.pow(1024, 1) <= size && size < Math.pow(1024, 2)) {
|
||||
return (size / Math.pow(1024, 1)).toFixed(2) + "KB";
|
||||
} 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)) {
|
||||
return (size / Math.pow(1024, 3)).toFixed(2) + "GB";
|
||||
return (size / Math.pow(1024, 3)).toFixed(2) + "GB";
|
||||
} else {
|
||||
return size + "B";
|
||||
}
|
||||
@ -578,8 +584,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
retag(tags: Tag[]) {
|
||||
if (tags && tags.length) {
|
||||
this.retagDialogOpened = true;
|
||||
this.retagSrcImage = this.repoName + ":" + tags[0].digest;
|
||||
this.retagDialogOpened = true;
|
||||
this.retagSrcImage = this.repoName + ":" + tags[0].digest;
|
||||
} else {
|
||||
this.errorHandler.error("One tag should be selected before retag.");
|
||||
}
|
||||
@ -587,23 +593,23 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
onRetag() {
|
||||
this.retagService.retag({
|
||||
targetProject: this.imageNameInput.projectName.value,
|
||||
targetRepo: this.imageNameInput.repoName.value,
|
||||
targetTag: this.imageNameInput.tagName.value,
|
||||
srcImage: this.retagSrcImage,
|
||||
override: true
|
||||
})
|
||||
.pipe(finalize(() => {
|
||||
targetProject: this.imageNameInput.projectName.value,
|
||||
targetRepo: this.imageNameInput.repoName.value,
|
||||
targetTag: this.imageNameInput.tagName.value,
|
||||
srcImage: this.retagSrcImage,
|
||||
override: true
|
||||
})
|
||||
.pipe(finalize(() => {
|
||||
this.retagDialogOpened = false;
|
||||
this.imageNameInput.form.reset();
|
||||
}))
|
||||
.subscribe(response => {
|
||||
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
||||
this.errorHandler.info(res);
|
||||
});
|
||||
}, error => {
|
||||
}))
|
||||
.subscribe(response => {
|
||||
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
||||
this.errorHandler.info(res);
|
||||
});
|
||||
}, error => {
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteTags(tags: Tag[]) {
|
||||
@ -631,8 +637,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.TAG
|
||||
&& message.state === ConfirmationState.CONFIRMED) {
|
||||
message.source === ConfirmationTargets.TAG
|
||||
&& message.state === ConfirmationState.CONFIRMED) {
|
||||
let tags: Tag[] = message.data;
|
||||
if (tags && tags.length) {
|
||||
let promiseLists: any[] = [];
|
||||
@ -660,27 +666,27 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
if (tag.signature) {
|
||||
forkJoin(this.translateService.get("BATCH.DELETED_FAILURE"),
|
||||
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 " +
|
||||
this.registryUrl + "/" + this.repoName +
|
||||
" " + name;
|
||||
operateChanges(operMessage, OperationState.failure, wrongInfo);
|
||||
});
|
||||
operateChanges(operMessage, OperationState.failure, wrongInfo);
|
||||
});
|
||||
} else {
|
||||
return toPromise<number>(this.tagService
|
||||
.deleteTag(this.repoName, tag.name))
|
||||
.then(
|
||||
response => {
|
||||
this.translateService.get("BATCH.DELETED_SUCCESS")
|
||||
.subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(error => {
|
||||
.deleteTag(this.repoName, tag.name))
|
||||
.then(
|
||||
response => {
|
||||
this.translateService.get("BATCH.DELETED_SUCCESS")
|
||||
.subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(error => {
|
||||
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]);
|
||||
});
|
||||
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
@ -744,13 +750,29 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
// Whether show the 'scan now' menu
|
||||
canScanNow(t: Tag[]): boolean {
|
||||
if (!this.withClair) { return false; }
|
||||
if (!this.hasProjectAdminRole) { return false; }
|
||||
let st: string = this.scanStatus(t[0]);
|
||||
if (!this.hasScanImagePermission) { return false; }
|
||||
let st: string = this.scanStatus(t[0]);
|
||||
|
||||
return st !== VULNERABILITY_SCAN_STATUS.pending &&
|
||||
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
|
||||
scanNow(t: Tag[]): void {
|
||||
if (t && t.length) {
|
||||
@ -763,14 +785,6 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
// pull command
|
||||
onCpError($event: any): void {
|
||||
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;
|
||||
this.copyInput.setPullCommendShow();
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,14 @@ export const HTTP_GET_OPTIONS: RequestOptions = new RequestOptions({
|
||||
"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({
|
||||
headers: new Headers({
|
||||
|
@ -10,7 +10,7 @@
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid>
|
||||
<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-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | 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 { FilterComponent } from '../filter/index';
|
||||
import {ChannelService} from "../channel/channel.service";
|
||||
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||
|
||||
describe('ResultGridComponent (inline template)', () => {
|
||||
let component: ResultGridComponent;
|
||||
@ -29,7 +30,8 @@ describe('ResultGridComponent (inline template)', () => {
|
||||
ErrorHandler,
|
||||
ChannelService,
|
||||
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService }
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -5,10 +5,12 @@ import {
|
||||
VulnerabilitySeverity
|
||||
} from '../service/index';
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
import { forkJoin } from "rxjs";
|
||||
|
||||
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({
|
||||
selector: 'hbr-vulnerabilities-grid',
|
||||
templateUrl: './result-grid.component.html',
|
||||
@ -20,12 +22,12 @@ export class ResultGridComponent implements OnInit {
|
||||
|
||||
@Input() tagId: string;
|
||||
@Input() repositoryId: string;
|
||||
@Input() withAdminRole: boolean;
|
||||
|
||||
hasScanImagePermission: boolean;
|
||||
constructor(
|
||||
private scanningService: ScanningResultService,
|
||||
private channel: ChannelService,
|
||||
private errorHandler: ErrorHandler
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -79,4 +81,14 @@ export class ResultGridComponent implements OnInit {
|
||||
scanNow(): void {
|
||||
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 { VULNERABILITY_SCAN_STATUS } from '../utils';
|
||||
import { UserPermissionService, UserPermissionDefaultService } from "../service/permission.service";
|
||||
|
||||
describe('ResultTipComponent (inline template)', () => {
|
||||
let component: ResultTipComponent;
|
||||
@ -41,7 +42,8 @@ describe('ResultTipComponent (inline template)', () => {
|
||||
SharedModule
|
||||
],
|
||||
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",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
|
||||
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.21",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
|
||||
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"mime-db": "~1.37.0"
|
||||
}
|
||||
@ -2757,9 +2759,9 @@
|
||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
|
||||
},
|
||||
"@types/jasmine": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.8.tgz",
|
||||
"integrity": "sha512-OJSUxLaxXsjjhob2DBzqzgrkLmukM3+JMpRp0r0E4HTdT1nwDCWhaswjYxazPij6uOdzHCJfNbDjmQ1/rnNbCg==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "http://registry.npm.taobao.org/@types/jasmine/download/@types/jasmine-3.3.8.tgz",
|
||||
"integrity": "sha1-/Gq9kvcSFDFoXsmG8OyPd7Q5Cj4=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jasminewd2": {
|
||||
@ -6284,7 +6286,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -6305,12 +6308,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -6325,17 +6330,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"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": {
|
||||
"version": "0.0.1",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
@ -6452,7 +6460,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -6464,6 +6473,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -6478,6 +6488,7 @@
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -6485,12 +6496,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
|
||||
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
@ -6509,6 +6522,7 @@
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -6589,7 +6603,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"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": {
|
||||
"version": "4.1.1",
|
||||
@ -6601,6 +6616,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -6686,7 +6702,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
@ -6722,6 +6739,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -6741,6 +6759,7 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -6784,12 +6803,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"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": {
|
||||
"version": "2.99.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
|
||||
"integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
|
||||
"dev": true
|
||||
"version": "3.3.0",
|
||||
"resolved": "http://registry.npm.taobao.org/jasmine-core/download/jasmine-core-3.3.0.tgz",
|
||||
"integrity": "sha1-3qHNxjS8k8fg1K0nGF3zD6lxsQ4="
|
||||
},
|
||||
"jasmine-diff": {
|
||||
"version": "0.1.3",
|
||||
@ -8965,7 +8985,7 @@
|
||||
},
|
||||
"karma-jasmine": {
|
||||
"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=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -42,6 +42,7 @@
|
||||
"buffer": "^5.2.1",
|
||||
"core-js": "^2.5.4",
|
||||
"intl": "^1.2.5",
|
||||
"jasmine-core": "^3.3.0",
|
||||
"jquery": "^3.3.1",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ng-packagr": "^4.1.1",
|
||||
@ -65,18 +66,17 @@
|
||||
"@angular/compiler-cli": "^7.1.3",
|
||||
"@angular/language-service": "^7.1.3",
|
||||
"@types/core-js": "^0.9.41",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasmine": "^3.3.1",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.2.1",
|
||||
"enhanced-resolve": "^3.0.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-cli": "^1.0.1",
|
||||
"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-mocha-reporter": "^2.2.4",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
|
@ -25,7 +25,10 @@
|
||||
<button id="config-label" clrTabLink>{{'CONFIG.LABEL' | translate }}</button>
|
||||
<ng-template [(clrIfActive)]="labelActive">
|
||||
<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>
|
||||
</ng-template>
|
||||
</clr-tab>
|
||||
|
@ -10,8 +10,6 @@
|
||||
[chartName]='chartName'
|
||||
[roleName]='roleName'
|
||||
[hasSignedIn]='hasSignedIn'
|
||||
[projectRoleID]='project_member_role_id'
|
||||
[hasProjectAdminRole]='hasProjectAdminRole'
|
||||
(versionClickEvt)='onVersionClick($event)'
|
||||
(backEvt)='gotoChartList()'>
|
||||
</hbr-helm-chart-version>
|
||||
|
@ -21,9 +21,7 @@ export class ListChartVersionsComponent implements OnInit {
|
||||
roleName: string;
|
||||
|
||||
hasSignedIn: boolean;
|
||||
hasProjectAdminRole: boolean;
|
||||
currentUser: SessionUser;
|
||||
project_member_role_id: number;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@ -39,10 +37,8 @@ export class ListChartVersionsComponent implements OnInit {
|
||||
let resolverData = this.route.snapshot.data;
|
||||
if (resolverData) {
|
||||
let project = <Project>(resolverData["projectResolver"]);
|
||||
this.hasProjectAdminRole = project.has_project_admin_role;
|
||||
this.roleName = project.role_name;
|
||||
this.projectName = project.name;
|
||||
this.project_member_role_id = project.current_user_role_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,5 @@
|
||||
[urlPrefix]='urlPrefix'
|
||||
[hasSignedIn]='hasSignedIn'
|
||||
[projectRoleID]='project_member_role_id'
|
||||
[hasProjectAdminRole]='hasProjectAdminRole'
|
||||
(chartClickEvt)='onChartClick($event)'>
|
||||
</hbr-helm-chart>
|
||||
|
@ -17,7 +17,6 @@ export class ListChartsComponent implements OnInit {
|
||||
projectName: string;
|
||||
urlPrefix: string;
|
||||
hasSignedIn: boolean;
|
||||
hasProjectAdminRole: boolean;
|
||||
project_member_role_id: number;
|
||||
currentUser: SessionUser;
|
||||
|
||||
@ -35,7 +34,6 @@ export class ListChartsComponent implements OnInit {
|
||||
if (resolverData) {
|
||||
let project = <Project>(resolverData["projectResolver"]);
|
||||
this.projectName = project.name;
|
||||
this.hasProjectAdminRole = project.has_project_admin_role;
|
||||
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">
|
||||
<label for="checkrads_project_admin">{{'MEMBER.PROJECT_ADMIN' | translate}}</label>
|
||||
</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">
|
||||
<input type="radio" name="member_role" id="checkrads_developer" [value]=2 [(ngModel)]="member.role_id">
|
||||
<label for="checkrads_developer">{{'MEMBER.DEVELOPER' | translate}}</label>
|
||||
|
@ -13,21 +13,22 @@
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<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>
|
||||
</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>
|
||||
</button>
|
||||
<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>
|
||||
<clr-dropdown-menu *clrIfOpen>
|
||||
<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, 2)" [disabled]="!(selectedRow.length && hasProjectAdminRole) || onlySelf">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 3)" [disabled]="!(selectedRow.length && hasProjectAdminRole) || onlySelf">{{'MEMBER.GUEST' | translate}}</button>
|
||||
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 1)" [disabled]="!(selectedRow.length && hasUpdateMemberPermission) || onlySelf">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||
<button clrDropdownItem (click)="changeMembersRole(selectedRow, 4)" [disabled]="!(selectedRow.length && hasUpdateMemberPermission) || onlySelf">{{'MEMBER.PROJECT_MASTER' | 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>
|
||||
<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>
|
||||
</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.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -15,9 +15,9 @@ import {finalize} from 'rxjs/operators';
|
||||
// limitations under the License.
|
||||
import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Subscription } from "rxjs";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "@harbor/ui";
|
||||
import { Subscription, forkJoin } from "rxjs";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { operateChanges, OperateInfo, OperationService, OperationState } from "@harbor/ui";
|
||||
|
||||
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
|
||||
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 { MemberService } from "./member.service";
|
||||
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({
|
||||
templateUrl: "member.component.html",
|
||||
@ -46,7 +47,6 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
delSub: Subscription;
|
||||
|
||||
currentUser: SessionUser;
|
||||
hasProjectAdminRole: boolean;
|
||||
|
||||
batchOps = 'delete';
|
||||
searchMember: string;
|
||||
@ -65,7 +65,9 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild(AddGroupComponent)
|
||||
addGroupComponent: AddGroupComponent;
|
||||
|
||||
hasCreateMemberPermission: boolean;
|
||||
hasUpdateMemberPermission: boolean;
|
||||
hasDeleteMemberPermission: boolean;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
@ -76,6 +78,8 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
private session: SessionService,
|
||||
private operationService: OperationService,
|
||||
private appConfigService: AppConfigService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private ref: ChangeDetectorRef) {
|
||||
|
||||
this.delSub = OperateDialogService.confirmationConfirm$.subscribe(message => {
|
||||
@ -102,14 +106,12 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
this.projectId = +this.route.snapshot.parent.params["id"];
|
||||
// Get current user from registered resolver.
|
||||
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, "");
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
this.isLdapMode = true;
|
||||
}
|
||||
// get member permission rule
|
||||
this.getMemberPermissionRule(this.projectId);
|
||||
}
|
||||
|
||||
doSearch(searchMember: string) {
|
||||
@ -126,23 +128,23 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
this.selectedRow = [];
|
||||
this.memberService
|
||||
.listMembers(projectId, username).pipe(
|
||||
finalize(() => this.loading = false))
|
||||
finalize(() => this.loading = false))
|
||||
.subscribe(
|
||||
response => {
|
||||
this.members = response;
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
},
|
||||
error => {
|
||||
this.router.navigate(["/harbor", "projects"]);
|
||||
this.messageHandlerService.handleError(error);
|
||||
});
|
||||
response => {
|
||||
this.members = response;
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
},
|
||||
error => {
|
||||
this.router.navigate(["/harbor", "projects"]);
|
||||
this.messageHandlerService.handleError(error);
|
||||
});
|
||||
}
|
||||
|
||||
get onlySelf(): boolean {
|
||||
if (this.selectedRow.length === 1 &&
|
||||
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 false;
|
||||
@ -173,7 +175,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
addedGroup(result: boolean) {
|
||||
this.searchMember = "";
|
||||
this.retrieve(this.projectId, "");
|
||||
}
|
||||
}
|
||||
|
||||
changeMembersRole(members: Member[], roleId: number) {
|
||||
if (!members) {
|
||||
@ -182,9 +184,9 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
|
||||
let changeOperate = (projectId: number, member: Member, ) => {
|
||||
return this.memberService
|
||||
.changeMemberRole(projectId, member.id, roleId)
|
||||
.then( () => this.batchChangeRoleInfos[member.id] = 'done')
|
||||
.catch(error => this.messageHandlerService.handleError(error + ": " + member.entity_name));
|
||||
.changeMemberRole(projectId, member.id, roleId)
|
||||
.then(() => this.batchChangeRoleInfos[member.id] = 'done')
|
||||
.catch(error => this.messageHandlerService.handleError(error + ": " + member.entity_name));
|
||||
};
|
||||
|
||||
// Preparation for members role change
|
||||
@ -223,7 +225,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
ConfirmationTargets.PROJECT_MEMBER,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.OperateDialogService.openComfirmDialog(deletionMessage);
|
||||
this.OperateDialogService.openComfirmDialog(deletionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,15 +252,15 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
return this.memberService
|
||||
.deleteMember(projectId, member.id)
|
||||
.then(response => {
|
||||
this.translate.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
this.translate.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Deleting member then wating for results
|
||||
@ -270,4 +272,17 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
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="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>
|
@ -28,7 +28,6 @@ export class ProjectConfigComponent implements OnInit {
|
||||
projectName: string;
|
||||
currentUser: SessionUser;
|
||||
hasSignedIn: boolean;
|
||||
hasProjectAdminRole: boolean;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@ -42,7 +41,6 @@ export class ProjectConfigComponent implements OnInit {
|
||||
let resolverData = this.route.snapshot.parent.data;
|
||||
if (resolverData) {
|
||||
let pro: Project = <Project>resolverData['projectResolver'];
|
||||
this.hasProjectAdminRole = pro.has_project_admin_role;
|
||||
this.projectName = pro.name;
|
||||
}
|
||||
}
|
||||
|
@ -4,28 +4,28 @@
|
||||
<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">
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -11,7 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { Project } from '../project';
|
||||
@ -19,26 +19,38 @@ import { Project } from '../project';
|
||||
import { SessionService } from '../../shared/session.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({
|
||||
selector: 'project-detail',
|
||||
templateUrl: 'project-detail.component.html',
|
||||
styleUrls: [ 'project-detail.component.scss' ]
|
||||
selector: 'project-detail',
|
||||
templateUrl: 'project-detail.component.html',
|
||||
styleUrls: ['project-detail.component.scss']
|
||||
})
|
||||
export class ProjectDetailComponent {
|
||||
export class ProjectDetailComponent implements OnInit {
|
||||
|
||||
hasSignedIn: boolean;
|
||||
currentProject: Project;
|
||||
|
||||
isMember: boolean;
|
||||
roleName: string;
|
||||
|
||||
projectId: number;
|
||||
hasHelmChartsListPermission: boolean;
|
||||
hasRepositoryListPermission: boolean;
|
||||
hasMemberListPermission: boolean;
|
||||
hasReplicationListPermission: boolean;
|
||||
hasLabelListPermission: boolean;
|
||||
hasLabelCreatePermission: boolean;
|
||||
hasLogListPermission: boolean;
|
||||
hasConfigurationListPermission: boolean;
|
||||
hasRobotListPermission: boolean;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private sessionService: SessionService,
|
||||
private appConfigService: AppConfigService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private projectService: ProjectService) {
|
||||
|
||||
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
||||
@ -48,14 +60,42 @@ export class ProjectDetailComponent {
|
||||
this.roleName = this.currentProject.role_name;
|
||||
});
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
let account = this.sessionService.getCurrentUser();
|
||||
return account && account.has_admin_role;
|
||||
ngOnInit() {
|
||||
this.projectId = this.route.snapshot.params['id'];
|
||||
this.getPermissionsList(this.projectId);
|
||||
}
|
||||
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 {
|
||||
return this.currentProject.has_project_admin_role;
|
||||
}, error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
public get isSessionValid(): boolean {
|
||||
|
@ -1,5 +1,10 @@
|
||||
<div class="row">
|
||||
<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>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 label-top">
|
||||
<hbr-label
|
||||
[projectId]="projectId"
|
||||
[scope]="'p'"
|
||||
[hasCreateLabelPermission]="hasCreateLabelPermission"
|
||||
[hasUpdateLabelPermission]="hasUpdateLabelPermission"
|
||||
[hasDeleteLabelPermission]="hasDeleteLabelPermission"></hbr-label>
|
||||
</div>
|
||||
</div>
|
@ -16,6 +16,8 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
import { SessionUser } from '../../shared/session-user';
|
||||
import { Project } from '../project';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { UserPermissionService, USERSTATICPERMISSION, ErrorHandler } from "@harbor/ui";
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-config',
|
||||
@ -29,21 +31,34 @@ export class ProjectLabelComponent implements OnInit {
|
||||
currentUser: SessionUser;
|
||||
hasSignedIn: boolean;
|
||||
hasProjectAdminRole: boolean;
|
||||
|
||||
hasCreateLabelPermission: boolean;
|
||||
hasUpdateLabelPermission: boolean;
|
||||
hasDeleteLabelPermission: boolean;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private session: SessionService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||
this.currentUser = this.session.getCurrentUser();
|
||||
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
||||
let resolverData = this.route.snapshot.parent.data;
|
||||
if (resolverData) {
|
||||
let pro: Project = <Project>resolverData['projectResolver'];
|
||||
this.hasProjectAdminRole = pro.has_project_admin_role;
|
||||
this.projectName = pro.name;
|
||||
}
|
||||
this.getLabelPermissionRule(this.projectId);
|
||||
}
|
||||
|
||||
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 class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-dg-action-bar>
|
||||
<button class="btn btn-sm btn-secondary"
|
||||
(click)="openAddRobotModal()">
|
||||
<span><clr-icon shape="plus" size="16"></clr-icon> {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
|
||||
| translate }}</span>
|
||||
<button class="btn btn-sm btn-secondary" [disabled]="!hasRobotCreatePermission" (click)="openAddRobotModal()">
|
||||
<span>
|
||||
<clr-icon shape="plus" size="16"></clr-icon> {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
|
||||
| translate }}
|
||||
</span>
|
||||
</button>
|
||||
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="btn btn-sm
|
||||
btn-link"
|
||||
clrDropdownTrigger>
|
||||
btn-link" clrDropdownTrigger>
|
||||
<span>{{'MEMBER.ACTION' | translate}}<clr-icon shape="caret
|
||||
down"></clr-icon></span>
|
||||
<clr-dropdown-menu *clrIfOpen>
|
||||
<button clrDropdownItem [disabled]="!(selectedRow.length ==
|
||||
<button clrDropdownItem [disabled]="!hasRobotUpdatePermission||!(selectedRow.length ==
|
||||
1)" (click)="changeAccountStatus(selectedRow)">
|
||||
<span *ngIf="selectedRow[0] && !selectedRow[0].disabled
|
||||
|| selectedRow.length!==1">{{'ROBOT_ACCOUNT.DISABLE_ACCOUNT'
|
||||
@ -35,9 +35,7 @@
|
||||
| translate}}</span>
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button clrDropdownItem
|
||||
(click)="openDeleteRobotsDialog(selectedRow)"
|
||||
[disabled]="!selectedRow.length">{{'ROBOT_ACCOUNT.DELETE'
|
||||
<button clrDropdownItem (click)="openDeleteRobotsDialog(selectedRow)" [disabled]="!hasRobotDeletePermission || !selectedRow.length">{{'ROBOT_ACCOUNT.DELETE'
|
||||
| translate}}</button>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
@ -49,12 +47,8 @@
|
||||
<clr-dg-row *clrDgItems="let r of robots" [clrDgItem]="r">
|
||||
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
||||
<clr-dg-cell [ngSwitch]="r.disabled">
|
||||
<clr-icon shape="check-circle" *ngSwitchCase="false"
|
||||
size="20"
|
||||
class="color-green"></clr-icon>
|
||||
<clr-icon shape="times-circle" *ngSwitchCase="true"
|
||||
size="16"
|
||||
class="color-red red-position"></clr-icon>
|
||||
<clr-icon shape="check-circle" *ngSwitchCase="false" size="20" 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>{{r.description}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
@ -68,6 +62,5 @@
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
<add-robot [projectId]="projectId" [projectName]="projectName"
|
||||
(create)="createAccount($event)"></add-robot>
|
||||
</div>
|
||||
<add-robot [projectId]="projectId" [projectName]="projectName" (create)="createAccount($event)"></add-robot>
|
||||
</div>
|
@ -25,7 +25,10 @@ import {
|
||||
operateChanges,
|
||||
OperateInfo,
|
||||
OperationService,
|
||||
OperationState
|
||||
OperationState,
|
||||
UserPermissionService,
|
||||
USERSTATICPERMISSION,
|
||||
ErrorHandler
|
||||
} from "@harbor/ui";
|
||||
|
||||
@Component({
|
||||
@ -48,12 +51,17 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||
robots: Robot[];
|
||||
projectId: number;
|
||||
subscription: Subscription;
|
||||
hasRobotCreatePermission: boolean;
|
||||
hasRobotUpdatePermission: boolean;
|
||||
hasRobotDeletePermission: boolean;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private robotService: RobotService,
|
||||
private OperateDialogService: ConfirmationDialogService,
|
||||
private operationService: OperationService,
|
||||
private translate: TranslateService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private ref: ChangeDetectorRef,
|
||||
private messageHandlerService: MessageHandlerService
|
||||
) {
|
||||
@ -80,8 +88,24 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
this.searchRobot = "";
|
||||
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 {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
@ -122,7 +146,7 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||
this.selectedRow = [];
|
||||
})
|
||||
)
|
||||
.subscribe(() => {});
|
||||
.subscribe(() => { });
|
||||
}
|
||||
|
||||
delOperate(robot: Robot) {
|
||||
|
@ -1,3 +1,12 @@
|
||||
<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>
|
@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
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 {ProjectService} from "../project/project.service";
|
||||
import { SessionService } from "../shared/session.service";
|
||||
import { Project } from "../project/project";
|
||||
import { ProjectService } from "../project/project.service";
|
||||
import { ReplicationComponent, UserPermissionService, USERSTATICPERMISSION, ErrorHandler } from "@harbor/ui";
|
||||
import { forkJoin } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'replication',
|
||||
@ -28,25 +28,30 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
|
||||
projectIdentify: string | number;
|
||||
@ViewChild("replicationView") replicationView: ReplicationComponent;
|
||||
projectName: string;
|
||||
|
||||
hasCreateReplicationPermission: boolean;
|
||||
hasUpdateReplicationPermission: boolean;
|
||||
hasDeleteReplicationPermission: boolean;
|
||||
hasExecuteReplicationPermission: boolean;
|
||||
constructor(private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private proService: ProjectService,
|
||||
private session: SessionService) { }
|
||||
private router: Router,
|
||||
private proService: ProjectService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private session: SessionService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectIdentify = +this.route.snapshot.parent.params['id'];
|
||||
|
||||
this.getReplicationPermissions(this.projectIdentify);
|
||||
this.proService.listProjects("", undefined).toPromise()
|
||||
.then(response => {
|
||||
let projects = response.json() as Project[];
|
||||
if (projects.length) {
|
||||
let project = projects.find(data => data.project_id === this.projectIdentify);
|
||||
if (project) {
|
||||
this.projectName = project.name;
|
||||
}
|
||||
.then(response => {
|
||||
let projects = response.json() as Project[];
|
||||
if (projects.length) {
|
||||
let project = projects.find(data => data.project_id === this.projectIdentify);
|
||||
if (project) {
|
||||
this.projectName = project.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
@ -66,4 +71,24 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
|
||||
goRegistry(): void {
|
||||
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>
|
||||
<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>
|
@ -5,9 +5,9 @@
|
||||
<a (click)="goBack(repositoryId)">< {{repositoryId}}</a>
|
||||
</div>
|
||||
<hbr-tag-detail (backEvt)="goBack($event)"
|
||||
[tagId]="tagId"
|
||||
[withClair]="withClair"
|
||||
[withAdmiral]="withAdmiral"
|
||||
[repositoryId]="repositoryId"
|
||||
[withAdminRole]="withAdminRole"></hbr-tag-detail>
|
||||
[tagId]="tagId"
|
||||
[withClair]="withClair"
|
||||
[withAdmiral]="withAdmiral"
|
||||
[projectId]="projectId"
|
||||
[repositoryId]="repositoryId"></hbr-tag-detail>
|
||||
</div>
|
@ -48,10 +48,6 @@ export class TagDetailPageComponent implements OnInit {
|
||||
return this.appConfigService.getConfig().with_clair;
|
||||
}
|
||||
|
||||
get withAdminRole(): boolean {
|
||||
return this.session.getCurrentUser().has_admin_role;
|
||||
}
|
||||
|
||||
goBack(tag: string): void {
|
||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
import { Injectable } from '@angular/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 { AlertType, httpStatusCode } from '../../shared/shared.const';
|
||||
@ -27,6 +27,7 @@ export class MessageHandlerService implements ErrorHandler {
|
||||
constructor(
|
||||
private msgService: MessageService,
|
||||
private translate: TranslateService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private session: SessionService) { }
|
||||
|
||||
// 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);
|
||||
// Session is invalid now, clare session cache
|
||||
this.session.clear();
|
||||
this.userPermissionService.clearPermissionCache();
|
||||
} else {
|
||||
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.
|
||||
this.projectService.getProject(projectId).subscribe(project => {
|
||||
if (project.public === 1) {
|
||||
if (project.metadata && project.metadata.public === 'true') {
|
||||
return resolve(true);
|
||||
}
|
||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
||||
|
@ -20,10 +20,11 @@ import {
|
||||
} from '@angular/router';
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
import { CommonRoutes } from '../../shared/shared.const';
|
||||
import { UserPermissionService } from "@harbor/ui";
|
||||
|
||||
@Injectable()
|
||||
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 {
|
||||
// If user has logged in, should not login again
|
||||
@ -34,6 +35,8 @@ export class SignInGuard implements CanActivate, CanActivateChild {
|
||||
this.authService.signOff()
|
||||
.then(() => {
|
||||
this.authService.clear(); // Destroy session cache
|
||||
this.userPermission.clearPermissionCache();
|
||||
|
||||
return resolve(true);
|
||||
})
|
||||
.catch(error => {
|
||||
|
@ -20,7 +20,7 @@ import { Member } from '../project/member/member';
|
||||
|
||||
import { SignInCredential } from './sign-in-credential';
|
||||
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 currentUserEndpoint = "/api/users/current";
|
||||
@ -67,7 +67,7 @@ export class SessionService {
|
||||
signIn(signInCredential: SignInCredential): Promise<any> {
|
||||
// Build the form package
|
||||
let queryParam: string = 'principal=' + encodeURIComponent(signInCredential.principal) +
|
||||
'&password=' + encodeURIComponent(signInCredential.password);
|
||||
'&password=' + encodeURIComponent(signInCredential.password);
|
||||
|
||||
// Trigger Http
|
||||
return this.http.post(signInUrl, queryParam, HTTP_FORM_OPTIONS)
|
||||
@ -144,9 +144,9 @@ export class SessionService {
|
||||
return Promise.reject("Invalid account settings");
|
||||
}
|
||||
return this.http.post(renameAdminEndpoint, JSON.stringify({}), HTTP_JSON_OPTIONS)
|
||||
.toPromise()
|
||||
.then(() => null)
|
||||
.catch(error => this.handleError(error));
|
||||
.toPromise()
|
||||
.then(() => null)
|
||||
.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 RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
||||
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': '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',
|
||||
'master': 'MEMBER.PROJECT_MASTER', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
||||
export const ProjectRoles = [
|
||||
{ id: 1, value: "MEMBER.PROJECT_ADMIN" },
|
||||
{ id: 2, value: "MEMBER.DEVELOPER" },
|
||||
{ id: 3, value: "MEMBER.GUEST" }
|
||||
{ id: 3, value: "MEMBER.GUEST" },
|
||||
{ id: 4, value: "MEMBER.PROJECT_MASTER" },
|
||||
];
|
||||
|
||||
export enum Roles {
|
||||
PROJECT_ADMIN = 1,
|
||||
PROJECT_MASTER = 4,
|
||||
DEVELOPER = 2,
|
||||
GUEST = 3,
|
||||
OTHER = 0,
|
||||
|
@ -26,8 +26,8 @@ import { AppConfigService } from '../app-config.service';
|
||||
import { NewUserModalComponent } from './new-user-modal.component';
|
||||
import { UserService } from './user.service';
|
||||
import { User } from './user';
|
||||
import {ChangePasswordComponent} from "./change-password/change-password.component";
|
||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "@harbor/ui";
|
||||
import { ChangePasswordComponent } from "./change-password/change-password.component";
|
||||
import { operateChanges, OperateInfo, OperationService, OperationState } from "@harbor/ui";
|
||||
/**
|
||||
* NOTES:
|
||||
* 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;
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
ngOnInit(): void { }
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.deletionSubscription) {
|
||||
@ -223,15 +223,15 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(promiseLists).then(() => {
|
||||
this.selectedRow = [];
|
||||
this.refresh();
|
||||
})
|
||||
Promise.all(promiseLists).then(() => {
|
||||
this.selectedRow = [];
|
||||
this.refresh();
|
||||
})
|
||||
.catch(error => {
|
||||
this.selectedRow = [];
|
||||
this.msgHandler.handleError(error);
|
||||
});
|
||||
}
|
||||
this.selectedRow = [];
|
||||
this.msgHandler.handleError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the specified user
|
||||
@ -298,7 +298,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
this.translate.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Refresh the user list
|
||||
@ -310,15 +310,15 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.originalUsers = this.userService.getUsers();
|
||||
this.originalUsers.then(users => {
|
||||
this.onGoing = false;
|
||||
this.onGoing = false;
|
||||
|
||||
this.totalCount = users.length;
|
||||
this.users = users.slice(from, to); // First page
|
||||
this.totalCount = users.length;
|
||||
this.users = users.slice(from, to); // First page
|
||||
|
||||
this.forceRefreshView(5000);
|
||||
this.forceRefreshView(5000);
|
||||
|
||||
return users;
|
||||
})
|
||||
return users;
|
||||
})
|
||||
.catch(error => {
|
||||
this.onGoing = false;
|
||||
this.msgHandler.handleError(error);
|
||||
|
@ -219,6 +219,7 @@
|
||||
"ROLE": "Role",
|
||||
"SYS_ADMIN": "System Admin",
|
||||
"PROJECT_ADMIN": "Project Admin",
|
||||
"PROJECT_MASTER": "Master",
|
||||
"DEVELOPER": "Developer",
|
||||
"GUEST": "Guest",
|
||||
"DELETE": "Delete",
|
||||
|
@ -219,6 +219,7 @@
|
||||
"ROLE": "Rol",
|
||||
"SYS_ADMIN": "Administrador del sistema",
|
||||
"PROJECT_ADMIN": "Administrador del proyecto",
|
||||
"PROJECT_MASTER": "Mantenedor",
|
||||
"DEVELOPER": "Desarrollador",
|
||||
"GUEST": "Invitado",
|
||||
"DELETE": "Eliminar",
|
||||
|
@ -224,6 +224,7 @@
|
||||
"USER_TYPE": "User",
|
||||
"SYS_ADMIN": "System Admin",
|
||||
"PROJECT_ADMIN": "Project Admin",
|
||||
"PROJECT_MASTER": "préposé à la maintenance",
|
||||
"DEVELOPER": "Développeur",
|
||||
"GUEST": "Invité",
|
||||
"DELETE": "Supprimer",
|
||||
|
@ -217,6 +217,7 @@
|
||||
"ROLE": "Função",
|
||||
"SYS_ADMIN": "Administrador do Sistema",
|
||||
"PROJECT_ADMIN": "Administrador do Projeto",
|
||||
"PROJECT_MASTER": "Mantenedor",
|
||||
"DEVELOPER": "Desenvolvedor",
|
||||
"GUEST": "Visitante",
|
||||
"DELETE": "Remover",
|
||||
|
@ -219,6 +219,7 @@
|
||||
"ROLE": "角色",
|
||||
"SYS_ADMIN": "系统管理员",
|
||||
"PROJECT_ADMIN": "项目管理员",
|
||||
"PROJECT_MASTER": "维护人员",
|
||||
"DEVELOPER": "开发人员",
|
||||
"GUEST": "访客",
|
||||
"DELETE": "删除",
|
||||
|
@ -8,6 +8,7 @@ ${HARBOR_VERSION} V1.1.1
|
||||
*** Keywords ***
|
||||
|
||||
Goto Project Config
|
||||
Sleep 3
|
||||
Click Element //project-detail//ul/li[contains(.,'Configuration')]
|
||||
Sleep 2
|
||||
|
||||
|
@ -52,6 +52,7 @@ Go To Project Log
|
||||
Sleep 2
|
||||
|
||||
Switch To Member
|
||||
Sleep 3
|
||||
Click Element xpath=${project_member_xpath}
|
||||
Sleep 1
|
||||
|
||||
@ -89,22 +90,22 @@ Search Private Projects
|
||||
Make Project Private
|
||||
[Arguments] ${projectname}
|
||||
Go Into Project ${project name}
|
||||
Sleep 1
|
||||
Sleep 2
|
||||
Click Element xpath=//project-detail//a[contains(.,'Configuration')]
|
||||
Sleep 1
|
||||
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')]
|
||||
Click Element //button[contains(.,'SAVE')]
|
||||
Wait Until Page Contains Configuration has been successfully saved
|
||||
|
||||
Make Project Public
|
||||
[Arguments] ${projectname}
|
||||
Go Into Project ${project name}
|
||||
Sleep 1
|
||||
Go Into Project ${project name}
|
||||
Sleep 2
|
||||
Click Element xpath=//project-detail//a[contains(.,'Configuration')]
|
||||
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')]
|
||||
Click Element //button[contains(.,'SAVE')]
|
||||
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
|
||||
|
||||
*** Variables ***
|
||||
${member_action_xpath} //*[@id='member-action']
|
||||
${delete_action_xpath} //clr-dropdown/clr-dropdown-menu/button[4]
|
||||
${member_action_xpath} //*[@id="member-action"]
|
||||
${delete_action_xpath} //clr-dropdown/clr-dropdown-menu/button[5]
|
||||
|
@ -63,6 +63,7 @@ Enable Scan On Push
|
||||
Sleep 10
|
||||
|
||||
Vulnerability Not Ready Project Hint
|
||||
Sleep 2
|
||||
${element}= Set Variable xpath=//span[contains(@class, 'db-status-warning')]
|
||||
Wait Until Element Is Visible And Enabled ${element}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user