0
# Database Models and ORM
1
2
JupyterHub uses SQLAlchemy ORM for database operations, providing persistent storage for users, servers, groups, roles, services, and authentication state. The database models support the full JupyterHub feature set including role-based access control, server sharing, and OAuth integration.
3
4
## Capabilities
5
6
### Core User Management Models
7
8
Database models for managing users and their associated data.
9
10
```python { .api }
11
class User(Base):
12
"""
13
Database model for JupyterHub users.
14
15
Represents a user account with servers, groups, roles, and tokens.
16
"""
17
18
# Primary attributes
19
id: int # Primary key
20
name: str # Username (unique)
21
admin: bool # Whether user is an admin
22
created: datetime # When user was created
23
last_activity: datetime # Last recorded activity
24
25
# Authentication state
26
cookie_id: str # Cookie identifier
27
state: Dict[str, Any] # Arbitrary state dictionary
28
encrypted_auth_state: bytes # Encrypted authentication state
29
30
# Relationships
31
servers: List[Server] # User's servers
32
api_tokens: List[APIToken] # User's API tokens
33
oauth_tokens: List[APIToken] # OAuth tokens
34
groups: List[Group] # Groups user belongs to
35
roles: List[Role] # Roles assigned to user
36
37
def new_api_token(self, token=None, roles=None, scopes=None, note=None, expires_in=None):
38
"""
39
Create a new API token for this user.
40
41
Args:
42
token: Token string (generated if None)
43
roles: List of role names
44
scopes: List of scopes
45
note: Description of token
46
expires_in: Expiration time in seconds
47
48
Returns:
49
APIToken object
50
"""
51
52
def get_server(self, name=''):
53
"""
54
Get a named server for this user.
55
56
Args:
57
name: Server name (empty string for default server)
58
59
Returns:
60
Server object or None
61
"""
62
63
@property
64
def escaped_name(self) -> str:
65
"""URL-escaped username for safe use in URLs"""
66
67
@property
68
def auth_state(self) -> Dict[str, Any]:
69
"""Decrypted authentication state"""
70
71
class Server(Base):
72
"""
73
Database model for user notebook servers.
74
75
Represents a single-user notebook server instance.
76
"""
77
78
# Primary attributes
79
id: int # Primary key
80
name: str # Server name (empty for default)
81
url: str # Server URL
82
bind_url: str # Internal bind URL
83
last_activity: datetime # Last recorded activity
84
started: datetime # When server was started
85
base_url: str # Base URL prefix
86
87
# State and configuration
88
state: Dict[str, Any] # Spawner state
89
90
# Relationships
91
user_id: int # Foreign key to User
92
user: User # User who owns this server
93
oauth_tokens: List[APIToken] # OAuth tokens for this server
94
95
@property
96
def ready(self) -> bool:
97
"""Whether server is ready to accept connections"""
98
99
@property
100
def pending(self) -> bool:
101
"""Whether server is starting up"""
102
103
def stop(self):
104
"""Mark server as stopped"""
105
```
106
107
### Group and Role Models
108
109
Models for organizing users and managing permissions.
110
111
```python { .api }
112
class Group(Base):
113
"""
114
Database model for user groups.
115
116
Groups provide a way to organize users and assign permissions.
117
"""
118
119
# Primary attributes
120
id: int # Primary key
121
name: str # Group name (unique)
122
description: str # Group description
123
124
# Relationships
125
users: List[User] # Users in this group
126
roles: List[Role] # Roles assigned to this group
127
128
@property
129
def member_names(self) -> List[str]:
130
"""List of usernames in this group"""
131
132
class Role(Base):
133
"""
134
Database model for roles in the RBAC system.
135
136
Roles define collections of scopes that can be assigned to users,
137
groups, services, and tokens.
138
"""
139
140
# Primary attributes
141
id: int # Primary key
142
name: str # Role name (unique)
143
description: str # Role description
144
145
# Permissions
146
scopes: List[str] # List of scope strings
147
148
# Relationships
149
users: List[User] # Users with this role
150
groups: List[Group] # Groups with this role
151
services: List[Service] # Services with this role
152
tokens: List[APIToken] # Tokens with this role
153
154
@classmethod
155
def find(cls, db, name):
156
"""
157
Find role by name.
158
159
Args:
160
db: Database session
161
name: Role name
162
163
Returns:
164
Role object or None
165
"""
166
```
167
168
### Service and Authentication Models
169
170
Models for external services and authentication tokens.
171
172
```python { .api }
173
class Service(Base):
174
"""
175
Database model for JupyterHub services.
176
177
Services are external applications that integrate with JupyterHub.
178
"""
179
180
# Primary attributes
181
id: int # Primary key
182
name: str # Service name (unique)
183
admin: bool # Whether service has admin privileges
184
url: str # Service URL
185
prefix: str # URL prefix for routing
186
pid: int # Process ID (for managed services)
187
188
# Relationships
189
api_tokens: List[APIToken] # Service API tokens
190
oauth_tokens: List[APIToken] # OAuth tokens
191
roles: List[Role] # Roles assigned to service
192
oauth_client: OAuthClient # OAuth client info
193
194
class APIToken(Base):
195
"""
196
Database model for API authentication tokens.
197
198
Tokens provide API access for users, services, and OAuth clients.
199
"""
200
201
# Primary attributes
202
id: int # Primary key
203
hashed: str # Hashed token value
204
prefix: str # Token prefix (for identification)
205
created: datetime # When token was created
206
last_activity: datetime # Last token usage
207
expires_at: datetime # Token expiration (optional)
208
note: str # Description/note about token
209
210
# Relationships
211
user_id: int # Owner user (optional)
212
user: User # User who owns token
213
service_id: int # Owner service (optional)
214
service: Service # Service that owns token
215
oauth_client_id: str # OAuth client (optional)
216
oauth_client: OAuthClient # OAuth client
217
roles: List[Role] # Roles assigned to token
218
219
@property
220
def scopes(self) -> List[str]:
221
"""All scopes granted to this token"""
222
223
def match(self, token):
224
"""
225
Check if provided token matches this record.
226
227
Args:
228
token: Token string to check
229
230
Returns:
231
True if token matches
232
"""
233
```
234
235
### OAuth Integration Models
236
237
Models supporting OAuth 2.0 integration for external applications.
238
239
```python { .api }
240
class OAuthClient(Base):
241
"""
242
Database model for OAuth client applications.
243
244
OAuth clients can obtain tokens to access JupyterHub APIs.
245
"""
246
247
# Primary attributes
248
id: str # Client ID (primary key)
249
identifier: str # Client identifier
250
secret: str # Client secret (hashed)
251
description: str # Client description
252
redirect_uri: str # OAuth redirect URI
253
allowed_scopes: List[str] # Scopes client can request
254
255
# Relationships
256
tokens: List[APIToken] # Tokens issued to this client
257
codes: List[OAuthCode] # Authorization codes
258
259
def check_secret(self, secret):
260
"""
261
Verify client secret.
262
263
Args:
264
secret: Secret to verify
265
266
Returns:
267
True if secret matches
268
"""
269
270
class OAuthCode(Base):
271
"""
272
Database model for OAuth authorization codes.
273
274
Temporary codes used in OAuth authorization flow.
275
"""
276
277
# Primary attributes
278
id: int # Primary key
279
client_id: str # OAuth client ID
280
code: str # Authorization code
281
expires_at: datetime # Code expiration
282
redirect_uri: str # Redirect URI
283
session_id: str # Session identifier
284
285
# Relationships
286
client: OAuthClient # OAuth client
287
user_id: int # User who authorized
288
user: User # Authorizing user
289
```
290
291
### Server Sharing Models
292
293
Models supporting the server sharing feature.
294
295
```python { .api }
296
class Share(Base):
297
"""
298
Database model for server shares.
299
300
Represents a shared server that can be accessed by multiple users.
301
"""
302
303
# Primary attributes
304
id: int # Primary key
305
user_id: int # Owner user ID
306
server_name: str # Shared server name
307
308
# Relationships
309
user: User # User who owns the shared server
310
codes: List[ShareCode] # Access codes for this share
311
312
@property
313
def server(self) -> Server:
314
"""The shared server object"""
315
316
class ShareCode(Base):
317
"""
318
Database model for server share access codes.
319
320
Codes that allow access to shared servers.
321
"""
322
323
# Primary attributes
324
id: int # Primary key
325
code: str # Access code
326
created: datetime # When code was created
327
last_used: datetime # Last time code was used
328
accept_count: int # Number of times code was used
329
expires_at: datetime # Code expiration (optional)
330
331
# Relationships
332
share_id: int # Foreign key to Share
333
share: Share # The share this code belongs to
334
```
335
336
## Usage Examples
337
338
### Database Operations
339
340
```python
341
from jupyterhub.orm import User, Server, Group, Role
342
from sqlalchemy.orm import sessionmaker
343
344
# Database session example
345
Session = sessionmaker()
346
db = Session()
347
348
# Create a new user
349
user = User(name='alice', admin=False)
350
db.add(user)
351
db.commit()
352
353
# Find user by name
354
user = db.query(User).filter(User.name == 'alice').first()
355
356
# Create API token for user
357
token = user.new_api_token(
358
note='API access token',
359
scopes=['read:users', 'servers']
360
)
361
db.commit()
362
363
# Query user's servers
364
servers = db.query(Server).filter(Server.user == user).all()
365
```
366
367
### Group Management
368
369
```python
370
# Create group
371
group = Group(name='students', description='Student users')
372
db.add(group)
373
374
# Add users to group
375
alice = db.query(User).filter(User.name == 'alice').first()
376
bob = db.query(User).filter(User.name == 'bob').first()
377
group.users.extend([alice, bob])
378
379
# Create role and assign to group
380
role = Role(
381
name='student-role',
382
description='Basic student permissions',
383
scopes=['self', 'servers']
384
)
385
group.roles.append(role)
386
db.commit()
387
```
388
389
### Service Registration
390
391
```python
392
# Register external service
393
service = Service(
394
name='announcement-service',
395
admin=False,
396
url='http://localhost:8001',
397
prefix='/announcements'
398
)
399
db.add(service)
400
401
# Create API token for service
402
token = service.new_api_token(
403
scopes=['read:users', 'read:servers'],
404
note='Service API access'
405
)
406
db.commit()
407
```
408
409
### OAuth Client Setup
410
411
```python
412
# Register OAuth client
413
client = OAuthClient(
414
id='my-app',
415
identifier='my-app-client',
416
description='My Application',
417
redirect_uri='http://localhost:3000/callback',
418
allowed_scopes=['read:users', 'read:servers']
419
)
420
client.secret = 'hashed-secret-value'
421
db.add(client)
422
db.commit()
423
```
424
425
## Database Configuration
426
427
### Connection Setup
428
429
```python
430
# jupyterhub_config.py
431
432
# SQLite (default)
433
c.JupyterHub.db_url = 'sqlite:///jupyterhub.sqlite'
434
435
# PostgreSQL
436
c.JupyterHub.db_url = 'postgresql://user:password@localhost/jupyterhub'
437
438
# MySQL
439
c.JupyterHub.db_url = 'mysql+pymysql://user:password@localhost/jupyterhub'
440
```
441
442
### Migration and Upgrades
443
444
```python
445
# Database upgrade command
446
# jupyterhub upgrade-db
447
448
# Programmatic upgrade
449
from jupyterhub.dbutil import upgrade_if_needed
450
upgrade_if_needed(db_url, log=app.log)
451
```
452
453
### Query Patterns
454
455
```python
456
# Common query patterns
457
from jupyterhub.orm import User, Server, Group
458
459
# Active users with servers
460
active_users = db.query(User).join(Server).filter(
461
Server.last_activity > cutoff_date
462
).all()
463
464
# Admin users
465
admins = db.query(User).filter(User.admin == True).all()
466
467
# Users in specific group
468
group_users = db.query(User).join(User.groups).filter(
469
Group.name == 'students'
470
).all()
471
472
# Expired tokens
473
expired = db.query(APIToken).filter(
474
APIToken.expires_at < datetime.utcnow()
475
).all()
476
```