mirror of
https://github.com/goharbor/harbor.git
synced 2025-03-13 15:09:29 +01:00
Improve repo_read_only header on the UI (#18729)
1. Fixes #18694 2. Now non-system-admin users can also see the repo_read_only header Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
43c6586af4
commit
c399e5ee4a
@ -224,7 +224,9 @@
|
|||||||
type="button"
|
type="button"
|
||||||
id="submit-btn"
|
id="submit-btn"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
[disabled]="!isValid || showProgress || !isUserDataChange()"
|
[disabled]="
|
||||||
|
!isValid || showProgress || !isUserDataChange() || checkProgress
|
||||||
|
"
|
||||||
(click)="submit()">
|
(click)="submit()">
|
||||||
{{ 'BUTTON.OK' | translate }}
|
{{ 'BUTTON.OK' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -18,6 +18,7 @@ import { InlineAlertComponent } from '../../shared/components/inline-alert/inlin
|
|||||||
import { ConfirmationDialogService } from '../global-confirmation-dialog/confirmation-dialog.service';
|
import { ConfirmationDialogService } from '../global-confirmation-dialog/confirmation-dialog.service';
|
||||||
import { ConfirmationMessage } from '../global-confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../global-confirmation-dialog/confirmation-message';
|
||||||
import { UserService } from '../../../../ng-swagger-gen/services/user.service';
|
import { UserService } from '../../../../ng-swagger-gen/services/user.service';
|
||||||
|
import { AppConfigService } from '../../services/app-config.service';
|
||||||
|
|
||||||
describe('AccountSettingsModalComponent', () => {
|
describe('AccountSettingsModalComponent', () => {
|
||||||
let component: AccountSettingsModalComponent;
|
let component: AccountSettingsModalComponent;
|
||||||
@ -74,6 +75,12 @@ describe('AccountSettingsModalComponent', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MockedAppConfigService = {
|
||||||
|
getConfig() {
|
||||||
|
return { self_registration: true };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -110,6 +117,7 @@ describe('AccountSettingsModalComponent', () => {
|
|||||||
provide: ConfirmationDialogService,
|
provide: ConfirmationDialogService,
|
||||||
useValue: fakeConfirmationDialogService,
|
useValue: fakeConfirmationDialogService,
|
||||||
},
|
},
|
||||||
|
{ provide: AppConfigService, useValue: MockedAppConfigService },
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@ -30,6 +30,7 @@ import { ConfirmationDialogComponent } from '../../shared/components/confirmatio
|
|||||||
import { InlineAlertComponent } from '../../shared/components/inline-alert/inline-alert.component';
|
import { InlineAlertComponent } from '../../shared/components/inline-alert/inline-alert.component';
|
||||||
import { ConfirmationMessage } from '../global-confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../global-confirmation-dialog/confirmation-message';
|
||||||
import { UserService } from 'ng-swagger-gen/services/user.service';
|
import { UserService } from 'ng-swagger-gen/services/user.service';
|
||||||
|
import { AppConfigService } from '../../services/app-config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'account-settings-modal',
|
selector: 'account-settings-modal',
|
||||||
@ -72,7 +73,8 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
|
|||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private searchTrigger: SearchTriggerService,
|
private searchTrigger: SearchTriggerService,
|
||||||
private userService: UserService
|
private userService: UserService,
|
||||||
|
private appConfigService: AppConfigService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private validationStateMap: any = {
|
private validationStateMap: any = {
|
||||||
@ -136,29 +138,34 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mail changed
|
// Mail changed, if self-registration disabled, only system admin can check mail-existing status
|
||||||
this.checkOnGoing = true;
|
if (
|
||||||
this.session
|
this.session.getCurrentUser()?.has_admin_role ||
|
||||||
.checkUserExisting('email', this.account.email)
|
this.appConfigService.getConfig()?.self_registration
|
||||||
.subscribe(
|
) {
|
||||||
(res: boolean) => {
|
this.checkOnGoing = true;
|
||||||
this.checkOnGoing = false;
|
this.session
|
||||||
this.validationStateMap[key] = !res;
|
.checkUserExisting('email', this.account.email)
|
||||||
if (res) {
|
.subscribe(
|
||||||
this.emailTooltip =
|
(res: boolean) => {
|
||||||
'TOOLTIP.EMAIL_EXISTING';
|
this.checkOnGoing = false;
|
||||||
|
this.validationStateMap[key] = !res;
|
||||||
|
if (res) {
|
||||||
|
this.emailTooltip =
|
||||||
|
'TOOLTIP.EMAIL_EXISTING';
|
||||||
|
}
|
||||||
|
this.mailAlreadyChecked[
|
||||||
|
this.account.email
|
||||||
|
] = {
|
||||||
|
result: res,
|
||||||
|
}; // Tag it checked
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.checkOnGoing = false;
|
||||||
|
this.validationStateMap[key] = false; // Not valid @ backend
|
||||||
}
|
}
|
||||||
this.mailAlreadyChecked[
|
);
|
||||||
this.account.email
|
}
|
||||||
] = {
|
|
||||||
result: res,
|
|
||||||
}; // Tag it checked
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this.checkOnGoing = false;
|
|
||||||
this.validationStateMap[key] = false; // Not valid @ backend
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<clr-alerts>
|
<clr-alerts>
|
||||||
<clr-alert
|
<clr-alert
|
||||||
*ngIf="showReadOnly && isLogin()"
|
*ngIf="showReadOnly && isLogin()"
|
||||||
[clrAlertType]="message?.type"
|
[clrAlertType]="'warning'"
|
||||||
[clrAlertAppLevel]="true"
|
[clrAlertAppLevel]="true"
|
||||||
[clrAlertClosable]="false">
|
[clrAlertClosable]="false">
|
||||||
<clr-alert-item>
|
<clr-alert-item>
|
||||||
<span class="alert-text">{{ message?.message | translate }}</span>
|
<span class="alert-text">{{ 'REPO_READ_ONLY' | translate }}</span>
|
||||||
</clr-alert-item>
|
</clr-alert-item>
|
||||||
</clr-alert>
|
</clr-alert>
|
||||||
<clr-alert
|
<clr-alert
|
||||||
|
@ -7,6 +7,7 @@ import { delay } from 'rxjs/operators';
|
|||||||
import { Scanner } from '../../left-side-nav/interrogation-services/scanner/scanner';
|
import { Scanner } from '../../left-side-nav/interrogation-services/scanner/scanner';
|
||||||
import { ScannerService } from 'ng-swagger-gen/services/scanner.service';
|
import { ScannerService } from 'ng-swagger-gen/services/scanner.service';
|
||||||
import { SessionService } from 'src/app/shared/services/session.service';
|
import { SessionService } from 'src/app/shared/services/session.service';
|
||||||
|
import { AppConfigService } from '../../../services/app-config.service';
|
||||||
|
|
||||||
describe('AppLevelAlertsComponent', () => {
|
describe('AppLevelAlertsComponent', () => {
|
||||||
let component: AppLevelAlertsComponent;
|
let component: AppLevelAlertsComponent;
|
||||||
@ -44,6 +45,11 @@ describe('AppLevelAlertsComponent', () => {
|
|||||||
return { has_admin_role: true };
|
return { has_admin_role: true };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const MockedAppConfigService = {
|
||||||
|
getConfig() {
|
||||||
|
return { read_only: false };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
@ -55,6 +61,7 @@ describe('AppLevelAlertsComponent', () => {
|
|||||||
useValue: fakeScannerService,
|
useValue: fakeScannerService,
|
||||||
},
|
},
|
||||||
{ provide: SessionService, useValue: fakeSessionService },
|
{ provide: SessionService, useValue: fakeSessionService },
|
||||||
|
{ provide: AppConfigService, useValue: MockedAppConfigService },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { MessageService } from '../../../shared/components/global-message/message.service';
|
import { MessageService } from '../../../shared/components/global-message/message.service';
|
||||||
import { Message } from '../../../shared/components/global-message/message';
|
import { Message } from '../../../shared/components/global-message/message';
|
||||||
import { JobServiceDashboardHealthCheckService } from '../../left-side-nav/job-service-dashboard/job-service-dashboard-health-check.service';
|
import { JobServiceDashboardHealthCheckService } from '../../left-side-nav/job-service-dashboard/job-service-dashboard-health-check.service';
|
||||||
import { AppLevelMessage } from '../../../shared/services/message-handler.service';
|
import { AppConfigService } from '../../../services/app-config.service';
|
||||||
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
||||||
const YES: string = 'yes';
|
const YES: string = 'yes';
|
||||||
@Component({
|
@Component({
|
||||||
@ -29,14 +29,14 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
|
|||||||
appLevelMsgSub: Subscription;
|
appLevelMsgSub: Subscription;
|
||||||
clearSub: Subscription;
|
clearSub: Subscription;
|
||||||
showLogin: boolean = false;
|
showLogin: boolean = false;
|
||||||
showReadOnly: boolean = false;
|
|
||||||
constructor(
|
constructor(
|
||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private scannerService: ScannerService,
|
private scannerService: ScannerService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private jobServiceDashboardHealthCheckService: JobServiceDashboardHealthCheckService
|
private jobServiceDashboardHealthCheckService: JobServiceDashboardHealthCheckService,
|
||||||
|
private appConfigService: AppConfigService
|
||||||
) {}
|
) {}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (
|
if (
|
||||||
@ -53,10 +53,6 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
|
|||||||
this.appLevelMsgSub =
|
this.appLevelMsgSub =
|
||||||
this.messageService.appLevelAnnounced$.subscribe(message => {
|
this.messageService.appLevelAnnounced$.subscribe(message => {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.showReadOnly =
|
|
||||||
message.statusCode === httpStatusCode.AppLevelWarning &&
|
|
||||||
message.message === AppLevelMessage.REPO_READ_ONLY;
|
|
||||||
|
|
||||||
if (message.statusCode === httpStatusCode.Unauthorized) {
|
if (message.statusCode === httpStatusCode.Unauthorized) {
|
||||||
this.showLogin = true;
|
this.showLogin = true;
|
||||||
// User session timed out, then redirect to sign-in page
|
// User session timed out, then redirect to sign-in page
|
||||||
@ -86,7 +82,6 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
|
|||||||
if (!this.clearSub) {
|
if (!this.clearSub) {
|
||||||
this.clearSub = this.messageService.clearChan$.subscribe(clear => {
|
this.clearSub = this.messageService.clearChan$.subscribe(clear => {
|
||||||
this.showLogin = false;
|
this.showLogin = false;
|
||||||
this.showReadOnly = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +91,9 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
|
|||||||
this.appLevelMsgSub = null;
|
this.appLevelMsgSub = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
get showReadOnly(): boolean {
|
||||||
|
return this.appConfigService.getConfig()?.read_only;
|
||||||
|
}
|
||||||
shouldShowScannerInfo(): boolean {
|
shouldShowScannerInfo(): boolean {
|
||||||
return (
|
return (
|
||||||
this.session.getCurrentUser()?.has_admin_role &&
|
this.session.getCurrentUser()?.has_admin_role &&
|
||||||
@ -186,6 +183,6 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isLogin(): boolean {
|
isLogin(): boolean {
|
||||||
return this.session.getCurrentUser()?.has_admin_role;
|
return !!this.session.getCurrentUser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,12 +139,6 @@ export class SystemSettingsComponent implements OnInit {
|
|||||||
// Unfortunately API does not do that
|
// Unfortunately API does not do that
|
||||||
// So we need to call update function again
|
// So we need to call update function again
|
||||||
this.conf.updateConfig();
|
this.conf.updateConfig();
|
||||||
// Handle read only
|
|
||||||
if (changes['read_only']) {
|
|
||||||
this.errorHandler.handleReadOnly();
|
|
||||||
} else {
|
|
||||||
this.errorHandler.clear();
|
|
||||||
}
|
|
||||||
// Reload bootstrap option
|
// Reload bootstrap option
|
||||||
this.appConfigService.load().subscribe(
|
this.appConfigService.load().subscribe(
|
||||||
() => {},
|
() => {},
|
||||||
|
@ -84,9 +84,6 @@ export class NavigatorComponent implements OnInit {
|
|||||||
this.translateClarityComponents();
|
this.translateClarityComponents();
|
||||||
}
|
}
|
||||||
this.selectedDatetimeRendering = getDatetimeRendering();
|
this.selectedDatetimeRendering = getDatetimeRendering();
|
||||||
if (this.appConfigService.getConfig().read_only) {
|
|
||||||
this.msgHandler.handleReadOnly();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//Internationalization for Clarity components, refer to https://clarity.design/documentation/internationalization
|
//Internationalization for Clarity components, refer to https://clarity.design/documentation/internationalization
|
||||||
translateClarityComponents() {
|
translateClarityComponents() {
|
||||||
|
@ -44,13 +44,6 @@ export class AuthCheckGuard {
|
|||||||
): Observable<boolean> | boolean {
|
): Observable<boolean> | boolean {
|
||||||
// When routing change, clear
|
// When routing change, clear
|
||||||
this.msgHandler.clear();
|
this.msgHandler.clear();
|
||||||
if (
|
|
||||||
this.appConfigService.getConfig().read_only &&
|
|
||||||
this.appConfigService.getConfig().read_only.toString() === 'true'
|
|
||||||
) {
|
|
||||||
this.msgHandler.handleReadOnly();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.searchTrigger.closeSearch(true);
|
this.searchTrigger.closeSearch(true);
|
||||||
return new Observable(observer => {
|
return new Observable(observer => {
|
||||||
// if the url has the queryParam `publicAndNotLogged=yes`, then skip auth check
|
// if the url has the queryParam `publicAndNotLogged=yes`, then skip auth check
|
||||||
|
@ -72,14 +72,6 @@ export class MessageHandlerService implements ErrorHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleReadOnly(): void {
|
|
||||||
this.msgService.announceAppLevelMessage(
|
|
||||||
httpStatusCode.AppLevelWarning,
|
|
||||||
AppLevelMessage.REPO_READ_ONLY,
|
|
||||||
AlertType.WARNING
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public showError(message: string, params: any): void {
|
public showError(message: string, params: any): void {
|
||||||
if (!params) {
|
if (!params) {
|
||||||
params = {};
|
params = {};
|
||||||
@ -131,7 +123,3 @@ export class MessageHandlerService implements ErrorHandler {
|
|||||||
this.showInfo(log);
|
this.showInfo(log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AppLevelMessage {
|
|
||||||
REPO_READ_ONLY = 'REPO_READ_ONLY',
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user