Skip to content

Commit

Permalink
Jwt policies min ui (#1109)
Browse files Browse the repository at this point in the history
* first commit

* add basic error handler, connect to redux for notification

* update

* upgrade packages, fix typing

* upgrade tweek-management-client api

* small chages

* remove log

* small changes, add tests

* add addiotnal test

* added validation for acl

* small changes

* small fixes

* first e2e iteration - single test

* added all tests

* changed error message

* bump editor version

* cr

* CR

* CR

* CR

* CR

* prettier all!

* small refactor

* removed typescript :(

* fix unit tests
  • Loading branch information
Yshayy authored Feb 18, 2019
1 parent 4ce2af1 commit ba373df
Show file tree
Hide file tree
Showing 26 changed files with 964 additions and 1,260 deletions.
11 changes: 0 additions & 11 deletions deployments/dev/docker-compose.local-editor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ services:
args:
target: Release

editor:
environment:
- CONTINUOUS_UPDATER_INTERVAL=500

authoring:
environment:
- CONTINUOUS_UPDATER_INTERVAL=500
Expand All @@ -20,13 +16,6 @@ services:
target: Release
depends_on:
- publishing
environment:
- CorsPolicies__Keys__Origins=http://localhost:3000
- CorsPolicies__Keys__Methods=GET
- CorsPolicies__Keys__MaxPreflightAge=60
- CorsPolicies__Keys__Headers=
- CorsPolicies__Keys__ExposedHeaders=
- CorsEnabled=true

oidc-server-mock:
container_name: oidc-server-mock
Expand Down
10 changes: 5 additions & 5 deletions docs/pages/1.getting-started/02.using-tweek.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Tweek provide UI and rest api for editing context.
- Set value "Candada" for property Country

After that, we can query Tweek API with:
- http://localhost:8080/api/v1/keys/my_app/sign_button/color?user=john -> expected to be "blue"
- http://localhost:8080/api/v2/values/my_app/sign_button/color?user=john -> expected to be "blue"

You can also use the api for updating Tweek context:
- curl -X POST http://localhost:8080/api/v2/context/user/john \
Expand All @@ -63,10 +63,10 @@ More on [Context.](https://docs.tweek.fm/concepts/context/intro-to-context)
Create new key in the editor "my_app/sign_button/is_enabled" with value type "boolean" and default value False.
Add new rule, remove all conditions, set the the rule value to gradual release with 50%.
Try querying configuration with different users and You'll have different results.
- http://localhost:8080/api/v1/keys/my_app/sign_button/is_enabled?user=barny
- http://localhost:8080/api/v1/keys/my_app/sign_button/is_enabled?user=robin
- http://localhost:8080/api/v1/keys/my_app/sign_button/is_enabled?user=ted
- http://localhost:8080/api/v1/keys/my_app/sign_button/is_enabled?user=lily
- http://localhost:8080/api/v2/values/my_app/sign_button/is_enabled?user=barny
- http://localhost:8080/api/v2/values/my_app/sign_button/is_enabled?user=robin
- http://localhost:8080/api/v2/values/my_app/sign_button/is_enabled?user=ted
- http://localhost:8080/api/v2/values/my_app/sign_button/is_enabled?user=lily
- etc...

More on how multi-variant keys work in Tweek. (link)
4 changes: 2 additions & 2 deletions e2e/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"build": "yarn docker-compose build",
"test": "testcafe -q -S -s screenshots chrome spec",
"test:fll-env": "yarn docker-compose up --build -d editor && yarn test",
"test:full-env": "yarn docker-compose up --build -d editor && yarn test",
"test:docker": "yarn build && yarn docker-compose run --rm e2e-ui",
"logs": "yarn docker-compose logs",
"teardown": "yarn docker-compose down --remove-orphans",
Expand All @@ -15,7 +15,7 @@
"chai": "^4.2.0",
"nconf": "^0.10.0",
"ramda": "^0.26.1",
"tweek-client": "^1.0.0-rc5",
"tweek-client": "^1.0.0-rc7",
"uuid": "^3.3.2"
},
"devDependencies": {
Expand Down
62 changes: 62 additions & 0 deletions e2e/ui/pages/Settings/Policies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Selector, t, ClientFunction } from 'testcafe';

const read = ClientFunction(() => window.monaco.editor.getModels()[0].getValue());
const update = ClientFunction(() => window.monaco.editor.getModels()[0].setValue(text));

export class PolicyEditor {
constructor(container) {
this.container = container;
this.editor = this.container.find('.monaco-editor');
this.saveButton = this.container.find('button.save-button');
}

async read() {
return await read();
}

async update(text) {
return await update.with({
dependencies: { text },
})();
}

hasChanges() {
return this.saveButton.withAttribute('data-state-has-changes', 'true').exists;
}

isValid() {
return this.saveButton.withAttribute('data-state-is-valid', 'true').exists;
}

async save() {
return await t
.expect(this.hasChanges())
.ok()
.click(this.saveButton);
}
}

export default class PoliciesSection {
container = Selector('.policies-page');
tab = 'ACL';

async currentTab() {
const panel = this.container.find(`[role=tabpanel]`);
await t.expect(panel.visible).ok();
return new PolicyEditor(panel);
}

async changeTab(name) {
const tabButton = this.container.find('[role=tab]').withText(name);
const id = await tabButton.getAttribute('id');
const panel = this.container.find(`[role=tabpanel][aria-labelledby=${id}]`);

//container
await t
.click(this.container.find('[role=tab]').withText(name))
.expect(panel.visible)
.ok();

return new PolicyEditor(panel);
}
}
18 changes: 18 additions & 0 deletions e2e/ui/pages/Settings/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Selector, t } from 'testcafe';
import { attributeSelector, dataComp } from '../../utils/selector-utils';
import Identity from './Identity';
import PoliciesSection from './Policies';
import { editorUrl } from '../../utils/constants';
import { getLocation } from '../../utils/location-utils';

export default class SettingsPage {
sideMenu = Selector('.side-menu');
Expand Down Expand Up @@ -41,4 +44,19 @@ export default class SettingsPage {

return currentIdentity;
}

async openPoliciesPage() {
const button = this.sideMenu.find('a').withExactText('Policies');
const policies = new PoliciesSection();
await t
.expect(button.visible)
.ok()
.click(button)
.expect(policies.container.visible)
.ok()
.expect(getLocation())
.eql(`${editorUrl}/settings/policies`);

return policies;
}
}
125 changes: 125 additions & 0 deletions e2e/ui/spec/settings/edit-policies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { expect } from 'chai';
import { Selector } from 'testcafe';
import { credentials, login } from '../../utils/auth-utils';
import { editorUrl } from '../../utils/constants';
import { waitFor } from '../../utils/assertion-utils';
import SettingsPage from '../../pages/Settings';
import { tweekManagementClient } from '../../clients/tweek-clients';

const settingsPage = new SettingsPage();

let policies;
let jwtPolicy;
const reset = () => {
tweekManagementClient.savePolicies(policies);
tweekManagementClient.saveJWTExtractionPolicy(jwtPolicy);
};

fixture`Edit Policies`.page`${editorUrl}/settings`
.httpAuth(credentials)
.beforeEach(login)
.before(async () => {
policies = await tweekManagementClient.getPolicies();
jwtPolicy = await tweekManagementClient.getJWTExtractionPolicy();
})
.after(reset);

test('Edit acl policies and save', async (t) => {
const policiesPage = await settingsPage.openPoliciesPage();

const editor = await policiesPage.currentTab();
const data = await editor.read();
expect(JSON.stringify(policies, null, 4)).eql(data);
const newData = JSON.stringify(
[
...policies,
{
group: 'dummy',
user: 'dummy',
contexts: {},
object: 'values/*',
action: '*',
effect: 'allow',
},
],
null,
4,
);

await editor.update(newData);

await editor.save();

await waitFor(async () =>
expect(JSON.stringify(await tweekManagementClient.getPolicies(), null, 4)).eql(newData),
);
}).before(async (t) => {
reset();
await login(t);
});

test('invalid acl policies, save disabled', async (t) => {
const policiesPage = await settingsPage.openPoliciesPage();

const editor = await policiesPage.currentTab();
const newData = JSON.stringify(
[
...policies,
{
group: 'dummy',
user: 'dummy',
contexts: {},
object: 'val/*',
action: '*',
effect: 'allow',
},
],
null,
4,
);

await editor.update(newData);

await t.expect(editor.hasChanges()).eql(true);
await t.expect(editor.isValid()).eql(false);
});

test('updated JWT policy and save', async (t) => {
const policiesPage = await settingsPage.openPoliciesPage();

const editor = await policiesPage.changeTab('JWT Extraction');
const newData = jwtPolicy.replace('input.sub', 'input.upn');

await editor.update(newData);

await editor.save();

await waitFor(async () =>
expect(await tweekManagementClient.getJWTExtractionPolicy()).eql(newData),
);
}).before(async (t) => {
reset();
await login(t);
});

test('Attempt to save JWT policy, show error', async (t) => {
const policiesPage = await settingsPage.openPoliciesPage();

const editor = await policiesPage.changeTab('JWT Extraction');
const newData = jwtPolicy.replace('input.sub', 'inpu"t.upn');

await editor.update(newData);

await editor.save();

await t
.expect(
Selector('.notifications-br .notification-error .notification-title').withExactText(
'Error saving jwt-policy',
).visible,
)
.ok();
}).before(async (t) => {
reset();
await login(t);
});
Loading

0 comments on commit ba373df

Please sign in to comment.