Merge pull request #12098 from AllForNothing/release-1.10.0

Fix permission control for webhook
This commit is contained in:
Will Sun 2020-05-29 14:50:49 +08:00 committed by GitHub
commit 52edc28690
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 17 deletions

View File

@ -155,6 +155,9 @@ export const USERSTATICPERMISSION = {
"VALUE": {
"LIST": "list",
"READ": "read",
"CREATE": "create",
"UPDATE": "update",
"DELETE": "delete"
}
},
"SCANNER": {

View File

@ -7,7 +7,7 @@
<label for="edit_endpoint_url" class="clr-control-label required">{{'WEBHOOK.ENDPOINT_URL' | translate}}</label>
<div class="clr-control-container" [class.clr-error]="enpointURL.errors && enpointURL.errors.required && (enpointURL.dirty || enpointURL.touched)">
<div class="clr-input-wrapper">
<input class="clr-input" type="text" id="edit_endpoint_url" [disabled]="checking" [(ngModel)]="webhookTarget.address"
<input class="clr-input" type="text" id="edit_endpoint_url" [disabled]="checking || !hasCreatPermission" [(ngModel)]="webhookTarget.address"
size="30" name="edit_endpoint_url" #enpointURL="ngModel" required placeholder="http(s)://192.168.1.1">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
</div>
@ -22,7 +22,7 @@
translate }}</label>
<div class="clr-control-container">
<div class="clr-input-wrapper">
<input class="clr-input" type="text" id="auth_header" [disabled]="checking"
<input class="clr-input" type="text" id="auth_header" [disabled]="checking || !hasCreatPermission"
[(ngModel)]="webhookTarget.auth_header" size="30" name="auth_header">
</div>
</div>
@ -39,18 +39,18 @@
</clr-tooltip>
</label>
<div class="clr-control-container padding-top-3">
<input type="checkbox" [disabled]="checking" clrCheckbox name="verify_remote_cert" id="verify_remote_cert"
<input type="checkbox" [disabled]="checking || !hasCreatPermission" clrCheckbox name="verify_remote_cert" id="verify_remote_cert"
(ngModelChange)="setCertValue($event)" [ngModel]="!webhookTarget.skip_cert_verify"/> </div>
</div>
</section>
</form>
<div class="mt-1" *ngIf="!isModify">
<button type="button" id="new-webhook-continue" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.CONTINUE' | translate}}</button>
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
<button type="button" id="new-webhook-continue" class="btn btn-primary" [disabled]="!isValid || !hasCreatPermission" (click)="onSubmit()">{{'BUTTON.CONTINUE' | translate}}</button>
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors || !hasCreatPermission">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
</div>
<div class="mt-1" *ngIf="isModify">
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" id="webhook-test" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" id="webhook-test" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors || !hasUpdatePermission">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
<button type="button" class="btn btn-outline" id="edit-webhook-cancel" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" id="edit-webhook-save" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.SAVE' | translate}}</button>
<button type="button" class="btn btn-primary" id="edit-webhook-save" [disabled]="!isValid || !hasUpdatePermission" (click)="onSubmit()">{{'BUTTON.SAVE' | translate}}</button>
</div>
</div>

View File

@ -17,6 +17,9 @@ describe('AddWebhookFormComponent', () => {
const mockWebhookService = {
getCurrentUser: () => {
return of(null);
},
getPermissions() {
return of([true, true]);
}
};
const mockMessageHandlerService = {

View File

@ -40,6 +40,8 @@ export class AddWebhookFormComponent implements OnInit, OnChanges {
@Output() close = new EventEmitter<boolean>();
@ViewChild("webhookForm", { static: true }) currentForm: NgForm;
@ViewChild(InlineAlertComponent, { static: false }) inlineAlert: InlineAlertComponent;
hasCreatPermission: boolean = false;
hasUpdatePermission: boolean = false;
constructor(
private webhookService: WebhookService,
@ -48,6 +50,14 @@ export class AddWebhookFormComponent implements OnInit, OnChanges {
) { }
ngOnInit() {
this.getPermissions();
}
getPermissions() {
this.webhookService.getPermissions(this.projectId).subscribe(
rules => {
[this.hasCreatPermission, this.hasUpdatePermission] = rules;
}
);
}
ngOnChanges(changes: SimpleChanges) {
@ -55,7 +65,6 @@ export class AddWebhookFormComponent implements OnInit, OnChanges {
Object.assign(this.webhookTarget, this.webhook.targets[0]);
}
}
onTestEndpoint() {
this.checkBtnState = ClrLoadingState.LOADING;
this.checking = true;

View File

@ -8,11 +8,11 @@
<div class="flex-xs-middle option-left">
<div>
<span class="endpoint-label">Webhook endpoint</span>: {{endpoint}}
<button class="btn btn-link" id="edit-webhook" (click)="openAddWebhookModal()">{{'WEBHOOK.EDIT_BUTTON' | translate}}</button>
<button [disabled]="!hasUpdatePermission" class="btn btn-link" id="edit-webhook" (click)="openAddWebhookModal()">{{'WEBHOOK.EDIT_BUTTON' | translate}}</button>
</div>
<div [ngSwitch]="isEnabled">
<button *ngSwitchCase="false" id="enable-webhook-action" class="btn btn-link" (click)="switchWebhookStatus(true)">{{'WEBHOOK.ENABLED_BUTTON' | translate}}</button>
<button *ngSwitchCase="true" id="disable-webhook-action" class="btn btn-link disabled-btn" (click)="switchWebhookStatus(false)">{{'WEBHOOK.DISABLED_BUTTON' | translate}}</button>
<button [disabled]="!hasUpdatePermission" *ngSwitchCase="false" id="enable-webhook-action" class="btn btn-link" (click)="switchWebhookStatus(true)">{{'WEBHOOK.ENABLED_BUTTON' | translate}}</button>
<button [disabled]="!hasUpdatePermission" *ngSwitchCase="true" id="disable-webhook-action" class="btn btn-link disabled-btn" (click)="switchWebhookStatus(false)">{{'WEBHOOK.DISABLED_BUTTON' | translate}}</button>
</div>
</div>
</div>

View File

@ -31,6 +31,9 @@ describe('WebhookComponent', () => {
}
]);
},
getPermissions() {
return of([true, true]);
}
};
const mockActivatedRoute = {
RouterparamMap: of({ get: (key) => 'value' }),

View File

@ -34,7 +34,6 @@ import { ConfirmationDialogComponent } from "../../shared/confirmation-dialog/co
@Component({
templateUrl: './webhook.component.html',
styleUrls: ['./webhook.component.scss'],
// changeDetection: ChangeDetectionStrategy.OnPush
})
export class WebhookComponent implements OnInit {
@ViewChild(AddWebhookComponent, { static: false } )
@ -53,6 +52,8 @@ export class WebhookComponent implements OnInit {
loadingWebhook: boolean = true;
projectId: number;
projectName: string;
hasCreatPermission: boolean = false;
hasUpdatePermission: boolean = false;
constructor(
private route: ActivatedRoute,
private translate: TranslateService,
@ -67,8 +68,15 @@ export class WebhookComponent implements OnInit {
this.projectName = project.name;
}
this.getData(this.projectId);
this.getPermissions();
}
getPermissions() {
this.webhookService.getPermissions(this.projectId).subscribe(
rules => {
[this.hasCreatPermission, this.hasUpdatePermission] = rules;
}
);
}
getData(projectId: number) {
this.getLastTriggers(projectId);
this.getWebhook(projectId);

View File

@ -1,6 +1,7 @@
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { WebhookService } from './webhook.service';
import { UserPermissionService } from '@harbor/ui';
describe('WebhookService', () => {
beforeEach(() => {
@ -8,7 +9,7 @@ describe('WebhookService', () => {
imports: [
HttpClientTestingModule
],
providers: [WebhookService]
providers: [WebhookService, UserPermissionService]
});
});

View File

@ -11,15 +11,17 @@
// 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 { throwError as observableThrowError, Observable } from "rxjs";
import { throwError as observableThrowError, Observable, forkJoin } from 'rxjs';
import { map, catchError } from "rxjs/operators";
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Webhook, LastTrigger } from "./webhook";
import { UserPermissionService, USERSTATICPERMISSION } from '@harbor/ui';
@Injectable()
export class WebhookService {
constructor(private http: HttpClient) { }
constructor(private http: HttpClient,
private userPermissionService: UserPermissionService) { }
public listWebhook(projectId: number): Observable<Webhook[]> {
return this.http
@ -53,4 +55,13 @@ export class WebhookService {
.post(`/api/projects/${projectId}/webhook/policies/test`, param)
.pipe(catchError(error => observableThrowError(error)));
}
getPermissions(projectId: number): Observable<any> {
const permissionsList: Observable<boolean>[] = [];
permissionsList.push(this.userPermissionService.getPermission(projectId,
USERSTATICPERMISSION.WEBHOOK.KEY, USERSTATICPERMISSION.WEBHOOK.VALUE.CREATE));
permissionsList.push(this.userPermissionService.getPermission(projectId,
USERSTATICPERMISSION.WEBHOOK.KEY, USERSTATICPERMISSION.WEBHOOK.VALUE.UPDATE));
return forkJoin(...permissionsList);
}
}