diff --git a/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.html b/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.html index c0092c78bbe..78e563b9d2f 100644 --- a/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.html +++ b/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.html @@ -16,6 +16,84 @@ {{ 'PROJECT_CONFIG.PUBLIC_POLICY' | translate }} + + + + + + + {{ 'PROJECT.PROXY_CACHE_TOOLTIP' | translate }} + + +
+ +
+ + +
+
+ +
+ + +
+ +
+ + {{ 'PROJECT.SPEED_LIMIT_TIP' | translate }} + +
+
+
+ { let fixture: ComponentFixture, component: TestHostComponent; @@ -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, diff --git a/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.ts b/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.ts index 2be4f340772..525d2b83e49 100644 --- a/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.ts +++ b/src/portal/src/app/base/project/project-config/project-policy-config/project-policy-config.component.ts @@ -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'; @@ -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'; @@ -33,6 +39,9 @@ export class ProjectPolicy { PreventVulImgSeverity: string; ScanImgOnPush: boolean; GenerateSbomOnPush: boolean; + ProxyCacheEnabled: boolean; + RegistryId?: number | null; + Bandwidth?: number | null; constructor() { this.Public = false; @@ -42,6 +51,9 @@ export class ProjectPolicy { this.PreventVulImgSeverity = LOW; this.ScanImgOnPush = false; this.GenerateSbomOnPush = false; + this.ProxyCacheEnabled = false; + this.RegistryId = null; + this.Bandwidth = -1; } initByProject(pro: Project) { @@ -55,8 +67,12 @@ 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.Bandwidth = pro.metadata.bandwidth ? pro.metadata.bandwidth : -1; } } +const PAGE_SIZE: number = 100; @Component({ selector: 'hbr-project-policy-config', @@ -65,6 +81,7 @@ export class ProjectPolicy { }) export class ProjectPolicyConfigComponent implements OnInit { onGoing = false; + allowUpdateProxyCacheConfiguration = false; @Input() projectId: number; @Input() projectName = 'unknown'; @Input() isProxyCacheProject: boolean = false; @@ -81,6 +98,7 @@ export class ProjectPolicyConfigComponent implements OnInit { orgProjectPolicy = new ProjectPolicy(); projectPolicy = new ProjectPolicy(); hasChangeConfigRole: boolean; + severityOptions = [ { severity: 'critical', @@ -102,6 +120,20 @@ 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, @@ -109,6 +141,9 @@ export class ProjectPolicyConfigComponent implements OnInit { private projectService: ProjectService, private systemInfoService: SystemInfoService, private userPermission: UserPermissionService, + private session: SessionService, + private messageHandlerService: MessageHandlerService, + private endpointService: RegistryService, private event: EventService ) {} @@ -135,6 +170,74 @@ export class ProjectPolicyConfigComponent implements OnInit { this.retrieve(); this.getPermission(); this.getSystemAllowlist(); + if (this.isSystemAdmin) { + this.getRegistries(); + } + } + + validateBandwidth(): void { + const value = Number(this.projectPolicy.Bandwidth); + 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[] = []; + 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() { @@ -171,6 +274,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 => { diff --git a/src/portal/src/app/base/project/project-config/project-policy-config/project.ts b/src/portal/src/app/base/project/project-config/project-policy-config/project.ts index 8772eab01ad..d8d505fdebc 100644 --- a/src/portal/src/app/base/project/project-config/project-policy-config/project.ts +++ b/src/portal/src/app/base/project/project-config/project-policy-config/project.ts @@ -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; @@ -21,6 +22,7 @@ export class Project { auto_scan: string | boolean; auto_sbom_generation: string | boolean; reuse_sys_cve_allowlist?: string; + bandwidth?: number | null; }; cve_allowlist?: object; constructor() { @@ -30,5 +32,6 @@ export class Project { this.metadata.severity = 'low'; this.metadata.auto_scan = false; this.metadata.auto_sbom_generation = false; + this.metadata.bandwidth = -1; } } diff --git a/src/portal/src/app/shared/services/project.service.ts b/src/portal/src/app/shared/services/project.service.ts index 6b8fa2e74e2..4fa43a961a4 100644 --- a/src/portal/src/app/shared/services/project.service.ts +++ b/src/portal/src/app/shared/services/project.service.ts @@ -144,6 +144,7 @@ export class ProjectDefaultService extends ProjectService { .put( `${baseUrl}/${projectId}`, { + registry_id: projectPolicy.RegistryId, metadata: { public: projectPolicy.Public ? 'true' : 'false', enable_content_trust: projectPolicy.ContentTrust @@ -162,6 +163,7 @@ export class ProjectDefaultService extends ProjectService { ? 'true' : 'false', reuse_sys_cve_allowlist: reuseSysCVEVAllowlist, + proxy_speed_kb: projectPolicy.Bandwidth.toString(), }, cve_allowlist: projectAllowlist, },