ASSISTANT
Managing a complex user management structure in Odoo 17 Enterprise involves defining user groups, access rights, and record rules that align with your organizational hierarchy and localization requirements. Below is a comprehensive guide, including a Python script, that automates the creation of `access.csv`, record rules, and user groups based on your specified models, roles, and restriction rules.
**Please Note:** While I can't execute or test the script, it has been crafted based on Odoo's standard practices. You should review and test it in a development environment before deploying it to production.
---
## Overview
1. **Define User Groups:** Create groups such as `admin`, `republic`, `region`, `district`, and `mahalla`.
2. **Generate `access.csv`:** Assign CRUD permissions to each group for the specified models.
3. **Create Record Rules:** Implement hierarchy and localization restrictions.
4. **Automate with a Python Script:** Streamline the creation process.
---
## Step 1: Define User Groups
User groups determine the access levels and permissions for different roles within the system.
### Example XML for User Groups (`security/groups.xml`)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- Admin Group -->
<record id="group_admin" model="res.groups">
<field name="name">Admin</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<!-- Republic Group -->
<record id="group_republic" model="res.groups">
<field name="name">Republic</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<!-- Region Group -->
<record id="group_region" model="res.groups">
<field name="name">Region</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<!-- District Group -->
<record id="group_district" model="res.groups">
<field name="name">District</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<!-- Mahalla Group -->
<record id="group_mahalla" model="res.groups">
<field name="name">Mahalla</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
</data>
</odoo>
```
---
## Step 2: Generate `access.csv`
The `access.csv` file defines the access rights for each group on specific models.
### Access Rights Structure
Each entry in `access.csv` typically has the following columns:
```
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
```
### Example Python Script to Generate `access.csv`
```python
import csv
# Define models and groups
models = [
"firebase.token", "mobile.app.push.notification", "push.notification.log.device",
"push.notification.log.history", "announcement", "mahallam.application",
"mahallam.application.media", "mahallam.application.after.media",
"mahallam.application.category", "mahallam.application.subcategory",
"mahallam.application.state", "profile.notifications", "question",
"questionnaire", "option", "user.questionnaire", "user.question",
"user.question.option", "res.users", "change.password.user",
"change.password.wizard", "card", "contact.info", "faq", "gift",
"model.history", "mobile.translation", "mobile.onboarding.image",
"mobile.onboarding", "paylov.transaction.history", "user.device",
"res.partner", "localization.mixin"
]
groups = ["group_admin", "group_republic", "group_region", "group_district", "group_mahalla"]
# Define permissions per group
# For simplicity, let's assume:
# - Admin has all permissions
# - Republic has CRUD on certain models
# - Region has CRUD on fewer models
# - District and Mahalla have more restricted access
permissions = {
"group_admin": {"perm_read": 1, "perm_write": 1, "perm_create": 1, "perm_unlink": 1},
"group_republic": {"perm_read": 1, "perm_write": 1, "perm_create": 1, "perm_unlink": 1},
"group_region": {"perm_read": 1, "perm_write": 1, "perm_create": 1, "perm_unlink": 0},
"group_district": {"perm_read": 1, "perm_write": 0, "perm_create": 0, "perm_unlink": 0},
"group_mahalla": {"perm_read": 1, "perm_write": 0, "perm_create": 0, "perm_unlink": 0},
}
# Define model access per group (customize as needed)
group_model_access = {
"group_admin": models, # Admin has access to all models
"group_republic": [
"res.users", "res.partner", "localization.mixin", "mahallam.application"
# Add other models as needed
],
"group_region": [
"res.users", "res.partner", "localization.mixin"
# Add other models as needed
],
"group_district": [
"res.users", "res.partner"
# Add other models as needed
],
"group_mahalla": [
"res.users"
# Add other models as needed
],
}
# Generate access.csv
with open('access.csv', mode='w', newline='') as file:
writer = csv.writer(file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
# Write header
writer.writerow(['id', 'name', 'model_id:id', 'group_id:id', 'perm_read', 'perm_write', 'perm_create', 'perm_unlink'])
for group in groups:
for model in group_model_access.get(group, []):
access_id = f'access_{group}_{model.replace(".", "_")}'
name = f'Access {model} for {group}'
model_id = f'model_{model.replace(".", "_")}'
group_id = group
perms = permissions.get(group, {"perm_read":0, "perm_write":0, "perm_create":0, "perm_unlink":0})
writer.writerow([
access_id,
name,
model_id,
group_id,
perms["perm_read"],
perms["perm_write"],
perms["perm_create"],
perms["perm_unlink"],
])
print("access.csv has been generated successfully.")
```
### Explanation:
- **Models and Groups:** The script starts by defining the list of models and user groups.
- **Permissions:** It outlines the permissions each group has. Admins have full permissions, while lower roles have more restricted access.
- **Group-Model Access Mapping:** Specifies which models each group can access.
- **CSV Generation:** The script writes these configurations into an `access.csv` file following Odoo's expected format.
---
## Step 3: Create Record Rules
Record rules enforce data visibility and access based on specific conditions, such as hierarchical relationships and localization scopes.
### Example XML for Record Rules (`security/record_rules.xml`)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- Hierarchy Record Rule -->
<record id="rule_hierarchy_admin" model="ir.rule">
<field name="name">Admin Hierarchy Rule</field>
<field name="model_id" ref="model_res_users"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_admin'))]"/>
<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>
<!-- Localization Restrictions -->
<record id="rule_localization_republic" model="ir.rule">
<field name="name">Republic Localization Rule</field>
<field name="model_id" ref="model_res_partner"/>
<field name="domain_force">[('localization', '=', 'republic')]</field>
<field name="groups" eval="[(4, ref('group_republic'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="rule_localization_region" model="ir.rule">
<field name="name">Region Localization Rule</field>
<field name="model_id" ref="model_res_partner"/>
<field name="domain_force">[('localization', '=', 'region')]</field>
<field name="groups" eval="[(4, ref('group_region'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<!-- Add similar rules for district and mahalla -->
</data>
</odoo>
```
### Explanation:
- **Hierarchy Rule:** Admins have unrestricted access (`domain_force` is always `True`).
- **Localization Rules:** Restricts data visibility based on the localization scope (`republic`, `region`, etc.) for respective groups.
- **Extensibility:** You can add more rules for `district` and `mahalla` by following the same pattern.
---
## Step 4: Automate with a Python Script
While the previous steps provide a manual approach, automating the process ensures consistency and efficiency, especially when dealing with numerous models and groups.
Here's an enhanced Python script that integrates user group creation, access rights, and record rules generation.
### Comprehensive Python Script (`generate_security.py`)
```python
import csv
from jinja2 import Template
# Define models and groups
models = [
"firebase.token", "mobile.app.push.notification", "push.notification.log.device",
"push.notification.log.history", "announcement", "mahallam.application",
"mahallam.application.media", "mahallam.application.after.media",
"mahallam.application.category", "mahallam.application.subcategory",
"mahallam.application.state", "profile.notifications", "question",
"questionnaire", "option", "user.questionnaire", "user.question",
"user.question.option", "res.users", "change.password.user",
"change.password.wizard", "card", "contact.info", "faq", "gift",
"model.history", "mobile.translation", "mobile.onboarding.image",
"mobile.onboarding", "paylov.transaction.history", "user.device",
"res.partner", "localization.mixin"
]
groups = ["group_admin", "group_republic", "group_region", "group_district", "group_mahalla"]
# Define permissions per group
permissions = {
"group_admin": {"perm_read":1, "perm_write":1, "perm_create":1, "perm_unlink":1},
"group_republic": {"perm_read":1, "perm_write":1, "perm_create":1, "perm_unlink":1},
"group_region": {"perm_read":1, "perm_write":1, "perm_create":1, "perm_unlink":0},
"group_district": {"perm_read":1, "perm_write":0, "perm_create":0, "perm_unlink":0},
"group_mahalla": {"perm_read":1, "perm_write":0, "perm_create":0, "perm_unlink":0},
}
# Define model access per group
group_model_access = {
"group_admin": models,
"group_republic": [
"res.users", "res.partner", "localization.mixin", "mahallam.application"
# Add other models as needed
],
"group_region": [
"res.users", "res.partner", "localization.mixin"
# Add other models as needed
],
"group_district": [
"res.users", "res.partner"
# Add other models as needed
],
"group_mahalla": [
"res.users"
# Add other models as needed
],
}
# Generate access.csv
def generate_access_csv():
with open('access.csv', mode='w', newline='') as file:
writer = csv.writer(file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
writer.writerow(['id', 'name', 'model_id:id', 'group_id:id', 'perm_read', 'perm_write', 'perm_create', 'perm_unlink'])
for group in groups:
for model in group_model_access.get(group, []):
access_id = f'access_{group}_{model.replace(".", "_")}'
name = f'Access {model} for {group}'
model_id = f'model_{model.replace(".", "_")}'
group_id = group
perms = permissions.get(group, {"perm_read":0, "perm_write":0, "perm_create":0, "perm_unlink":0})
writer.writerow([
access_id,
name,
model_id,
group_id,
perms["perm_read"],
perms["perm_write"],
perms["perm_create"],
perms["perm_unlink"],
])
print("access.csv has been generated successfully.")
# Generate Record Rules XML using Jinja2 templates
def generate_record_rules_xml():
record_rule_template = Template("""<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- Hierarchy Record Rule -->
<record id="rule_hierarchy_admin" model="ir.rule">
<field name="name">Admin Hierarchy Rule</field>
<field name="model_id" ref="model_res_users"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_admin'))]"/>
<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>
<!-- Localization Restrictions -->
{% for group, scope in localization_scopes.items() %}
<record id="rule_localization_{{ group }}" model="ir.rule">
<field name="name">{{ group.capitalize() }} Localization Rule</field>
<field name="model_id" ref="model_res_partner"/>
<field name="domain_force">[('localization', '=', '{{ scope }}')]</field>
<field name="groups" eval="[(4, ref('{{ group }}'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="{{ 'False' if group != 'group_admin' else 'True' }}"/>
<field name="perm_create" eval="{{ 'False' if group != 'group_admin' else 'True' }}"/>
<field name="perm_unlink" eval="{{ 'False' if group != 'group_admin' else 'True' }}"/>
</record>
{% endfor %}
</data>
</odoo>
""")
localization_scopes = {
"group_republic": "republic",
"group_region": "region",
"group_district": "district",
"group_mahalla": "mahalla"
}
rendered_xml = record_rule_template.render(localization_scopes=localization_scopes)
with open('record_rules.xml', 'w', encoding='utf-8') as f:
f.write(rendered_xml)
print("record_rules.xml has been generated successfully.")
# Generate User Groups XML
def generate_groups_xml():
groups_template = Template("""<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
{% for group in groups %}
<record id="{{ group }}" model="res.groups">
<field name="name">{{ group.split('_')[1].capitalize() }}</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
{% endfor %}
</data>
</odoo>
""")
rendered_xml = groups_template.render(groups=groups)
with open('groups.xml', 'w', encoding='utf-8') as f:
f.write(rendered_xml)
print("groups.xml has been generated successfully.")
def main():
generate_groups_xml()
generate_access_csv()
generate_record_rules_xml()
print("All security files have been generated successfully.")
if __name__ == "__main__":
main()
```
### Explanation:
1. **Dependencies:** The script uses the `jinja2` library to template XML files. Install it using `pip install jinja2` if not already installed.
2. **Functions:**
- `generate_access_csv()`: Creates the `access.csv` as explained earlier.
- `generate_record_rules_xml()`: Uses a Jinja2 template to create `record_rules.xml` based on localization scopes.
- `generate_groups_xml()`: Generates `groups.xml` defining the user groups.
- `main()`: Calls the above functions in sequence.
3. **Execution:** Run the script in your project directory. It will generate `groups.xml`, `access.csv`, and `record_rules.xml`.
4. **Integration with Odoo Module:**
- Place `groups.xml` and `record_rules.xml` in your module's `security` directory.
- Update your module's `__manifest__.py` to include these files.
### Example `__manifest__.py`
```python
{
'name': 'Custom User Management',
'version': '1.0',
'depends': ['base'],
'data': [
'security/groups.xml',
'security/access.csv',
'security/record_rules.xml',
# Other data files
],
'installable': True,
'application': False,
}
```
---
## Step 5: Testing
Before deploying the script and associated files:
1. **Backup:** Always backup your database before making significant changes.
2. **Development Environment:** Test the script in a staging or development environment.
3. **Validation:** After loading the security files, verify:
- Users are assigned to the correct groups.
- Access rights align with the defined permissions.
- Record rules appropriately restrict data visibility.
---
## Conclusion
By following the steps above and utilizing the provided Python script, you can automate the creation of user groups, access rights, and record rules in Odoo 17 Enterprise. This ensures a scalable and maintainable security structure that aligns with your organizational hierarchy and localization requirements.
**Remember:** Always validate and test security configurations in a safe environment to prevent unintended access issues.