The changes in `auth_password_policy` are largely the owlification of the password meter widget: - modernize the password policy module and convert it to an odoo-module (note: now exports a pseudo-abstract class which is really a policy, for the sake of somewhat sensibly typing `recommendations`) - replace the implementation of the Meter and PasswordField widgets by owl versions The changes to web and base stem from taking a look at converting the ChangePassword wizard, and finding that it would be a pain in the ass but also... unnecessary? It seems to have been done as a wizard completely in javascript despite being backend-only for legacy reasons: apparently one of the very old web clients (v5 or v6 probably) implemented it as a "native action" which was directly part of the client's UI, and so it had to be implemented entirely in the client. Over time it was moved back into the regular UI (and moved around quite a bit), hooked as a client action to maintain access to the existing UI / dialog. But since it's been an action opened via a button for years it can just... be a normal wizard, with password fields, which auth_password_policy can then set the widget of. So did that: - removed the old unnecessary JS, and its dedicated endpoint (which is *not* used by portal, portal has its own endpoint) - used check_identity for the "old password check" - split out `change_password` with an internal bit so we can have a safer (and logged) "set user password" without needing to provide the old password, which is now used for the bulk password change wizard as well - added a small wizard which just takes a new password (and confirmation), for safety a given change password wizard is only accessible to their creator (also the wizard is restricted to employees though technically it would probably be fine for portal users as well) Rather than extensive messy rewrite / monkeypatching (the original wizard was 57 LOC, though also 22 LOC of template, the auth_policy hooking / patching was 33, plus 8 lines of CSS), `auth_password_policy` just sets the widget of the `new_password` field in the new wizard, much as it did the bulk wizard. Also improve the "hide meter if field is empty" feature by leveraging `:placeholder-shown`. This requires setting a placeholder, and while empty works fine in firefox, it doesn't work in chrome. So the placeholder needs to be a single space. Still, seems better than updating a fake attribute or manipulating a class for the sake of trivial styling. Notes on unlink + transient vacuum Although the wizard object is only created when actually calling `change_password`, and is deleted on success, it is possible for the user to get an error and fail to continue (it should be unlikely without overrides since the passwords are checked while creating / saving but...). While in that case the `new_password` in the database is not the user's own, it could be their *future* password, or give evidence as to their password-creation scheme, or some other signal useful to attack that front of the user's life and behavior. As such, quickly removing leftovers from the database (by setting a very low transient lifetime) seems like a good idea. This is compounded by the `check_identity` having a grace period of 10 minutes. 0.1 is 6 minutes, but because the cron runs every 10 the user effectively has 6~10 minutes between the moment they create an incorrect / incomplete version of the wizard and the moment where it is destroyed if they just leave it. closes odoo/odoo#99458 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
219 lines
11 KiB
XML
219 lines
11 KiB
XML
<?xml version="1.0"?>
|
|
<odoo>
|
|
<record id="group_private_addresses" model="res.groups">
|
|
<field name="name">Access to Private Addresses</field>
|
|
<field name="category_id" ref="base.module_category_hidden"/>
|
|
</record>
|
|
|
|
<data noupdate="1">
|
|
<record model="ir.rule" id="res_users_log_rule">
|
|
<field name="name">res.users.log per user</field>
|
|
<field name="model_id" ref="model_res_users_log"/>
|
|
<field name="domain_force">[('create_uid','=', user.id)]</field>
|
|
<field name="perm_read" eval="False"/>
|
|
</record>
|
|
|
|
<record model="ir.rule" id="res_partner_rule">
|
|
<field name="name">res.partner company</field>
|
|
<field name="model_id" ref="base.model_res_partner"/>
|
|
<!-- We exclude partners that have internal users (`partner_share` field) from
|
|
the multi-company rule because it might interfere with the user's company rule
|
|
and make some users unselectable in relational fields. This means that partners
|
|
of internal users are always visible, not matter the company setting. -->
|
|
<field name="domain_force">['|', '|', ('partner_share', '=', False), ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
|
|
</record>
|
|
|
|
<record model="ir.rule" id="res_partner_portal_public_rule">
|
|
<field name="name">res_partner: portal/public: read access on my commercial partner</field>
|
|
<field name="model_id" ref="base.model_res_partner"/>
|
|
<field name="domain_force">[('id', 'child_of', user.commercial_partner_id.id)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_portal')), Command.link(ref('base.group_public'))]"/>
|
|
<field name="perm_create" eval="False"/>
|
|
<field name="perm_unlink" eval="False"/>
|
|
<field name="perm_write" eval="False"/>
|
|
</record>
|
|
|
|
<record model="ir.rule" id="ir_default_user_rule">
|
|
<field name="name">Defaults: alter personal defaults</field>
|
|
<field name="model_id" ref="model_ir_default"/>
|
|
<field name="domain_force">[('user_id','=',user.id)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_user'))]"/>
|
|
<field name="perm_read" eval="False"/>
|
|
</record>
|
|
|
|
<record model="ir.rule" id="ir_default_system_rule">
|
|
<field name="name">Defaults: alter all defaults</field>
|
|
<field name="model_id" ref="model_ir_default"/>
|
|
<field name="domain_force">[(1,'=',1)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_system'))]"/>
|
|
<field name="perm_read" eval="False"/>
|
|
</record>
|
|
|
|
<!-- Used for dashboard customizations, users should only be able to edit their own dashboards -->
|
|
<record model="ir.rule" id="ir_ui_view_custom_personal">
|
|
<field name="name">ir.ui.view_custom rule</field>
|
|
<field name="model_id" ref="model_ir_ui_view_custom"/>
|
|
<field name="domain_force">[('user_id','=',user.id)]</field>
|
|
</record>
|
|
|
|
<record id="res_partner_bank_rule" model="ir.rule">
|
|
<field name="name">Partner bank company rule</field>
|
|
<field name="model_id" ref="model_res_partner_bank"/>
|
|
<field name="domain_force">['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
|
|
</record>
|
|
|
|
<record id="res_currency_rate_rule" model="ir.rule">
|
|
<field name="name">multi-company currency rate rule</field>
|
|
<field name="model_id" ref="model_res_currency_rate"/>
|
|
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
|
</record>
|
|
|
|
<record id="change_password_rule" model="ir.rule">
|
|
<field name="name">change user password rule</field>
|
|
<field name="model_id" ref="model_change_password_user"/>
|
|
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
|
</record>
|
|
|
|
<!-- Security restriction for private addresses -->
|
|
<record id="res_partner_rule_private_employee" model="ir.rule">
|
|
<field name="name">res.partner.rule.private.employee</field>
|
|
<field name="model_id" ref="base.model_res_partner"/>
|
|
<field name="domain_force">
|
|
['|', ('type', '!=', 'private'), ('type', '=', False)]
|
|
</field>
|
|
<field name="groups" eval="[
|
|
Command.link(ref('base.group_user')),
|
|
]"/>
|
|
<field name="perm_read" eval="True"/>
|
|
<field name="perm_write" eval="True"/>
|
|
<field name="perm_create" eval="True"/>
|
|
<field name="perm_unlink" eval="True"/>
|
|
</record>
|
|
<!-- Relex previous rule for group_private_addresses -->
|
|
<record id="res_partner_rule_private_group" model="ir.rule">
|
|
<field name="name">res.partner.rule.private.group</field>
|
|
<field name="model_id" ref="base.model_res_partner"/>
|
|
<field name="domain_force">
|
|
[('type', '=', 'private')]
|
|
</field>
|
|
<field name="groups" eval="[
|
|
Command.link(ref('base.group_private_addresses'))
|
|
]"/>
|
|
<field name="perm_read" eval="True"/>
|
|
<field name="perm_write" eval="True"/>
|
|
<field name="perm_create" eval="True"/>
|
|
<field name="perm_unlink" eval="True"/>
|
|
</record>
|
|
|
|
<!-- Restrict modifications on ir.filters to owner only -->
|
|
<record id="ir_filters_admin_all_rights_rule" model="ir.rule">
|
|
<field name="name">ir.filters.admin.all.rights</field>
|
|
<field name="model_id" ref="model_ir_filters"/>
|
|
<field name="domain_force">[(1, '=', 1)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_erp_manager'))]"/>
|
|
<field name="perm_read" eval="True"/>
|
|
<field name="perm_write" eval="True"/>
|
|
<field name="perm_create" eval="True"/>
|
|
<field name="perm_unlink" eval="True"/>
|
|
</record>
|
|
|
|
<record id="ir_filters_employee_rule" model="ir.rule">
|
|
<field name="name">ir.filters.owner</field>
|
|
<field name="model_id" ref="model_ir_filters"/>
|
|
<field name="domain_force">[('user_id','in',[False,user.id])]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_user'))]"/>
|
|
<field name="perm_unlink" eval="False"/>
|
|
</record>
|
|
|
|
<record id="ir_filters_delete_own_rule" model="ir.rule">
|
|
<field name="name">ir.filters.own.rule.delete</field>
|
|
<field name="model_id" ref="model_ir_filters"/>
|
|
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_user'))]"/>
|
|
<field name="perm_read" eval="False"/>
|
|
<field name="perm_write" eval="False"/>
|
|
<field name="perm_create" eval="False"/>
|
|
</record>
|
|
|
|
<record id="ir_filters_portal_public_rule" model="ir.rule">
|
|
<field name="name">ir.filter: portal/public</field>
|
|
<field name="model_id" ref="model_ir_filters"/>
|
|
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_portal')), Command.link(ref('base.group_public'))]"/>
|
|
</record>
|
|
|
|
<!-- Record Rules For Company -->
|
|
<record id="res_company_rule_portal" model="ir.rule">
|
|
<field name="name">company rule portal</field>
|
|
<field name="model_id" ref="model_res_company"/>
|
|
<field eval="False" name="global"/>
|
|
<field name="groups" eval="[Command.set([ref('base.group_portal')])]"/>
|
|
<field name="domain_force">[('id','in', company_ids)]</field>
|
|
</record>
|
|
<record id="res_company_rule_employee" model="ir.rule">
|
|
<field name="name">company rule employee</field>
|
|
<field name="model_id" ref="model_res_company"/>
|
|
<field eval="False" name="global"/>
|
|
<field name="groups" eval="[Command.set([ref('base.group_user')])]"/>
|
|
<field name="domain_force">[('id','in', company_ids)]</field>
|
|
</record>
|
|
<record id="res_company_rule_public" model="ir.rule">
|
|
<field name="name">company rule public</field>
|
|
<field name="model_id" ref="model_res_company"/>
|
|
<field eval="False" name="global"/>
|
|
<field name="groups" eval="[Command.set([ref('base.group_public')])]"/>
|
|
<field name="domain_force">[('id','in', company_ids)]</field>
|
|
</record>
|
|
<record id="res_company_rule_erp_manager" model="ir.rule">
|
|
<field name="name">company rule erp manager</field>
|
|
<field name="model_id" ref="model_res_company"/>
|
|
<field eval="False" name="global"/>
|
|
<field name="groups" eval="[Command.set([ref('base.group_erp_manager')])]"/>
|
|
<field name="domain_force">[(1,'=',1)]</field>
|
|
</record>
|
|
|
|
<record id="res_users_identity_check" model="ir.rule">
|
|
<field name="name">users can only access their own id check</field>
|
|
<field name="model_id" ref="model_res_users_identitycheck"/>
|
|
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
|
</record>
|
|
|
|
<!-- Record Rule For User -->
|
|
<record id="res_users_rule" model="ir.rule">
|
|
<field name="name">user rule</field>
|
|
<field name="model_id" ref="model_res_users"/>
|
|
<field eval="True" name="global"/>
|
|
<field name="domain_force">['|', ('share', '=', False), ('company_ids', 'in', company_ids)]</field>
|
|
</record>
|
|
|
|
<record id="change_password_own_rule" model="ir.rule">
|
|
<field name="name">change own password</field>
|
|
<field name="model_id" ref="model_change_password_own"/>
|
|
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
|
</record>
|
|
|
|
<!-- rules for API token -->
|
|
<record id="api_key_public" model="ir.rule">
|
|
<field name="name">Public users can't interact with keys at all</field>
|
|
<field name="model_id" ref="model_res_users_apikeys"/>
|
|
<field name="domain_force">[(0, '=', 1)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_public'))]"/>
|
|
</record>
|
|
<record id="api_key_user" model="ir.rule">
|
|
<field name="name">Users can read and delete their own keys</field>
|
|
<field name="model_id" ref="model_res_users_apikeys"/>
|
|
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
|
<field name="groups" eval="[
|
|
Command.link(ref('base.group_portal')),
|
|
Command.link(ref('base.group_user')),
|
|
]"/>
|
|
</record>
|
|
<record id="api_key_admin" model="ir.rule">
|
|
<field name="name">Administrators can view user keys to revoke them</field>
|
|
<field name="model_id" ref="model_res_users_apikeys"/>
|
|
<field name="domain_force">[(1, '=', 1)]</field>
|
|
<field name="groups" eval="[Command.link(ref('base.group_system'))]"/>
|
|
</record>
|
|
</data>
|
|
</odoo>
|