Compare commits
2 Commits
chore56-ad
...
feat-rbac-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
993054a569 | ||
|
|
841d808a24 |
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"projectId": "cgz5j5",
|
||||
"video": false
|
||||
"video": false,
|
||||
"testFiles": [
|
||||
"init.spec.js",
|
||||
"rbac.spec.js"
|
||||
]
|
||||
}
|
||||
|
||||
19
test/e2e/cypress/integration/init.spec.js
Normal file
19
test/e2e/cypress/integration/init.spec.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// Tests to run
|
||||
context('Init admin user test', () => {
|
||||
//Browse to homepage before each test
|
||||
beforeEach(() => {
|
||||
cy.restoreLocalStorage();
|
||||
});
|
||||
|
||||
describe('Init admin', function () {
|
||||
it('Create user and verify success', function () {
|
||||
cy.initAdmin('admin', 'portaineriscool');
|
||||
cy.url().should('include', 'init/endpoint');
|
||||
cy.saveLocalStorage();
|
||||
});
|
||||
it('Select endpoint and init', function () {
|
||||
cy.initEndpoint();
|
||||
cy.url().should('include', 'home');
|
||||
});
|
||||
});
|
||||
});
|
||||
74
test/e2e/cypress/integration/rbac.spec.js
Normal file
74
test/e2e/cypress/integration/rbac.spec.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// RBAC testing with different authentication options
|
||||
context('RBAC Tests', () => {
|
||||
// Test with Internal Authentication
|
||||
describe('Standard RBAC', function () {
|
||||
before(() => {
|
||||
// Start cypress server for XHR waiting
|
||||
cy.server();
|
||||
cy.frontendAuth('admin', 'portaineriscool');
|
||||
// Wait for auth and redirection to the dashboard
|
||||
cy.wait(500);
|
||||
cy.frontendEnableExtension(Cypress.env('RBAC_KEY'));
|
||||
cy.saveLocalStorage();
|
||||
});
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.restoreLocalStorage();
|
||||
|
||||
// Create users and teams required for test
|
||||
cy.apiCreateUser('adam', 'portainer');
|
||||
cy.apiCreateUser('eve', 'portainer');
|
||||
cy.apiCreateUser('bob', 'portainer');
|
||||
cy.apiCreateUser('steve', 'portainer');
|
||||
cy.apiCreateTeam('admins');
|
||||
cy.apiCreateTeam('helpdesk');
|
||||
cy.apiCreateTeam('standard');
|
||||
cy.apiCreateTeam('readonly');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Cleanup users and teams required for test
|
||||
// cy.apiDeleteUser('adam', 'portainer');
|
||||
// cy.apiDeleteUser('eve', 'portainer');
|
||||
// cy.apiDeleteUser('bob', 'portainer');
|
||||
// cy.apiDeleteUser('steve', 'portainer');
|
||||
// cy.apiDeleteTeam('admins');
|
||||
// cy.apiDeleteTeam('helpdesk');
|
||||
// cy.apiDeleteTeam('standard');
|
||||
// cy.apiDeleteTeam('readonly');
|
||||
});
|
||||
|
||||
it('User assigned as endpoint-admin against an endpoint', function () {
|
||||
cy.assignAccess('adam', 'user', 'Endpoint administrator');
|
||||
cy.assignToTeam('adam', 'admins');
|
||||
cy.assignToTeam('eve', 'helpdesk');
|
||||
cy.assignToTeam('bob', 'standard');
|
||||
cy.assignToTeam('steve', 'readonly');
|
||||
cy.assignAccess('admins', 'team', 'Endpoint administrator');
|
||||
cy.assignAccess('helpdesk', 'team', 'Helpdesk');
|
||||
cy.assignAccess('standard', 'team', 'Standard user');
|
||||
cy.assignAccess('readonly', 'team', 'Read-only user');
|
||||
});
|
||||
|
||||
// TODO: Implement RBAC tests
|
||||
// it('User created, user logged in, user assigned as endpoint-admin against an endpoint', function () {
|
||||
// });
|
||||
// it('User created, user assigned as helpdesk against an endpoint', function () {
|
||||
// });
|
||||
// it('User created, user logged in, user assigned as helpdesk against an endpoint', function () {
|
||||
// });
|
||||
// it('User created, user assigned as standard user against an endpoint', function () {
|
||||
// });
|
||||
// it('User created, user logged in, user assigned as standard user against an endpoint', function () {
|
||||
// });
|
||||
// it('User created, user assigned as read-only against an endpoint', function () {
|
||||
// });
|
||||
// it('User created, user logged in, user assigned as read-only against an endpoint', function () {
|
||||
// });
|
||||
});
|
||||
|
||||
// TODO: Test with OAuth authentication
|
||||
// describe('OAuth RBAC', function () {});
|
||||
// TODO: Test against LDAP
|
||||
// describe('LDAP RBAC', function () {});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
// Tests to run
|
||||
context('Tests to run', () => {
|
||||
//Browse to homepage before each test
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
describe('Init admin', function () {
|
||||
it('Create user and verify success', function () {
|
||||
cy.get('#username').should('have.value', 'admin');
|
||||
cy.get('#password').type('portaineriscool').should('have.value', 'portaineriscool');
|
||||
cy.get('#confirm_password').type('portaineriscool').should('have.value', 'portaineriscool');
|
||||
cy.get('[type=submit]').click();
|
||||
cy.url().should('include', '/init/endpoint');
|
||||
});
|
||||
});
|
||||
});
|
||||
80
test/e2e/cypress/support/apiCommands.js
Normal file
80
test/e2e/cypress/support/apiCommands.js
Normal file
@@ -0,0 +1,80 @@
|
||||
Cypress.Commands.add('apiAuth', (username, password) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: 'http://e2e-portainer:9000/api/auth',
|
||||
body: {
|
||||
username: username,
|
||||
password: password,
|
||||
},
|
||||
})
|
||||
.its('body')
|
||||
.then((body) => {
|
||||
localStorage.setItem('portainer.JWT', body.jwt);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiEnableExtension', (licenseKey) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: 'http://e2e-portainer:9000/api/extensions',
|
||||
auth: {
|
||||
bearer: localStorage.getItem('portainer.JWT').slice(1, -1),
|
||||
},
|
||||
body: {
|
||||
license: licenseKey,
|
||||
},
|
||||
});
|
||||
// .its('body')
|
||||
// .then(body => {
|
||||
// cy.log(body.license);
|
||||
// })
|
||||
// localStorage.setItem("portainer.EXTENSION_STATE", [{"Id":3,"Enabled":true,"Version":"1.0.1","UpdateAvailable":false}])
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateUser', (username, password) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: 'http://e2e-portainer:9000/api/users',
|
||||
auth: {
|
||||
bearer: localStorage.getItem('portainer.JWT').slice(1, -1),
|
||||
},
|
||||
body: {
|
||||
username: username,
|
||||
password: password,
|
||||
role: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteUser', (userID) => {
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: 'http://e2e-portainer:9000/api/users/' + userID,
|
||||
auth: {
|
||||
bearer: localStorage.getItem('portainer.JWT').slice(1, -1),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateTeam', (teamName) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: 'http://e2e-portainer:9000/api/teams',
|
||||
auth: {
|
||||
bearer: localStorage.getItem('portainer.JWT').slice(1, -1),
|
||||
},
|
||||
body: {
|
||||
Name: teamName,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteTeam', (teamID) => {
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: 'http://e2e-portainer:9000/api/teams/' + teamID,
|
||||
auth: {
|
||||
bearer: localStorage.getItem('portainer.JWT').slice(1, -1),
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
122
test/e2e/cypress/support/frontendCommands.js
Normal file
122
test/e2e/cypress/support/frontendCommands.js
Normal file
@@ -0,0 +1,122 @@
|
||||
let LOCAL_STORAGE_MEMORY = {};
|
||||
|
||||
Cypress.Commands.add('saveLocalStorage', () => {
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('restoreLocalStorage', () => {
|
||||
Object.keys(LOCAL_STORAGE_MEMORY).forEach((key) => {
|
||||
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('initAdmin', (username, password) => {
|
||||
cy.visit('/#/init/admin');
|
||||
if (username != 'admin') {
|
||||
cy.get('#username').clear().type(username);
|
||||
}
|
||||
cy.get('#password').type(password);
|
||||
cy.get('#confirm_password').type(password);
|
||||
cy.get('[type=submit]').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('initEndpoint', () => {
|
||||
cy.get('[for=local_endpoint]').click();
|
||||
cy.get('[type=submit]').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('frontendAuth', (username, password) => {
|
||||
cy.route('POST', '**/auth').as('postAuth');
|
||||
|
||||
cy.visit('/#/auth');
|
||||
cy.get('#username').click();
|
||||
cy.get('#username').type(username);
|
||||
cy.get('#password').type(password);
|
||||
cy.get('ng-transclude > .ng-scope:nth-child(1)').click();
|
||||
cy.wait('@postAuth');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('frontendEnableExtension', (licenseKey) => {
|
||||
cy.route('POST', '**/extensions').as('postExtensions');
|
||||
|
||||
cy.visit('/#/extensions');
|
||||
cy.get('[name="extension_license"]').type(licenseKey);
|
||||
cy.contains('button', 'Enable extension').click();
|
||||
cy.wait('@postExtensions');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('frontendCreateUser', (username, password) => {
|
||||
// Setup user route to wait for response
|
||||
cy.route('POST', '**/users').as('users');
|
||||
|
||||
cy.get('#username').click();
|
||||
cy.get('#username').type(username);
|
||||
cy.get('#password').type(password);
|
||||
cy.get('#confirm_password').type(password);
|
||||
cy.get('.btn-primary').click();
|
||||
cy.wait('@users');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('frontendCreateTeam', (teamName) => {
|
||||
// Setup team route to wait for response
|
||||
cy.route('POST', '**/teams').as('teams');
|
||||
|
||||
cy.visit('/#/teams');
|
||||
cy.get('#team_name').click();
|
||||
cy.get('#team_name').type(teamName);
|
||||
cy.get('.btn-primary').click();
|
||||
cy.wait('@teams');
|
||||
});
|
||||
|
||||
// Navigate to teams view and assign a user to a team
|
||||
Cypress.Commands.add('assignToTeam', (username, teamName) => {
|
||||
cy.visit('/#/teams');
|
||||
|
||||
// Click team to browse to related team details view
|
||||
cy.clickLink(teamName);
|
||||
|
||||
// Get users table and execute within
|
||||
cy.contains('.widget', 'Users').within(() => {
|
||||
cy.contains('td', ' ' + username + ' ')
|
||||
.children('span')
|
||||
.click();
|
||||
});
|
||||
});
|
||||
|
||||
// Navigate to the endpoints view and give the user/team access
|
||||
Cypress.Commands.add('assignAccess', (entityName, entityType, role) => {
|
||||
cy.visit('/#/endpoints');
|
||||
cy.wait(500);
|
||||
|
||||
// Click Manage Access in endpoint row
|
||||
cy.clickLink('Manage access');
|
||||
|
||||
// Click user/team dropdown
|
||||
cy.get('.multiSelect > .ng-binding').click();
|
||||
|
||||
// Make sure to select right type of entity
|
||||
var type;
|
||||
if (entityType == 'team') {
|
||||
type = 'fa-users';
|
||||
} else {
|
||||
type = 'fa-user';
|
||||
}
|
||||
cy.get('.' + type)
|
||||
.parent()
|
||||
.contains(entityName)
|
||||
.click();
|
||||
cy.get('.multiSelect > .ng-binding').click();
|
||||
|
||||
// Click role dropdown and select role
|
||||
cy.get('.form-control:nth-child(1)').select(role);
|
||||
|
||||
// Click Create access button
|
||||
cy.get('button[type=submit]').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('clickLink', (label) => {
|
||||
// Timeout included to wait for element to be rendered
|
||||
cy.contains('a', label, { timeout: 60000 }).click();
|
||||
});
|
||||
@@ -1,20 +1,2 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
import './frontendCommands';
|
||||
import './apiCommands';
|
||||
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
- e2e-ci
|
||||
|
||||
cypress:
|
||||
image: cypress/included:3.5.0
|
||||
image: cypress/included:4.7.0
|
||||
container_name: e2e-cypress
|
||||
command: --browser chrome
|
||||
depends_on:
|
||||
|
||||
Reference in New Issue
Block a user