RGW_ROLE_NAME = 'rgwRoleName'
RGW_ROLE_PATH = 'rgwRolePath'
FILE = 'file'
+ RGW_ROLE_SESSION_DURATION = 'rgwRoleSessionDuration'
class FormField(NamedTuple):
properties[field.key]['title'] = field.name
field_ui_schema['key'] = field_key
field_ui_schema['readonly'] = field.readonly
+ if field.readonly:
+ field_ui_schema['templateOptions'] = {
+ 'disabled': True
+ }
field_ui_schema['help'] = f'{field.help}'
field_ui_schema['validators'] = [i.value for i in field.validators]
items.append(field_ui_schema)
self.forms = []
self.columnKey = ''
self.detail_columns = []
+ self.resource = ''
class CRUDCollectionMethod(NamedTuple):
actions: Optional[List[TableAction]] = None,
permissions: Optional[List[str]] = None, forms: Optional[List[Form]] = None,
column_key: Optional[str] = None,
+ resource: Optional[str] = None,
meta: CRUDMeta = CRUDMeta(), get_all: Optional[CRUDCollectionMethod] = None,
create: Optional[CRUDCollectionMethod] = None,
delete: Optional[CRUDCollectionMethod] = None,
self.detail_columns = detail_columns if detail_columns is not None else []
self.extra_endpoints = extra_endpoints if extra_endpoints is not None else []
self.selection_type = selection_type
+ self.resource = resource
def __call__(self, cls: Any):
self.create_crud_class(cls)
self.generate_forms(model_key)
self.set_permissions()
self.set_column_key()
+ self.set_table_resource()
self.get_detail_columns()
selection_type = self.__class__.outer_self.selection_type
self.__class__.outer_self.meta.table.set_selection_type(selection_type)
if self.__class__.outer_self.column_key:
self.outer_self.meta.columnKey = self.__class__.outer_self.column_key
+ def set_table_resource(self):
+ if self.__class__.outer_self.resource:
+ self.outer_self.meta.resource = self.__class__.outer_self.resource
+
class_name = self.router.path.replace('/', '')
meta_class = type(f'{class_name}_CRUDClassMetadata',
(RESTController,),
'generate_forms': generate_forms,
'set_permissions': set_permissions,
'set_column_key': set_column_key,
+ 'set_table_resource': set_table_resource,
'get_detail_columns': get_detail_columns,
'outer_self': self,
})
TableAction(name='Create', permission='create', icon=Icon.ADD.value,
routerLink='/cluster/user/create'),
TableAction(name='Edit', permission='update', icon=Icon.EDIT.value,
- click='edit'),
+ click='edit', routerLink='/cluster/user/edit'),
TableAction(name='Delete', permission='delete', icon=Icon.DESTROY.value,
click='delete', disable=True),
TableAction(name='Import', permission='create', icon=Icon.IMPORT.value,
permissions=[Scope.CONFIG_OPT],
forms=[create_form, edit_form, import_user_form],
column_key='entity',
+ resource='user',
get_all=CRUDCollectionMethod(
func=CephUserEndpoints.user_list,
doc=EndpointDoc("Get Ceph Users")
# -*- coding: utf-8 -*-
+# pylint: disable=C0302
import json
import logging
import re
rgw_client.create_role(role_name, role_path, role_assume_policy_doc)
return f'Role {role_name} created successfully'
+ @staticmethod
+ def role_update(_, role_name: str, max_session_duration: str):
+ assert role_name
+ assert max_session_duration
+ # convert max_session_duration which is in hours to seconds
+ max_session_duration = int(float(max_session_duration) * 3600)
+ rgw_client = RgwClient.admin_instance()
+ rgw_client.update_role(role_name, str(max_session_duration))
+ return f'Role {role_name} updated successfully'
@staticmethod
def role_delete(_, role_name: str):
rgw_client.delete_role(role_name)
return f'Role {role_name} deleted successfully'
+ @staticmethod
+ def model(role_name: str):
+ assert role_name
+ rgw_client = RgwClient.admin_instance()
+ role = rgw_client.get_role(role_name)
+ model = {'role_name': '', 'max_session_duration': ''}
+ model['role_name'] = role['RoleName']
+ # convert maxsessionduration which is in seconds to hours
+ if role['MaxSessionDuration']:
+ model['max_session_duration'] = role['MaxSessionDuration'] / 3600
+ return model
# pylint: disable=C0301
'target="_blank">click here.</a>'
)
+max_session_duration_help = (
+ 'The maximum session duration (in hours) that you want to set for the specified role.This setting can have a value from 1 hour to 12 hours.' # noqa: E501
+)
+
create_container = VerticalContainer('Create Role', 'create_role', fields=[
FormField('Role name', 'role_name', validators=[Validator.RGW_ROLE_NAME]),
FormField('Path', 'role_path', validators=[Validator.RGW_ROLE_PATH]),
field_type='textarea',
validators=[Validator.JSON]),
])
-create_role_form = Form(path='/rgw/roles/create',
+
+edit_container = VerticalContainer('Edit Role', 'edit_role', fields=[
+ FormField('Role name', 'role_name', readonly=True),
+ FormField('Max Session Duration', 'max_session_duration',
+ help=max_session_duration_help,
+ validators=[Validator.RGW_ROLE_SESSION_DURATION])
+])
+
+create_role_form = Form(path='/create',
root_container=create_container,
task_info=FormTaskInfo("IAM RGW Role '{role_name}' created successfully",
['role_name']),
method_type=MethodType.POST.value)
+edit_role_form = Form(path='/edit',
+ root_container=edit_container,
+ task_info=FormTaskInfo("IAM RGW Role '{role_name}' edited successfully",
+ ['role_name']),
+ method_type=MethodType.PUT.value,
+ model_callback=RGWRoleEndpoints.model)
+
@CRUDEndpoint(
router=APIRouter('/rgw/roles', Scope.RGW),
doc=APIDoc("List of RGW roles", "RGW"),
actions=[
TableAction(name='Create', permission='create', icon=Icon.ADD.value,
+ routerLink='/rgw/roles/create'),
+ TableAction(name='Edit', permission='update', icon=Icon.EDIT.value,
+ click='edit', routerLink='/rgw/roles/edit'),
TableAction(name='Delete', permission='delete', icon=Icon.DESTROY.value,
click='delete', disable=True),
],
- forms=[create_role_form],
- permissions=[Scope.CONFIG_OPT],
+ forms=[create_role_form, edit_role_form],
+ column_key='RoleName',
+ resource='Role',
+ permissions=[Scope.RGW],
get_all=CRUDCollectionMethod(
func=RGWRoleEndpoints.role_list,
doc=EndpointDoc("List RGW roles")
func=RGWRoleEndpoints.role_create,
doc=EndpointDoc("Create RGW role")
),
+ edit=CRUDCollectionMethod(
+ func=RGWRoleEndpoints.role_update,
+ doc=EndpointDoc("Edit RGW role")
+ ),
delete=CRUDCollectionMethod(
func=RGWRoleEndpoints.role_delete,
doc=EndpointDoc("Delete RGW role")
});
describe('Create, Edit & Delete rgw roles', () => {
+ const roleName = 'testRole';
+
it('should create rgw roles', () => {
roles.navigateTo('create');
- roles.create('testRole', '/', '{}');
+ roles.create(roleName, '/', '{}');
roles.navigateTo();
- roles.checkExist('testRole', true);
+ roles.checkExist(roleName, true);
+ });
+
+ it('should edit rgw role', () => {
+ roles.edit(roleName, 3);
+ });
+
+ it('should delete rgw role', () => {
+ roles.delete(roleName);
});
});
});
columnIndex = {
roleName: 2,
path: 3,
- arn: 4
+ arn: 4,
+ createDate: 5,
+ maxSessionDuration: 6
};
@PageHelper.restrictTo(pages.create.url)
create(name: string, path: string, policyDocument: string) {
- cy.get('#formly_3_string_role_name_0').type(name);
- cy.get('#formly_3_textarea_role_assume_policy_doc_2').type(policyDocument);
- cy.get('#formly_3_string_role_path_1').type(path);
+ cy.get('[id$="string_role_name_0"]').type(name);
+ cy.get('[id$="role_assume_policy_doc_2"]').type(policyDocument);
+ cy.get('[id$="role_path_1"]').type(path);
cy.get("[aria-label='Create Role']").should('exist').click();
cy.get('cd-crud-table').should('exist');
}
+ edit(name: string, maxSessionDuration: number) {
+ this.navigateEdit(name);
+ cy.get('[id$="max_session_duration_1"]').clear().type(maxSessionDuration.toString());
+ cy.get("[aria-label='Edit Role']").should('exist').click();
+ cy.get('cd-crud-table').should('exist');
+
+ this.getTableCell(this.columnIndex.roleName, name)
+ .click()
+ .parent()
+ .find(`datatable-body-cell:nth-child(${this.columnIndex.maxSessionDuration})`)
+ .should(($elements) => {
+ const roleName = $elements.map((_, el) => el.textContent).get();
+ expect(roleName).to.include(`${maxSessionDuration} hours`);
+ });
+ }
+
@PageHelper.restrictTo(pages.index.url)
checkExist(name: string, exist: boolean) {
this.getTableCell(this.columnIndex.roleName, name).should(($elements) => {
data: {
breadcrumbs: ActionLabels.CREATE
}
+ },
+ {
+ path: URLVerbs.EDIT,
+ component: CrudFormComponent,
+ data: {
+ breadcrumbs: ActionLabels.EDIT
+ }
}
]
},
private subs = new Subscription();
private rgwUrlPrefix = '/rgw';
private rgwUserUrlPrefix = '/rgw/user';
+ private rgwRoleUrlPrefix = '/rgw/roles';
private rgwBuckerUrlPrefix = '/rgw/bucket';
permissions: Permissions;
featureToggleMap$: FeatureTogglesMap$;
isRgwRoute =
document.location.href.includes(this.rgwUserUrlPrefix) ||
- document.location.href.includes(this.rgwBuckerUrlPrefix);
+ document.location.href.includes(this.rgwBuckerUrlPrefix) ||
+ document.location.href.includes(this.rgwRoleUrlPrefix);
constructor(
private authStorageService: AuthStorageService,
.pipe(filter((event: Event) => event instanceof NavigationEnd))
.subscribe(
() =>
- (this.isRgwRoute = [this.rgwBuckerUrlPrefix, this.rgwUserUrlPrefix].some((urlPrefix) =>
- this.router.url.startsWith(urlPrefix)
- ))
+ (this.isRgwRoute = [
+ this.rgwBuckerUrlPrefix,
+ this.rgwUserUrlPrefix,
+ this.rgwRoleUrlPrefix
+ ].some((urlPrefix) => this.router.url.startsWith(urlPrefix)))
)
);
// Set daemon list polling only when in RGW route:
delete() {
const selectedKey = this.selection.first()[this.meta.columnKey];
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
- itemDescription: $localize`${this.meta.columnKey}`,
+ itemDescription: $localize`${this.meta.resource}`,
itemNames: [selectedKey],
submitAction: () => {
this.taskWrapper
if (this.selection.hasSelection) {
key = this.selection.first()[this.meta.columnKey];
}
- this.router.navigate(['/cluster/user/edit'], { queryParams: { key: key } });
+
+ const editAction = this.meta.actions.find((action) => action.name === 'Edit');
+ this.router.navigate([editAction.routerLink], { queryParams: { key: key } });
}
authExport() {
'Role path must start and finish with a slash "/".' +
' (pattern: (\u002F)|(\u002F[\u0021-\u007E]+\u002F))'
},
- { name: 'file_size', message: 'File size must not exceed 4KiB' }
+ { name: 'file_size', message: 'File size must not exceed 4KiB' },
+ {
+ name: 'rgwRoleSessionDuration',
+ message: 'This field must be a number and should be a value from 1 hour to 12 hour'
+ }
],
wrappers: [{ name: 'input-wrapper', component: FormlyInputWrapperComponent }]
}),
import { forEach } from 'lodash';
import { formlyAsyncFileValidator } from './validators/file-validator';
import { formlyAsyncJsonValidator } from './validators/json-validator';
-import { formlyRgwRoleNameValidator, formlyRgwRolePath } from './validators/rgw-role-validator';
+import {
+ formlyFormNumberValidator,
+ formlyRgwRoleNameValidator,
+ formlyRgwRolePath
+} from './validators/rgw-role-validator';
export function getFieldState(field: FormlyFieldConfig, uiSchema: any[] = undefined) {
const formState: any[] = uiSchema || field.options?.formState;
validators.push(formlyAsyncFileValidator);
break;
}
+ case 'rgwRoleSessionDuration': {
+ validators.push(formlyFormNumberValidator);
+ break;
+ }
}
});
field.asyncValidators = { validation: validators };
resolve({ rgwRoleName: true });
});
}
+
+export function formlyFormNumberValidator(control: AbstractControl): Promise<any> {
+ return new Promise((resolve, _reject) => {
+ if (control.value.match('^[0-9.]+$')) {
+ if (control.value <= 12 && control.value >= 1) resolve(null);
+ }
+ resolve({ rgwRoleSessionDuration: true });
+ });
+}
forms: any;
columnKey: string;
detail_columns: string[];
+ resource: string;
}
trace.
security:
- jwt: []
- summary: Create Ceph User
+ summary: Create RGW role
+ tags:
+ - RGW
+ put:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ max_session_duration:
+ type: string
+ role_name:
+ type: string
+ required:
+ - role_name
+ - max_session_duration
+ type: object
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Resource updated.
+ '202':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Operation is still executing. Please check the task queue.
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ summary: Edit RGW role
+ tags:
+ - RGW
+ /api/rgw/roles/{role_name}:
+ delete:
+ parameters:
+ - in: path
+ name: role_name
+ required: true
+ schema:
+ type: string
+ responses:
+ '202':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Operation is still executing. Please check the task queue.
+ '204':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Resource deleted.
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ summary: Delete RGW role
tags:
- RGW
/api/rgw/site:
'Looks like the document has a wrong format.'
f' For more information about the format look at {link}')
raise DashboardException(msg=msg, component='rgw')
-
+
+ def get_role(self, role_name: str):
+ rgw_get_role_command = ['role', 'get', '--role-name', role_name]
+ code, role, _err = mgr.send_rgwadmin_command(rgw_get_role_command)
+ if code != 0:
+ raise DashboardException(msg=f'Error getting role with code {code}: {_err}',
+ component='rgw')
+ return role
+
+ def update_role(self, role_name: str, max_session_duration: str):
+ rgw_update_role_command = ['role', 'update', '--role-name',
+ role_name, '--max_session_duration', max_session_duration]
+ code, _, _err = mgr.send_rgwadmin_command(rgw_update_role_command,
+ stdout_as_json=False)
+ if code != 0:
+ raise DashboardException(msg=f'Error updating role with code {code}: {_err}',
+ component='rgw')
+
def delete_role(self, role_name: str) -> None:
rgw_delete_role_command = ['role', 'delete', '--role-name', role_name]
code, _, _err = mgr.send_rgwadmin_command(rgw_delete_role_command,