Add the get_permissions plugin hook (contract 0.10.0)
Plugins declare their own RBAC permissions instead of core accumulating them: 36 permissions moved out of the core catalog into the 9 owning plugins (core keeps the 19 its own blueprints enforce). The catalog is resolved dynamically (core + enabled plugins) and feeds the roles grid, the token scope picker and ceiling, and flask seed permissions; installing or enabling a plugin seeds its permissions automatically. A disabled plugin drops out of the assignable catalog while existing role links keep working. New plugins - bundled or external - now bring their permissions with zero core edits. 781 tests pass; live-verified with a machines.edit-scoped token. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -330,20 +330,27 @@ def create_tool():
|
||||
...
|
||||
```
|
||||
|
||||
The `measuringtools.*` permissions are seeded exactly the way warranty seeds its
|
||||
own, by adding them to `Permission.PERMISSIONS` in `shopdb/core/models/user.py`:
|
||||
The `measuringtools.*` permissions belong to the plugin, not to core. The plugin
|
||||
declares them from the `get_permissions` hook (contract 0.10.0) so core never edits
|
||||
its catalog to accommodate a plugin:
|
||||
|
||||
```python
|
||||
# Measuring tools
|
||||
('measuringtools.view', 'View measuring tools', 'measuringtools'),
|
||||
('measuringtools.create', 'Create measuring tools', 'measuringtools'),
|
||||
('measuringtools.edit', 'Edit measuring tools', 'measuringtools'),
|
||||
('measuringtools.delete', 'Delete measuring tools', 'measuringtools'),
|
||||
class MeasuringToolsPlugin(BasePlugin):
|
||||
def get_permissions(self):
|
||||
return [
|
||||
('measuringtools.view', 'View measuring tools', 'measuringtools'),
|
||||
('measuringtools.create', 'Create measuring tools', 'measuringtools'),
|
||||
('measuringtools.edit', 'Edit measuring tools', 'measuringtools'),
|
||||
('measuringtools.delete', 'Delete measuring tools', 'measuringtools'),
|
||||
]
|
||||
```
|
||||
|
||||
`flask seed permissions` is idempotent, so re-running it just adds the four new
|
||||
rows. The `admin` role bypasses every permission check, so an admin can operate the
|
||||
plugin before anyone grants the granular permissions.
|
||||
Installing or enabling the plugin seeds these rows automatically, and
|
||||
`flask seed permissions` (which now seeds core plus every enabled plugin) is
|
||||
idempotent, so re-running it just adds any missing rows. The `admin` role bypasses
|
||||
every permission check, so an admin can operate the plugin before anyone grants the
|
||||
granular permissions. See `get_permissions` in `docs/PLUGIN-HOOKS.md` for the
|
||||
disabled-plugin edge case.
|
||||
|
||||
**Responses use the framework helpers.** `success_response`, `error_response` (with
|
||||
`ErrorCodes`), and `paginated_response` produce the standard envelope
|
||||
@@ -666,7 +673,7 @@ When you build a plugin, confirm all of this before you call it done:
|
||||
- [ ] Imports only from `shopdb.api` and `shopdb.plugins.base` (contract test green).
|
||||
- [ ] Blueprint: jwt-optional reads, permission-gated writes; framework response and
|
||||
pagination helpers; audit logs on writes.
|
||||
- [ ] Permissions added to `Permission.PERMISSIONS`; `flask seed permissions` run.
|
||||
- [ ] Permissions declared from the `get_permissions` hook; install/enable (or `flask seed permissions`) seeds them.
|
||||
- [ ] `on_install` seeds the asset type and any reference data, idempotently.
|
||||
- [ ] Hooks: navigation, reports, models implemented; config schema and collector
|
||||
implemented or consciously skipped with a reason.
|
||||
|
||||
Reference in New Issue
Block a user