mirror of
https://github.com/bringout/oca-ocb-security.git
synced 2026-04-22 02:52:06 +02:00
19.0 vanilla
This commit is contained in:
parent
20ddc1b4a3
commit
c0efcc53f5
1162 changed files with 125577 additions and 105287 deletions
|
|
@ -0,0 +1,113 @@
|
|||
import { rpc } from "@web/core/network/rpc";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const openUserPreferenceSecurity = () => [{
|
||||
content: 'Open user account menu',
|
||||
trigger: '.o_user_menu .dropdown-toggle',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "Open preferences / profile screen",
|
||||
trigger: '[data-menu=preferences]',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "Switch to security tab",
|
||||
trigger: 'a[role=tab]:contains("Security")',
|
||||
run: 'click',
|
||||
}]
|
||||
|
||||
registry.category("web_tour.tours").add('apikeys_tour_setup', {
|
||||
url: '/odoo?debug=1', // Needed as API key part is now only displayed in debug mode
|
||||
steps: () => [
|
||||
...openUserPreferenceSecurity(), {
|
||||
content: "Open API keys wizard",
|
||||
trigger: 'button:contains("Add API Key")',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Check that we have to enter enhanced security mode",
|
||||
trigger: ".modal div:contains(Enter your current password)",
|
||||
}, {
|
||||
content: "Input password",
|
||||
trigger: '.modal [name=password] input',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "Confirm",
|
||||
trigger: ".modal button:contains(Confirm Password)",
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Check that we're now on the key description dialog",
|
||||
trigger: '.modal p:contains("Enter a description of and purpose for the key.")',
|
||||
}, {
|
||||
content: "Enter description",
|
||||
trigger: '.modal [name=name] input',
|
||||
run: "edit my key",
|
||||
}, {
|
||||
content: "Confirm key creation",
|
||||
trigger: '.modal button:contains("Generate key")',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Check that we're on the last step & grab key",
|
||||
trigger: '.modal p:contains("Here is your new API key")',
|
||||
run: async () => {
|
||||
const key = document.querySelector("code [name=key] span").textContent;
|
||||
await rpc('/web/dataset/call_kw', {
|
||||
model: 'ir.logging', method: 'send_key',
|
||||
args: [key],
|
||||
kwargs: {},
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: "button:contains(Done)",
|
||||
run: "click",
|
||||
},
|
||||
...openUserPreferenceSecurity(),
|
||||
{
|
||||
content: "check that our key is present",
|
||||
trigger: '[name=api_key_ids] .o_kanban_record:contains("my key")',
|
||||
}]});
|
||||
|
||||
// deletes the previously created key
|
||||
registry.category("web_tour.tours").add('apikeys_tour_teardown', {
|
||||
url: '/odoo?debug=1', // Needed as API key part is now only displayed in debug mode
|
||||
steps: () => [{
|
||||
content: 'Open preferences',
|
||||
trigger: '.o_user_menu .dropdown-toggle',
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: '[data-menu=preferences]',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Switch to security tab",
|
||||
trigger: 'a[role=tab]:contains("Security")',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "Open kanban dropdown menu of the key",
|
||||
trigger: '[name=api_key_ids] .o_kanban_record:contains("my key") .oe_kanban_action[name="remove"]',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "Input password for security mode again",
|
||||
trigger: ".modal [name=password] input",
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "And confirm",
|
||||
trigger: ".modal button:contains(Confirm Password)",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: "body:not(:has(.modal))",
|
||||
},
|
||||
{
|
||||
content: 'Re-open preferences again',
|
||||
trigger: '.o_user_menu .dropdown-toggle',
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: '[data-menu=preferences]',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Switch to security tab",
|
||||
trigger: 'a[role=tab]:contains("Security")',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "Check that there's no more keys",
|
||||
trigger: "body:not(:has(.o_notebook [name=api_key_ids]))",
|
||||
}]});
|
||||
|
|
@ -1,339 +1,424 @@
|
|||
odoo.define('auth_totp.tours', function(require) {
|
||||
"use strict";
|
||||
|
||||
const tour = require('web_tour.tour');
|
||||
const ajax = require('web.ajax');
|
||||
import { WORKER_STATE } from "@bus/workers/websocket_worker";
|
||||
import { rpc } from "@web/core/network/rpc";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { stepUtils } from "@web_tour/tour_utils";
|
||||
import { whenReady } from "@odoo/owl";
|
||||
|
||||
function openRoot() {
|
||||
return [{
|
||||
content: "return to client root to avoid race condition",
|
||||
trigger: 'body',
|
||||
run() {
|
||||
$('body').addClass('wait');
|
||||
window.location = '/web';
|
||||
}
|
||||
document.querySelector("body").classList.add("wait");
|
||||
window.location = '/odoo';
|
||||
},
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "wait for client reload",
|
||||
trigger: 'body:not(.wait)',
|
||||
run() {}
|
||||
}];
|
||||
}
|
||||
function openUserProfileAtSecurityTab() {
|
||||
function openUserPreferencesAtSecurityTab() {
|
||||
return [{
|
||||
content: 'Open user account menu',
|
||||
trigger: '.o_user_menu .oe_topbar_name',
|
||||
trigger: '.o_user_menu .dropdown-toggle',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "Open preferences / profile screen",
|
||||
trigger: '[data-menu=settings]',
|
||||
content: "Open My Preferences",
|
||||
trigger: '[data-menu=preferences]',
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "wait for security tab",
|
||||
trigger: 'a[role=tab]:contains("Security")',
|
||||
}, {
|
||||
content: "Switch to security tab",
|
||||
trigger: 'a[role=tab]:contains("Account Security")',
|
||||
trigger: 'a[role=tab]:contains("Security")',
|
||||
run: 'click',
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the TOTP button is in the specified state (true = enabled =
|
||||
* can disable, false = disabled = can enable), then closes the profile dialog
|
||||
* can disable, false = disabled = can enable), then closes the preferences dialog
|
||||
* if it's one (= hr not installed).
|
||||
*
|
||||
* If no totp state is provided, just checks that the toggle exists.
|
||||
*/
|
||||
function closeProfileDialog({content, totp_state}) {
|
||||
function closePreferencesDialog({content, totp_state}) {
|
||||
let trigger;
|
||||
switch (totp_state) {
|
||||
case true: trigger = 'button[name=action_totp_disable]'; break;
|
||||
case false: trigger = 'button[name=action_totp_enable_wizard]'; break;
|
||||
case undefined: trigger = 'button.o_auth_2fa_btn'; break;
|
||||
case undefined: trigger = 'div:contains("Two-factor Authentication") + button'; break;
|
||||
default: throw new Error(`Invalid totp state ${totp_state}`)
|
||||
}
|
||||
|
||||
return [{
|
||||
content,
|
||||
trigger: 'a[role=tab]:contains("Security").active',
|
||||
},
|
||||
{
|
||||
trigger,
|
||||
run() {
|
||||
const $modal = this.$anchor.parents('.o_dialog_container');
|
||||
if ($modal.length) {
|
||||
$modal.find('button[name=preference_cancel]').click()
|
||||
async run(helpers) {
|
||||
const modal = document.querySelector(".o_dialog");
|
||||
if (modal) {
|
||||
modal.querySelector("button[name=preference_cancel]").click();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
trigger: 'body',
|
||||
async run() {
|
||||
while (document.querySelector('.o_dialog_container .o_dialog')) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
this.$anchor.addClass('dialog-closed');
|
||||
},
|
||||
}, {
|
||||
trigger: 'body.dialog-closed',
|
||||
run() {},
|
||||
},
|
||||
{
|
||||
trigger: 'body:not(:has(.o_dialog))',
|
||||
}];
|
||||
}
|
||||
|
||||
tour.register('totp_tour_setup', {
|
||||
test: true,
|
||||
url: '/web'
|
||||
}, [...openUserProfileAtSecurityTab(), {
|
||||
registry.category("web_tour.tours").add('totp_tour_setup', {
|
||||
url: '/odoo',
|
||||
steps: () => [
|
||||
...openUserPreferencesAtSecurityTab(),
|
||||
{
|
||||
content: "Open totp wizard",
|
||||
trigger: 'button[name=action_totp_enable_wizard]',
|
||||
}, {
|
||||
trigger: 'a[role=tab]:contains("Security").active',
|
||||
},
|
||||
{
|
||||
trigger: "button[name=action_totp_enable_wizard]",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".modal div:contains(Enter your current password)",
|
||||
},
|
||||
{
|
||||
content: "Check that we have to enter enhanced security mode and input password",
|
||||
extra_trigger: 'div:contains("enter your password")',
|
||||
trigger: '[name=password] input',
|
||||
run: 'text demo',
|
||||
run: 'edit test_user',
|
||||
}, {
|
||||
content: "Confirm",
|
||||
trigger: "button:contains(Confirm Password)",
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Check the wizard has opened",
|
||||
trigger: 'li:contains("When requested to do so")',
|
||||
run() {}
|
||||
trigger: '.modal:contains("Two-Factor Authentication Activation")',
|
||||
}, {
|
||||
content: "Get secret from collapsed div",
|
||||
trigger: 'a:contains("Cannot scan it?")',
|
||||
trigger: `.modal a:contains("Cannot scan it?")`,
|
||||
async run(helpers) {
|
||||
const $secret = this.$anchor.closest('div').find('[name=secret] span:first-child');
|
||||
const $copyBtn = $secret.find('button');
|
||||
$copyBtn.remove();
|
||||
const token = await ajax.jsonRpc('/totphook', 'call', {
|
||||
secret: $secret.text()
|
||||
const secret = this.anchor
|
||||
.closest("div")
|
||||
.querySelector("[name=secret] span:first-child");
|
||||
const copyBtn = secret.querySelector("button");
|
||||
if (copyBtn) {
|
||||
copyBtn.remove();
|
||||
}
|
||||
const token = await rpc('/totphook', {
|
||||
secret: secret.textContent,
|
||||
offset: 0,
|
||||
});
|
||||
helpers.text(token, '[name=code] input');
|
||||
helpers.click('button.btn-primary:contains(Activate)');
|
||||
$('body').addClass('got-token')
|
||||
await helpers.edit(token, '[name=code] input');
|
||||
}
|
||||
}, {
|
||||
content: 'wait for rpc',
|
||||
trigger: 'body.got-token',
|
||||
run() {}
|
||||
},
|
||||
{
|
||||
trigger: ".modal button.btn-primary:contains(Enable Two-Factor Authentication)",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".o_notification_content:contains(2-Factor authentication is now enabled)",
|
||||
},
|
||||
...openRoot(),
|
||||
...openUserProfileAtSecurityTab(),
|
||||
...closeProfileDialog({
|
||||
...openUserPreferencesAtSecurityTab(),
|
||||
...closePreferencesDialog({
|
||||
content: "Check that the button has changed",
|
||||
totp_state: true,
|
||||
}),
|
||||
]);
|
||||
]});
|
||||
|
||||
tour.register('totp_login_enabled', {
|
||||
test: true,
|
||||
url: '/'
|
||||
}, [{
|
||||
registry.category("web_tour.tours").add('totp_login_enabled', {
|
||||
url: '/',
|
||||
steps: () => [{
|
||||
content: "check that we're on the login page or go to it",
|
||||
trigger: 'input#login, a:contains(Sign in)'
|
||||
isActive: ["body:not(:has(input#login))"],
|
||||
trigger: "a:contains(Sign in)",
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "input login",
|
||||
trigger: 'input#login',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: 'input password',
|
||||
trigger: 'input#password',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "click da button",
|
||||
trigger: 'button:contains("Log in")',
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "expect totp screen",
|
||||
trigger: 'label:contains(Authentication Code)',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "input incorrect code",
|
||||
trigger: 'input[name=totp_token]',
|
||||
async run(helpers) {
|
||||
// set the offset in the past, so the token will be always wrong
|
||||
await rpc("/totphook", { offset: -2 });
|
||||
helpers.edit("123456");
|
||||
}
|
||||
}, {
|
||||
trigger: `button:contains("Log in")`,
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "using an incorrect token should fail",
|
||||
trigger: "p.alert.alert-danger:contains(Verification failed, please double-check the 6-digit code)",
|
||||
}, {
|
||||
content: "reuse same code",
|
||||
trigger: 'input[name=totp_token]',
|
||||
async run(helpers) {
|
||||
// send the same token as the one last one from the setup tour
|
||||
const token = await rpc("/totphook", { offset: 0 });
|
||||
helpers.edit(token);
|
||||
}
|
||||
}, {
|
||||
trigger: `button:contains("Log in")`,
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "reusing the same token should fail",
|
||||
trigger: "p.alert.alert-danger:contains(Verification failed, please use the latest 6-digit code)",
|
||||
}, {
|
||||
content: "input code",
|
||||
trigger: 'input[name=totp_token]',
|
||||
async run(helpers) {
|
||||
// TODO: if tours are ever async-aware the click should get moved out,
|
||||
// but currently there's no great way to make the tour wait until
|
||||
// we've retrieved and set the token: `:empty()` is aboutthe text
|
||||
// content of the HTML element, not the JS value property. We
|
||||
// could set a class but that's really no better than
|
||||
// procedurally clicking the button after we've set the input.
|
||||
const token = await ajax.jsonRpc('/totphook', 'call', {});
|
||||
helpers.text(token);
|
||||
helpers.click('button:contains("Log in")');
|
||||
const token = await rpc('/totphook', { offset: 1 });
|
||||
helpers.edit(token);
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: `button:contains("Log in")`,
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "check we're logged in",
|
||||
trigger: ".o_user_menu .oe_topbar_name",
|
||||
run() {}
|
||||
}]);
|
||||
trigger: ".o_user_menu .dropdown-toggle",
|
||||
}]});
|
||||
|
||||
tour.register('totp_login_device', {
|
||||
test: true,
|
||||
url: '/'
|
||||
}, [{
|
||||
registry.category("web_tour.tours").add('totp_login_device', {
|
||||
url: '/',
|
||||
steps: () => [{
|
||||
content: "check that we're on the login page or go to it",
|
||||
trigger: 'input#login, a:contains(Sign in)'
|
||||
isActive: ["body:not(:has(input#login))"],
|
||||
trigger: "a:contains(Sign in)",
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "input login",
|
||||
trigger: 'input#login',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: 'input password',
|
||||
trigger: 'input#password',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "click da button",
|
||||
trigger: 'button:contains("Log in")',
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "expect totp screen",
|
||||
trigger: 'label:contains(Authentication Code)',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "check remember device box",
|
||||
trigger: 'label[for=switch-remember]',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "input code",
|
||||
trigger: 'input[name=totp_token]',
|
||||
async run(helpers) {
|
||||
const token = await ajax.jsonRpc('/totphook', 'call', {})
|
||||
helpers.text(token);
|
||||
helpers.click('button:contains("Log in")');
|
||||
const token = await rpc('/totphook', { offset: 2 });
|
||||
helpers.edit(token);
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
trigger: "button:contains(Log in)",
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
},
|
||||
{
|
||||
trigger: ".o_web_client .o_navbar",
|
||||
async run() {
|
||||
await whenReady();
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ".o_web_client .o_navbar",
|
||||
async run() {
|
||||
await new Promise((resolve) => {
|
||||
const bus = odoo.__WOWL_DEBUG__.root.env.services.bus_service;
|
||||
bus.addEventListener("BUS:CONNECT", resolve, { once: true });
|
||||
if (bus.workerState === WORKER_STATE.CONNECTED) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
content: "check we're logged in",
|
||||
trigger: ".o_user_menu .oe_topbar_name",
|
||||
trigger: ".o_user_menu .dropdown-toggle",
|
||||
run: 'click',
|
||||
}, {
|
||||
content: "click the Log out button",
|
||||
trigger: '.dropdown-item[data-menu=logout]',
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "check that we're back on the login page or go to it",
|
||||
trigger: 'input#login, a:contains(Log in)'
|
||||
trigger: 'input#login, a:contains(Log in)',
|
||||
run: "edit Test",
|
||||
}, {
|
||||
content: "input login again",
|
||||
trigger: 'input#login',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: 'input password again',
|
||||
trigger: 'input#password',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "click da button again",
|
||||
trigger: 'button:contains("Log in")',
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "check we're logged in without 2FA",
|
||||
trigger: ".o_user_menu .oe_topbar_name",
|
||||
run() {}
|
||||
trigger: ".o_user_menu .dropdown-toggle",
|
||||
},
|
||||
// now go and disable two-factor authentication would be annoying to do in a separate tour
|
||||
// because we'd need to login & totp again as HttpCase.authenticate can't
|
||||
// succeed w/ totp enabled
|
||||
...openUserProfileAtSecurityTab(),
|
||||
...openUserPreferencesAtSecurityTab(),
|
||||
{
|
||||
content: "Open totp wizard",
|
||||
trigger: 'button[name=action_totp_disable]',
|
||||
}, {
|
||||
trigger: 'a[role=tab]:contains("Security").active',
|
||||
},
|
||||
{
|
||||
trigger: "button[name=action_totp_disable]",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".modal div:contains(Enter your current password)",
|
||||
},
|
||||
{
|
||||
content: "Check that we have to enter enhanced security mode and input password",
|
||||
extra_trigger: 'div:contains("enter your password")',
|
||||
trigger: '[name=password] input',
|
||||
run: 'text demo',
|
||||
trigger: '.modal [name=password] input',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "Confirm",
|
||||
trigger: "button:contains(Confirm Password)",
|
||||
trigger: ".modal button:contains(Confirm Password)",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger:".o_notification_content:contains(Two-factor authentication disabled)",
|
||||
},
|
||||
...openRoot(),
|
||||
...openUserProfileAtSecurityTab(),
|
||||
...closeProfileDialog({
|
||||
...openUserPreferencesAtSecurityTab(),
|
||||
...closePreferencesDialog({
|
||||
content: "Check that the button has changed",
|
||||
totp_state: false
|
||||
}),
|
||||
]);
|
||||
]});
|
||||
|
||||
tour.register('totp_login_disabled', {
|
||||
test: true,
|
||||
url: '/'
|
||||
}, [{
|
||||
registry.category("web_tour.tours").add('totp_login_disabled', {
|
||||
url: '/',
|
||||
steps: () => [{
|
||||
content: "check that we're on the login page or go to it",
|
||||
trigger: 'input#login, a:contains(Sign in)'
|
||||
isActive: ["body:not(:has(input#login))"],
|
||||
trigger: "a:contains(Sign in)",
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
}, {
|
||||
content: "input login",
|
||||
trigger: 'input#login',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: 'input password',
|
||||
trigger: 'input#password',
|
||||
run: 'text demo',
|
||||
run: "edit test_user",
|
||||
}, {
|
||||
content: "click da button",
|
||||
trigger: 'button:contains("Log in")',
|
||||
run: "click",
|
||||
expectUnloadPage: true,
|
||||
},
|
||||
// normally we'd end the tour here as it's all we care about but there are a
|
||||
// bunch of ongoing queries from the loading of the web client which cause
|
||||
// issues, so go and open the preferences / profile screen to make sure
|
||||
// issues, so go and open the preferences screen to make sure
|
||||
// everything settles down
|
||||
...openUserProfileAtSecurityTab(),
|
||||
...openUserPreferencesAtSecurityTab(),
|
||||
// close the dialog if that makes sense
|
||||
...closeProfileDialog({})
|
||||
]);
|
||||
...closePreferencesDialog({})
|
||||
]});
|
||||
|
||||
const columns = {};
|
||||
tour.register('totp_admin_disables', {
|
||||
test: true,
|
||||
url: '/web'
|
||||
}, [tour.stepUtils.showAppsMenuItem(), {
|
||||
registry.category("web_tour.tours").add('totp_admin_disables', {
|
||||
url: '/odoo',
|
||||
steps: () => [stepUtils.showAppsMenuItem(), {
|
||||
content: 'Go to settings',
|
||||
trigger: '[data-menu-xmlid="base.menu_administration"]'
|
||||
trigger: '[data-menu-xmlid="base.menu_administration"]',
|
||||
run: "click",
|
||||
}, {
|
||||
content: 'Wait for page',
|
||||
trigger: '.o_menu_brand:contains("Settings")',
|
||||
run() {}
|
||||
}, {
|
||||
content: "Open Users menu",
|
||||
trigger: '[data-menu-xmlid="base.menu_users"]'
|
||||
trigger: '[data-menu-xmlid="base.menu_users"]',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Open Users view",
|
||||
trigger: '[data-menu-xmlid="base.menu_action_res_users"]',
|
||||
run(helpers) {
|
||||
// funny story: the users view we're trying to reach, sometimes we're
|
||||
// already there, but if we re-click the next step executes before the
|
||||
// action has the time to re-load, the one after that doesn't, and our
|
||||
// selection get discarded by the action reloading, so here try to
|
||||
// see if we're already on the users action through the breadcrumb and
|
||||
// just close the menu if so
|
||||
const $crumb = $('.breadcrumb');
|
||||
if ($crumb.text().indexOf('Users') === -1) {
|
||||
// on general settings page, click menu
|
||||
helpers.click();
|
||||
} else {
|
||||
// else close menu
|
||||
helpers.click($('[data-menu-xmlid="base.menu_users"]'));
|
||||
}
|
||||
}
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Find Demo User",
|
||||
trigger: 'td.o_data_cell:contains("demo")',
|
||||
run(helpers) {
|
||||
const $titles = this.$anchor.closest('table').find('tr:first th');
|
||||
for (let i=0; i<$titles.length; ++i) {
|
||||
columns[$titles[i].getAttribute('data-name')] = i;
|
||||
}
|
||||
const $row = this.$anchor.closest('tr');
|
||||
const sel = $row.find('.o_list_record_selector input[type=checkbox]');
|
||||
helpers.click(sel);
|
||||
}
|
||||
content: "Find test_user User",
|
||||
trigger: 'tr:has(td.o_data_cell:contains("test_user")) ' +
|
||||
'.o_list_record_selector input[type=checkbox]',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Open Actions menu",
|
||||
trigger: 'button.dropdown-toggle:contains("Action")'
|
||||
trigger: 'button.dropdown-toggle:contains("Action")',
|
||||
run: "click",
|
||||
}, {
|
||||
content: "Select totp remover",
|
||||
trigger: 'span.dropdown-item:contains(Disable two-factor authentication)'
|
||||
}, { // enhanced security yo
|
||||
trigger: 'span.dropdown-item:contains(Disable two-factor authentication)',
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".modal div:contains(Enter your current password)",
|
||||
},
|
||||
{ // enhanced security yo
|
||||
content: "Check that we have to enter enhanced security mode & input password",
|
||||
extra_trigger: 'div:contains("enter your password")',
|
||||
trigger: '[name=password] input',
|
||||
run: 'text admin',
|
||||
trigger: '.modal [name=password] input',
|
||||
run: "edit admin",
|
||||
}, {
|
||||
content: "Confirm",
|
||||
trigger: "button:contains(Confirm Password)",
|
||||
}, {
|
||||
trigger: ".modal button:contains(Confirm Password)",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
content: "Wait for user to be unchecked (~ action done)",
|
||||
trigger: 'tr:has(td.o_data_cell:contains(test_user)) ' +
|
||||
'.o_list_record_selector input[type=checkbox]:not(:checked)',
|
||||
},
|
||||
{
|
||||
content: "open the user's form",
|
||||
trigger: "td.o_data_cell:contains(demo)",
|
||||
trigger: "td.o_data_cell:contains(test_user)",
|
||||
run: "click",
|
||||
}, {
|
||||
content: "go to Account security Tab",
|
||||
trigger: "a.nav-link:contains(Account Security)",
|
||||
}, ...closeProfileDialog({
|
||||
content: "check that demo user has been de-totp'd",
|
||||
totp_state: false,
|
||||
}),
|
||||
])
|
||||
});
|
||||
content: "wait for Security Tab to appear",
|
||||
trigger: "a.nav-link:contains(Security)",
|
||||
},{
|
||||
content: "go to Security Tab",
|
||||
trigger: "a.nav-link:contains(Security)",
|
||||
run: "click",
|
||||
}, {
|
||||
content: "check 2FA button: should be disabled",
|
||||
trigger: 'body:not(:has(button[name=action_totp_enable_wizard]))',
|
||||
}
|
||||
]})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue