0
# RBAC and Permissions
1
2
JupyterHub implements a comprehensive Role-Based Access Control (RBAC) system with fine-grained permissions through scopes. The system supports roles that can be assigned to users, groups, services, and API tokens, providing flexible authorization for all JupyterHub operations.
3
4
## Capabilities
5
6
### Role Management
7
8
Core functions for managing roles in the RBAC system.
9
10
```python { .api }
11
def get_default_roles() -> Dict[str, Dict[str, Any]]:
12
"""
13
Get the default role definitions for JupyterHub.
14
15
Returns:
16
Dictionary of default roles with their scopes and descriptions:
17
- 'user': Basic user permissions
18
- 'admin': Full system administrator permissions
19
- 'server': Permissions for single-user servers
20
- 'token': Permissions for API tokens
21
"""
22
23
# Default roles structure
24
DEFAULT_ROLES = {
25
'user': {
26
'description': 'Default permissions for users',
27
'scopes': [
28
'self',
29
'servers!user',
30
'access:servers!user'
31
]
32
},
33
'admin': {
34
'description': 'Unrestricted administrative privileges',
35
'scopes': ['admin:*']
36
},
37
'server': {
38
'description': 'Permissions for single-user servers',
39
'scopes': [
40
'access:servers!user',
41
'read:users:activity!user',
42
'read:servers!user'
43
]
44
},
45
'token': {
46
'description': 'Default permissions for API tokens',
47
'scopes': ['identify']
48
}
49
}
50
```
51
52
### Scope Definitions
53
54
Complete scope system defining all available permissions in JupyterHub.
55
56
```python { .api }
57
# Dictionary of all available scopes with descriptions
58
scope_definitions: Dict[str, str] = {
59
# Administrative scopes
60
'admin:*': 'Full administrative privileges (all permissions)',
61
'admin:users': 'Full user administration',
62
'admin:groups': 'Full group administration',
63
'admin:servers': 'Full server administration',
64
'admin:auth': 'Authentication administration',
65
66
# User management scopes
67
'users': 'Full user management (read/write)',
68
'users:activity': 'Read user activity information',
69
'read:users': 'Read user information',
70
'read:users:name': 'Read usernames',
71
'read:users:groups': 'Read user group membership',
72
'read:users:activity': 'Read user activity timestamps',
73
74
# Group management scopes
75
'groups': 'Full group management (read/write)',
76
'read:groups': 'Read group information',
77
'read:groups:name': 'Read group names',
78
79
# Server management scopes
80
'servers': 'Full server management (read/write/delete)',
81
'read:servers': 'Read server information',
82
'access:servers': 'Access servers (connect to running servers)',
83
'delete:servers': 'Stop/delete servers',
84
85
# Service scopes
86
'services': 'Full service management',
87
'read:services': 'Read service information',
88
89
# Role and token scopes
90
'roles': 'Full role management',
91
'read:roles': 'Read role information',
92
'tokens': 'Full token management',
93
'read:tokens': 'Read token information',
94
95
# Proxy scopes
96
'proxy': 'Full proxy management',
97
'read:proxy': 'Read proxy routing information',
98
99
# Hub scopes
100
'read:hub': 'Read hub information',
101
'shutdown': 'Shutdown the hub',
102
103
# Special scopes
104
'self': 'User\'s own resources (expands to user-specific scopes)',
105
'identify': 'Identify the token owner',
106
'inherit': 'Inherit permissions from user/service'
107
}
108
109
# Scope expansion functions
110
def expand_scopes(*scopes) -> Set[str]:
111
"""
112
Expand scope patterns into concrete scopes.
113
114
Args:
115
*scopes: Variable number of scope strings
116
117
Returns:
118
Set of expanded concrete scopes
119
"""
120
121
def check_scopes(required_scopes, available_scopes) -> bool:
122
"""
123
Check if available scopes satisfy required scopes.
124
125
Args:
126
required_scopes: Scopes needed for operation
127
available_scopes: Scopes granted to user/token
128
129
Returns:
130
True if access should be granted
131
"""
132
133
def describe_scope(scope: str) -> str:
134
"""
135
Get human-readable description of a scope.
136
137
Args:
138
scope: Scope string
139
140
Returns:
141
Description string
142
"""
143
```
144
145
### Role Assignment and Inheritance
146
147
Functions for assigning roles and managing permission inheritance.
148
149
```python { .api }
150
class Role(Base):
151
"""Role database model with permission management"""
152
153
name: str
154
description: str
155
scopes: List[str]
156
157
def has_scope(self, scope: str) -> bool:
158
"""
159
Check if role has specific scope.
160
161
Args:
162
scope: Scope to check
163
164
Returns:
165
True if role grants this scope
166
"""
167
168
def grant_scope(self, scope: str) -> None:
169
"""
170
Grant a scope to this role.
171
172
Args:
173
scope: Scope to grant
174
"""
175
176
def revoke_scope(self, scope: str) -> None:
177
"""
178
Revoke a scope from this role.
179
180
Args:
181
scope: Scope to revoke
182
"""
183
184
# Permission inheritance functions
185
def get_user_scopes(user: User) -> Set[str]:
186
"""
187
Get all scopes for a user (direct + inherited from groups/roles).
188
189
Args:
190
user: User object
191
192
Returns:
193
Set of all scopes available to user
194
"""
195
196
def get_token_scopes(token: APIToken) -> Set[str]:
197
"""
198
Get all scopes for an API token.
199
200
Args:
201
token: API token object
202
203
Returns:
204
Set of scopes granted to token
205
"""
206
```
207
208
## Usage Examples
209
210
### Basic Role Assignment
211
212
```python
213
from jupyterhub.orm import User, Group, Role
214
from jupyterhub.scopes import get_default_roles
215
216
# Create custom role
217
instructor_role = Role(
218
name='instructor',
219
description='Course instructor permissions',
220
scopes=[
221
'read:users',
222
'read:users:activity',
223
'servers!group=students',
224
'access:servers!group=students'
225
]
226
)
227
db.add(instructor_role)
228
229
# Assign role to user
230
instructor = db.query(User).filter(User.name == 'prof_smith').first()
231
instructor.roles.append(instructor_role)
232
db.commit()
233
```
234
235
### Group-based Permissions
236
237
```python
238
# Create student group with role
239
students_group = Group(name='students')
240
student_role = Role(
241
name='student',
242
description='Student permissions',
243
scopes=[
244
'self', # Own resources
245
'read:groups:name!group=students' # Read group member names
246
]
247
)
248
students_group.roles.append(student_role)
249
250
# Add users to group
251
alice = db.query(User).filter(User.name == 'alice').first()
252
students_group.users.append(alice)
253
db.commit()
254
```
255
256
### Service Permissions
257
258
```python
259
# Create service with specific permissions
260
monitoring_service = Service(
261
name='monitoring',
262
url='http://localhost:8002'
263
)
264
265
monitoring_role = Role(
266
name='monitoring-service',
267
description='Monitoring service permissions',
268
scopes=[
269
'read:users:activity',
270
'read:servers',
271
'read:hub'
272
]
273
)
274
monitoring_service.roles.append(monitoring_role)
275
276
# Create API token for service
277
token = monitoring_service.new_api_token(
278
note='Monitoring service token',
279
roles=['monitoring-service']
280
)
281
db.commit()
282
```
283
284
### Custom Scope Patterns
285
286
```python
287
# Scoped permissions using filters
288
custom_scopes = [
289
'servers!user=alice', # Alice's servers only
290
'read:users!group=students', # Users in students group
291
'access:servers!server=shared-*', # Servers with shared- prefix
292
'admin:users!user!=admin' # All users except admin
293
]
294
295
# Create role with scoped permissions
296
ta_role = Role(
297
name='teaching-assistant',
298
description='TA permissions for specific course',
299
scopes=[
300
'read:users!group=cs101-students',
301
'servers!group=cs101-students',
302
'read:users:activity!group=cs101-students'
303
]
304
)
305
```
306
307
### Token Scoping
308
309
```python
310
# Create limited-scope API token
311
user = db.query(User).filter(User.name == 'researcher').first()
312
limited_token = user.new_api_token(
313
note='Data analysis token',
314
scopes=[
315
'read:servers!user', # Only own servers
316
'access:servers!user', # Access own servers
317
'read:users:name' # Read usernames for sharing
318
],
319
expires_in=3600 # 1 hour expiration
320
)
321
```
322
323
## Advanced RBAC Patterns
324
325
### Hierarchical Permissions
326
327
```python
328
# Create hierarchy of roles
329
roles_hierarchy = {
330
'student': [
331
'self',
332
'read:groups:name'
333
],
334
'ta': [
335
'inherit:student', # Inherit student permissions
336
'read:users!group=assigned-students',
337
'servers!group=assigned-students'
338
],
339
'instructor': [
340
'inherit:ta', # Inherit TA permissions
341
'admin:users!group=course-students',
342
'admin:servers!group=course-students'
343
],
344
'admin': [
345
'admin:*' # Full admin access
346
]
347
}
348
349
# Apply hierarchical roles
350
for role_name, scopes in roles_hierarchy.items():
351
role = Role(name=role_name, scopes=scopes)
352
db.add(role)
353
```
354
355
### Conditional Permissions
356
357
```python
358
class ConditionalRole(Role):
359
"""Role with time-based or context-based conditions"""
360
361
conditions: Dict[str, Any] # Additional conditions
362
363
def check_conditions(self, context: Dict[str, Any]) -> bool:
364
"""
365
Check if role conditions are met.
366
367
Args:
368
context: Current request context
369
370
Returns:
371
True if conditions are satisfied
372
"""
373
# Time-based conditions
374
if 'time_range' in self.conditions:
375
current_time = datetime.now().time()
376
start, end = self.conditions['time_range']
377
if not (start <= current_time <= end):
378
return False
379
380
# IP-based conditions
381
if 'allowed_ips' in self.conditions:
382
client_ip = context.get('client_ip')
383
if client_ip not in self.conditions['allowed_ips']:
384
return False
385
386
return True
387
388
# Usage with conditions
389
lab_access_role = ConditionalRole(
390
name='lab-access',
391
scopes=['access:servers!server=lab-*'],
392
conditions={
393
'time_range': (time(9, 0), time(17, 0)), # 9 AM - 5 PM
394
'allowed_ips': ['192.168.1.0/24'] # Lab network only
395
}
396
)
397
```
398
399
### Dynamic Role Assignment
400
401
```python
402
async def assign_dynamic_roles(user: User, authenticator_data: Dict[str, Any]):
403
"""
404
Dynamically assign roles based on authentication data.
405
406
Args:
407
user: User object
408
authenticator_data: Data from authenticator (LDAP groups, OAuth claims, etc.)
409
"""
410
411
# Clear existing dynamic roles
412
user.roles = [role for role in user.roles if not role.name.startswith('dynamic-')]
413
414
# Assign roles based on LDAP groups
415
ldap_groups = authenticator_data.get('groups', [])
416
417
role_mapping = {
418
'faculty': 'instructor',
419
'graduate_students': 'ta',
420
'undergraduate_students': 'student'
421
}
422
423
for ldap_group, role_name in role_mapping.items():
424
if ldap_group in ldap_groups:
425
role = db.query(Role).filter(Role.name == role_name).first()
426
if role:
427
user.roles.append(role)
428
429
db.commit()
430
```
431
432
## Permission Checking
433
434
### API Permission Decorators
435
436
```python
437
from jupyterhub.scopes import needs_scope
438
439
class CustomAPIHandler(APIHandler):
440
"""API handler with scope-based permissions"""
441
442
@needs_scope('read:users')
443
def get(self):
444
"""Endpoint requiring read:users scope"""
445
users = self.db.query(User).all()
446
self.write({'users': [user.name for user in users]})
447
448
@needs_scope('admin:users')
449
def post(self):
450
"""Endpoint requiring admin:users scope"""
451
user_data = self.get_json_body()
452
user = User(**user_data)
453
self.db.add(user)
454
self.db.commit()
455
```
456
457
### Programmatic Permission Checks
458
459
```python
460
from jupyterhub.scopes import check_scopes
461
462
def check_user_permission(user: User, required_scope: str, resource: str = None) -> bool:
463
"""
464
Check if user has permission for specific operation.
465
466
Args:
467
user: User object
468
required_scope: Scope needed for operation
469
resource: Optional resource identifier
470
471
Returns:
472
True if user has permission
473
"""
474
user_scopes = get_user_scopes(user)
475
476
# Add resource context if provided
477
if resource:
478
required_scope = f"{required_scope}!{resource}"
479
480
return check_scopes([required_scope], user_scopes)
481
482
# Usage
483
if check_user_permission(user, 'servers', 'user=alice'):
484
# User can manage Alice's servers
485
pass
486
```
487
488
## Configuration Examples
489
490
### Role-based Hub Configuration
491
492
```python
493
# jupyterhub_config.py
494
495
# Define custom roles
496
c.JupyterHub.custom_roles = [
497
{
498
'name': 'instructor',
499
'description': 'Course instructor permissions',
500
'scopes': [
501
'read:users!group=students',
502
'servers!group=students',
503
'admin:users!group=students'
504
]
505
},
506
{
507
'name': 'grader',
508
'description': 'Grading assistant permissions',
509
'scopes': [
510
'read:users!group=students',
511
'access:servers!group=students'
512
]
513
}
514
]
515
516
# Load balancing with role-based spawning
517
c.JupyterHub.load_roles = [
518
{
519
'name': 'instructor',
520
'users': ['prof_smith', 'prof_jones'],
521
'scopes': ['inherit:instructor']
522
},
523
{
524
'name': 'grader',
525
'groups': ['teaching-assistants'],
526
'scopes': ['inherit:grader']
527
}
528
]
529
```