Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes-21021 Display proxy cache in project configuration page #21048

Merged
merged 1 commit into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,84 @@
{{ 'PROJECT_CONFIG.PUBLIC_POLICY' | translate }}
</clr-control-helper>
</clr-checkbox-container>
<clr-checkbox-container *ngIf="isSystemAdmin" clrInline>
<label class="label-color-input">
{{ 'PROJECT.PROXY_CACHE' | translate }}
</label>
<clr-checkbox-wrapper id="is-project-proxy-cache-enabled">
<input
type="checkbox"
clrCheckbox
disabled="!allowUpdateProxyCacheConfiguration"
[(ngModel)]="projectPolicy.ProxyCacheEnabled"
name="project-proxy-cache-enabled" />
</clr-checkbox-wrapper>
<clr-control-helper class="config-subtext">
{{ 'PROJECT.PROXY_CACHE_TOOLTIP' | translate }}
</clr-control-helper>
</clr-checkbox-container>
<div
*ngIf="isSystemAdmin && projectPolicy.ProxyCacheEnabled"
class="clr-form-control mt-0">
<label class="clr-control-label"></label>
<div class="clr-select-wrapper row-inline">
<label class="clr-control-label">
{{ 'PROJECT.ENDPOINT' | translate }}
</label>
<select
class="width-164 ml-1"
id="registry"
name="registry"
disabled="!allowUpdateProxyCacheConfiguration"
[(ngModel)]="projectPolicy.RegistryId">
<option class="display-none" value=""></option>
<option *ngFor="let r of registries" [value]="r.id">
{{ r.name }}-{{ r.url }}
</option>
</select>
</div>
<div class="row-inline ml-2" clrInline>
<label class="clr-control-label">
{{ 'PROJECT.BANDWIDTH' | translate }}
</label>
<div
class="clr-control-container ml-1"
[class.clr-error]="bandwidthError">
<input
type="number"
id="bandwidth"
[(ngModel)]="projectPolicy.ProxySpeedKb"
name="bandwidth"
disabled="!allowUpdateProxyCacheConfiguration"
class="clr-input width-164 mr-10 clr-input-underline"
autocomplete="off"
(ngModelChange)="validateBandwidth()" />
<clr-icon
*ngIf="bandwidthError"
class="clr-validate-icon"
shape="exclamation-circle"></clr-icon>
<div class="clr-select-wrapper mr-10 margin-left-05">
<select
id="bandwidth_unit"
name="bandwidth_unit"
disabled="!allowUpdateProxyCacheConfiguration"
[(ngModel)]="speedUnit">
<option
*ngFor="let unit of speedUnits"
[value]="unit.UNIT">
{{ unit.UNIT }}
</option>
</select>
</div>
<clr-control-error
*ngIf="bandwidthError"
class="tooltip-content">
{{ 'PROJECT.SPEED_LIMIT_TIP' | translate }}
</clr-control-error>
</div>
</div>
</div>

<clr-checkbox-container *ngIf="!isProxyCacheProject" clrInline>
<label
><span>{{ 'PROJECT_CONFIG.SECURITY' | translate }}</span></label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,15 @@
.margin-top-05 {
margin-top: 0.5rem;
}

.margin-left-05 {
margin-left: 0.5rem;
}

.row-inline {
display: flex;
}

.row-inline label {
align-self: end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { of } from 'rxjs';
import { SharedTestingModule } from '../../../../shared/shared.module';
import { ErrorHandler } from '../../../../shared/units/error-handler';
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
import { SessionService } from '../../../../shared/services/session.service';
import { Component, ViewChild } from '@angular/core';

const mockSystemInfo: SystemInfo[] = [
Expand Down Expand Up @@ -99,6 +100,14 @@ const userPermissionService = {
return of(true);
},
};

const sessionService = {
getCurrentUser() {
return of({
has_admin_role: true,
});
},
};
describe('ProjectPolicyConfigComponent', () => {
let fixture: ComponentFixture<TestHostComponent>,
component: TestHostComponent;
Expand All @@ -114,6 +123,7 @@ describe('ProjectPolicyConfigComponent', () => {
{ provide: ErrorHandler, useClass: MessageHandlerService },
{ provide: ProjectService, useValue: projectService },
{ provide: SystemInfoService, useValue: systemInfoService },
{ provide: SessionService, useValue: sessionService },
{
provide: UserPermissionService,
useValue: userPermissionService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ProjectService } from '../../../../shared/services';
import { ErrorHandler } from '../../../../shared/units/error-handler';
import { State, SystemCVEAllowlist } from '../../../../shared/services';
import {
BandwidthUnit,
ConfirmationState,
ConfirmationTargets,
} from '../../../../shared/entities/shared.const';
Expand All @@ -15,10 +16,15 @@ import { Project } from './project';
import { SystemInfo, SystemInfoService } from '../../../../shared/services';
import { UserPermissionService } from '../../../../shared/services';
import { USERSTATICPERMISSION } from '../../../../shared/services';
import { SessionService } from '../../../../shared/services/session.service';
import { Registry } from '../../../../../../ng-swagger-gen/models/registry';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import { forkJoin, Observable } from 'rxjs';
import { MessageHandlerService } from 'src/app/shared/services/message-handler.service';
import { RegistryService } from 'ng-swagger-gen/services';

const ONE_THOUSAND: number = 1000;
const LOW: string = 'low';
Expand All @@ -33,6 +39,9 @@ export class ProjectPolicy {
PreventVulImgSeverity: string;
ScanImgOnPush: boolean;
GenerateSbomOnPush: boolean;
ProxyCacheEnabled: boolean;
RegistryId?: number | null;
ProxySpeedKb?: number | null;

constructor() {
this.Public = false;
Expand All @@ -42,6 +51,9 @@ export class ProjectPolicy {
this.PreventVulImgSeverity = LOW;
this.ScanImgOnPush = false;
this.GenerateSbomOnPush = false;
this.ProxyCacheEnabled = false;
this.RegistryId = null;
this.ProxySpeedKb = -1;
}

initByProject(pro: Project) {
Expand All @@ -55,8 +67,14 @@ export class ProjectPolicy {
}
this.ScanImgOnPush = pro.metadata.auto_scan === 'true';
this.GenerateSbomOnPush = pro.metadata.auto_sbom_generation === 'true';
this.ProxyCacheEnabled = pro.registry_id ? true : false;
this.RegistryId = pro.registry_id;
this.ProxySpeedKb = pro.metadata.proxy_speed_kb
? pro.metadata.proxy_speed_kb
: -1;
}
}
const PAGE_SIZE: number = 100;

@Component({
selector: 'hbr-project-policy-config',
Expand All @@ -65,6 +83,7 @@ export class ProjectPolicy {
})
export class ProjectPolicyConfigComponent implements OnInit {
onGoing = false;
allowUpdateProxyCacheConfiguration = false;
@Input() projectId: number;
@Input() projectName = 'unknown';
@Input() isProxyCacheProject: boolean = false;
Expand All @@ -81,6 +100,7 @@ export class ProjectPolicyConfigComponent implements OnInit {
orgProjectPolicy = new ProjectPolicy();
projectPolicy = new ProjectPolicy();
hasChangeConfigRole: boolean;

severityOptions = [
{
severity: 'critical',
Expand All @@ -102,13 +122,30 @@ export class ProjectPolicyConfigComponent implements OnInit {
systemAllowlistOrProjectAllowlistOrigin: string;
projectAllowlist;
projectAllowlistOrigin;
speedUnit = BandwidthUnit.KB;
speedUnits = [
{
UNIT: BandwidthUnit.KB,
},
{
UNIT: BandwidthUnit.MB,
},
];
// **Added property for bandwidth error message**
bandwidthError: string | null = null;
registries: Registry[] = [];
supportedRegistryTypeQueryString: string =
'type={docker-hub harbor azure-acr aws-ecr google-gcr quay docker-registry github-ghcr jfrog-artifactory}';

constructor(
private errorHandler: ErrorHandler,
private translate: TranslateService,
private projectService: ProjectService,
private systemInfoService: SystemInfoService,
private userPermission: UserPermissionService,
private session: SessionService,
private messageHandlerService: MessageHandlerService,
private endpointService: RegistryService,
private event: EventService
) {}

Expand All @@ -135,6 +172,74 @@ export class ProjectPolicyConfigComponent implements OnInit {
this.retrieve();
this.getPermission();
this.getSystemAllowlist();
if (this.isSystemAdmin) {
this.getRegistries();
}
}

validateBandwidth(): void {
const value = Number(this.projectPolicy.ProxySpeedKb);
if (
isNaN(value) ||
(!Number.isInteger(value) && value !== -1) ||
(value <= 0 && value !== -1)
) {
this.bandwidthError =
'Please enter -1 or an integer greater than 0.';
} else {
this.bandwidthError = null;
}
}

getRegistries() {
this.endpointService
.listRegistriesResponse({
page: 1,
pageSize: PAGE_SIZE,
q: this.supportedRegistryTypeQueryString,
})
.subscribe(
result => {
// Get total count
if (result.headers) {
const xHeader: string =
result.headers.get('X-Total-Count');
const totalCount = parseInt(xHeader, 0);
let arr = result.body || [];
if (totalCount <= PAGE_SIZE) {
// already gotten all Registries
this.registries = result.body || [];
} else {
// get all the registries in specified times
const times: number = Math.ceil(
totalCount / PAGE_SIZE
);
const observableList: Observable<Registry[]>[] = [];
for (let i = 2; i <= times; i++) {
observableList.push(
this.endpointService.listRegistries({
page: i,
pageSize: PAGE_SIZE,
q: this
.supportedRegistryTypeQueryString,
})
);
}
forkJoin(observableList).subscribe(res => {
if (res && res.length) {
res.forEach(item => {
arr = arr.concat(item);
});
this.registries = arr;
}
});
}
}
},
error => {
this.messageHandlerService.error(error);
}
);
}

getSystemAllowlist() {
Expand Down Expand Up @@ -171,6 +276,11 @@ export class ProjectPolicyConfigComponent implements OnInit {
});
}

public get isSystemAdmin(): boolean {
let account = this.session.getCurrentUser();
return account != null && account.has_admin_role;
}

retrieve(state?: State): any {
this.projectService.getProject(this.projectId).subscribe(
response => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export class Project {
project_id: number;
owner_id?: number;
registry_id?: number | null;
name: string;
creation_time?: Date | string;
deleted?: number;
Expand All @@ -21,6 +22,7 @@ export class Project {
auto_scan: string | boolean;
auto_sbom_generation: string | boolean;
reuse_sys_cve_allowlist?: string;
proxy_speed_kb?: number | null;
};
cve_allowlist?: object;
constructor() {
Expand All @@ -30,5 +32,6 @@ export class Project {
this.metadata.severity = 'low';
this.metadata.auto_scan = false;
this.metadata.auto_sbom_generation = false;
this.metadata.proxy_speed_kb = -1;
}
}
2 changes: 2 additions & 0 deletions src/portal/src/app/shared/services/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export class ProjectDefaultService extends ProjectService {
.put<any>(
`${baseUrl}/${projectId}`,
{
registry_id: projectPolicy.RegistryId,
metadata: {
public: projectPolicy.Public ? 'true' : 'false',
enable_content_trust: projectPolicy.ContentTrust
Expand All @@ -162,6 +163,7 @@ export class ProjectDefaultService extends ProjectService {
? 'true'
: 'false',
reuse_sys_cve_allowlist: reuseSysCVEVAllowlist,
proxy_speed_kb: projectPolicy.ProxySpeedKb.toString(),
},
cve_allowlist: projectAllowlist,
},
Expand Down
Loading