Skip to content

Commit

Permalink
Admin - Model Registry RBAC Management Projects
Browse files Browse the repository at this point in the history
  • Loading branch information
ppadti committed Aug 2, 2024
1 parent 3aeb340 commit 4a1539d
Show file tree
Hide file tree
Showing 8 changed files with 440 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,35 @@ import { TableRow } from './components/table';

class PermissionsTableRow extends TableRow {}

class UsersTab {
class UsersTab extends Contextual<HTMLElement> {
findAddUserButton() {
return this.find().findByTestId('add-button User');
}

findAddGroupButton() {
return this.find().findByTestId('add-button Group');
}

getUserTable() {
return new PermissionTable(() => this.find().findByTestId('role-binding-table User'));
}

getGroupTable() {
return new PermissionTable(() => this.find().findByTestId('role-binding-table Group'));
}
}

class ProjectsTab extends Contextual<HTMLElement> {
findAddProjectButton() {
return this.find().findByTestId('add-project-button');
}

getProjectTable() {
return new PermissionTable(() => this.find().findByTestId('role-binding-table Group'));
}
}

class MRPermissions {
visit(mrName: string, wait = true) {
cy.visitWithLogin(`/modelRegistrySettings/permissions/${mrName}`);
if (wait) {
Expand All @@ -16,20 +44,16 @@ class UsersTab {
cy.testA11y();
}

findAddUserButton() {
return cy.findByTestId('add-button User');
}

findAddGroupButton() {
return cy.findByTestId('add-button Group');
findProjectTab() {
return cy.findByTestId('projects-tab');
}

getUserTable() {
return new PermissionTable(() => cy.findByTestId('role-binding-table User'));
getUsersContent() {
return new UsersTab(() => cy.findByTestId('users-tab-content'));
}

getGroupTable() {
return new PermissionTable(() => cy.findByTestId('role-binding-table Group'));
getProjectsContent() {
return new ProjectsTab(() => cy.findByTestId('projects-tab-content'));
}
}

Expand All @@ -46,7 +70,7 @@ class PermissionTable extends Contextual<HTMLElement> {
return this.find().findByTestId(['role-binding-name-input', id]);
}

findGroupSelect() {
findNameSelect() {
return this.find().get(`[aria-label="Name selection"]`);
}

Expand All @@ -69,4 +93,4 @@ class PermissionTable extends Contextual<HTMLElement> {
}
}

export const usersTab = new UsersTab();
export const modelRegistryPermissions = new MRPermissions();
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { mockK8sResourceList } from '~/__mocks__';
import { mockK8sResourceList, mockProjectK8sResource } from '~/__mocks__';
import { mock200Status } from '~/__mocks__/mockK8sStatus';
import { mockRoleBindingK8sResource } from '~/__mocks__/mockRoleBindingK8sResource';
import { be } from '~/__tests__/cypress/cypress/utils/should';
import {
GroupModel,
ModelRegistryModel,
ProjectModel,
RoleBindingModel,
} from '~/__tests__/cypress/cypress/utils/models';
import type { RoleBindingSubject } from '~/k8sTypes';
import { asProductAdminUser, asProjectEditUser } from '~/__tests__/cypress/cypress/utils/mockUsers';
import { mockModelRegistry } from '~/__mocks__/mockModelRegistry';
import { mockGroup } from '~/__mocks__/mockGroup';
import { usersTab } from '~/__tests__/cypress/cypress/pages/modelRegistryPermissions';
import { modelRegistryPermissions } from '~/__tests__/cypress/cypress/pages/modelRegistryPermissions';

const MODEL_REGISTRY_DEFAULT_NAMESPACE = 'odh-model-registries';

Expand All @@ -31,6 +32,14 @@ const groupSubjects: RoleBindingSubject[] = [
},
];

const projectSubjects: RoleBindingSubject[] = [
{
kind: 'Group',
apiGroup: 'rbac.authorization.k8s.io',
name: 'system:serviceaccounts:projectName',
},
];

type HandlersProps = {
isEmpty?: boolean;
hasPermission?: boolean;
Expand All @@ -52,6 +61,7 @@ const initIntercepts = ({ isEmpty = false, hasPermission = true }: HandlersProps
{ model: GroupModel },
mockK8sResourceList([mockGroup({ name: 'example-mr-group-option' })]),
);
cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProjectK8sResource({})]));
cy.interceptK8sList(
{ model: RoleBindingModel, ns: MODEL_REGISTRY_DEFAULT_NAMESPACE },
mockK8sResourceList(
Expand Down Expand Up @@ -86,31 +96,40 @@ const initIntercepts = ({ isEmpty = false, hasPermission = true }: HandlersProps
roleRefName: 'registry-user-example-mr',
modelRegistryName: 'example-mr',
}),
mockRoleBindingK8sResource({
namespace: MODEL_REGISTRY_DEFAULT_NAMESPACE,
subjects: projectSubjects,
roleRefName: 'registry-user-example-mr',
modelRegistryName: 'example-mr',
}),
],
),
);
};

describe('MR Permissions', () => {
const usersTab = modelRegistryPermissions.getUsersContent();
const projectsTab = modelRegistryPermissions.getProjectsContent();
const userTable = usersTab.getUserTable();
const groupTable = usersTab.getGroupTable();
const projectTable = projectsTab.getProjectTable();

it('should not be accessible for non-project admins', () => {
initIntercepts({ isEmpty: false, hasPermission: false });
usersTab.visit('example-mr', false);
modelRegistryPermissions.visit('example-mr', false);
cy.findByTestId('not-found-page').should('exist');
});

it('redirect if no modelregistry', () => {
initIntercepts({ isEmpty: true });
usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');
cy.url().should('eq', `${Cypress.config().baseUrl}/modelRegistrySettings`);
});

describe('Users table', () => {
it('Table sorting for users table', () => {
initIntercepts({ isEmpty: false });
usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');
userTable.findRows().should('have.length', 2);

// by name
Expand Down Expand Up @@ -139,7 +158,7 @@ describe('MR Permissions', () => {
modelRegistryName: 'example-mr',
}),
).as('addUser');
usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');

usersTab.findAddUserButton().click();

Expand Down Expand Up @@ -189,7 +208,7 @@ describe('MR Permissions', () => {
mock200Status({}),
).as('deleteUser');

usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');

userTable.getTableRow('example-mr-user').findKebabAction('Edit').click();
userTable.findEditInput('example-mr-user').clear().type('edited-user');
Expand Down Expand Up @@ -226,7 +245,7 @@ describe('MR Permissions', () => {
{ model: RoleBindingModel, ns: MODEL_REGISTRY_DEFAULT_NAMESPACE, name: 'example-mr-user' },
mock200Status({}),
).as('deleteUser');
usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');

userTable.getTableRow('example-mr-user').findKebabAction('Delete').click();

Expand All @@ -238,7 +257,7 @@ describe('MR Permissions', () => {
it('Table sorting for groups table', () => {
initIntercepts({ isEmpty: false });

usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');

groupTable.findTableHeaderButton('Name').click();
groupTable.findTableHeaderButton('Name').should(be.sortDescending);
Expand All @@ -264,11 +283,11 @@ describe('MR Permissions', () => {
modelRegistryName: 'example-mr',
}),
).as('addGroup');
usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');

usersTab.findAddGroupButton().click();

groupTable.findGroupSelect().fill('new-example-mr-group');
groupTable.findNameSelect().fill('new-example-mr-group');
cy.findByText('Create "new-example-mr-group"').click();
groupTable.findSaveNewButton().click();

Expand Down Expand Up @@ -319,10 +338,10 @@ describe('MR Permissions', () => {
mock200Status({}),
).as('deleteGroup');

usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');

groupTable.getTableRow('example-mr-users-2').findKebabAction('Edit').click();
groupTable.findGroupSelect().clear().type('example-mr-group-opti');
groupTable.findNameSelect().clear().type('example-mr-group-opti');
cy.findByText('example-mr-group-option').click();
groupTable.findEditSaveButton('example-mr-group-option').click();

Expand Down Expand Up @@ -368,17 +387,143 @@ describe('MR Permissions', () => {
mock200Status({}),
).as('deleteGroup');

usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');
groupTable.getTableRow('example-mr-users-2').findKebabAction('Delete').click();

cy.wait('@deleteGroup');
});

it('Disabled actions on default group', () => {
initIntercepts({ isEmpty: false });
usersTab.visit('example-mr');
modelRegistryPermissions.visit('example-mr');
groupTable.getTableRow('example-mr-users').findKebab().should('be.disabled');
groupTable.getTableRow('example-mr-users-2').findKebab().should('not.be.disabled');
});
});

describe('Projects table', () => {
beforeEach(() => {
initIntercepts({ isEmpty: false });
modelRegistryPermissions.visit('example-mr');
modelRegistryPermissions.findProjectTab().click();
});

it('table sorting', () => {
// by name
projectTable.findTableHeaderButton('Name').click();
projectTable.findTableHeaderButton('Name').should(be.sortDescending);
projectTable.findTableHeaderButton('Name').click();
projectTable.findTableHeaderButton('Name').should(be.sortAscending);
//by permissions
projectTable.findTableHeaderButton('Permission').click();
projectTable.findTableHeaderButton('Permission').should(be.sortAscending);
projectTable.findTableHeaderButton('Permission').click();
projectTable.findTableHeaderButton('Permission').should(be.sortDescending);
//by date added
projectTable.findTableHeaderButton('Date added').click();
projectTable.findTableHeaderButton('Date added').should(be.sortAscending);
projectTable.findTableHeaderButton('Date added').click();
projectTable.findTableHeaderButton('Date added').should(be.sortDescending);
});

it('Add project', () => {
cy.interceptK8s(
'POST',
RoleBindingModel,
mockRoleBindingK8sResource({
namespace: MODEL_REGISTRY_DEFAULT_NAMESPACE,
subjects: [{ ...projectSubjects[0], name: 'project-1' }],
roleRefName: 'registry-user-example-mr',
modelRegistryName: 'example-mr',
}),
).as('addProject');

projectsTab.findAddProjectButton().click();
projectTable.findNameSelect().findSelectOption('test-project').click();
projectTable.findSaveNewButton().click();

cy.wait('@addProject').then((interception) => {
expect(interception.request.body).to.containSubset({
metadata: {
namespace: 'odh-model-registries',
},
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: 'registry-user-example-mr',
},
subjects: [
{
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Group',
name: 'system:serviceaccounts:test-project',
},
],
});
});
});

it('Edit project', () => {
cy.interceptK8s(
'POST',
RoleBindingModel,
mockRoleBindingK8sResource({
namespace: MODEL_REGISTRY_DEFAULT_NAMESPACE,
subjects: [{ ...projectSubjects[0], name: 'project-1' }],
roleRefName: 'registry-user-example-mr',
modelRegistryName: 'example-mr',
}),
).as('editProject');
cy.interceptK8s(
'DELETE',
{
model: RoleBindingModel,
ns: MODEL_REGISTRY_DEFAULT_NAMESPACE,
name: 'test-name-view',
},
mock200Status({}),
).as('deleteProject');

projectTable.getTableRow('projectName').findKebabAction('Edit').click();
projectTable.findNameSelect().findSelectOption('test-project').click();
projectTable.findEditSaveButton('test-project').click();

cy.wait('@editProject').then((interception) => {
expect(interception.request.body).to.containSubset({
metadata: {
namespace: 'odh-model-registries',
},
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: 'registry-user-example-mr',
},
subjects: [
{
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Group',
name: 'system:serviceaccounts:test-project',
},
],
});
});
cy.wait('@deleteProject');
});

it('Delete project', () => {
cy.interceptK8s(
'DELETE',
{
model: RoleBindingModel,
ns: MODEL_REGISTRY_DEFAULT_NAMESPACE,
name: 'test-name-view',
},
mock200Status({}),
).as('deleteProject');

projectTable.getTableRow('projectName').findKebabAction('Delete').click();

cy.wait('@deleteProject');
});
});
});
Loading

0 comments on commit 4a1539d

Please sign in to comment.