mirror of
https://github.com/ceph/ceph
synced 2025-02-24 19:47:44 +00:00
Merge pull request #58114 from rhcs-dashboard/add-configuration-page-rgw
mgr/dashboard: add a new configuration page in side nav bar Object > Configuration Reviewed-by: Ankush Behl <cloudbehl@gmail.com> Reviewed-by: Nizamudeen A <nia@redhat.com>
This commit is contained in:
commit
7789fcc6c2
@ -31,11 +31,6 @@ describe('RGW buckets page', () => {
|
||||
buckets.delete(bucket_name);
|
||||
});
|
||||
|
||||
it('should check default encryption is SSE-S3', () => {
|
||||
buckets.navigateTo('create');
|
||||
buckets.checkForDefaultEncryption();
|
||||
});
|
||||
|
||||
it('should create bucket with object locking enabled', () => {
|
||||
buckets.navigateTo('create');
|
||||
buckets.create(bucket_name, BucketsPageHelper.USERS[0], true);
|
||||
|
@ -50,14 +50,6 @@ export class BucketsPageHelper extends PageHelper {
|
||||
this.getFirstTableCell(name).should('exist');
|
||||
}
|
||||
|
||||
@PageHelper.restrictTo(pages.create.url)
|
||||
checkForDefaultEncryption() {
|
||||
cy.get("a[aria-label='click here']").click();
|
||||
cy.get('cd-modal').within(() => {
|
||||
cy.get('input[id=s3Enabled]').should('be.checked');
|
||||
});
|
||||
}
|
||||
|
||||
@PageHelper.restrictTo(pages.index.url)
|
||||
edit(name: string, new_owner: string, isLocking = false) {
|
||||
this.navigateEdit(name);
|
||||
|
@ -0,0 +1,36 @@
|
||||
import { ConfigurationPageHelper } from './configuration.po';
|
||||
|
||||
describe('RGW configuration page', () => {
|
||||
const configurations = new ConfigurationPageHelper();
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
configurations.navigateTo();
|
||||
});
|
||||
|
||||
describe('breadcrumb and tab tests', () => {
|
||||
it('should open and show breadcrumb', () => {
|
||||
configurations.expectBreadcrumbText('Configuration');
|
||||
});
|
||||
|
||||
it('should show one tab', () => {
|
||||
configurations.getTabsCount().should('eq', 1);
|
||||
});
|
||||
|
||||
it('should show Server-side Encryption Config list tab at first', () => {
|
||||
configurations.getTabText(0).should('eq', 'Server-side Encryption');
|
||||
});
|
||||
});
|
||||
|
||||
describe('create and edit encryption configuration', () => {
|
||||
it('should create configuration', () => {
|
||||
configurations.create('vault', 'agent', 'transit', 'https://localhost:8080');
|
||||
configurations.getFirstTableCell('SSE_KMS').should('exist');
|
||||
});
|
||||
|
||||
it('should edit configuration', () => {
|
||||
configurations.edit('https://localhost:9090');
|
||||
configurations.getDataTables().should('contain.text', 'https://localhost:9090');
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,52 @@
|
||||
import { PageHelper } from '../page-helper.po';
|
||||
|
||||
export class ConfigurationPageHelper extends PageHelper {
|
||||
pages = {
|
||||
index: { url: '#/rgw/configuration', id: 'cd-rgw-configuration-page' }
|
||||
};
|
||||
|
||||
columnIndex = {
|
||||
address: 4
|
||||
};
|
||||
|
||||
create(provider: string, auth_method: string, secret_engine: string, address: string) {
|
||||
cy.contains('button', 'Create').click();
|
||||
this.selectKmsProvider(provider);
|
||||
cy.get('#kms_provider').should('have.class', 'ng-valid');
|
||||
this.selectAuthMethod(auth_method);
|
||||
cy.get('#auth_method').should('have.class', 'ng-valid');
|
||||
this.selectSecretEngine(secret_engine);
|
||||
cy.get('#secret_engine').should('have.class', 'ng-valid');
|
||||
cy.get('#address').type(address);
|
||||
cy.get('#address').should('have.class', 'ng-valid');
|
||||
cy.contains('button', 'Submit').click();
|
||||
this.getFirstTableCell('SSE_KMS').should('exist');
|
||||
}
|
||||
|
||||
edit(new_address: string) {
|
||||
this.navigateEdit('SSE_KMS', true, false);
|
||||
cy.get('#address').clear().type(new_address);
|
||||
cy.get('#address').should('have.class', 'ng-valid');
|
||||
cy.get('#kms_provider').should('be.disabled');
|
||||
cy.contains('button', 'Submit').click();
|
||||
this.getTableCell(this.columnIndex.address, new_address)
|
||||
.parent()
|
||||
.find(`datatable-body-cell:nth-child(${this.columnIndex.address})`)
|
||||
.should(($elements) => {
|
||||
const address = $elements.text();
|
||||
expect(address).to.eq(new_address);
|
||||
});
|
||||
}
|
||||
|
||||
private selectKmsProvider(provider: string) {
|
||||
return this.selectOption('kms_provider', provider);
|
||||
}
|
||||
|
||||
private selectAuthMethod(auth_method: string) {
|
||||
return this.selectOption('auth_method', auth_method);
|
||||
}
|
||||
|
||||
private selectSecretEngine(secret_engine: string) {
|
||||
return this.selectOption('secret_engine', secret_engine);
|
||||
}
|
||||
}
|
@ -1,7 +1,37 @@
|
||||
export class RgwBucketEncryptionModel {
|
||||
kmsProviders = ['vault'];
|
||||
authMethods = ['token', 'agent'];
|
||||
secretEngines = ['kv', 'transit'];
|
||||
sse_s3 = 'AES256';
|
||||
sse_kms = 'aws:kms';
|
||||
enum KmsProviders {
|
||||
Vault = 'vault'
|
||||
}
|
||||
|
||||
enum AuthMethods {
|
||||
Token = 'token',
|
||||
Agent = 'agent'
|
||||
}
|
||||
|
||||
enum SecretEngines {
|
||||
KV = 'kv',
|
||||
Transit = 'transit'
|
||||
}
|
||||
|
||||
enum sseS3 {
|
||||
SSE_S3 = 'AES256'
|
||||
}
|
||||
|
||||
enum sseKms {
|
||||
SSE_KMS = 'aws:kms'
|
||||
}
|
||||
|
||||
interface RgwBucketEncryptionModel {
|
||||
kmsProviders: KmsProviders[];
|
||||
authMethods: AuthMethods[];
|
||||
secretEngines: SecretEngines[];
|
||||
SSE_S3: sseS3;
|
||||
SSE_KMS: sseKms;
|
||||
}
|
||||
|
||||
export const rgwBucketEncryptionModel: RgwBucketEncryptionModel = {
|
||||
kmsProviders: [KmsProviders.Vault],
|
||||
authMethods: [AuthMethods.Token, AuthMethods.Agent],
|
||||
secretEngines: [SecretEngines.KV, SecretEngines.Transit],
|
||||
SSE_S3: sseS3.SSE_S3,
|
||||
SSE_KMS: sseKms.SSE_KMS
|
||||
};
|
||||
|
@ -296,12 +296,11 @@
|
||||
name="encryption_enabled"
|
||||
formControlName="encryption_enabled"
|
||||
type="checkbox"
|
||||
[attr.disabled]="!kmsVaultConfig && !s3VaultConfig ? true : null"/>
|
||||
[attr.disabled]="!kmsConfigured && !s3Configured ? true : null"/>
|
||||
<cd-help-text aria-label="encryption helper">
|
||||
<span i18n>Enables encryption for the objects in the bucket.
|
||||
To enable encryption on a bucket you need to set the configuration values for SSE-S3 or SSE-KMS.
|
||||
To set the configuration values <a href="#/rgw/bucket/create"
|
||||
(click)="openConfigModal()"
|
||||
To set the configuration values <a href="#/rgw/configuration"
|
||||
aria-label="click here">Click here</a></span>
|
||||
</cd-help-text>
|
||||
</div>
|
||||
@ -317,10 +316,11 @@
|
||||
type="radio"
|
||||
name="encryption_type"
|
||||
value="AES256"
|
||||
[attr.disabled]="!s3VaultConfig ? true : null">
|
||||
[attr.disabled]="!s3Configured ? true : null">
|
||||
<label class="form-control-label"
|
||||
[ngClass]="{'text-muted': !s3Configured}"
|
||||
for="sse_S3_enabled"
|
||||
i18n>SSE-S3 Encryption</label>
|
||||
i18n>SSE-S3</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -333,9 +333,10 @@
|
||||
id="kms_enabled"
|
||||
name="encryption_type"
|
||||
value="aws:kms"
|
||||
[attr.disabled]="!kmsVaultConfig ? true : null"
|
||||
[attr.disabled]="!kmsConfigured ? true : null"
|
||||
type="radio">
|
||||
<label class="form-control-label"
|
||||
[ngClass]="{'text-muted': !kmsConfigured}"
|
||||
for="kms_enabled"
|
||||
i18n>Connect to an external key management service</label>
|
||||
</div>
|
||||
|
@ -25,7 +25,7 @@ import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
|
||||
import { CdValidators } from '~/app/shared/forms/cd-validators';
|
||||
import { ModalService } from '~/app/shared/services/modal.service';
|
||||
import { NotificationService } from '~/app/shared/services/notification.service';
|
||||
import { RgwBucketEncryptionModel } from '../models/rgw-bucket-encryption';
|
||||
import { rgwBucketEncryptionModel } from '../models/rgw-bucket-encryption';
|
||||
import { RgwBucketMfaDelete } from '../models/rgw-bucket-mfa-delete';
|
||||
import {
|
||||
AclPermissionsType,
|
||||
@ -33,7 +33,6 @@ import {
|
||||
RgwBucketAclGrantee as Grantee
|
||||
} from './rgw-bucket-acl-permissions.enum';
|
||||
import { RgwBucketVersioning } from '../models/rgw-bucket-versioning';
|
||||
import { RgwConfigModalComponent } from '../rgw-config-modal/rgw-config-modal.component';
|
||||
import { BucketTagModalComponent } from '../bucket-tag-modal/bucket-tag-modal.component';
|
||||
import { TextAreaJsonFormatterService } from '~/app/shared/services/text-area-json-formatter.service';
|
||||
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
|
||||
@ -44,8 +43,7 @@ import { TextAreaXmlFormatterService } from '~/app/shared/services/text-area-xml
|
||||
@Component({
|
||||
selector: 'cd-rgw-bucket-form',
|
||||
templateUrl: './rgw-bucket-form.component.html',
|
||||
styleUrls: ['./rgw-bucket-form.component.scss'],
|
||||
providers: [RgwBucketEncryptionModel]
|
||||
styleUrls: ['./rgw-bucket-form.component.scss']
|
||||
})
|
||||
export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewChecked {
|
||||
@ViewChild('bucketPolicyTextArea')
|
||||
@ -64,8 +62,8 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC
|
||||
isVersioningAlreadyEnabled = false;
|
||||
isMfaDeleteAlreadyEnabled = false;
|
||||
icons = Icons;
|
||||
kmsVaultConfig = false;
|
||||
s3VaultConfig = false;
|
||||
kmsConfigured = false;
|
||||
s3Configured = false;
|
||||
tags: Record<string, string>[] = [];
|
||||
dirtyTags = false;
|
||||
tagConfig = [
|
||||
@ -97,7 +95,6 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC
|
||||
private modalService: ModalService,
|
||||
private rgwUserService: RgwUserService,
|
||||
private notificationService: NotificationService,
|
||||
private rgwEncryptionModal: RgwBucketEncryptionModel,
|
||||
private textAreaJsonFormatterService: TextAreaJsonFormatterService,
|
||||
private textAreaXmlFormatterService: TextAreaXmlFormatterService,
|
||||
public actionLabels: ActionLabelsI18n,
|
||||
@ -187,15 +184,20 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC
|
||||
)
|
||||
);
|
||||
|
||||
this.kmsProviders = this.rgwEncryptionModal.kmsProviders;
|
||||
this.kmsProviders = rgwBucketEncryptionModel.kmsProviders;
|
||||
this.rgwBucketService.getEncryptionConfig().subscribe((data) => {
|
||||
this.kmsVaultConfig = data[0];
|
||||
this.s3VaultConfig = data[1];
|
||||
if (this.kmsVaultConfig && this.s3VaultConfig) {
|
||||
if (data['SSE_KMS']?.length > 0) {
|
||||
this.kmsConfigured = true;
|
||||
}
|
||||
if (data['SSE_S3']?.length > 0) {
|
||||
this.s3Configured = true;
|
||||
}
|
||||
// Set the encryption type based on the configurations
|
||||
if (this.kmsConfigured && this.s3Configured) {
|
||||
this.bucketForm.get('encryption_type').setValue('');
|
||||
} else if (this.kmsVaultConfig) {
|
||||
} else if (this.kmsConfigured) {
|
||||
this.bucketForm.get('encryption_type').setValue('aws:kms');
|
||||
} else if (this.s3VaultConfig) {
|
||||
} else if (this.s3Configured) {
|
||||
this.bucketForm.get('encryption_type').setValue('AES256');
|
||||
} else {
|
||||
this.bucketForm.get('encryption_type').setValue('');
|
||||
@ -459,13 +461,6 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC
|
||||
this.bucketForm.updateValueAndValidity();
|
||||
}
|
||||
|
||||
openConfigModal() {
|
||||
const modalRef = this.modalService.show(RgwConfigModalComponent, null, { size: 'lg' });
|
||||
modalRef.componentInstance.configForm
|
||||
.get('encryptionType')
|
||||
.setValue(this.bucketForm.getValue('encryption_type') || 'AES256');
|
||||
}
|
||||
|
||||
showTagModal(index?: number) {
|
||||
const modalRef = this.modalService.show(BucketTagModalComponent);
|
||||
const modalComponent = modalRef.componentInstance as BucketTagModalComponent;
|
||||
|
@ -0,0 +1,17 @@
|
||||
<ng-container *ngIf="selection">
|
||||
<nav ngbNav
|
||||
#nav="ngbNav"
|
||||
id="tabset-config-details"
|
||||
class="nav-tabs"
|
||||
cdStatefulTab="config-details">
|
||||
<ng-container ngbNavItem="details">
|
||||
<a ngbNavLink
|
||||
i18n>Details</a>
|
||||
<ng-template ngbNavContent>
|
||||
<cd-table-key-value [data]="transformedData">
|
||||
</cd-table-key-value>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</nav>
|
||||
<div [ngbNavOutlet]="nav"></div>
|
||||
</ng-container>
|
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RgwConfigDetailsComponent } from './rgw-config-details.component';
|
||||
|
||||
describe('RgwConfigDetailsComponent', () => {
|
||||
let component: RgwConfigDetailsComponent;
|
||||
let fixture: ComponentFixture<RgwConfigDetailsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [RgwConfigDetailsComponent]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RgwConfigDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,37 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { rgwEncryptionConfigKeys } from '~/app/shared/models/rgw-encryption-config-keys';
|
||||
|
||||
@Component({
|
||||
selector: 'cd-rgw-config-details',
|
||||
templateUrl: './rgw-config-details.component.html',
|
||||
styleUrls: ['./rgw-config-details.component.scss']
|
||||
})
|
||||
export class RgwConfigDetailsComponent implements OnChanges {
|
||||
transformedData: {};
|
||||
@Input()
|
||||
selection: any;
|
||||
|
||||
@Input()
|
||||
excludeProps: any[] = [];
|
||||
filteredEncryptionConfigValues: {};
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.selection) {
|
||||
this.filteredEncryptionConfigValues = Object.keys(this.selection)
|
||||
.filter((key) => !this.excludeProps.includes(key))
|
||||
.reduce((obj, key) => {
|
||||
obj[key] = this.selection[key];
|
||||
return obj;
|
||||
}, {});
|
||||
const transformedData = {};
|
||||
for (const key in this.filteredEncryptionConfigValues) {
|
||||
if (rgwEncryptionConfigKeys[key]) {
|
||||
transformedData[rgwEncryptionConfigKeys[key]] = this.filteredEncryptionConfigValues[key];
|
||||
} else {
|
||||
transformedData[key] = this.filteredEncryptionConfigValues[key];
|
||||
}
|
||||
}
|
||||
this.transformedData = transformedData;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<cd-modal [modalRef]="activeModal">
|
||||
<ng-container i18n="form title"
|
||||
class="modal-title">Update RGW Encryption Configurations</ng-container>
|
||||
class="modal-title">{{ action | titlecase }} RGW Encryption Configurations</ng-container>
|
||||
|
||||
<ng-container class="modal-content">
|
||||
<form name="configForm"
|
||||
@ -17,10 +17,13 @@
|
||||
id="s3Enabled"
|
||||
type="radio"
|
||||
name="encryptionType"
|
||||
(change)="checkKmsProviders()"
|
||||
[attr.disabled]="editing && configForm.getValue('encryptionType') !== 'AES256' ? true : null"
|
||||
value="AES256">
|
||||
<label class="custom-check-label"
|
||||
[ngClass]="{'text-muted': editing && configForm.getValue('encryptionType') !== 'AES256'}"
|
||||
for="s3Enabled"
|
||||
i18n>SSE-S3 Encryption</label>
|
||||
i18n>SSE-S3</label>
|
||||
</div>
|
||||
|
||||
<div class="col-md-auto custom-checkbox form-check-inline">
|
||||
@ -28,11 +31,14 @@
|
||||
formControlName="encryptionType"
|
||||
id="kmsEnabled"
|
||||
name="encryptionType"
|
||||
(change)="checkKmsProviders()"
|
||||
value="aws:kms"
|
||||
[attr.disabled]="editing && configForm.getValue('encryptionType') !== 'aws:kms' ? true : null"
|
||||
type="radio">
|
||||
<label class="custom-check-label"
|
||||
[ngClass]="{'text-muted': editing && configForm.getValue('encryptionType') !== 'aws:kms'}"
|
||||
for="kmsEnabled"
|
||||
i18n>SSE-KMS Encryption</label>
|
||||
i18n>SSE-KMS</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -46,9 +52,12 @@
|
||||
name="kms_provider"
|
||||
class="form-select"
|
||||
formControlName="kms_provider">
|
||||
<option i18n
|
||||
*ngIf="kmsProviders !== null"
|
||||
[ngValue]="null">-- Select a provider --</option>
|
||||
<option *ngIf="kmsProviders !== null && kmsProviders.length === 0"
|
||||
ngValue="null"
|
||||
i18n>-- No kms providers available --</option>
|
||||
<option *ngIf="kmsProviders !== null && kmsProviders.length > 0"
|
||||
ngValue=""
|
||||
i18n>-- Select a provider --</option>
|
||||
<option *ngFor="let provider of kmsProviders"
|
||||
[value]="provider">{{ provider }}</option>
|
||||
</select>
|
||||
@ -59,168 +68,170 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<div *ngIf="kmsProviders.length !== 0 && configForm.getValue('kms_provider') !== ''">
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="auth_method"
|
||||
i18n>Authentication Method</label>
|
||||
<div class="cd-col-form-input">
|
||||
<select id="auth_method"
|
||||
name="auth_method"
|
||||
class="form-select"
|
||||
formControlName="auth_method">
|
||||
<option *ngFor="let auth_method of authMethods"
|
||||
[value]="auth_method">{{ auth_method }}</option>
|
||||
</select>
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('auth_method', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="secret_engine"
|
||||
i18n>Secret Engine</label>
|
||||
<div class="cd-col-form-input">
|
||||
<select id="secret_engine"
|
||||
name="secret_engine"
|
||||
class="form-select"
|
||||
formControlName="secret_engine">
|
||||
<option *ngFor="let secret_engine of secretEngines"
|
||||
[value]="secret_engine">{{ secret_engine }}</option>
|
||||
</select>
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('secret_engine', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="secret_path"
|
||||
i18n>Secret Path
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input id="secret_path"
|
||||
name="secret_path"
|
||||
class="form-control"
|
||||
type="text"
|
||||
formControlName="secret_path">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('secret_path', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="namespace"
|
||||
i18n>Namespace
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input id="namespace"
|
||||
name="namespace"
|
||||
class="form-control"
|
||||
type="text"
|
||||
formControlName="namespace">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="address"
|
||||
i18n>Vault Address
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input id="address"
|
||||
name="address"
|
||||
class="form-control"
|
||||
formControlName="address"
|
||||
placeholder="http://127.0.0.1:8000">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('address', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('auth_method') === 'token'"
|
||||
class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="auth_method"
|
||||
i18n>Authentication Method</label>
|
||||
<div class="cd-col-form-input">
|
||||
<select id="auth_method"
|
||||
name="auth_method"
|
||||
class="form-select"
|
||||
formControlName="auth_method">
|
||||
<option *ngFor="let auth_method of authMethods"
|
||||
[value]="auth_method">{{ auth_method }}</option>
|
||||
</select>
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('auth_method', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="secret_engine"
|
||||
i18n>Secret Engine</label>
|
||||
<div class="cd-col-form-input">
|
||||
<select id="secret_engine"
|
||||
name="secret_engine"
|
||||
class="form-select"
|
||||
formControlName="secret_engine">
|
||||
<option *ngFor="let secret_engine of secretEngines"
|
||||
[value]="secret_engine">{{ secret_engine }}</option>
|
||||
</select>
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('secret_engine', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="secret_path"
|
||||
i18n>Secret Path
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input id="secret_path"
|
||||
name="secret_path"
|
||||
class="form-control"
|
||||
type="text"
|
||||
formControlName="secret_path">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('secret_path', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="namespace"
|
||||
i18n>Namespace
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input id="namespace"
|
||||
name="namespace"
|
||||
class="form-control"
|
||||
type="text"
|
||||
formControlName="namespace">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="address"
|
||||
i18n>Vault Address
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input id="address"
|
||||
name="address"
|
||||
class="form-control"
|
||||
formControlName="address"
|
||||
placeholder="http://127.0.0.1:8000">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('address', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('auth_method') === 'token'"
|
||||
class="form-group row">
|
||||
<label class="cd-col-form-label required"
|
||||
for="token">
|
||||
<span i18n>Token</span>
|
||||
<cd-helper i18n>
|
||||
The token authentication method expects a Vault token to be present in a plaintext file.
|
||||
</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
formControlName="token"
|
||||
(change)="fileUpload($event.target.files, 'token')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('token', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="ssl_cert">
|
||||
<span i18n>CA Certificate</span>
|
||||
<cd-helper i18n>The SSL certificate in PEM format.</cd-helper>
|
||||
for="token">
|
||||
<span i18n>Token</span>
|
||||
<cd-helper i18n>
|
||||
The token authentication method expects a Vault token to be present in a plaintext file.
|
||||
</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
formControlName="ssl_cert"
|
||||
(change)="fileUpload($event.target.files, 'ssl_cert')">
|
||||
formControlName="token"
|
||||
(change)="fileUpload($event.target.files, 'token')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('ssl_cert', frm, 'required')"
|
||||
*ngIf="configForm.showError('token', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="client_cert">
|
||||
<span i18n>Client Certificate</span>
|
||||
<cd-helper i18n>The Client certificate in PEM format.</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
formControlName="client_cert"
|
||||
(change)="fileUpload($event.target.files, 'client_cert')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('client_cert', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="ssl_cert">
|
||||
<span i18n>CA Certificate</span>
|
||||
<cd-helper i18n>The SSL certificate in PEM format.</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
formControlName="ssl_cert"
|
||||
(change)="fileUpload($event.target.files, 'ssl_cert')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('ssl_cert', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="client_key">
|
||||
<span i18n>Client Private Key</span>
|
||||
<cd-helper i18n>The Client Private Key in PEM format.</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
(change)="fileUpload($event.target.files, 'client_key')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('client_key', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="client_cert">
|
||||
<span i18n>Client Certificate</span>
|
||||
<cd-helper i18n>The Client certificate in PEM format.</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
formControlName="client_cert"
|
||||
(change)="fileUpload($event.target.files, 'client_cert')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('client_cert', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="configForm.getValue('encryptionType') === 'aws:kms' || configForm.getValue('encryptionType') === 'AES256'">
|
||||
<div class="form-group row">
|
||||
<label class="cd-col-form-label"
|
||||
for="client_key">
|
||||
<span i18n>Client Private Key</span>
|
||||
<cd-helper i18n>The Client Private Key in PEM format.</cd-helper>
|
||||
</label>
|
||||
<div class="cd-col-form-input">
|
||||
<input type="file"
|
||||
(change)="fileUpload($event.target.files, 'client_key')">
|
||||
<span class="invalid-feedback"
|
||||
*ngIf="configForm.showError('client_key', frm, 'required')"
|
||||
i18n>This field is required.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,13 +12,12 @@ import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
|
||||
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
|
||||
import { CdValidators } from '~/app/shared/forms/cd-validators';
|
||||
import { NotificationService } from '~/app/shared/services/notification.service';
|
||||
import { RgwBucketEncryptionModel } from '../models/rgw-bucket-encryption';
|
||||
import { rgwBucketEncryptionModel } from '../models/rgw-bucket-encryption';
|
||||
|
||||
@Component({
|
||||
selector: 'cd-rgw-config-modal',
|
||||
templateUrl: './rgw-config-modal.component.html',
|
||||
styleUrls: ['./rgw-config-modal.component.scss'],
|
||||
providers: [RgwBucketEncryptionModel]
|
||||
styleUrls: ['./rgw-config-modal.component.scss']
|
||||
})
|
||||
export class RgwConfigModalComponent implements OnInit {
|
||||
readonly vaultAddress = /^((https?:\/\/)|(www.))(?:([a-zA-Z]+)|(\d+\.\d+.\d+.\d+)):\d{4}$/;
|
||||
@ -32,21 +31,75 @@ export class RgwConfigModalComponent implements OnInit {
|
||||
authMethods: string[];
|
||||
secretEngines: string[];
|
||||
|
||||
selectedEncryptionConfigValues: any = {};
|
||||
allEncryptionConfigValues: any = [];
|
||||
editing = false;
|
||||
action: string;
|
||||
|
||||
constructor(
|
||||
private formBuilder: CdFormBuilder,
|
||||
public activeModal: NgbActiveModal,
|
||||
private router: Router,
|
||||
public actionLabels: ActionLabelsI18n,
|
||||
private rgwBucketService: RgwBucketService,
|
||||
private rgwEncryptionModal: RgwBucketEncryptionModel,
|
||||
private notificationService: NotificationService
|
||||
) {
|
||||
this.createForm();
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.kmsProviders = this.rgwEncryptionModal.kmsProviders;
|
||||
this.authMethods = this.rgwEncryptionModal.authMethods;
|
||||
this.secretEngines = this.rgwEncryptionModal.secretEngines;
|
||||
this.kmsProviders = rgwBucketEncryptionModel.kmsProviders;
|
||||
this.authMethods = rgwBucketEncryptionModel.authMethods;
|
||||
this.secretEngines = rgwBucketEncryptionModel.secretEngines;
|
||||
if (this.editing && this.selectedEncryptionConfigValues) {
|
||||
const patchValues = {
|
||||
address: this.selectedEncryptionConfigValues['addr'],
|
||||
encryptionType:
|
||||
rgwBucketEncryptionModel[this.selectedEncryptionConfigValues['encryption_type']],
|
||||
kms_provider: this.selectedEncryptionConfigValues['backend'],
|
||||
auth_method: this.selectedEncryptionConfigValues['auth'],
|
||||
secret_engine: this.selectedEncryptionConfigValues['secret_engine'],
|
||||
secret_path: this.selectedEncryptionConfigValues['prefix'],
|
||||
namespace: this.selectedEncryptionConfigValues['namespace']
|
||||
};
|
||||
this.configForm.patchValue(patchValues);
|
||||
this.configForm.get('kms_provider').disable();
|
||||
}
|
||||
this.checkKmsProviders();
|
||||
}
|
||||
|
||||
checkKmsProviders() {
|
||||
this.kmsProviders = rgwBucketEncryptionModel.kmsProviders;
|
||||
if (
|
||||
this.allEncryptionConfigValues &&
|
||||
this.allEncryptionConfigValues.hasOwnProperty('SSE_KMS') &&
|
||||
!this.editing
|
||||
) {
|
||||
const sseKmsBackends = this.allEncryptionConfigValues['SSE_KMS'].map(
|
||||
(config: any) => config.backend
|
||||
);
|
||||
if (this.configForm.get('encryptionType').value === rgwBucketEncryptionModel.SSE_KMS) {
|
||||
this.kmsProviders = this.kmsProviders.filter(
|
||||
(provider) => !sseKmsBackends.includes(provider)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
this.allEncryptionConfigValues &&
|
||||
this.allEncryptionConfigValues.hasOwnProperty('SSE_S3') &&
|
||||
!this.editing
|
||||
) {
|
||||
const sseS3Backends = this.allEncryptionConfigValues['SSE_S3'].map(
|
||||
(config: any) => config.backend
|
||||
);
|
||||
if (this.configForm.get('encryptionType').value === rgwBucketEncryptionModel.SSE_S3) {
|
||||
this.kmsProviders = this.kmsProviders.filter(
|
||||
(provider) => !sseS3Backends.includes(provider)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.kmsProviders.length > 0 && !this.kmsProviders.includes('vault')) {
|
||||
this.configForm.get('kms_provider').setValue('');
|
||||
}
|
||||
}
|
||||
|
||||
createForm() {
|
||||
@ -98,7 +151,7 @@ export class RgwConfigModalComponent implements OnInit {
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
const values = this.configForm.value;
|
||||
const values = this.configForm.getRawValue();
|
||||
this.rgwBucketService
|
||||
.setEncryptionConfig(
|
||||
values['encryptionType'],
|
||||
|
@ -0,0 +1,32 @@
|
||||
<nav ngbNav
|
||||
#nav="ngbNav"
|
||||
class="nav-tabs">
|
||||
<ng-container ngbNavItem>
|
||||
<a ngbNavLink
|
||||
i18n>Server-side Encryption</a>
|
||||
<ng-template ngbNavContent>
|
||||
<cd-table #table
|
||||
[data]="encryptionConfigValues"
|
||||
[columns]="columns"
|
||||
identifier="unique_id"
|
||||
[forceIdentifier]="true"
|
||||
[hasDetails]="true"
|
||||
(updateSelection)="updateSelection($event)"
|
||||
(setExpandedRow)="setExpandedRow($event)"
|
||||
columnMode="flex"
|
||||
selectionType="single">
|
||||
<cd-table-actions class="table-actions"
|
||||
[permission]="permissions.configOpt"
|
||||
[selection]="selection"
|
||||
[tableActions]="tableActions">
|
||||
</cd-table-actions>
|
||||
<cd-rgw-config-details cdTableDetail
|
||||
[selection]="expandedRow"
|
||||
[excludeProps]="excludeProps">
|
||||
</cd-rgw-config-details>
|
||||
</cd-table>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</nav>
|
||||
|
||||
<div [ngbNavOutlet]="nav"></div>
|
@ -0,0 +1,28 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RgwConfigurationPageComponent } from './rgw-configuration-page.component';
|
||||
import { NgbActiveModal, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { SharedModule } from '~/app/shared/shared.module';
|
||||
import { RgwModule } from '../rgw.module';
|
||||
|
||||
describe('RgwConfigurationPageComponent', () => {
|
||||
let component: RgwConfigurationPageComponent;
|
||||
let fixture: ComponentFixture<RgwConfigurationPageComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [RgwConfigurationPageComponent],
|
||||
providers: [NgbActiveModal],
|
||||
imports: [HttpClientTestingModule, SharedModule, NgbNavModule, RgwModule]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RgwConfigurationPageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,148 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
|
||||
import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { Permissions } from '~/app/shared/models/permissions';
|
||||
|
||||
import { RgwBucketService } from '~/app/shared/api/rgw-bucket.service';
|
||||
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
|
||||
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
|
||||
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
|
||||
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
|
||||
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
|
||||
import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
|
||||
import { CdTableAction } from '~/app/shared/models/cd-table-action';
|
||||
import { Icons } from '~/app/shared/enum/icons.enum';
|
||||
import { ModalService } from '~/app/shared/services/modal.service';
|
||||
import { RgwConfigModalComponent } from '../rgw-config-modal/rgw-config-modal.component';
|
||||
import { rgwBucketEncryptionModel } from '../models/rgw-bucket-encryption';
|
||||
|
||||
@Component({
|
||||
selector: 'cd-rgw-configuration-page',
|
||||
templateUrl: './rgw-configuration-page.component.html',
|
||||
styleUrls: ['./rgw-configuration-page.component.scss']
|
||||
})
|
||||
export class RgwConfigurationPageComponent extends ListWithDetails implements OnInit {
|
||||
readonly vaultAddress = /^((https?:\/\/)|(www.))(?:([a-zA-Z]+)|(\d+\.\d+.\d+.\d+)):\d{4}$/;
|
||||
|
||||
kmsProviders: string[];
|
||||
|
||||
columns: Array<CdTableColumn> = [];
|
||||
|
||||
configForm: CdFormGroup;
|
||||
permissions: Permissions;
|
||||
encryptionConfigValues: any = [];
|
||||
selection: CdTableSelection = new CdTableSelection();
|
||||
|
||||
@Output()
|
||||
submitAction = new EventEmitter();
|
||||
authMethods: string[];
|
||||
secretEngines: string[];
|
||||
tableActions: CdTableAction[];
|
||||
bsModalRef: NgbModalRef;
|
||||
filteredEncryptionConfigValues: {};
|
||||
excludeProps: any[] = [];
|
||||
disableCreate = false;
|
||||
allEncryptionValues: any;
|
||||
|
||||
constructor(
|
||||
public activeModal: NgbActiveModal,
|
||||
public actionLabels: ActionLabelsI18n,
|
||||
private rgwBucketService: RgwBucketService,
|
||||
public authStorageService: AuthStorageService,
|
||||
private modalService: ModalService
|
||||
) {
|
||||
super();
|
||||
this.permissions = this.authStorageService.getPermissions();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.columns = [
|
||||
{
|
||||
name: $localize`Encryption Type`,
|
||||
prop: 'encryption_type',
|
||||
flexGrow: 1
|
||||
},
|
||||
{
|
||||
name: $localize`Key Management Service Provider`,
|
||||
prop: 'backend',
|
||||
flexGrow: 1
|
||||
},
|
||||
{
|
||||
name: $localize`Address`,
|
||||
prop: 'addr',
|
||||
flexGrow: 1
|
||||
}
|
||||
];
|
||||
this.tableActions = [
|
||||
{
|
||||
permission: 'create',
|
||||
icon: Icons.add,
|
||||
name: this.actionLabels.CREATE,
|
||||
click: () => this.openRgwConfigModal(false),
|
||||
disable: () => this.disableCreate
|
||||
},
|
||||
{
|
||||
permission: 'update',
|
||||
icon: Icons.edit,
|
||||
name: this.actionLabels.EDIT,
|
||||
click: () => this.openRgwConfigModal(true)
|
||||
}
|
||||
];
|
||||
|
||||
this.rgwBucketService.getEncryptionConfig().subscribe((data: any) => {
|
||||
this.allEncryptionValues = data;
|
||||
const allowedBackends = rgwBucketEncryptionModel.kmsProviders;
|
||||
|
||||
const kmsBackends = this.getBackend(data, 'SSE_KMS');
|
||||
const s3Backends = this.getBackend(data, 'SSE_S3');
|
||||
|
||||
const allKmsBackendsPresent = this.areAllAllowedBackendsPresent(allowedBackends, kmsBackends);
|
||||
const allS3BackendsPresent = this.areAllAllowedBackendsPresent(allowedBackends, s3Backends);
|
||||
|
||||
this.disableCreate = allKmsBackendsPresent && allS3BackendsPresent;
|
||||
this.encryptionConfigValues = Object.values(data).flat();
|
||||
});
|
||||
|
||||
this.excludeProps = this.columns.map((column) => column.prop);
|
||||
this.excludeProps.push('unique_id');
|
||||
}
|
||||
|
||||
getBackend(encryptionData: { [x: string]: any[] }, encryptionType: string) {
|
||||
return new Set(encryptionData[encryptionType].map((item) => item.backend));
|
||||
}
|
||||
|
||||
areAllAllowedBackendsPresent(allowedBackends: any[], backendsSet: Set<any>) {
|
||||
return allowedBackends.every((backend) => backendsSet.has(backend));
|
||||
}
|
||||
|
||||
openRgwConfigModal(edit: boolean) {
|
||||
if (edit) {
|
||||
const initialState = {
|
||||
action: 'edit',
|
||||
editing: true,
|
||||
selectedEncryptionConfigValues: this.selection.first()
|
||||
};
|
||||
this.bsModalRef = this.modalService.show(RgwConfigModalComponent, initialState, {
|
||||
size: 'lg'
|
||||
});
|
||||
} else {
|
||||
const initialState = {
|
||||
action: 'create',
|
||||
allEncryptionConfigValues: this.allEncryptionValues
|
||||
};
|
||||
this.bsModalRef = this.modalService.show(RgwConfigModalComponent, initialState, {
|
||||
size: 'lg'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateSelection(selection: CdTableSelection) {
|
||||
this.selection = selection;
|
||||
}
|
||||
|
||||
setExpandedRow(expandedRow: any) {
|
||||
super.setExpandedRow(expandedRow);
|
||||
}
|
||||
}
|
@ -56,6 +56,8 @@ import { NfsListComponent } from '../nfs/nfs-list/nfs-list.component';
|
||||
import { NfsFormComponent } from '../nfs/nfs-form/nfs-form.component';
|
||||
import { RgwMultisiteSyncPolicyComponent } from './rgw-multisite-sync-policy/rgw-multisite-sync-policy.component';
|
||||
import { RgwMultisiteSyncPolicyFormComponent } from './rgw-multisite-sync-policy-form/rgw-multisite-sync-policy-form.component';
|
||||
import { RgwConfigurationPageComponent } from './rgw-configuration-page/rgw-configuration-page.component';
|
||||
import { RgwConfigDetailsComponent } from './rgw-config-details/rgw-config-details.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -116,7 +118,9 @@ import { RgwMultisiteSyncPolicyFormComponent } from './rgw-multisite-sync-policy
|
||||
RgwSyncDataInfoComponent,
|
||||
BucketTagModalComponent,
|
||||
RgwMultisiteSyncPolicyComponent,
|
||||
RgwMultisiteSyncPolicyFormComponent
|
||||
RgwMultisiteSyncPolicyFormComponent,
|
||||
RgwConfigDetailsComponent,
|
||||
RgwConfigurationPageComponent
|
||||
],
|
||||
providers: [TitleCasePipe]
|
||||
})
|
||||
@ -253,6 +257,11 @@ const routes: Routes = [
|
||||
data: { breadcrumbs: ActionLabels.EDIT }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'configuration',
|
||||
data: { breadcrumbs: 'Configuration' },
|
||||
children: [{ path: '', component: RgwConfigurationPageComponent }]
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -229,6 +229,11 @@
|
||||
i18n-title
|
||||
*ngIf="permissions.nfs.read && enabledFeature.nfs"
|
||||
class="tc_submenuitem tc_submenuitem_rgw_nfs"><span i18n>NFS</span></cds-sidenav-item>
|
||||
<cds-sidenav-item route="/rgw/configuration"
|
||||
[useRouter]="true"
|
||||
title="Configuration"
|
||||
i18n-title
|
||||
class="tc_submenuitem tc_submenuitem_rgw_configuration"><span i18n>Configuration</span></cds-sidenav-item>
|
||||
</cds-sidenav-menu>
|
||||
<!-- Filesystem -->
|
||||
<cds-sidenav-menu title="File"
|
||||
|
@ -0,0 +1,21 @@
|
||||
export enum rgwEncryptionConfigKeys {
|
||||
auth = 'Authentication Method',
|
||||
encryption_type = 'Encryption Type',
|
||||
backend = 'Backend',
|
||||
prefix = 'Prefix',
|
||||
namespace = 'Namespace',
|
||||
secret_engine = 'Secret Engine',
|
||||
addr = 'Address',
|
||||
token_file = 'Token File',
|
||||
ssl_cacert = 'SSL CA Certificate',
|
||||
ssl_clientcert = 'SSL Client Certificate',
|
||||
ssl_clientkey = 'SSL Client Key',
|
||||
verify_ssl = 'Verify SSL',
|
||||
ca_path = 'CA Path',
|
||||
client_cert = 'Client Certificate',
|
||||
client_key = 'Client Key',
|
||||
kms_key_template = 'KMS Key Template',
|
||||
password = 'Password',
|
||||
s3_key_template = 'S3 Key Template',
|
||||
username = 'Username'
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
import rados
|
||||
from mgr_module import CommandResult
|
||||
@ -10,7 +11,7 @@ from mgr_util import get_most_recent_rate, get_time_series_rates, name_to_config
|
||||
from .. import mgr
|
||||
|
||||
try:
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
except ImportError:
|
||||
pass # For typing only
|
||||
|
||||
@ -24,6 +25,45 @@ class SendCommandError(rados.Error):
|
||||
super(SendCommandError, self).__init__(err, errno)
|
||||
|
||||
|
||||
class BackendConfig(ABC):
|
||||
@abstractmethod
|
||||
def get_config_keys(self) -> List[str]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_required_keys(self) -> List[str]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_key_pattern(self, enc_type: str) -> str:
|
||||
pass
|
||||
|
||||
|
||||
class VaultConfig(BackendConfig):
|
||||
def get_config_keys(self) -> List[str]:
|
||||
return ['addr', 'auth', 'namespace', 'prefix', 'secret_engine',
|
||||
'token_file', 'ssl_cacert', 'ssl_clientcert', 'ssl_clientkey',
|
||||
'verify_ssl']
|
||||
|
||||
def get_required_keys(self) -> List[str]:
|
||||
return ['auth', 'prefix', 'secret_engine', 'addr']
|
||||
|
||||
def get_key_pattern(self, enc_type: str) -> str:
|
||||
return 'rgw_crypt_{backend}_{key}' if enc_type == 'SSE_KMS' else 'rgw_crypt_sse_s3_{backend}_{key}' # noqa E501 #pylint: disable=line-too-long
|
||||
|
||||
|
||||
class KmipConfig(BackendConfig):
|
||||
def get_config_keys(self) -> List[str]:
|
||||
return ['addr', 'ca_path', 'client_cert', 'client_key', 'kms_key_template',
|
||||
'password', 's3_key_template', 'username']
|
||||
|
||||
def get_required_keys(self) -> List[str]:
|
||||
return ['addr', 'username', 'password']
|
||||
|
||||
def get_key_pattern(self, enc_type: str) -> str:
|
||||
return 'rgw_crypt_{backend}_{key}' if enc_type == 'SSE_KMS' else 'rgw_crypt_sse_s3_{backend}_{key}' # noqa E501 #pylint: disable=line-too-long
|
||||
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
class CephService(object):
|
||||
|
||||
@ -183,64 +223,59 @@ class CephService(object):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_encryption_config(cls, daemon_name):
|
||||
kms_vault_configured = False
|
||||
s3_vault_configured = False
|
||||
kms_backend: str = ''
|
||||
sse_s3_backend: str = ''
|
||||
vault_stats = []
|
||||
def get_encryption_config(cls, daemon_name: str) -> Dict[str, List[Dict[str, Any]]]:
|
||||
# Define backends with their respective configuration classes
|
||||
backends: Dict[str, Dict[str, BackendConfig]] = {
|
||||
'SSE_KMS': {
|
||||
'vault': VaultConfig(),
|
||||
'kmip': KmipConfig()
|
||||
},
|
||||
'SSE_S3': {
|
||||
'vault': VaultConfig()
|
||||
}
|
||||
}
|
||||
|
||||
# Final configuration values
|
||||
config_values: Dict[str, List[Dict[str, Any]]] = {
|
||||
'SSE_KMS': [],
|
||||
'SSE_S3': []
|
||||
}
|
||||
|
||||
full_daemon_name = 'rgw.' + daemon_name
|
||||
|
||||
kms_backend = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name),
|
||||
key='rgw_crypt_s3_kms_backend')
|
||||
sse_s3_backend = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name),
|
||||
key='rgw_crypt_sse_s3_backend')
|
||||
for enc_type, backend_list in backends.items():
|
||||
for backend_name, backend in backend_list.items():
|
||||
config_keys = backend.get_config_keys()
|
||||
required_keys = backend.get_required_keys()
|
||||
key_pattern = backend.get_key_pattern(enc_type)
|
||||
|
||||
if kms_backend.strip() == 'vault':
|
||||
kms_vault_auth: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_vault_auth')
|
||||
kms_vault_engine: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_vault_secret_engine')
|
||||
kms_vault_address: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_vault_addr')
|
||||
kms_vault_token: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_vault_token_file') # noqa E501 #pylint: disable=line-too-long
|
||||
if (kms_vault_auth.strip() != "" and kms_vault_engine.strip() != "" and kms_vault_address.strip() != ""): # noqa E501 #pylint: disable=line-too-long
|
||||
if(kms_vault_auth == 'token' and kms_vault_token.strip() == ""):
|
||||
kms_vault_configured = False
|
||||
else:
|
||||
kms_vault_configured = True
|
||||
# Check if all required configurations are present and not empty
|
||||
all_required_configs_present = True
|
||||
for key in required_keys:
|
||||
config_key = key_pattern.format(backend=backend_name, key=key)
|
||||
value = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name),
|
||||
key=config_key)
|
||||
if not (isinstance(value, str) and value.strip()):
|
||||
all_required_configs_present = False
|
||||
break
|
||||
|
||||
if sse_s3_backend.strip() == 'vault':
|
||||
s3_vault_auth: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_sse_s3_vault_auth')
|
||||
s3_vault_engine: str = CephService.send_command('mon',
|
||||
'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_sse_s3_vault_secret_engine') # noqa E501 #pylint: disable=line-too-long
|
||||
s3_vault_address: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_sse_s3_vault_addr')
|
||||
s3_vault_token: str = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key='rgw_crypt_sse_s3_vault_token_file') # noqa E501 #pylint: disable=line-too-long
|
||||
# If all required configurations are present, gather all config values
|
||||
if all_required_configs_present:
|
||||
config_dict = {}
|
||||
for key in config_keys:
|
||||
config_key = key_pattern.format(backend=backend_name, key=key)
|
||||
value = CephService.send_command('mon', 'config get',
|
||||
who=name_to_config_section(full_daemon_name), # noqa E501 #pylint: disable=line-too-long
|
||||
key=config_key)
|
||||
if value:
|
||||
config_dict[key] = value.strip() if isinstance(value, str) else value
|
||||
config_dict['backend'] = backend_name
|
||||
config_dict['encryption_type'] = enc_type
|
||||
config_dict['unique_id'] = enc_type + '-' + backend_name
|
||||
config_values[enc_type].append(config_dict)
|
||||
|
||||
if (s3_vault_auth.strip() != "" and s3_vault_engine.strip() != "" and s3_vault_address.strip() != ""): # noqa E501 #pylint: disable=line-too-long
|
||||
if(s3_vault_auth == 'token' and s3_vault_token.strip() == ""):
|
||||
s3_vault_configured = False
|
||||
else:
|
||||
s3_vault_configured = True
|
||||
|
||||
vault_stats.append(kms_vault_configured)
|
||||
vault_stats.append(s3_vault_configured)
|
||||
return vault_stats
|
||||
return config_values
|
||||
|
||||
@classmethod
|
||||
def set_encryption_config(cls, encryption_type, kms_provider, auth_method,
|
||||
|
Loading…
Reference in New Issue
Block a user