diff --git a/src/portal/src/app/user/user.component.html b/src/portal/src/app/user/user.component.html
index aeb2ca739..9e855c479 100644
--- a/src/portal/src/app/user/user.component.html
+++ b/src/portal/src/app/user/user.component.html
@@ -9,7 +9,7 @@
-
+
@@ -35,7 +35,7 @@
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'USER.OF' | translate }}
{{pagination.totalItems}} {{'USER.ITEMS' | translate }}
-
+
diff --git a/src/portal/src/app/user/user.component.ts b/src/portal/src/app/user/user.component.ts
index 91f3ec16c..e50455871 100644
--- a/src/portal/src/app/user/user.component.ts
+++ b/src/portal/src/app/user/user.component.ts
@@ -11,13 +11,19 @@
// 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, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
+import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core';
import { Subscription, Observable, forkJoin } from "rxjs";
import { TranslateService } from '@ngx-translate/core';
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
-import { operateChanges, OperateInfo, OperationService, OperationState, errorHandler as errorHandFn } from '@harbor/ui';
+import {
+ operateChanges,
+ OperateInfo,
+ OperationService,
+ OperationState,
+ errorHandler as errorHandFn,
+} from '@harbor/ui';
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message';
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
@@ -41,348 +47,305 @@ import { throwError as observableThrowError } from "rxjs";
*/
@Component({
- selector: 'harbor-user',
- templateUrl: 'user.component.html',
- styleUrls: ['user.component.scss'],
- providers: [UserService],
- changeDetection: ChangeDetectionStrategy.OnPush
+ selector: 'harbor-user',
+ templateUrl: 'user.component.html',
+ styleUrls: ['user.component.scss'],
+ providers: [UserService],
})
export class UserComponent implements OnInit, OnDestroy {
- users: User[] = [];
- originalUsers: Observable;
- selectedRow: User[] = [];
- ISADMNISTRATOR: string = "USER.ENABLE_ADMIN_ACTION";
+ users: User[] = [];
+ selectedRow: User[] = [];
+ ISADMNISTRATOR: string = "USER.ENABLE_ADMIN_ACTION";
- currentTerm: string;
- totalCount: number = 0;
- currentPage: number = 1;
- timerHandler: any;
+ currentTerm: string;
+ totalCount: number = 0;
+ currentPage: number = 1;
+ pageSize: number = 15;
+ timerHandler: any;
- private onGoing: boolean = true;
- private adminMenuText: string = "";
- private adminColumn: string = "";
- private deletionSubscription: Subscription;
- @ViewChild(NewUserModalComponent)
- newUserDialog: NewUserModalComponent;
- @ViewChild(ChangePasswordComponent)
- changePwdDialog: ChangePasswordComponent;
+ private onGoing: boolean = true;
+ private adminMenuText: string = "";
+ private adminColumn: string = "";
+ private deletionSubscription: Subscription;
+ @ViewChild(NewUserModalComponent)
+ newUserDialog: NewUserModalComponent;
+ @ViewChild(ChangePasswordComponent)
+ changePwdDialog: ChangePasswordComponent;
- constructor(
- private userService: UserService,
- private translate: TranslateService,
- private deletionDialogService: ConfirmationDialogService,
- private msgHandler: MessageHandlerService,
- private session: SessionService,
- private appConfigService: AppConfigService,
- private operationService: OperationService,
- private ref: ChangeDetectorRef) {
- this.deletionSubscription = deletionDialogService.confirmationConfirm$.subscribe(confirmed => {
- if (confirmed &&
- confirmed.source === ConfirmationTargets.USER &&
- confirmed.state === ConfirmationState.CONFIRMED) {
- this.delUser(confirmed.data);
- }
- });
- }
-
- isMySelf(uid: number): boolean {
- let currentUser = this.session.getCurrentUser();
- if (currentUser) {
- if (currentUser.user_id === uid) {
- return true;
- }
+ constructor(
+ private userService: UserService,
+ private translate: TranslateService,
+ private deletionDialogService: ConfirmationDialogService,
+ private msgHandler: MessageHandlerService,
+ private session: SessionService,
+ private appConfigService: AppConfigService,
+ private operationService: OperationService) {
+ this.deletionSubscription = deletionDialogService.confirmationConfirm$.subscribe(confirmed => {
+ if (confirmed &&
+ confirmed.source === ConfirmationTargets.USER &&
+ confirmed.state === ConfirmationState.CONFIRMED) {
+ this.delUser(confirmed.data);
+ }
+ });
}
- return false;
- }
+ isMySelf(uid: number): boolean {
+ let currentUser = this.session.getCurrentUser();
+ if (currentUser) {
+ if (currentUser.user_id === uid) {
+ return true;
+ }
+ }
- get onlySelf(): boolean {
- return this.selectedRow.length === 1 && this.isMySelf(this.selectedRow[0].user_id);
- }
-
- public get canCreateUser(): boolean {
- let appConfig = this.appConfigService.getConfig();
- if (appConfig) {
- return !(appConfig.auth_mode === 'ldap_auth' || appConfig.auth_mode === 'uaa_auth' || appConfig.auth_mode === 'oidc_auth');
- } else {
- return true;
- }
- }
-
- public get ifSameRole(): boolean {
- let usersRole: number[] = [];
- this.selectedRow.forEach(user => {
- if (user.user_id === 0 || this.isMySelf(user.user_id)) {
return false;
- }
- if (user.has_admin_role) {
- usersRole.push(1);
- } else {
- usersRole.push(0);
- }
- });
- if (usersRole.length && usersRole.every(num => num === 0)) {
- this.ISADMNISTRATOR = 'USER.ENABLE_ADMIN_ACTION';
- return true;
- }
- if (usersRole.length && usersRole.every(num => num === 1)) {
- this.ISADMNISTRATOR = 'USER.DISABLE_ADMIN_ACTION';
- return true;
- }
- return false;
- }
-
- isSystemAdmin(u: User): string {
- if (!u) {
- return "{{MISS}}";
- }
- let key: string = u.has_admin_role ? "USER.IS_ADMIN" : "USER.IS_NOT_ADMIN";
- this.translate.get(key).subscribe((res: string) => this.adminColumn = res);
- return this.adminColumn;
- }
-
- adminActions(u: User): string {
- if (!u) {
- return "{{MISS}}";
- }
- let key: string = u.has_admin_role ? "USER.DISABLE_ADMIN_ACTION" : "USER.ENABLE_ADMIN_ACTION";
- this.translate.get(key).subscribe((res: string) => this.adminMenuText = res);
- return this.adminMenuText;
- }
-
- public get inProgress(): boolean {
- return this.onGoing;
- }
-
- ngOnInit(): void { }
-
- ngOnDestroy(): void {
- if (this.deletionSubscription) {
- this.deletionSubscription.unsubscribe();
}
- if (this.timerHandler) {
- clearInterval(this.timerHandler);
- this.timerHandler = null;
- }
- }
-
- openChangePwdModal(): void {
- if (this.selectedRow.length === 1) {
- this.changePwdDialog.open(this.selectedRow[0].user_id);
+ get onlySelf(): boolean {
+ return this.selectedRow.length === 1 && this.isMySelf(this.selectedRow[0].user_id);
}
- }
+ public get canCreateUser(): boolean {
+ let appConfig = this.appConfigService.getConfig();
+ if (appConfig) {
+ return !(appConfig.auth_mode === 'ldap_auth' || appConfig.auth_mode === 'uaa_auth' || appConfig.auth_mode === 'oidc_auth');
+ } else {
+ return true;
+ }
+ }
- // Filter items by keywords
- doFilter(terms: string): void {
- this.selectedRow = [];
- this.currentTerm = terms;
- this.originalUsers.subscribe(users => {
- if (terms.trim() === "") {
- this.refreshUser((this.currentPage - 1) * 15, this.currentPage * 15);
- } else {
- let selectUsers = users.filter(user => {
- return this.isMatchFilterTerm(terms, user.username);
+ public get ifSameRole(): boolean {
+ let usersRole: number[] = [];
+ this.selectedRow.forEach(user => {
+ if (user.user_id === 0 || this.isMySelf(user.user_id)) {
+ return false;
+ }
+ if (user.has_admin_role) {
+ usersRole.push(1);
+ } else {
+ usersRole.push(0);
+ }
});
- this.totalCount = selectUsers.length;
- this.users = selectUsers.slice((this.currentPage - 1) * 15, this.currentPage * 15); // First page
-
- this.forceRefreshView(5000);
- }
- });
- }
-
- // Disable the admin role for the specified user
- changeAdminRole(): void {
- let observableLists: any[] = [];
- if (this.selectedRow.length) {
- if (this.ISADMNISTRATOR === 'USER.ENABLE_ADMIN_ACTION') {
- for (let i = 0; i < this.selectedRow.length; i++) {
- // Double confirm user is existing
- if (this.selectedRow[i].user_id === 0 || this.isMySelf(this.selectedRow[i].user_id)) {
- continue;
- }
- let updatedUser: User = new User();
- updatedUser.user_id = this.selectedRow[i].user_id;
-
- updatedUser.has_admin_role = true; // Set as admin
- observableLists.push(this.userService.updateUserRole(updatedUser));
+ if (usersRole.length && usersRole.every(num => num === 0)) {
+ this.ISADMNISTRATOR = 'USER.ENABLE_ADMIN_ACTION';
+ return true;
}
- }
- if (this.ISADMNISTRATOR === 'USER.DISABLE_ADMIN_ACTION') {
- for (let i = 0; i < this.selectedRow.length; i++) {
- // Double confirm user is existing
- if (this.selectedRow[i].user_id === 0 || this.isMySelf(this.selectedRow[i].user_id)) {
- continue;
- }
- let updatedUser: User = new User();
- updatedUser.user_id = this.selectedRow[i].user_id;
-
- updatedUser.has_admin_role = false; // Set as none admin
- observableLists.push(this.userService.updateUserRole(updatedUser));
+ if (usersRole.length && usersRole.every(num => num === 1)) {
+ this.ISADMNISTRATOR = 'USER.DISABLE_ADMIN_ACTION';
+ return true;
}
- }
-
- forkJoin(...observableLists).subscribe(() => {
- this.selectedRow = [];
- this.refresh();
- }, error => {
- this.selectedRow = [];
- this.msgHandler.handleError(error);
- });
- }
- }
-
- // Delete the specified user
- deleteUsers(users: User[]): void {
- let userArr: string[] = [];
- if (this.onlySelf) {
- return;
+ return false;
}
- if (users && users.length) {
- users.forEach(user => {
- userArr.push(user.username);
- });
+ isSystemAdmin(u: User): string {
+ if (!u) {
+ return "{{MISS}}";
+ }
+ let key: string = u.has_admin_role ? "USER.IS_ADMIN" : "USER.IS_NOT_ADMIN";
+ this.translate.get(key).subscribe((res: string) => this.adminColumn = res);
+ return this.adminColumn;
}
- // Confirm deletion
- let msg: ConfirmationMessage = new ConfirmationMessage(
- "USER.DELETION_TITLE",
- "USER.DELETION_SUMMARY",
- userArr.join(','),
- users,
- ConfirmationTargets.USER,
- ConfirmationButtons.DELETE_CANCEL
- );
- this.deletionDialogService.openComfirmDialog(msg);
- }
- delUser(users: User[]): void {
- let observableLists: any[] = [];
- if (users && users.length) {
- users.forEach(user => {
- observableLists.push(this.delOperate(user));
- });
+ adminActions(u: User): string {
+ if (!u) {
+ return "{{MISS}}";
+ }
+ let key: string = u.has_admin_role ? "USER.DISABLE_ADMIN_ACTION" : "USER.ENABLE_ADMIN_ACTION";
+ this.translate.get(key).subscribe((res: string) => this.adminMenuText = res);
+ return this.adminMenuText;
+ }
- forkJoin(...observableLists).subscribe((item) => {
+ public get inProgress(): boolean {
+ return this.onGoing;
+ }
+
+ ngOnInit(): void {
+ }
+
+ ngOnDestroy(): void {
+ if (this.deletionSubscription) {
+ this.deletionSubscription.unsubscribe();
+ }
+
+ if (this.timerHandler) {
+ clearInterval(this.timerHandler);
+ this.timerHandler = null;
+ }
+ }
+
+ openChangePwdModal(): void {
+ if (this.selectedRow.length === 1) {
+ this.changePwdDialog.open(this.selectedRow[0].user_id);
+ }
+ }
+
+ // Filter items by keywords
+ doFilter(terms: string): void {
this.selectedRow = [];
+ this.currentTerm = terms.trim();
+ this.currentPage = 1;
+ this.onGoing = true;
+ this.getUserListByPaging();
+ }
+
+ // Disable the admin role for the specified user
+ changeAdminRole(): void {
+ let observableLists: any[] = [];
+ if (this.selectedRow.length) {
+ if (this.ISADMNISTRATOR === 'USER.ENABLE_ADMIN_ACTION') {
+ for (let i = 0; i < this.selectedRow.length; i++) {
+ // Double confirm user is existing
+ if (this.selectedRow[i].user_id === 0 || this.isMySelf(this.selectedRow[i].user_id)) {
+ continue;
+ }
+ let updatedUser: User = new User();
+ updatedUser.user_id = this.selectedRow[i].user_id;
+
+ updatedUser.has_admin_role = true; // Set as admin
+ observableLists.push(this.userService.updateUserRole(updatedUser));
+ }
+ }
+ if (this.ISADMNISTRATOR === 'USER.DISABLE_ADMIN_ACTION') {
+ for (let i = 0; i < this.selectedRow.length; i++) {
+ // Double confirm user is existing
+ if (this.selectedRow[i].user_id === 0 || this.isMySelf(this.selectedRow[i].user_id)) {
+ continue;
+ }
+ let updatedUser: User = new User();
+ updatedUser.user_id = this.selectedRow[i].user_id;
+
+ updatedUser.has_admin_role = false; // Set as none admin
+ observableLists.push(this.userService.updateUserRole(updatedUser));
+ }
+ }
+
+ forkJoin(...observableLists).subscribe(() => {
+ this.selectedRow = [];
+ this.refresh();
+ }, error => {
+ this.selectedRow = [];
+ this.msgHandler.handleError(error);
+ });
+ }
+ }
+
+ // Delete the specified user
+ deleteUsers(users: User[]): void {
+ let userArr: string[] = [];
+ if (this.onlySelf) {
+ return;
+ }
+
+ if (users && users.length) {
+ users.forEach(user => {
+ userArr.push(user.username);
+ });
+ }
+ // Confirm deletion
+ let msg: ConfirmationMessage = new ConfirmationMessage(
+ "USER.DELETION_TITLE",
+ "USER.DELETION_SUMMARY",
+ userArr.join(','),
+ users,
+ ConfirmationTargets.USER,
+ ConfirmationButtons.DELETE_CANCEL
+ );
+ this.deletionDialogService.openComfirmDialog(msg);
+ }
+
+ delUser(users: User[]): void {
+ let observableLists: any[] = [];
+ if (users && users.length) {
+ users.forEach(user => {
+ observableLists.push(this.delOperate(user));
+ });
+
+ forkJoin(...observableLists).subscribe((item) => {
+ this.selectedRow = [];
+ this.currentTerm = '';
+ this.refresh();
+ });
+ }
+ }
+
+ delOperate(user: User): Observable {
+ // init operation info
+ let operMessage = new OperateInfo();
+ operMessage.name = 'OPERATION.DELETE_USER';
+ operMessage.data.id = user.user_id;
+ operMessage.state = OperationState.progressing;
+ operMessage.data.name = user.username;
+ this.operationService.publishInfo(operMessage);
+
+ if (this.isMySelf(user.user_id)) {
+ return this.translate.get('BATCH.DELETED_FAILURE').pipe(map(res => {
+ operateChanges(operMessage, OperationState.failure, res);
+ }));
+ }
+
+ return this.userService.deleteUser(user.user_id).pipe(map(() => {
+ this.translate.get('BATCH.DELETED_SUCCESS').subscribe(res => {
+ operateChanges(operMessage, OperationState.success);
+ });
+ }), catchError(error => {
+ const message = errorHandFn(error);
+ this.translate.get(message).subscribe(res =>
+ operateChanges(operMessage, OperationState.failure, res)
+ );
+ return observableThrowError(message);
+ }));
+ }
+
+ // Refresh the user list
+ refreshUser(): void {
+ this.selectedRow = [];
+ // Start to get
this.currentTerm = '';
+ this.onGoing = true;
+ this.getUserListByPaging();
+ }
+
+ // Add new user
+ addNewUser(): void {
+ if (!this.canCreateUser) {
+ return; // No response to this hacking action
+ }
+ this.newUserDialog.open();
+ }
+
+ // Add user to the user list
+ addUserToList(user: User): void {
+ // Currently we can only add it by reloading all
this.refresh();
- });
- }
- }
-
- delOperate(user: User): Observable {
- // init operation info
- let operMessage = new OperateInfo();
- operMessage.name = 'OPERATION.DELETE_USER';
- operMessage.data.id = user.user_id;
- operMessage.state = OperationState.progressing;
- operMessage.data.name = user.username;
- this.operationService.publishInfo(operMessage);
-
- if (this.isMySelf(user.user_id)) {
- return this.translate.get('BATCH.DELETED_FAILURE').pipe(map(res => {
- operateChanges(operMessage, OperationState.failure, res);
- }));
}
- return this.userService.deleteUser(user.user_id).pipe(map(() => {
- this.translate.get('BATCH.DELETED_SUCCESS').subscribe(res => {
- operateChanges(operMessage, OperationState.success);
- });
- }), catchError(error => {
- const message = errorHandFn(error);
- this.translate.get(message).subscribe(res =>
- operateChanges(operMessage, OperationState.failure, res)
- );
- return observableThrowError(message);
- }));
- }
-
- // Refresh the user list
- refreshUser(from: number, to: number): void {
- this.selectedRow = [];
- // Start to get
- this.currentTerm = '';
- this.onGoing = true;
-
- this.originalUsers = this.userService.getUsers();
- this.originalUsers.subscribe(users => {
- this.onGoing = false;
-
- this.totalCount = users.length;
- this.users = users.slice(from, to); // First page
-
- this.forceRefreshView(5000);
-
- return users;
- }, error => {
- this.onGoing = false;
- this.msgHandler.handleError(error);
- this.forceRefreshView(5000);
- });
- }
-
- // Add new user
- addNewUser(): void {
- if (!this.canCreateUser) {
- return; // No response to this hacking action
+ // Data loading
+ load(state: any): void {
+ this.selectedRow = [];
+ this.onGoing = true;
+ this.getUserListByPaging();
}
- this.newUserDialog.open();
- }
- // Add user to the user list
- addUserToList(user: User): void {
- // Currently we can only add it by reloading all
- this.refresh();
- }
-
- // Data loading
- load(state: any): void {
- this.selectedRow = [];
- if (state && state.page) {
- if (this.originalUsers) {
- this.originalUsers.subscribe(users => {
- this.users = users.slice(state.page.from, state.page.to + 1);
- });
- this.forceRefreshView(5000);
- } else {
- this.refreshUser(state.page.from, state.page.to + 1);
- }
- } else {
- // Refresh
- this.refresh();
+ refresh(): void {
+ this.currentPage = 1; // Refresh pagination
+ this.refreshUser();
}
- }
- refresh(): void {
- this.currentPage = 1; // Refresh pagination
- this.refreshUser(0, 15);
- }
-
- SelectedChange(): void {
- this.forceRefreshView(5000);
- }
-
- forceRefreshView(duration: number): void {
- // Reset timer
- if (this.timerHandler) {
- clearInterval(this.timerHandler);
+ getUserListByPaging() {
+ this.userService.getUserListByPaging(this.currentPage, this.pageSize, this.currentTerm)
+ .subscribe(response => {
+ // Get total count
+ if (response.headers) {
+ let xHeader: string = response.headers.get("X-Total-Count");
+ if (xHeader) {
+ this.totalCount = parseInt(xHeader, 0);
+ }
+ }
+ this.users = response.body as User[];
+ this.onGoing = false;
+ }, error => {
+ this.msgHandler.handleError(error);
+ this.onGoing = false;
+ });
}
- this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
- setTimeout(() => {
- if (this.timerHandler) {
- clearInterval(this.timerHandler);
- this.timerHandler = null;
- }
- }, duration);
- }
-
- private isMatchFilterTerm(terms: string, testedItem: string): boolean {
- return testedItem.toLowerCase().indexOf(terms.toLowerCase()) !== -1;
- }
-
}
diff --git a/src/portal/src/app/user/user.service.ts b/src/portal/src/app/user/user.service.ts
index 27efe88f7..717f1ea3d 100644
--- a/src/portal/src/app/user/user.service.ts
+++ b/src/portal/src/app/user/user.service.ts
@@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
import { map, catchError } from "rxjs/operators";
import { Observable, throwError as observableThrowError } from "rxjs";
-import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "@harbor/ui";
+import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS, buildHttpRequestOptionsWithObserveResponse} from "@harbor/ui";
import { User, LDAPUser } from './user';
import LDAPUsertoUser from './user';
+
const userMgmtEndpoint = '/api/users';
const userListSearch = '/api/users/search?';
const ldapUserEndpoint = '/api/ldap/users';
@@ -34,7 +35,19 @@ const ldapUserEndpoint = '/api/ldap/users';
export class UserService {
constructor(private http: HttpClient) { }
-
+ // Get paging user list
+ getUserListByPaging(page: number, pageSize: number, username?: string) {
+ let params = new HttpParams();
+ if (page && pageSize) {
+ params = params.set('page', page + '').set('page_size', pageSize + '');
+ }
+ if (username) {
+ params = params.set('username', username);
+ }
+ return this.http
+ .get>(userMgmtEndpoint, buildHttpRequestOptionsWithObserveResponse(params)).pipe(
+ catchError(error => observableThrowError(error)), );
+ }
// Handle the related exceptions
handleError(error: any): Observable {
return observableThrowError(error.error || error);