Merge pull request #8221 from jwangyangls/addGroupInhttpMode

Add user group when http auth mode
This commit is contained in:
jwangyangls 2019-07-10 14:38:31 +08:00 committed by GitHub
commit c73fa851ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 377 additions and 49 deletions

View File

@ -434,3 +434,8 @@ export interface HttpOptionTextInterface {
withCredentials?: boolean; withCredentials?: boolean;
} }
export interface ProjectRootInterface {
NAME: string;
VALUE: number;
LABEL: string;
}

View File

@ -84,3 +84,35 @@ export const LabelColor = [
{ 'color': '#F52F52', 'textColor': 'black' }, { 'color': '#FF5501', 'textColor': 'black' }, { 'color': '#F52F52', 'textColor': 'black' }, { 'color': '#FF5501', 'textColor': 'black' },
{ 'color': '#F57600', 'textColor': 'black' }, { 'color': '#FFDC0B', 'textColor': 'black' }, { 'color': '#F57600', 'textColor': 'black' }, { 'color': '#FFDC0B', 'textColor': 'black' },
]; ];
export const CONFIG_AUTH_MODE = {
HTTP_AUTH: "http_auth",
LDAP_AUTH: "ldap_auth"
};
export const PROJECT_ROOTS = [
{
NAME: "admin",
VALUE: 1,
LABEL: "GROUP.PROJECT_ADMIN"
},
{
NAME: "master",
VALUE: 4,
LABEL: "GROUP.PROJECT_MASTER"
},
{
NAME: "developer",
VALUE: 2,
LABEL: "GROUP.DEVELOPER"
},
{
NAME: "guest",
VALUE: 3,
LABEL: "GROUP.GUEST"
}
];
export enum GroupType {
LDAP_TYPE = 1,
HTTP_TYPE = 2
}

View File

@ -19,7 +19,7 @@ import { CookieService } from 'ngx-cookie';
import { AppConfig } from './app-config'; import { AppConfig } from './app-config';
import { CookieKeyOfAdmiral, HarborQueryParamKey } from './shared/shared.const'; import { CookieKeyOfAdmiral, HarborQueryParamKey } from './shared/shared.const';
import { maintainUrlQueryParmas } from './shared/shared.utils'; import { maintainUrlQueryParmas } from './shared/shared.utils';
import { HTTP_GET_OPTIONS} from '@harbor/ui'; import { HTTP_GET_OPTIONS , CONFIG_AUTH_MODE} from '@harbor/ui';
import { map, catchError } from "rxjs/operators"; import { map, catchError } from "rxjs/operators";
import { Observable, throwError as observableThrowError } from "rxjs"; import { Observable, throwError as observableThrowError } from "rxjs";
export const systemInfoEndpoint = "/api/systeminfo"; export const systemInfoEndpoint = "/api/systeminfo";
@ -67,7 +67,10 @@ export class AppConfigService {
} }
public isLdapMode(): boolean { public isLdapMode(): boolean {
return this.configurations && this.configurations.auth_mode === 'ldap_auth'; return this.configurations && this.configurations.auth_mode === CONFIG_AUTH_MODE.LDAP_AUTH;
}
public isHttpAuthMode(): boolean {
return this.configurations && this.configurations.auth_mode === CONFIG_AUTH_MODE.HTTP_AUTH;
} }
// Return the reconstructed admiral url // Return the reconstructed admiral url

View File

@ -28,7 +28,7 @@
<clr-icon shape="users" clrVerticalNavIcon></clr-icon> <clr-icon shape="users" clrVerticalNavIcon></clr-icon>
{{'SIDE_NAV.SYSTEM_MGMT.USER' | translate}} {{'SIDE_NAV.SYSTEM_MGMT.USER' | translate}}
</a> </a>
<a *ngIf='isLdapMode' clrVerticalNavLink routerLink="/harbor/groups" routerLinkActive="active"> <a *ngIf='isLdapMode || isHttpAuthMode' clrVerticalNavLink routerLink="/harbor/groups" routerLinkActive="active">
<clr-icon shape="users" clrVerticalNavIcon></clr-icon> <clr-icon shape="users" clrVerticalNavIcon></clr-icon>
{{'SIDE_NAV.SYSTEM_MGMT.GROUP' | translate}} {{'SIDE_NAV.SYSTEM_MGMT.GROUP' | translate}}
</a> </a>

View File

@ -54,6 +54,8 @@ export class HarborShellComponent implements OnInit, OnDestroy {
searchSub: Subscription; searchSub: Subscription;
searchCloseSub: Subscription; searchCloseSub: Subscription;
isLdapMode: boolean;
isHttpAuthMode: boolean;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@ -63,6 +65,11 @@ export class HarborShellComponent implements OnInit, OnDestroy {
private appConfigService: AppConfigService) { } private appConfigService: AppConfigService) { }
ngOnInit() { ngOnInit() {
if (this.appConfigService.isLdapMode()) {
this.isLdapMode = true;
} else if (this.appConfigService.isHttpAuthMode()) {
this.isHttpAuthMode = true;
}
this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(searchEvt => { this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(searchEvt => {
if (searchEvt && searchEvt.trim() !== "") { if (searchEvt && searchEvt.trim() !== "") {
this.isSearchResultsOpened = true; this.isSearchResultsOpened = true;
@ -70,7 +77,7 @@ export class HarborShellComponent implements OnInit, OnDestroy {
}); });
this.searchCloseSub = this.searchTrigger.searchCloseChan$.subscribe(close => { this.searchCloseSub = this.searchTrigger.searchCloseChan$.subscribe(close => {
this.isSearchResultsOpened = false; this.isSearchResultsOpened = false;
}); });
} }
@ -97,11 +104,6 @@ export class HarborShellComponent implements OnInit, OnDestroy {
return account != null && account.has_admin_role; return account != null && account.has_admin_role;
} }
public get isLdapMode(): boolean {
let appConfig = this.appConfigService.getConfig();
return appConfig.auth_mode === 'ldap_auth';
}
public get isUserExisting(): boolean { public get isUserExisting(): boolean {
let account = this.session.getCurrentUser(); let account = this.session.getCurrentUser();
return account != null; return account != null;

View File

@ -1,11 +1,12 @@
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false"> <clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
<h3 class="modal-title" *ngIf="mode === 'create'">{{'GROUP.IMPORT_LDAP_GROUP' | translate}}</h3> <h3 class="modal-title" *ngIf="mode === 'create' && isLdapMode">{{'GROUP.IMPORT_LDAP_GROUP' | translate}}</h3>
<h3 class="modal-title" *ngIf="mode === 'create' && isHttpAuthMode">{{'GROUP.IMPORT_HTTP_GROUP' | translate}}</h3>
<h3 class="modal-title" *ngIf="mode !== 'create'">{{'GROUP.EDIT' | translate}}</h3> <h3 class="modal-title" *ngIf="mode !== 'create'">{{'GROUP.EDIT' | translate}}</h3>
<div class="modal-body"> <div class="modal-body">
<form class="form" #groupForm="ngForm"> <form class="form" #groupForm="ngForm">
<section class="form-block"> <section class="form-block">
<div class="form-group"> <div class="form-group" *ngIf="isLdapMode">
<label for="ldap_group_dn" class="required">{{ 'GROUP.GROUP_DN' | translate}}</label> <label for="ldap_group_dn" class="required">{{ 'GROUP.GROUP_DN' | translate}}</label>
<label for="ldap_group_dn" <label for="ldap_group_dn"
aria-haspopup="true" aria-haspopup="true"
@ -22,7 +23,7 @@
</span> </span>
</label> </label>
</div> </div>
<div class="form-group"> <div class="form-group" *ngIf="isLdapMode">
<label for="type">{{'GROUP.TYPE' | translate}}</label> <label for="type">{{'GROUP.TYPE' | translate}}</label>
<label id="type">LDAP</label> <label id="type">LDAP</label>
</div> </div>

View File

@ -1,13 +1,15 @@
import {finalize} from 'rxjs/operators'; import { finalize } from 'rxjs/operators';
import { Subscription } from "rxjs"; import { Subscription } from "rxjs";
import { Component, OnInit, EventEmitter, Output, ChangeDetectorRef, OnDestroy, ViewChild } from "@angular/core"; import { Component, OnInit, EventEmitter, Output, ChangeDetectorRef, OnDestroy, ViewChild } from "@angular/core";
import { NgForm } from "@angular/forms"; import { NgForm } from "@angular/forms";
import { GroupType } from "@harbor/ui";
import { GroupService } from "../group.service"; import { GroupService } from "../group.service";
import { MessageHandlerService } from "./../../shared/message-handler/message-handler.service"; import { MessageHandlerService } from "./../../shared/message-handler/message-handler.service";
import { SessionService } from "./../../shared/session.service"; import { SessionService } from "./../../shared/session.service";
import { UserGroup } from "./../group"; import { UserGroup } from "./../group";
import { AppConfigService } from "../../app-config.service";
@Component({ @Component({
selector: "hbr-add-group-modal", selector: "hbr-add-group-modal",
@ -19,7 +21,7 @@ export class AddGroupModalComponent implements OnInit, OnDestroy {
mode = "create"; mode = "create";
dnTooltip = 'TOOLTIP.ITEM_REQUIRED'; dnTooltip = 'TOOLTIP.ITEM_REQUIRED';
group: UserGroup = new UserGroup(); group: UserGroup;
formChangeSubscription: Subscription; formChangeSubscription: Subscription;
@ -30,25 +32,36 @@ export class AddGroupModalComponent implements OnInit, OnDestroy {
@Output() dataChange = new EventEmitter(); @Output() dataChange = new EventEmitter();
isLdapMode: boolean;
isHttpAuthMode: boolean;
constructor( constructor(
private session: SessionService, private session: SessionService,
private msgHandler: MessageHandlerService, private msgHandler: MessageHandlerService,
private appConfigService: AppConfigService,
private groupService: GroupService, private groupService: GroupService,
private cdr: ChangeDetectorRef private cdr: ChangeDetectorRef
) {} ) { }
ngOnInit() { } ngOnInit() {
if (this.appConfigService.isLdapMode()) {
this.isLdapMode = true;
}
if (this.appConfigService.isHttpAuthMode()) {
this.isHttpAuthMode = true;
}
this.group = new UserGroup(this.isLdapMode ? GroupType.LDAP_TYPE : GroupType.HTTP_TYPE);
}
ngOnDestroy() { } ngOnDestroy() { }
public get isDNInvalid(): boolean { public get isDNInvalid(): boolean {
let dnControl = this.groupForm.controls['ldap_group_dn']; let dnControl = this.groupForm.controls['ldap_group_dn'];
return dnControl && dnControl.invalid && (dnControl.dirty || dnControl.touched); return dnControl && dnControl.invalid && (dnControl.dirty || dnControl.touched);
} }
public get isNameInvalid(): boolean { public get isNameInvalid(): boolean {
let dnControl = this.groupForm.controls['group_name']; let dnControl = this.groupForm.controls['group_name'];
return dnControl && dnControl.invalid && (dnControl.dirty || dnControl.touched); return dnControl && dnControl.invalid && (dnControl.dirty || dnControl.touched);
} }
public get isFormValid(): boolean { public get isFormValid(): boolean {
@ -83,7 +96,7 @@ export class AddGroupModalComponent implements OnInit, OnDestroy {
let groupCopy = Object.assign({}, this.group); let groupCopy = Object.assign({}, this.group);
this.groupService this.groupService
.createGroup(groupCopy).pipe( .createGroup(groupCopy).pipe(
finalize(() => this.close())) finalize(() => this.close()))
.subscribe( .subscribe(
res => { res => {
this.msgHandler.showSuccess("GROUP.ADD_GROUP_SUCCESS"); this.msgHandler.showSuccess("GROUP.ADD_GROUP_SUCCESS");
@ -97,7 +110,7 @@ export class AddGroupModalComponent implements OnInit, OnDestroy {
let groupCopy = Object.assign({}, this.group); let groupCopy = Object.assign({}, this.group);
this.groupService this.groupService
.editGroup(groupCopy).pipe( .editGroup(groupCopy).pipe(
finalize(() => this.close())) finalize(() => this.close()))
.subscribe( .subscribe(
res => { res => {
this.msgHandler.showSuccess("GROUP.EDIT_GROUP_SUCCESS"); this.msgHandler.showSuccess("GROUP.EDIT_GROUP_SUCCESS");
@ -108,7 +121,7 @@ export class AddGroupModalComponent implements OnInit, OnDestroy {
} }
resetGroup() { resetGroup() {
this.group = new UserGroup(); this.group = new UserGroup(this.isLdapMode ? GroupType.LDAP_TYPE : GroupType.HTTP_TYPE);
this.groupForm.reset(); this.groupForm.reset();
} }
} }

View File

@ -15,18 +15,18 @@
<clr-icon shape="plus" size="15"></clr-icon>&nbsp;{{'GROUP.ADD' | translate}}</button> <clr-icon shape="plus" size="15"></clr-icon>&nbsp;{{'GROUP.ADD' | translate}}</button>
<button type="button" class="btn btn-sm btn-secondary" (click)="editGroup()" [disabled]="!canEditGroup"> <button type="button" class="btn btn-sm btn-secondary" (click)="editGroup()" [disabled]="!canEditGroup">
<clr-icon shape="pencil" size="15"></clr-icon>&nbsp;{{'GROUP.EDIT' | translate}}</button> <clr-icon shape="pencil" size="15"></clr-icon>&nbsp;{{'GROUP.EDIT' | translate}}</button>
<button type="button" class="btn btn-sm btn-secondary" (click)="openDeleteConfirmationDialog()" [disabled]="!canEditGroup"> <button type="button" class="btn btn-sm btn-secondary" (click)="openDeleteConfirmationDialog()" [disabled]="!canDeleteGroup">
<clr-icon shape="times" size="15"></clr-icon>&nbsp;{{'GROUP.DELETE' | translate}}</button> <clr-icon shape="times" size="15"></clr-icon>&nbsp;{{'GROUP.DELETE' | translate}}</button>
</clr-dg-action-bar> </clr-dg-action-bar>
<clr-dg-column>{{'GROUP.NAME' | translate}}</clr-dg-column> <clr-dg-column>{{'GROUP.NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'GROUP.TYPE' | translate}}</clr-dg-column> <clr-dg-column>{{'GROUP.TYPE' | translate}}</clr-dg-column>
<clr-dg-column>{{'GROUP.DN' | translate}}</clr-dg-column> <clr-dg-column *ngIf="isLdapMode">{{'GROUP.DN' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let group of groups" [clrDgItem]="group"> <clr-dg-row *clrDgItems="let group of groups" [clrDgItem]="group">
<clr-dg-cell>{{group.group_name}}</clr-dg-cell> <clr-dg-cell>{{group.group_name}}</clr-dg-cell>
<clr-dg-cell>{{groupToSring(group.group_type) | translate}}</clr-dg-cell> <clr-dg-cell>{{groupToSring(group.group_type) | translate}}</clr-dg-cell>
<clr-dg-cell>{{group.ldap_group_dn}}</clr-dg-cell> <clr-dg-cell *ngIf="isLdapMode">{{group.ldap_group_dn}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer>
<clr-dg-pagination #pagination [clrDgPageSize]="15"> <clr-dg-pagination #pagination [clrDgPageSize]="15">

View File

@ -4,7 +4,7 @@ import { flatMap, catchError } from "rxjs/operators";
import { SessionService } from "./../shared/session.service"; import { SessionService } from "./../shared/session.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { Component, OnInit, ViewChild, OnDestroy } from "@angular/core"; import { Component, OnInit, ViewChild, OnDestroy } from "@angular/core";
import { operateChanges, OperateInfo, OperationService, OperationState, errorHandler as errorHandFn } from "@harbor/ui"; import { operateChanges, OperateInfo, OperationService, OperationState, errorHandler as errorHandFn, GroupType } from "@harbor/ui";
import { import {
ConfirmationTargets, ConfirmationTargets,
@ -19,6 +19,8 @@ import { UserGroup } from "./group";
import { GroupService } from "./group.service"; import { GroupService } from "./group.service";
import { MessageHandlerService } from "../shared/message-handler/message-handler.service"; import { MessageHandlerService } from "../shared/message-handler/message-handler.service";
import { throwError as observableThrowError } from "rxjs"; import { throwError as observableThrowError } from "rxjs";
import { AppConfigService } from '../app-config.service';
@Component({ @Component({
selector: "app-group", selector: "app-group",
templateUrl: "./group.component.html", templateUrl: "./group.component.html",
@ -35,6 +37,7 @@ export class GroupComponent implements OnInit, OnDestroy {
delSub: Subscription; delSub: Subscription;
batchOps = 'idle'; batchOps = 'idle';
batchInfos = new Map(); batchInfos = new Map();
isLdapMode: boolean;
@ViewChild(AddGroupModalComponent) newGroupModal: AddGroupModalComponent; @ViewChild(AddGroupModalComponent) newGroupModal: AddGroupModalComponent;
@ -46,10 +49,14 @@ export class GroupComponent implements OnInit, OnDestroy {
private msgHandler: MessageHandlerService, private msgHandler: MessageHandlerService,
private session: SessionService, private session: SessionService,
private translateService: TranslateService, private translateService: TranslateService,
private appConfigService: AppConfigService
) { } ) { }
ngOnInit() { ngOnInit() {
this.loadData(); this.loadData();
if (this.appConfigService.isLdapMode()) {
this.isLdapMode = true;
}
this.delSub = this.operateDialogService.confirmationConfirm$.subscribe( this.delSub = this.operateDialogService.confirmationConfirm$.subscribe(
message => { message => {
if ( if (
@ -150,7 +157,13 @@ export class GroupComponent implements OnInit, OnDestroy {
} }
groupToSring(type: number) { groupToSring(type: number) {
if (type === 1) { return 'GROUP.LDAP_TYPE'; } else { return 'UNKNOWN'; } if (type === GroupType.LDAP_TYPE) {
return 'GROUP.LDAP_TYPE';
} else if (type === GroupType.HTTP_TYPE) {
return 'GROUP.HTTP_TYPE';
} else {
return 'UNKNOWN';
}
} }
doFilter(groupName: string): void { doFilter(groupName: string): void {
@ -162,6 +175,12 @@ export class GroupComponent implements OnInit, OnDestroy {
} }
get canEditGroup(): boolean { get canEditGroup(): boolean {
return (
this.selectedGroups.length === 1 &&
this.session.currentUser.has_admin_role && this.isLdapMode
);
}
get canDeleteGroup(): boolean {
return ( return (
this.selectedGroups.length === 1 && this.selectedGroups.length === 1 &&
this.session.currentUser.has_admin_role this.session.currentUser.has_admin_role

View File

@ -4,9 +4,9 @@ export class UserGroup {
group_type: number; group_type: number;
ldap_group_dn?: string; ldap_group_dn?: string;
constructor() { constructor(groupType) {
{ {
this.group_type = 1; this.group_type = groupType;
} }
} }
} }

View File

@ -30,7 +30,7 @@ export class AddGroupComponent implements OnInit {
currentTerm = ''; currentTerm = '';
selectedRole = 1; selectedRole = 1;
group = new UserGroup(); group = new UserGroup(1);
selectedGroups: UserGroup[] = []; selectedGroups: UserGroup[] = [];
groups: UserGroup[] = []; groups: UserGroup[] = [];
totalCount = 0; totalCount = 0;
@ -89,7 +89,7 @@ export class AddGroupComponent implements OnInit {
resetModaldata() { resetModaldata() {
this.createGroupMode = false; this.createGroupMode = false;
this.group = new UserGroup(); this.group = new UserGroup(1);
this.selectedRole = 1; this.selectedRole = 1;
this.selectedGroups = []; this.selectedGroups = [];
this.groups = []; this.groups = [];

View File

@ -0,0 +1,36 @@
<clr-modal [(clrModalOpen)]="addHttpAuthOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
<h3 class="modal-title">{{'GROUP.NEW_MEMBER' | translate}}</h3>
<inline-alert class="modal-title padding-0"></inline-alert>
<div class="modal-body">
<label>{{ 'GROUP.NEW_USER_INFO' | translate}}</label>
<form #memberForm="ngForm">
<section class="form-block">
<div class="form-group">
<label for="member_name" class="col-md-4 form-group-label-override required">{{'GROUP.GROUP' | translate}} {{'GROUP.NAME' | translate}}</label>
<label for="member_name" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
<input type="text" id="member_name" [(ngModel)]="member_group.group_name"
name="member_name"
size="20"
minlength="3"
#memberName="ngModel"
required autocomplete="off">
</label>
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
</div>
<div class="form-group">
<label class="col-md-4 form-group-label-override">{{'GROUP.ROLE' | translate}}</label>
<div class="radio" *ngFor="let projectRoot of projectRoots">
<input type="radio" name="member_role" id="{{'check_root_project_' + projectRoot.NAME}}" [value]="projectRoot.VALUE" [(ngModel)]="role_id">
<label for="{{'check_root_project_' + projectRoot.NAME}}">{{ projectRoot.LABEL | translate}}</label>
</div>
</div>
</section>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
</div>
</clr-modal>

View File

@ -0,0 +1,8 @@
.form-group-label-override {
font-size: 14px;
font-weight: 400;
}
.padding-0 {
padding: 0;
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AddHttpAuthGroupComponent } from './add-http-auth-group.component';
describe('AddHttpAuthGroupComponent', () => {
let component: AddHttpAuthGroupComponent;
let fixture: ComponentFixture<AddHttpAuthGroupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AddHttpAuthGroupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddHttpAuthGroupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,120 @@
import { finalize } from 'rxjs/operators';
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {
Component,
Input,
EventEmitter,
Output,
ViewChild,
OnInit
} from '@angular/core';
import { NgForm } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { InlineAlertComponent } from '../../../shared/inline-alert/inline-alert.component';
import { UserService } from '../../../user/user.service';
import { errorHandler as errorHandFn, PROJECT_ROOTS, ProjectRootInterface } from "@harbor/ui";
import { MemberService } from '../member.service';
import { UserGroup } from "./../../../group/group";
@Component({
selector: 'add-http-auth-group',
templateUrl: './add-http-auth-group.component.html',
styleUrls: ['./add-http-auth-group.component.scss'],
providers: [UserService]
})
export class AddHttpAuthGroupComponent implements OnInit {
projectRoots: ProjectRootInterface[];
member_group: UserGroup = { group_name: '', group_type: 2 };
role_id: number;
addHttpAuthOpened: boolean;
memberForm: NgForm;
staticBackdrop: boolean = true;
closable: boolean = false;
@ViewChild('memberForm')
currentForm: NgForm;
@ViewChild(InlineAlertComponent)
inlineAlert: InlineAlertComponent;
@Input() projectId: number;
@Output() added = new EventEmitter<boolean>();
checkOnGoing: boolean = false;
constructor(private memberService: MemberService,
private translateService: TranslateService) { }
ngOnInit(): void {
this.projectRoots = PROJECT_ROOTS;
}
createGroupAsMember() {
this.checkOnGoing = true;
this.memberService.addGroupMember(this.projectId, this.member_group, this.role_id)
.pipe(
finalize(() => {
this.checkOnGoing = false;
}
))
.subscribe(
res => {
this.role_id = null;
this.addHttpAuthOpened = false;
this.added.emit(true);
},
err => {
let errorMessageKey: string = errorHandFn(err);
this.translateService
.get(errorMessageKey)
.subscribe(errorMessage => this.inlineAlert.showInlineError(errorMessage));
this.added.emit(false);
}
);
}
onSubmit(): void {
this.createGroupAsMember();
}
onCancel() {
this.role_id = null;
this.addHttpAuthOpened = false;
}
openAddMemberModal(): void {
this.currentForm.reset();
this.addHttpAuthOpened = true;
this.role_id = 1;
}
public get isValid(): boolean {
return this.currentForm &&
this.currentForm.valid &&
!this.checkOnGoing;
}
}

View File

@ -16,7 +16,7 @@
<button class="btn btn-sm btn-secondary" (click)="openAddMemberModal()" [disabled]="!hasCreateMemberPermission"> <button class="btn btn-sm btn-secondary" (click)="openAddMemberModal()" [disabled]="!hasCreateMemberPermission">
<span><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'MEMBER.USER' | translate }}</span> <span><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'MEMBER.USER' | translate }}</span>
</button> </button>
<button class="btn btn-sm btn-secondary" (click)="openAddGroupModal()" [disabled]="!hasCreateMemberPermission || !isLdapMode"> <button class="btn btn-sm btn-secondary" (click)="openAddGroupModal()" [disabled]="!hasCreateMemberPermission || !(isLdapMode || isHttpAuthMode)">
<span><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'MEMBER.LDAP_GROUP' | translate}}</span> <span><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'MEMBER.LDAP_GROUP' | translate}}</span>
</button> </button>
<clr-dropdown id='member-action' [clrCloseMenuOnItemClick]="false" class="btn btn-sm btn-link" clrDropdownTrigger> <clr-dropdown id='member-action' [clrCloseMenuOnItemClick]="false" class="btn btn-sm btn-link" clrDropdownTrigger>
@ -53,4 +53,5 @@
</div> </div>
<add-member [projectId]="projectId" [memberList]="members" (added)="addedMember($event)"></add-member> <add-member [projectId]="projectId" [memberList]="members" (added)="addedMember($event)"></add-member>
<add-group [projectId]="projectId" [memberList]="members" (added)="addedGroup($event)"></add-group> <add-group [projectId]="projectId" [memberList]="members" (added)="addedGroup($event)"></add-group>
<add-http-auth-group [projectId]="projectId" (added)="addedGroup($event)"></add-http-auth-group>
</div> </div>

View File

@ -17,8 +17,10 @@ import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, Chang
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { Subscription, forkJoin, Observable } from "rxjs"; import { Subscription, forkJoin, Observable } from "rxjs";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { operateChanges, OperateInfo, OperationService, OperationState, UserPermissionService, USERSTATICPERMISSION, ErrorHandler import {
, errorHandler as errorHandFn } from "@harbor/ui"; operateChanges, OperateInfo, OperationService, OperationState, UserPermissionService, USERSTATICPERMISSION, ErrorHandler
, errorHandler as errorHandFn
} from "@harbor/ui";
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service"; import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from "../../shared/shared.const"; import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from "../../shared/shared.const";
@ -30,6 +32,7 @@ import { Project } from "../../project/project";
import { Member } from "./member"; import { Member } from "./member";
import { SessionUser } from "../../shared/session-user"; import { SessionUser } from "../../shared/session-user";
import { AddGroupComponent } from './add-group/add-group.component'; import { AddGroupComponent } from './add-group/add-group.component';
import { AddHttpAuthGroupComponent } from './add-http-auth-group/add-http-auth-group.component';
import { MemberService } from "./member.service"; import { MemberService } from "./member.service";
import { AddMemberComponent } from "./add-member/add-member.component"; import { AddMemberComponent } from "./add-member/add-member.component";
import { AppConfigService } from "../../app-config.service"; import { AppConfigService } from "../../app-config.service";
@ -56,16 +59,18 @@ export class MemberComponent implements OnInit, OnDestroy {
isDelete = false; isDelete = false;
isChangeRole = false; isChangeRole = false;
loading = false; loading = false;
isLdapMode: boolean = false;
isChangingRole = false; isChangingRole = false;
batchChangeRoleInfos = {}; batchChangeRoleInfos = {};
isLdapMode: boolean;
isHttpAuthMode: boolean;
@ViewChild(AddMemberComponent) @ViewChild(AddMemberComponent)
addMemberComponent: AddMemberComponent; addMemberComponent: AddMemberComponent;
@ViewChild(AddGroupComponent) @ViewChild(AddGroupComponent)
addGroupComponent: AddGroupComponent; addGroupComponent: AddGroupComponent;
@ViewChild(AddHttpAuthGroupComponent)
addHttpAuthGroupComponent: AddHttpAuthGroupComponent;
hasCreateMemberPermission: boolean; hasCreateMemberPermission: boolean;
hasUpdateMemberPermission: boolean; hasUpdateMemberPermission: boolean;
hasDeleteMemberPermission: boolean; hasDeleteMemberPermission: boolean;
@ -108,13 +113,15 @@ export class MemberComponent implements OnInit, OnDestroy {
// Get current user from registered resolver. // Get current user from registered resolver.
this.currentUser = this.session.getCurrentUser(); this.currentUser = this.session.getCurrentUser();
this.retrieve(this.projectId, ""); this.retrieve(this.projectId, "");
// get member permission rule
this.getMemberPermissionRule(this.projectId);
if (this.appConfigService.isLdapMode()) { if (this.appConfigService.isLdapMode()) {
this.isLdapMode = true; this.isLdapMode = true;
} }
// get member permission rule if (this.appConfigService.isHttpAuthMode()) {
this.getMemberPermissionRule(this.projectId); this.isHttpAuthMode = true;
}
} }
doSearch(searchMember: string) { doSearch(searchMember: string) {
this.searchMember = searchMember; this.searchMember = searchMember;
this.retrieve(this.projectId, this.searchMember); this.retrieve(this.projectId, this.searchMember);
@ -172,7 +179,11 @@ export class MemberComponent implements OnInit, OnDestroy {
// Add group // Add group
openAddGroupModal() { openAddGroupModal() {
this.addGroupComponent.open(); if (this.isLdapMode) {
this.addGroupComponent.open();
} else {
this.addHttpAuthGroupComponent.openAddMemberModal();
}
} }
addedGroup(result: boolean) { addedGroup(result: boolean) {
this.searchMember = ""; this.searchMember = "";
@ -188,10 +199,10 @@ export class MemberComponent implements OnInit, OnDestroy {
return this.memberService return this.memberService
.changeMemberRole(projectId, member.id, roleId) .changeMemberRole(projectId, member.id, roleId)
.pipe(map(() => this.batchChangeRoleInfos[member.id] = 'done') .pipe(map(() => this.batchChangeRoleInfos[member.id] = 'done')
, catchError(error => { , catchError(error => {
this.messageHandlerService.handleError(error + ": " + member.entity_name); this.messageHandlerService.handleError(error + ": " + member.entity_name);
return observableThrowError(error); return observableThrowError(error);
})); }));
}; };
// Preparation for members role change // Preparation for members role change

View File

@ -38,6 +38,7 @@ import { ProjectLabelComponent } from "../project/project-label/project-label.co
import { HelmChartModule } from './helm-chart/helm-chart.module'; import { HelmChartModule } from './helm-chart/helm-chart.module';
import { RobotAccountComponent } from './robot-account/robot-account.component'; import { RobotAccountComponent } from './robot-account/robot-account.component';
import { AddRobotComponent } from './robot-account/add-robot/add-robot.component'; import { AddRobotComponent } from './robot-account/add-robot/add-robot.component';
import { AddHttpAuthGroupComponent } from './member/add-http-auth-group/add-http-auth-group.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -59,7 +60,8 @@ import { AddRobotComponent } from './robot-account/add-robot/add-robot.component
ProjectLabelComponent, ProjectLabelComponent,
AddGroupComponent, AddGroupComponent,
RobotAccountComponent, RobotAccountComponent,
AddRobotComponent AddRobotComponent,
AddHttpAuthGroupComponent
], ],
exports: [ProjectComponent, ListProjectComponent], exports: [ProjectComponent, ListProjectComponent],
providers: [ProjectRoutingResolver, MemberService, RobotService] providers: [ProjectRoutingResolver, MemberService, RobotService]

View File

@ -328,6 +328,7 @@
"GROUP": "Group", "GROUP": "Group",
"GROUPS": "Groups", "GROUPS": "Groups",
"IMPORT_LDAP_GROUP": "Import LDAP Group", "IMPORT_LDAP_GROUP": "Import LDAP Group",
"IMPORT_HTTP_GROUP": "New HTTP Group",
"ADD": "New Group", "ADD": "New Group",
"EDIT": "Edit", "EDIT": "Edit",
"DELETE": "Delete", "DELETE": "Delete",
@ -340,8 +341,17 @@
"ADD_GROUP_SUCCESS": "Add group success", "ADD_GROUP_SUCCESS": "Add group success",
"EDIT_GROUP_SUCCESS": "Edit group success", "EDIT_GROUP_SUCCESS": "Edit group success",
"LDAP_TYPE": "LDAP", "LDAP_TYPE": "LDAP",
"HTTP_TYPE": "HTTP",
"OF": "of", "OF": "of",
"ITEMS": "items" "ITEMS": "items",
"NEW_MEMBER": "New Group Member",
"NEW_USER_INFO": "Add a group to be a member of this project with specified role",
"ROLE": "Role",
"SYS_ADMIN": "System Admin",
"PROJECT_ADMIN": "Project Admin",
"PROJECT_MASTER": "Master",
"DEVELOPER": "Developer",
"GUEST": "Guest"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Username", "USERNAME": "Username",

View File

@ -329,6 +329,7 @@
"GROUP": "Group", "GROUP": "Group",
"GROUPS": "Groups", "GROUPS": "Groups",
"IMPORT_LDAP_GROUP": "Import LDAP Group", "IMPORT_LDAP_GROUP": "Import LDAP Group",
"IMPORT_HTTP_GROUP": "New HTTP Group",
"ADD": "Add", "ADD": "Add",
"EDIT": "Edit", "EDIT": "Edit",
"DELETE": "Delete", "DELETE": "Delete",
@ -340,8 +341,17 @@
"ADD_GROUP_SUCCESS": "Add group success", "ADD_GROUP_SUCCESS": "Add group success",
"EDIT_GROUP_SUCCESS": "Edit group success", "EDIT_GROUP_SUCCESS": "Edit group success",
"LDAP_TYPE": "LDAP", "LDAP_TYPE": "LDAP",
"HTTP_TYPE": "HTTP",
"OF": "of", "OF": "of",
"ITEMS": "items" "ITEMS": "items",
"NEW_MEMBER": "New Group Member",
"NEW_USER_INFO": "Add a group to be a member of this project with specified role",
"ROLE": "Role",
"SYS_ADMIN": "System Admin",
"PROJECT_ADMIN": "Project Admin",
"PROJECT_MASTER": "Master",
"DEVELOPER": "Developer",
"GUEST": "Guest"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Nombre de usuario", "USERNAME": "Nombre de usuario",

View File

@ -321,6 +321,7 @@
"Group": "Group", "Group": "Group",
"GROUPS": "Groups", "GROUPS": "Groups",
"IMPORT_LDAP_GROUP": "Import LDAP Group", "IMPORT_LDAP_GROUP": "Import LDAP Group",
"IMPORT_HTTP_GROUP": "New HTTP Group",
"ADD": "Add", "ADD": "Add",
"EDIT": "Edit", "EDIT": "Edit",
"DELETE": "Delete", "DELETE": "Delete",
@ -333,8 +334,17 @@
"ADD_GROUP_SUCCESS": "Add group success", "ADD_GROUP_SUCCESS": "Add group success",
"EDIT_GROUP_SUCCESS": "Edit group success", "EDIT_GROUP_SUCCESS": "Edit group success",
"LDAP_TYPE": "LDAP", "LDAP_TYPE": "LDAP",
"HTTP_TYPE": "HTTP",
"OF": "of", "OF": "of",
"ITEMS": "items" "ITEMS": "items",
"NEW_MEMBER": "New Group Member",
"NEW_USER_INFO": "Add a group to be a member of this project with specified role",
"ROLE": "Role",
"SYS_ADMIN": "System Admin",
"PROJECT_ADMIN": "Project Admin",
"PROJECT_MASTER": "Master",
"DEVELOPER": "Developer",
"GUEST": "Guest"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Nom d'utilisateur", "USERNAME": "Nom d'utilisateur",

View File

@ -326,6 +326,7 @@
"GROUP": "Grupo", "GROUP": "Grupo",
"GROUPS": "Grupos", "GROUPS": "Grupos",
"IMPORT_LDAP_GROUP": "Importar grupo do LDAP", "IMPORT_LDAP_GROUP": "Importar grupo do LDAP",
"IMPORT_HTTP_GROUP": "New HTTP Group",
"ADD": "Novo Grupo", "ADD": "Novo Grupo",
"EDIT": "Editar", "EDIT": "Editar",
"DELETE": "Remover", "DELETE": "Remover",
@ -338,8 +339,17 @@
"ADD_GROUP_SUCCESS": "Grupo adicionado com sucesso", "ADD_GROUP_SUCCESS": "Grupo adicionado com sucesso",
"EDIT_GROUP_SUCCESS": "Grupo editado com sucesso", "EDIT_GROUP_SUCCESS": "Grupo editado com sucesso",
"LDAP_TYPE": "LDAP", "LDAP_TYPE": "LDAP",
"HTTP_TYPE": "HTTP",
"OF": "de", "OF": "de",
"ITEMS": "itens" "ITEMS": "itens",
"NEW_MEMBER": "New Group Member",
"NEW_USER_INFO": "Add a group to be a member of this project with specified role",
"ROLE": "Role",
"SYS_ADMIN": "System Admin",
"PROJECT_ADMIN": "Project Admin",
"PROJECT_MASTER": "Master",
"DEVELOPER": "Developer",
"GUEST": "Guest"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Nome do usuário", "USERNAME": "Nome do usuário",

View File

@ -327,6 +327,7 @@
"GROUP": "组", "GROUP": "组",
"GROUPS": "组", "GROUPS": "组",
"IMPORT_LDAP_GROUP": "导入LDAP组", "IMPORT_LDAP_GROUP": "导入LDAP组",
"IMPORT_HTTP_GROUP": "新建HTTP组",
"ADD": "新增", "ADD": "新增",
"EDIT": "编辑", "EDIT": "编辑",
"DELETE": "删除", "DELETE": "删除",
@ -339,8 +340,17 @@
"ADD_GROUP_SUCCESS": "添加组成功", "ADD_GROUP_SUCCESS": "添加组成功",
"EDIT_GROUP_SUCCESS": "修改组成功", "EDIT_GROUP_SUCCESS": "修改组成功",
"LDAP_TYPE": "LDAP", "LDAP_TYPE": "LDAP",
"HTTP_TYPE": "HTTP",
"OF": "共计", "OF": "共计",
"ITEMS": "条记录" "ITEMS": "条记录",
"NEW_MEMBER": "新建组成员",
"NEW_USER_INFO": "添加一个组作为具有指定角色的此项目的成员",
"ROLE": "权限",
"SYS_ADMIN": "系统管理员",
"PROJECT_ADMIN": "项目管理员",
"PROJECT_MASTER": "维护人员",
"DEVELOPER": "开发者",
"GUEST": "访客"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "用户名", "USERNAME": "用户名",