Authorization
Bases: str, Enum
The built-in actions SQLAdmin asks about.
Members are plain strings as well, so Action.EDIT == "edit" and a grant
stored as ("user", "edit") matches ("user", Action.EDIT) -- use
whichever reads better.
Usage
from sqladmin.authorization import Action
grants = {("user", Action.LIST), ("user", Action.EDIT)}
Custom actions declared with @action are
asked about as "action:<slug>" -- see
custom_action.
Source code in sqladmin/authorization.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
Every Action, in display order.
Return the action name used for a custom @action endpoint.
Source code in sqladmin/authorization.py
65 66 67 68 | |
Bases: ABC
Base class for deciding what the current user may do.
Where AuthenticationBackend
answers "who is this request", this answers "may they do this". Subclass
it and implement
has_permission,
then pass an instance as Admin(authorization_backend=...).
Usage
class RoleAuthorization(AuthorizationBackend):
def has_permission(self, request, identity, action, obj=None):
role = request.session.get("role")
return role == "admin" or action in ("list", "details")
admin = Admin(app, engine, authorization_backend=RoleAuthorization())
Without one, Admin uses
AllowAllAuthorizationBackend.
Source code in sqladmin/authorization.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | |
setup(admin)
Check that admin is configured the way this backend needs.
Called once, when the Admin is created. Raise
ImproperlyConfigured to
report a mistake at startup rather than on the first request.
Does nothing by default.
Source code in sqladmin/authorization.py
115 116 117 118 119 120 121 122 123 | |
load(request)
async
Prepare per-request authorization state.
Called once per request, right after authentication succeeds and before
any permission is checked. This is where I/O belongs -- query the
database, call an external service -- storing the result on
request.state for has_permission to read.
Does nothing by default.
Source code in sqladmin/authorization.py
125 126 127 128 129 130 131 132 133 134 | |
has_permission(request, identity, action, obj=None)
abstractmethod
Return whether the current user may perform action on identity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
Request
|
The current request. |
required |
identity
|
str
|
The |
required |
action
|
str
|
One of |
required |
obj
|
Any | None
|
The specific object being acted on, when there is one. Passed
for |
None
|
This method is called many times while rendering a single page -- once
per row on the list page -- so it must be cheap and must not perform
I/O. Do the lookups in
load.
Source code in sqladmin/authorization.py
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | |
Bases: AuthorizationBackend
The backend Admin uses when none is configured: it allows everything.
With it, only the can_* flags and your own is_accessible /
check_can_* overrides decide what users may do.
Source code in sqladmin/authorization.py
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
Bases: AuthorizationBackend
An AuthorizationBackend backed by a set of (identity, action) grants.
Subclasses implement
get_grants;
this class loads them once per request and matches them, wildcards
included. A superuser is simply granted ("*", "*").
Usage
class SessionAuthorization(GrantsAuthorizationBackend):
async def get_grants(self, request):
if request.session.get("is_admin"):
return {("*", "*")}
# "user:action:deactivate" -> ("user", "action:deactivate")
return {tuple(g.split(":", 1)) for g in request.session["grants"]}
Source code in sqladmin/authorization.py
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
get_grants(request)
abstractmethod
async
Return the (identity, action) pairs granted to the current user.
Called once per request from load. Either element of a pair may be
[WILDCARD][sqladmin.authorization.WILDCARD]; return
{("*", "*")} for a user who may do everything.
Source code in sqladmin/authorization.py
199 200 201 202 203 204 205 206 | |
Check (identity, action) against a set of grants, honouring wildcards.
A grant of ("*", "edit") allows editing every view, ("user", "*")
allows every action on the user view, and ("*", "*") allows
everything.
Source code in sqladmin/authorization.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
Bases: GrantsAuthorizationBackend
Resolve a user's grants from the group tables, once per request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_maker
|
Any
|
A sync or async sessionmaker. |
required |
user_model
|
Any
|
Your user model. Required, keyword-only. |
required |
groups_attr
|
str
|
Relationship on the user model pointing at groups. |
'groups'
|
accesses_attr
|
str
|
Relationship on the group model pointing at grants. |
'accesses'
|
superuser_attr
|
str
|
Boolean attribute on the user model that bypasses all
grant checks. Missing attributes are treated as |
'is_superuser'
|
Usage
backend = DBAuthorizationBackend(session_maker, user_model=User)
admin = Admin(app, engine, authorization_backend=backend)
The user id comes from
AuthenticationBackend.get_user_id.
Superusers are granted ("*", "*"). Without an
Admin(authentication_backend=...) there is no user id to look up, so
creating the Admin fails with
ImproperlyConfigured.
Source code in sqladmin/contrib/rbac.py
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | |
__init__(session_maker, *, user_model, groups_attr='groups', accesses_attr='accesses', superuser_attr='is_superuser')
Source code in sqladmin/contrib/rbac.py
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | |
Declarative mixin for the group table.
Mix into your own Base and set __tablename__. If you name the access
model something other than GroupAccess, set __access_model__ to
match.
Source code in sqladmin/contrib/rbac.py
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
Declarative mixin for one (identity, action) grant on a group.
Mix into your own Base and set __tablename__. Set __group_table__
and __group_model__ if your group table or class is not named
admin_groups / Group.
Source code in sqladmin/contrib/rbac.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
Build the user-to-group association table.
This is a factory rather than a mixin because it needs your user table's name and primary key type, which SQLAdmin cannot know.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
Any
|
Your declarative base (its |
required |
user_table
|
str
|
Name of your users table, e.g. |
required |
user_pk_column
|
str
|
Primary key column on that table. |
'id'
|
user_pk_type
|
Any
|
SQLAlchemy type of that column -- |
Integer
|
group_table
|
str
|
Name of the group table. |
DEFAULT_GROUP_TABLE
|
table_name
|
str
|
Name for the association table itself. |
'admin_user_groups'
|
Source code in sqladmin/contrib/rbac.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | |
Bases: ModelView
Admin view for editing a group and its permissions.
Subclass it with your group model::
class MyGroupAdmin(GroupAdmin, model=Group):
pass
The permission matrix replaces the raw list of grant rows: one row per registered view, one checkbox per action.
Saving only touches the grants the matrix offers. Anything else stored on
the group -- wildcard grants such as ("*", "*"), or grants for an action
a view has since disabled -- is left alone.
By default an editor may only grant permissions they hold themselves
(superusers hold all of them), so edit access to this view cannot be
turned into more access than the editor already has. Ticking any other box
fails with a form error and nothing is saved. Override
can_grant to change that
rule. Removing grants is not restricted beyond access to this view:
who may edit or delete groups is decided by the group permissions.
The grants are written in the same transaction as the group, and the
resulting grants are recorded in the audit entry under the permissions
field. If you override on_model_change or after_model_change, call
super().
Source code in sqladmin/contrib/rbac.py
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | |
can_grant(request, identity, action)
Return whether the current user may give (identity, action) to a group.
Called for every newly ticked box when a group is saved. The default allows only permissions the user holds themselves, so nobody can use this screen to gain access they do not already have.
Usage
class MyGroupAdmin(GroupAdmin, model=Group):
def can_grant(self, request, identity, action):
# HR assigns billing access without using it themselves.
if identity == "billing":
return self.has_permission(request, "edit")
return super().can_grant(request, identity, action)
Source code in sqladmin/contrib/rbac.py
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | |
Bases: SQLAdminException, PermissionError
A group editor tried to grant a permission they may not grant.
See GroupAdmin.can_grant.
Source code in sqladmin/exceptions.py
31 32 33 34 35 | |
Bases: ImproperlyConfigured
A model has no relationship under the name SQLAdmin was told to use.
Raised at startup when an RBAC model or view is misconfigured, e.g. when
DBAuthorizationBackend(groups_attr=...) names a missing relationship.
Source code in sqladmin/exceptions.py
23 24 25 26 27 28 | |
Bases: SQLAdminException, ValueError
SQLAdmin was set up in a way that cannot work.
Raised as soon as the mistake can be detected: usually when the view or
backend is created, or on the first admin request when it depends on how
the Admin was configured. Also a ValueError, so existing
except ValueError handlers still work.
Source code in sqladmin/exceptions.py
13 14 15 16 17 18 19 20 | |