diff --git a/src/portal/lib/src/endpoint/endpoint.component.ts b/src/portal/lib/src/endpoint/endpoint.component.ts
index ad5213810..fd77076ff 100644
--- a/src/portal/lib/src/endpoint/endpoint.component.ts
+++ b/src/portal/lib/src/endpoint/endpoint.component.ts
@@ -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
diff --git a/src/portal/lib/src/harbor-library.module.ts b/src/portal/lib/src/harbor-library.module.ts
index 2c014e762..2c7370f89 100644
--- a/src/portal/lib/src/harbor-library.module.ts
+++ b/src/portal/lib/src/harbor-library.module.ts
@@ -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
]
diff --git a/src/portal/lib/src/helm-chart/helm-chart.component.html b/src/portal/lib/src/helm-chart/helm-chart.component.html
index bade63a46..a9d1b75c1 100644
--- a/src/portal/lib/src/helm-chart/helm-chart.component.html
+++ b/src/portal/lib/src/helm-chart/helm-chart.component.html
@@ -23,14 +23,14 @@
-
+
{{'HELM_CHART.UPLOAD' | translate}}
-
{{'BUTTON.DELETE' | translate}}
-
+
{{'HELM_CHART.DOWNLOAD' | translate}}
diff --git a/src/portal/lib/src/helm-chart/helm-chart.component.ts b/src/portal/lib/src/helm-chart/helm-chart.component.ts
index f3d6832ec..792eb9333 100644
--- a/src/portal/lib/src/helm-chart/helm-chart.component.ts
+++ b/src/portal/lib/src/helm-chart/helm-chart.component.ts
@@ -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();
@Output() chartDownloadEve = new EventEmitter();
@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(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) {
diff --git a/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.html b/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.html
index 1cd96ab41..929cdc64e 100644
--- a/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.html
+++ b/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.html
@@ -38,18 +38,18 @@
{{'HELM_CHART.DOWNLOAD' | translate}}
{{'BUTTON.DELETE' | translate}}
-
+
{{'REPOSITORY.ADD_LABELS' | translate}}
@@ -144,7 +144,7 @@
{{'HELM_CHART.DOWNLOAD' | translate}}
{{'BUTTON.DELETE' | translate}}
diff --git a/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.ts b/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.ts
index 276f5dbd6..c92e724ca 100644
--- a/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.ts
+++ b/src/portal/lib/src/helm-chart/versions/helm-chart-version.component.ts
@@ -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();
@Output() backEvt = new EventEmitter();
@@ -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));
}
}
diff --git a/src/portal/lib/src/label/label.component.html b/src/portal/lib/src/label/label.component.html
index 7749f6bb2..717f9faf9 100644
--- a/src/portal/lib/src/label/label.component.html
+++ b/src/portal/lib/src/label/label.component.html
@@ -11,9 +11,9 @@
- {{'LABEL.NEW_LABEL' | translate}}
- {{'LABEL.EDIT' | translate}}
- {{'LABEL.DELETE' | translate}}
+ {{'LABEL.NEW_LABEL' | translate}}
+ {{'LABEL.EDIT' | translate}}
+ {{'LABEL.DELETE' | translate}}
diff --git a/src/portal/lib/src/label/label.component.ts b/src/portal/lib/src/label/label.component.ts
index 7a3f3c374..1867440d6 100644
--- a/src/portal/lib/src/label/label.component.ts
+++ b/src/portal/lib/src/label/label.component.ts
@@ -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);
}
+
}
diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html
index 88d5a28cd..83188bda5 100644
--- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html
+++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html
@@ -1,10 +1,10 @@
- {{'REPLICATION.NEW_REPLICATION_RULE' | translate}}
- {{'REPLICATION.EDIT_POLICY' | translate}}
- {{'REPLICATION.DELETE_POLICY' | translate}}
- {{'REPLICATION.REPLICATE' | translate}}
+ {{'REPLICATION.NEW_REPLICATION_RULE' | translate}}
+ {{'REPLICATION.EDIT_POLICY' | translate}}
+ {{'REPLICATION.DELETE_POLICY' | translate}}
+ {{'REPLICATION.REPLICATE' | translate}}
{{'REPLICATION.NAME' | translate}}
{{'REPLICATION.STATUS' | translate}}
diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts
index 4107dc194..f593ff25a 100644
--- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts
+++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts
@@ -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();
@Output() selectOne = new EventEmitter();
@Output() editOne = new EventEmitter();
@@ -92,10 +95,10 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
enabledComparator: Comparator = new CustomComparator("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);
diff --git a/src/portal/lib/src/project-policy-config/project-policy-config.component.html b/src/portal/lib/src/project-policy-config/project-policy-config.component.html
index c9aa2458f..17263599f 100644
--- a/src/portal/lib/src/project-policy-config/project-policy-config.component.html
+++ b/src/portal/lib/src/project-policy-config/project-policy-config.component.html
@@ -5,7 +5,7 @@
+ [disabled]="!hasChangeConfigRole" />
{{ 'PROJECT_CONFIG.PUBLIC_TOGGLE' | translate }}
@@ -19,7 +19,7 @@
-
+
{{ 'PROJECT_CONFIG.CONTENT_TRUST_TOGGLE' | translate }}
@@ -28,7 +28,7 @@
-
{{'BUTTON.SAVE'
+ {{'BUTTON.SAVE'
| translate}}
- {{'BUTTON.CANCEL'
+ {{'BUTTON.CANCEL'
| translate}}
diff --git a/src/portal/lib/src/project-policy-config/project-policy-config.component.spec.ts b/src/portal/lib/src/project-policy-config/project-policy-config.component.spec.ts
index 640fb6913..4983c9f13 100644
--- a/src/portal/lib/src/project-policy-config/project-policy-config.component.spec.ts
+++ b/src/portal/lib/src/project-policy-config/project-policy-config.component.spec.ts
@@ -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();
diff --git a/src/portal/lib/src/project-policy-config/project-policy-config.component.ts b/src/portal/lib/src/project-policy-config/project-policy-config.component.ts
index e97f4429b..650e9b5d3 100644
--- a/src/portal/lib/src/project-policy-config/project-policy-config.component.ts
+++ b/src/portal/lib/src/project-policy-config/project-policy-config.component.ts
@@ -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;
}
diff --git a/src/portal/lib/src/replication/replication.component.html b/src/portal/lib/src/replication/replication.component.html
index 512d0b864..f4d9a2a6c 100644
--- a/src/portal/lib/src/replication/replication.component.html
+++ b/src/portal/lib/src/replication/replication.component.html
@@ -11,9 +11,14 @@
-
+ (reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"
+ [hasCreateReplicationPermission]="hasCreateReplicationPermission"
+ [hasUpdateReplicationPermission]="hasUpdateReplicationPermission"
+ [hasDeleteReplicationPermission]="hasDeleteReplicationPermission"
+ [hasExecuteReplicationPermission]="hasExecuteReplicationPermission"
+ >
diff --git a/src/portal/lib/src/replication/replication.component.ts b/src/portal/lib/src/replication/replication.component.ts
index d16bc80c3..ece3c95da 100644
--- a/src/portal/lib/src/replication/replication.component.ts
+++ b/src/portal/lib/src/replication/replication.component.ts
@@ -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
();
@Output() openCreateRule = new EventEmitter();
diff --git a/src/portal/lib/src/repository-gridview/repository-gridview.component.html b/src/portal/lib/src/repository-gridview/repository-gridview.component.html
index 6fd137c12..fc1fecde0 100644
--- a/src/portal/lib/src/repository-gridview/repository-gridview.component.html
+++ b/src/portal/lib/src/repository-gridview/repository-gridview.component.html
@@ -7,7 +7,7 @@
{{'CONFIG.REGISTRY_CERTIFICATE' | translate | uppercase}}
-
+
@@ -27,7 +27,7 @@
{{'REPOSITORY.DEPLOY' | translate}}
{{'REPOSITORY.ADDITIONAL_INFO' | translate}}
- {{'REPOSITORY.DELETE' | translate}}
+ {{'REPOSITORY.DELETE' | translate}}
{{'REPOSITORY.NAME' | translate}}
{{'REPOSITORY.TAGS_COUNT' | translate}}
diff --git a/src/portal/lib/src/repository-gridview/repository-gridview.component.spec.ts b/src/portal/lib/src/repository-gridview/repository-gridview.component.spec.ts
index e1fa5c128..8f0d06858 100644
--- a/src/portal/lib/src/repository-gridview/repository-gridview.component.spec.ts
+++ b/src/portal/lib/src/repository-gridview/repository-gridview.component.spec.ts
@@ -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;
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();
});
diff --git a/src/portal/lib/src/repository-gridview/repository-gridview.component.ts b/src/portal/lib/src/repository-gridview/repository-gridview.component.ts
index 904d6c8c1..baf7f9fce 100644
--- a/src/portal/lib/src/repository-gridview/repository-gridview.component.ts
+++ b/src/portal/lib/src/repository-gridview/repository-gridview.component.ts
@@ -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(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(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));
+ }
}
diff --git a/src/portal/lib/src/repository/repository.component.html b/src/portal/lib/src/repository/repository.component.html
index 207ea6d69..a5216ed33 100644
--- a/src/portal/lib/src/repository/repository.component.html
+++ b/src/portal/lib/src/repository/repository.component.html
@@ -55,7 +55,7 @@
diff --git a/src/portal/lib/src/repository/repository.component.spec.ts b/src/portal/lib/src/repository/repository.component.spec.ts
index 58ff2b932..7b90f9387 100644
--- a/src/portal/lib/src/repository/repository.component.spec.ts
+++ b/src/portal/lib/src/repository/repository.component.spec.ts
@@ -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 }
]
diff --git a/src/portal/lib/src/service/index.ts b/src/portal/lib/src/service/index.ts
index afc38321a..1772c0054 100644
--- a/src/portal/lib/src/service/index.ts
+++ b/src/portal/lib/src/service/index.ts
@@ -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";
diff --git a/src/portal/lib/src/service/interface.ts b/src/portal/lib/src/service/interface.ts
index 0b07259d2..7bd1c8993 100644
--- a/src/portal/lib/src/service/interface.ts
+++ b/src/portal/lib/src/service/interface.ts
@@ -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 {
}
/** @deprecated since 0.11 */
-export interface Comparator extends ClrDatagridComparatorInterface {}
+export interface Comparator extends ClrDatagridComparatorInterface { }
/** @deprecated since 0.11 */
-export interface ClrFilter extends ClrDatagridFilterInterface {}
+export interface ClrFilter extends ClrDatagridFilterInterface { }
/** @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;
+}
+
diff --git a/src/portal/lib/src/service/permission-static.ts b/src/portal/lib/src/service/permission-static.ts
new file mode 100644
index 000000000..da0aaf62c
--- /dev/null
+++ b/src/portal/lib/src/service/permission-static.ts
@@ -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",
+ }
+ },
+};
+
diff --git a/src/portal/lib/src/service/permission.service.ts b/src/portal/lib/src/service/permission.service.ts
new file mode 100644
index 000000000..0130c20af
--- /dev/null
+++ b/src/portal/lib/src/service/permission.service.ts
@@ -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;
+ private getPermissionFromBackend(projectId): Observable {
+ 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 {
+
+ 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;
+ }
+}
diff --git a/src/portal/lib/src/shared/shared.const.ts b/src/portal/lib/src/shared/shared.const.ts
index 3c6c83fee..f180073ad 100644
--- a/src/portal/lib/src/shared/shared.const.ts
+++ b/src/portal/lib/src/shared/shared.const.ts
@@ -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,
diff --git a/src/portal/lib/src/tag/tag-detail.component.html b/src/portal/lib/src/tag/tag-detail.component.html
index 70929ddd8..9fb78661a 100644
--- a/src/portal/lib/src/tag/tag-detail.component.html
+++ b/src/portal/lib/src/tag/tag-detail.component.html
@@ -91,15 +91,15 @@
-
+
{{'REPOSITORY.VULNERABILITY' | translate}}
-
+
- {{ 'REPOSITORY.BUILD_HISTORY' | translate }}
{{ 'REPOSITORY.BUILD_HISTORY' |
diff --git a/src/portal/lib/src/tag/tag-detail.component.spec.ts b/src/portal/lib/src/tag/tag-detail.component.spec.ts
index 17c59bef1..64a95b26b 100644
--- a/src/portal/lib/src/tag/tag-detail.component.spec.ts
+++ b/src/portal/lib/src/tag/tag-detail.component.spec.ts
@@ -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;
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();
});
diff --git a/src/portal/lib/src/tag/tag-detail.component.ts b/src/portal/lib/src/tag/tag-detail.component.ts
index af92d942a..79e001722 100644
--- a/src/portal/lib/src/tag/tag-detail.component.ts
+++ b/src/portal/lib/src/tag/tag-detail.component.ts
@@ -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 = new EventEmitter();
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));
+ }
}
diff --git a/src/portal/lib/src/tag/tag.component.html b/src/portal/lib/src/tag/tag.component.html
index 10a450d54..dd0f462a9 100644
--- a/src/portal/lib/src/tag/tag.component.html
+++ b/src/portal/lib/src/tag/tag.component.html
@@ -60,7 +60,7 @@
{{'VULNERABILITY.SCAN_NOW' | translate}}
{{'REPOSITORY.COPY_DIGEST_ID' | translate}}
- {{'REPOSITORY.ADD_LABELS' | translate}}
+ {{'REPOSITORY.ADD_LABELS' | translate}}
@@ -75,8 +75,8 @@
- {{'REPOSITORY.RETAG' | translate}}
- {{'REPOSITORY.DELETE' | translate}}
+ {{'REPOSITORY.RETAG' | translate}}
+ {{'REPOSITORY.DELETE' | translate}}
{{'REPOSITORY.TAG' | translate}}
{{'REPOSITORY.SIZE' | translate}}
diff --git a/src/portal/lib/src/tag/tag.component.spec.ts b/src/portal/lib/src/tag/tag.component.spec.ts
index 0e9429b41..22811acc3 100644
--- a/src/portal/lib/src/tag/tag.component.spec.ts
+++ b/src/portal/lib/src/tag/tag.component.spec.ts
@@ -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;
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)", () => {
}));
});
+
+
diff --git a/src/portal/lib/src/tag/tag.component.ts b/src/portal/lib/src/tag/tag.component.ts
index 69b389e53..09ef90dda 100644
--- a/src/portal/lib/src/tag/tag.component.ts
+++ b/src/portal/lib/src/tag/tag.component.ts
@@ -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 = new Subject ();
- stickLabelNameFilter: Subject = new Subject ();
+ labelNameFilter: Subject = new Subject();
+ stickLabelNameFilter: Subject = new Subject();
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(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(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(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(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(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(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();
}
}
diff --git a/src/portal/lib/src/utils.ts b/src/portal/lib/src/utils.ts
index cbbd31231..b90d3984d 100644
--- a/src/portal/lib/src/utils.ts
+++ b/src/portal/lib/src/utils.ts
@@ -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({
diff --git a/src/portal/lib/src/vulnerability-scanning/result-grid.component.html b/src/portal/lib/src/vulnerability-scanning/result-grid.component.html
index 70e0e1c9a..cbc3c96bb 100644
--- a/src/portal/lib/src/vulnerability-scanning/result-grid.component.html
+++ b/src/portal/lib/src/vulnerability-scanning/result-grid.component.html
@@ -10,7 +10,7 @@
- {{'VULNERABILITY.SCAN_NOW' | translate}}
+ {{'VULNERABILITY.SCAN_NOW' | translate}}
{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}
{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}
diff --git a/src/portal/lib/src/vulnerability-scanning/result-grid.component.spec.ts b/src/portal/lib/src/vulnerability-scanning/result-grid.component.spec.ts
index 920644c39..3b3948660 100644
--- a/src/portal/lib/src/vulnerability-scanning/result-grid.component.spec.ts
+++ b/src/portal/lib/src/vulnerability-scanning/result-grid.component.spec.ts
@@ -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 }
]
});
diff --git a/src/portal/lib/src/vulnerability-scanning/result-grid.component.ts b/src/portal/lib/src/vulnerability-scanning/result-grid.component.ts
index 2eb1bf692..4283fdfb0 100644
--- a/src/portal/lib/src/vulnerability-scanning/result-grid.component.ts
+++ b/src/portal/lib/src/vulnerability-scanning/result-grid.component.ts
@@ -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);
+ });
+ }
}
diff --git a/src/portal/lib/src/vulnerability-scanning/result-tip.component.spec.ts b/src/portal/lib/src/vulnerability-scanning/result-tip.component.spec.ts
index b1f41ab77..1c29e487e 100644
--- a/src/portal/lib/src/vulnerability-scanning/result-tip.component.spec.ts
+++ b/src/portal/lib/src/vulnerability-scanning/result-tip.component.spec.ts
@@ -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 }]
});
}));
diff --git a/src/portal/package-lock.json b/src/portal/package-lock.json
index 39788b6ae..b0e9571a3 100644
--- a/src/portal/package-lock.json
+++ b/src/portal/package-lock.json
@@ -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
},
diff --git a/src/portal/package.json b/src/portal/package.json
index 0111b1224..fc0b21174 100644
--- a/src/portal/package.json
+++ b/src/portal/package.json
@@ -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",
diff --git a/src/portal/src/app/config/config.component.html b/src/portal/src/app/config/config.component.html
index 2fa843afc..4e0b6a34b 100644
--- a/src/portal/src/app/config/config.component.html
+++ b/src/portal/src/app/config/config.component.html
@@ -25,7 +25,10 @@
{{'CONFIG.LABEL' | translate }}
-
+
diff --git a/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.html b/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.html
index b870f47af..6e5bbdcdf 100644
--- a/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.html
+++ b/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.html
@@ -10,8 +10,6 @@
[chartName]='chartName'
[roleName]='roleName'
[hasSignedIn]='hasSignedIn'
- [projectRoleID]='project_member_role_id'
- [hasProjectAdminRole]='hasProjectAdminRole'
(versionClickEvt)='onVersionClick($event)'
(backEvt)='gotoChartList()'>
diff --git a/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.ts b/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.ts
index a4315d97a..79d17c86e 100644
--- a/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.ts
+++ b/src/portal/src/app/project/list-chart-versions/list-chart-versions.component.ts
@@ -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 = (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;
}
}
diff --git a/src/portal/src/app/project/list-charts/list-charts.component.html b/src/portal/src/app/project/list-charts/list-charts.component.html
index 9f6c59098..7f17ab609 100644
--- a/src/portal/src/app/project/list-charts/list-charts.component.html
+++ b/src/portal/src/app/project/list-charts/list-charts.component.html
@@ -4,6 +4,5 @@
[urlPrefix]='urlPrefix'
[hasSignedIn]='hasSignedIn'
[projectRoleID]='project_member_role_id'
- [hasProjectAdminRole]='hasProjectAdminRole'
(chartClickEvt)='onChartClick($event)'>
diff --git a/src/portal/src/app/project/list-charts/list-charts.component.ts b/src/portal/src/app/project/list-charts/list-charts.component.ts
index 3f8452a73..5bb48e2c8 100644
--- a/src/portal/src/app/project/list-charts/list-charts.component.ts
+++ b/src/portal/src/app/project/list-charts/list-charts.component.ts
@@ -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 = (resolverData["projectResolver"]);
this.projectName = project.name;
- this.hasProjectAdminRole = project.has_project_admin_role;
this.project_member_role_id = project.current_user_role_id;
}
}
diff --git a/src/portal/src/app/project/member/add-member/add-member.component.html b/src/portal/src/app/project/member/add-member/add-member.component.html
index 3fc49c83d..25d6cf17e 100644
--- a/src/portal/src/app/project/member/add-member/add-member.component.html
+++ b/src/portal/src/app/project/member/add-member/add-member.component.html
@@ -32,6 +32,10 @@
{{'MEMBER.PROJECT_ADMIN' | translate}}
+
+
+ {{'MEMBER.PROJECT_MASTER' | translate}}
+
{{'MEMBER.DEVELOPER' | translate}}
diff --git a/src/portal/src/app/project/member/member.component.html b/src/portal/src/app/project/member/member.component.html
index f60bbb8ec..1b6c15255 100644
--- a/src/portal/src/app/project/member/member.component.html
+++ b/src/portal/src/app/project/member/member.component.html
@@ -13,21 +13,22 @@
-
+
{{'MEMBER.USER' | translate }}
-
+
{{'MEMBER.LDAP_GROUP' | translate}}
{{'MEMBER.ACTION' | translate}}
- {{'MEMBER.PROJECT_ADMIN' | translate}}
- {{'MEMBER.DEVELOPER' | translate}}
- {{'MEMBER.GUEST' | translate}}
+ {{'MEMBER.PROJECT_ADMIN' | translate}}
+ {{'MEMBER.PROJECT_MASTER' | translate}}
+ {{'MEMBER.DEVELOPER' | translate}}
+ {{'MEMBER.GUEST' | translate}}
- {{'MEMBER.REMOVE' | translate}}
+ {{'MEMBER.REMOVE' | translate}}
diff --git a/src/portal/src/app/project/member/member.component.ts b/src/portal/src/app/project/member/member.component.ts
index f2f141a38..85eb41682 100644
--- a/src/portal/src/app/project/member/member.component.ts
+++ b/src/portal/src/app/project/member/member.component.ts
@@ -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 = (
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));
+ }
}
diff --git a/src/portal/src/app/project/project-config/project-config.component.html b/src/portal/src/app/project/project-config/project-config.component.html
index d51542b48..bbfbc4254 100644
--- a/src/portal/src/app/project/project-config/project-config.component.html
+++ b/src/portal/src/app/project/project-config/project-config.component.html
@@ -1,5 +1,5 @@
\ No newline at end of file
diff --git a/src/portal/src/app/project/project-config/project-config.component.ts b/src/portal/src/app/project/project-config/project-config.component.ts
index d12a85057..5499aaf40 100644
--- a/src/portal/src/app/project/project-config/project-config.component.ts
+++ b/src/portal/src/app/project/project-config/project-config.component.ts
@@ -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 = resolverData['projectResolver'];
- this.hasProjectAdminRole = pro.has_project_admin_role;
this.projectName = pro.name;
}
}
diff --git a/src/portal/src/app/project/project-detail/project-detail.component.html b/src/portal/src/app/project/project-detail/project-detail.component.html
index 7b43be2ca..5d3ea84c8 100644
--- a/src/portal/src/app/project/project-detail/project-detail.component.html
+++ b/src/portal/src/app/project/project-detail/project-detail.component.html
@@ -4,28 +4,28 @@
{{currentProject.name}} {{roleName | translate}}
diff --git a/src/portal/src/app/project/project-detail/project-detail.component.ts b/src/portal/src/app/project/project-detail/project-detail.component.ts
index 5cd4c4ce6..b32f37659 100644
--- a/src/portal/src/app/project/project-detail/project-detail.component.ts
+++ b/src/portal/src/app/project/project-detail/project-detail.component.ts
@@ -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 {
diff --git a/src/portal/src/app/project/project-label/project-label.component.html b/src/portal/src/app/project/project-label/project-label.component.html
index c8419ea1c..c79f71e8c 100644
--- a/src/portal/src/app/project/project-label/project-label.component.html
+++ b/src/portal/src/app/project/project-label/project-label.component.html
@@ -1,5 +1,10 @@
-
\ No newline at end of file
diff --git a/src/portal/src/app/project/project-label/project-label.component.ts b/src/portal/src/app/project/project-label/project-label.component.ts
index fcef8497e..2f66904d4 100644
--- a/src/portal/src/app/project/project-label/project-label.component.ts
+++ b/src/portal/src/app/project/project-label/project-label.component.ts
@@ -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 =
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));
+}
}
diff --git a/src/portal/src/app/project/robot-account/robot-account.component.html b/src/portal/src/app/project/robot-account/robot-account.component.html
index 77bd257fa..5245893a9 100644
--- a/src/portal/src/app/project/robot-account/robot-account.component.html
+++ b/src/portal/src/app/project/robot-account/robot-account.component.html
@@ -14,18 +14,18 @@
-
- {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
- | translate }}
+
+
+ {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
+ | translate }}
+
+ btn-link" clrDropdownTrigger>
{{'MEMBER.ACTION' | translate}}
-
{{'ROBOT_ACCOUNT.DISABLE_ACCOUNT'
@@ -35,9 +35,7 @@
| translate}}
- {{'ROBOT_ACCOUNT.DELETE'
+ {{'ROBOT_ACCOUNT.DELETE'
| translate}}
@@ -49,12 +47,8 @@
{{r.name}}
-
-
+
+
{{r.description}}
@@ -68,6 +62,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/src/portal/src/app/project/robot-account/robot-account.component.ts b/src/portal/src/app/project/robot-account/robot-account.component.ts
index 006a59d81..d0f2d0cac 100644
--- a/src/portal/src/app/project/robot-account/robot-account.component.ts
+++ b/src/portal/src/app/project/robot-account/robot-account.component.ts
@@ -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) {
diff --git a/src/portal/src/app/replication/replication-page.component.html b/src/portal/src/app/replication/replication-page.component.html
index 579d1d5b1..e7efd3ce4 100644
--- a/src/portal/src/app/replication/replication-page.component.html
+++ b/src/portal/src/app/replication/replication-page.component.html
@@ -1,3 +1,12 @@
-
+
\ No newline at end of file
diff --git a/src/portal/src/app/replication/replication-page.component.ts b/src/portal/src/app/replication/replication-page.component.ts
index b43aa878e..2995ba9dd 100644
--- a/src/portal/src/app/replication/replication-page.component.ts
+++ b/src/portal/src/app/replication/replication-page.component.ts
@@ -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);
+ });
+ }
}
diff --git a/src/portal/src/app/replication/total-replication/total-replication-page.component.html b/src/portal/src/app/replication/total-replication/total-replication-page.component.html
index 13be7aad5..a823aac15 100644
--- a/src/portal/src/app/replication/total-replication/total-replication-page.component.html
+++ b/src/portal/src/app/replication/total-replication/total-replication-page.component.html
@@ -1,4 +1,13 @@
{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}
-
+
\ No newline at end of file
diff --git a/src/portal/src/app/repository/tag-detail/tag-detail-page.component.html b/src/portal/src/app/repository/tag-detail/tag-detail-page.component.html
index ca6f72401..6eb680c31 100644
--- a/src/portal/src/app/repository/tag-detail/tag-detail-page.component.html
+++ b/src/portal/src/app/repository/tag-detail/tag-detail-page.component.html
@@ -5,9 +5,9 @@
< {{repositoryId}}
+ [tagId]="tagId"
+ [withClair]="withClair"
+ [withAdmiral]="withAdmiral"
+ [projectId]="projectId"
+ [repositoryId]="repositoryId">
\ No newline at end of file
diff --git a/src/portal/src/app/repository/tag-detail/tag-detail-page.component.ts b/src/portal/src/app/repository/tag-detail/tag-detail-page.component.ts
index 9232789e6..f4c1d1f34 100644
--- a/src/portal/src/app/repository/tag-detail/tag-detail-page.component.ts
+++ b/src/portal/src/app/repository/tag-detail/tag-detail-page.component.ts
@@ -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]);
}
diff --git a/src/portal/src/app/shared/message-handler/message-handler.service.ts b/src/portal/src/app/shared/message-handler/message-handler.service.ts
index ed19f0875..efbc12642 100644
--- a/src/portal/src/app/shared/message-handler/message-handler.service.ts
+++ b/src/portal/src/app/shared/message-handler/message-handler.service.ts
@@ -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);
}
diff --git a/src/portal/src/app/shared/route/member-guard-activate.service.ts b/src/portal/src/app/shared/route/member-guard-activate.service.ts
index 9ff813ea6..05b907dce 100644
--- a/src/portal/src/app/shared/route/member-guard-activate.service.ts
+++ b/src/portal/src/app/shared/route/member-guard-activate.service.ts
@@ -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]);
diff --git a/src/portal/src/app/shared/route/sign-in-guard-activate.service.ts b/src/portal/src/app/shared/route/sign-in-guard-activate.service.ts
index 2cd39c961..4f6a72133 100644
--- a/src/portal/src/app/shared/route/sign-in-guard-activate.service.ts
+++ b/src/portal/src/app/shared/route/sign-in-guard-activate.service.ts
@@ -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 {
// 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 => {
diff --git a/src/portal/src/app/shared/session.service.ts b/src/portal/src/app/shared/session.service.ts
index c74e7e5b8..e03b8c8d0 100644
--- a/src/portal/src/app/shared/session.service.ts
+++ b/src/portal/src/app/shared/session.service.ts
@@ -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 {
// 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));
}
/**
diff --git a/src/portal/src/app/shared/shared.const.ts b/src/portal/src/app/shared/shared.const.ts
index 7cefb9dec..1bc2ddcf2 100644
--- a/src/portal/src/app/shared/shared.const.ts
+++ b/src/portal/src/app/shared/shared.const.ts
@@ -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,
diff --git a/src/portal/src/app/user/user.component.ts b/src/portal/src/app/user/user.component.ts
index 5123ac382..3a3e6e299 100644
--- a/src/portal/src/app/user/user.component.ts
+++ b/src/portal/src/app/user/user.component.ts
@@ -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);
diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json
index 5ea73b524..ebdeff096 100644
--- a/src/portal/src/i18n/lang/en-us-lang.json
+++ b/src/portal/src/i18n/lang/en-us-lang.json
@@ -219,6 +219,7 @@
"ROLE": "Role",
"SYS_ADMIN": "System Admin",
"PROJECT_ADMIN": "Project Admin",
+ "PROJECT_MASTER": "Master",
"DEVELOPER": "Developer",
"GUEST": "Guest",
"DELETE": "Delete",
diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json
index b286dd804..181433c6e 100644
--- a/src/portal/src/i18n/lang/es-es-lang.json
+++ b/src/portal/src/i18n/lang/es-es-lang.json
@@ -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",
diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json
index 13c03ec0b..af362a39c 100644
--- a/src/portal/src/i18n/lang/fr-fr-lang.json
+++ b/src/portal/src/i18n/lang/fr-fr-lang.json
@@ -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",
diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json
index 7cd47fef4..684cff475 100644
--- a/src/portal/src/i18n/lang/pt-br-lang.json
+++ b/src/portal/src/i18n/lang/pt-br-lang.json
@@ -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",
diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json
index 8766b43c4..a4475b4dc 100644
--- a/src/portal/src/i18n/lang/zh-cn-lang.json
+++ b/src/portal/src/i18n/lang/zh-cn-lang.json
@@ -219,6 +219,7 @@
"ROLE": "角色",
"SYS_ADMIN": "系统管理员",
"PROJECT_ADMIN": "项目管理员",
+ "PROJECT_MASTER": "维护人员",
"DEVELOPER": "开发人员",
"GUEST": "访客",
"DELETE": "删除",
diff --git a/tests/resources/Harbor-Pages/Project-Config.robot b/tests/resources/Harbor-Pages/Project-Config.robot
index 045238386..4d5467a7b 100644
--- a/tests/resources/Harbor-Pages/Project-Config.robot
+++ b/tests/resources/Harbor-Pages/Project-Config.robot
@@ -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
diff --git a/tests/resources/Harbor-Pages/Project.robot b/tests/resources/Harbor-Pages/Project.robot
index 9b60966f8..2a8f79799 100644
--- a/tests/resources/Harbor-Pages/Project.robot
+++ b/tests/resources/Harbor-Pages/Project.robot
@@ -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
diff --git a/tests/resources/Harbor-Pages/ToolKit_Elements.robot b/tests/resources/Harbor-Pages/ToolKit_Elements.robot
index 17d2ceb13..27db34257 100644
--- a/tests/resources/Harbor-Pages/ToolKit_Elements.robot
+++ b/tests/resources/Harbor-Pages/ToolKit_Elements.robot
@@ -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]
diff --git a/tests/resources/Harbor-Pages/Vulnerability.robot b/tests/resources/Harbor-Pages/Vulnerability.robot
index 98618b824..0e377229b 100644
--- a/tests/resources/Harbor-Pages/Vulnerability.robot
+++ b/tests/resources/Harbor-Pages/Vulnerability.robot
@@ -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}