Access Control
Contents
Access Control#
This guide is for sys admins configuring user access to OpenSPP.
OpenSPP uses a three-tier Role-Based Access Control (RBAC) system. You assign users to groups that control what they can see and do.
Architecture Overview#
TIER 1: ROLES (Composite - Optional)
├── SPP Field Officer → Registry + Change Request + Program viewing
├── SPP Program Officer → Program management + Entitlements
└── SPP Supervisor → All domains (manager level)
TIER 2: FUNCTIONAL PRIVILEGES (What Users See)
├── Registry
│ ├── Viewer → Read only
│ ├── Officer → Create and edit
│ └── Manager → Full access + delete
├── Programs
│ ├── Viewer
│ ├── Officer
│ └── Manager
└── Other domains (GRM, Approvals, etc.)
TIER 3: BASE PERMISSIONS (Technical)
├── Registry: Read, Write, Create, Delete
├── Programs: Read, Write, Create
└── API: Read, Write
Security Modules#
OpenSPP's access control is split across modules:
Module |
What It Provides |
|---|---|
|
Category hierarchy, admin group |
|
Registry domain groups (always installed) |
|
Program domain groups (if using programs) |
|
GRM domain groups (if using case management) |
|
Pre-built composite roles (optional) |
Key principle: Groups only exist when their domain module is installed. If you don't install programs, users won't see program privileges.
Assigning User Access#
Via Web UI#
Navigate to Settings → Users & Companies → Users
Select the user
Go to the OpenSPP tab
Enable privileges based on role:
For This Role |
Enable These Privileges |
|---|---|
Field Officer |
Registry Officer, Change Request Agent, Programs Viewer |
Data Entry Clerk |
Registry Officer |
Program Officer |
Programs Officer, Registry Viewer |
Supervisor |
Registry Manager, Programs Manager, GRM Manager |
System Admin |
OpenSPP Administrator |
Via Command Line#
Create users with groups assigned:
# Using Odoo shell
odoo-bin shell -d openspp_prod -c /etc/odoo/odoo.conf
# In Python shell
user = env['res.users'].create({
'name': 'Jane Doe',
'login': 'jane.doe@example.org',
'email': 'jane.doe@example.org',
'groups_id': [(6, 0, [
env.ref('spp_registry_base.group_registry_officer').id,
env.ref('spp_change_request.group_cr_agent').id,
])]
})
Via XML Data#
<!-- In a custom module -->
<record id="user_field_officer_1" model="res.users">
<field name="name">Field Officer</field>
<field name="login">officer@example.org</field>
<field name="groups_id" eval="[
Command.link(ref('spp_registry_base.group_registry_officer')),
Command.link(ref('spp_change_request.group_cr_agent')),
Command.link(ref('spp_programs_base.group_program_viewer')),
]"/>
</record>
Domain Groups Reference#
Registry Domain#
Always available (core module).
Group |
Can Do |
XML ID |
|---|---|---|
Viewer |
View beneficiaries and groups |
|
Officer |
Create and edit beneficiaries |
|
Manager |
Full access including delete |
|
Programs Domain#
Available when spp_programs_base installed.
Group |
Can Do |
XML ID |
|---|---|---|
Viewer |
View programs and cycles |
|
Officer |
Manage cycles and enrollment |
|
Manager |
Full program configuration |
|
Change Request Domain#
Available when spp_change_request installed.
Group |
Can Do |
XML ID |
|---|---|---|
Agent |
Submit change requests |
|
Validator |
Review and approve CRs |
|
Admin |
Full CR configuration |
|
GRM Domain#
Available when spp_grm installed.
Group |
Can Do |
XML ID |
|---|---|---|
Viewer |
View cases/tickets |
|
Officer |
Create and manage cases |
|
Manager |
Full GRM access |
|
API Access#
Available when API modules installed.
Group |
Can Do |
XML ID |
|---|---|---|
Read |
GET requests only |
|
Write |
POST, PUT, DELETE |
|
Note: API users need BOTH api access AND functional domain access (e.g., group_api_read + group_registry_viewer).
Administrative Access#
Group |
Purpose |
XML ID |
|---|---|---|
OpenSPP Administrator |
Full system access |
|
Odoo System |
Technical settings |
|
Warning: OpenSPP Admin gets all OpenSPP groups automatically. Only assign to trusted users.
Checking User Permissions#
From Shell#
# Check user's groups
odoo-bin shell -d openspp_prod
user = env['res.users'].search([('login', '=', 'jane.doe@example.org')])
for group in user.groups_id:
print(f" - {group.full_name}")
# Check if user has specific group
user.has_group('spp_registry_base.group_registry_officer') # True/False
From Database#
-- List user's groups
SELECT u.login, g.name
FROM res_users u
JOIN res_groups_users_rel r ON r.uid = u.id
JOIN res_groups g ON g.id = r.gid
WHERE u.login = 'jane.doe@example.org'
ORDER BY g.name;
-- Count users per group
SELECT g.name, COUNT(r.uid) as user_count
FROM res_groups g
LEFT JOIN res_groups_users_rel r ON r.gid = g.id
WHERE g.name LIKE 'SPP%' OR g.name LIKE '%Registry%'
GROUP BY g.name
ORDER BY user_count DESC;
Record Rules (Row-Level Security)#
Record rules filter which records users can see.
Multi-Company Rule#
Users only see records from their company:
# Automatically applied to most models
domain: [
'|',
('company_id', '=', False),
('company_id', 'in', company_ids)
]
Own Records Only#
Change Request agents only see their own requests:
# Applied to CR agents
domain: [('created_by', '=', user.id)]
Role-Based Visibility#
CR validators see all requests:
# Applied to CR validators
domain: [(1, '=', 1)] # See all
Auditing Access Changes#
All group changes are logged via Odoo's chatter:
# View group assignment history
odoo-bin shell -d openspp_prod
user = env['res.users'].search([('login', '=', 'jane.doe@example.org')])
for message in user.message_ids.filtered(lambda m: 'groups_id' in m.body):
print(f"{message.date}: {message.body}")
Troubleshooting#
User can't see menu item
Check privilege assignment:
user = env['res.users'].search([('login', '=', 'user@example.org')])
print("Groups:", [g.name for g in user.groups_id])
# Menu requires this group:
menu = env.ref('spp_registry_base.menu_registry')
print("Menu groups:", [g.name for g in menu.groups_id])
User can't access specific model
Check ACL (Access Control List):
# Find ACL entries for model
acls = env['ir.model.access'].search([
('model_id.model', '=', 'res.partner')
])
for acl in acls:
print(f"{acl.name}: {acl.group_id.name} - Read:{acl.perm_read} Write:{acl.perm_write}")
User sees records they shouldn't
Check record rules:
# Find rules for model
rules = env['ir.rule'].search([
('model_id.model', '=', 'spp.change.request')
])
for rule in rules:
print(f"{rule.name}:")
print(f" Groups: {[g.name for g in rule.groups]}")
print(f" Domain: {rule.domain_force}")
Security Checklist#
Before production deployment:
[ ] All users assigned to appropriate groups
[ ] No users with System Admin unless necessary
[ ] Multi-company rules enabled
[ ] Record rules tested for each role
[ ] API users have limited scope
[ ] Group assignments logged and audited
[ ] Test users can only see their own company data
[ ] Field-level security configured (see Data Classification)
openspp.org