or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

auth-permissions.mdcontent-negotiation.mddecorators.mdfields-validation.mdgeneric-views.mdindex.mdpagination-filtering.mdrequest-response.mdrouters-urls.mdserializers.mdstatus-exceptions.mdtesting.mdviews-viewsets.md

auth-permissions.mddocs/

0

# Authentication and Permissions

1

2

Security framework providing authentication backends for user identification and permission classes for access control. Supports multiple authentication methods and granular permission systems.

3

4

## Capabilities

5

6

### Authentication Classes

7

8

Authentication classes identify users making requests to the API.

9

10

```python { .api }

11

class BaseAuthentication:

12

"""

13

Base class for all authentication backends.

14

"""

15

def authenticate(self, request):

16

"""

17

Authenticate the request and return user/auth tuple.

18

19

Args:

20

request: DRF Request object

21

22

Returns:

23

tuple or None: (user, auth) tuple or None if not authenticated

24

"""

25

26

def authenticate_header(self, request):

27

"""

28

Return string for WWW-Authenticate header when authentication fails.

29

30

Args:

31

request: DRF Request object

32

33

Returns:

34

str or None: Authentication header value

35

"""

36

37

class BasicAuthentication(BaseAuthentication):

38

"""

39

HTTP Basic authentication using username and password.

40

"""

41

www_authenticate_realm = 'api'

42

43

def authenticate(self, request):

44

"""Authenticate using HTTP Basic credentials."""

45

46

def authenticate_credentials(self, userid, password, request=None):

47

"""

48

Authenticate credentials against user database.

49

50

Args:

51

userid (str): Username

52

password (str): Password

53

request: HTTP request

54

55

Returns:

56

tuple: (user, None) if valid

57

58

Raises:

59

AuthenticationFailed: If credentials invalid

60

"""

61

62

class SessionAuthentication(BaseAuthentication):

63

"""

64

Django session-based authentication.

65

"""

66

def authenticate(self, request):

67

"""Authenticate using Django session."""

68

69

def enforce_csrf(self, request):

70

"""

71

Enforce CSRF validation for authenticated requests.

72

73

Raises:

74

PermissionDenied: If CSRF validation fails

75

"""

76

77

class TokenAuthentication(BaseAuthentication):

78

"""

79

Token-based authentication using database-stored tokens.

80

"""

81

keyword = 'Token' # Authorization header keyword

82

model = Token # Token model class

83

84

def authenticate(self, request):

85

"""Authenticate using token from Authorization header."""

86

87

def authenticate_credentials(self, key):

88

"""

89

Authenticate token key against database.

90

91

Args:

92

key (str): Token key

93

94

Returns:

95

tuple: (user, token) if valid

96

97

Raises:

98

AuthenticationFailed: If token invalid

99

"""

100

101

class RemoteUserAuthentication(BaseAuthentication):

102

"""

103

Authentication using REMOTE_USER header from web server.

104

"""

105

header = 'HTTP_REMOTE_USER' # Header name

106

107

def authenticate(self, request):

108

"""Authenticate using remote user header."""

109

110

def clean_username(self, username):

111

"""

112

Clean username for user lookup.

113

114

Args:

115

username (str): Raw username

116

117

Returns:

118

str: Cleaned username

119

"""

120

```

121

122

### Permission Classes

123

124

Permission classes control access to views and objects.

125

126

```python { .api }

127

class BasePermission:

128

"""

129

Base class for all permission checks with operator support.

130

"""

131

def has_permission(self, request, view):

132

"""

133

Check if request has permission to access view.

134

135

Args:

136

request: DRF Request object

137

view: View being accessed

138

139

Returns:

140

bool: True if permission granted

141

"""

142

143

def has_object_permission(self, request, view, obj):

144

"""

145

Check if request has permission to access specific object.

146

147

Args:

148

request: DRF Request object

149

view: View being accessed

150

obj: Object being accessed

151

152

Returns:

153

bool: True if permission granted

154

"""

155

156

def __and__(self, other):

157

"""Combine permissions with AND logic."""

158

return OperandHolder(AND, self, other)

159

160

def __or__(self, other):

161

"""Combine permissions with OR logic."""

162

return OperandHolder(OR, self, other)

163

164

def __invert__(self):

165

"""Negate permission."""

166

return SingleOperandHolder(NOT, self)

167

168

class AllowAny(BasePermission):

169

"""

170

Allow access to any request.

171

"""

172

def has_permission(self, request, view):

173

return True

174

175

class IsAuthenticated(BasePermission):

176

"""

177

Allow access only to authenticated users.

178

"""

179

def has_permission(self, request, view):

180

return bool(request.user and request.user.is_authenticated)

181

182

class IsAdminUser(BasePermission):

183

"""

184

Allow access only to admin users.

185

"""

186

def has_permission(self, request, view):

187

return bool(request.user and request.user.is_staff)

188

189

class IsAuthenticatedOrReadOnly(BasePermission):

190

"""

191

Allow read permissions to any request, write permissions to authenticated users.

192

"""

193

def has_permission(self, request, view):

194

return (

195

request.method in SAFE_METHODS or

196

request.user and request.user.is_authenticated

197

)

198

199

class DjangoModelPermissions(BasePermission):

200

"""

201

Use Django's built-in model permissions system.

202

"""

203

perms_map = {

204

'GET': [],

205

'OPTIONS': [],

206

'HEAD': [],

207

'POST': ['%(app_label)s.add_%(model_name)s'],

208

'PUT': ['%(app_label)s.change_%(model_name)s'],

209

'PATCH': ['%(app_label)s.change_%(model_name)s'],

210

'DELETE': ['%(app_label)s.delete_%(model_name)s'],

211

}

212

213

def get_required_permissions(self, method, model_cls):

214

"""

215

Get required permissions for HTTP method and model.

216

217

Args:

218

method (str): HTTP method

219

model_cls: Django model class

220

221

Returns:

222

list: Required permission strings

223

"""

224

225

class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):

226

"""

227

Django model permissions with anonymous read access.

228

"""

229

230

class DjangoObjectPermissions(DjangoModelPermissions):

231

"""

232

Use Django's object-level permissions.

233

"""

234

def has_object_permission(self, request, view, obj):

235

"""Check object-level permissions using Django's system."""

236

```

237

238

### Permission Operators

239

240

Combine permissions using logical operators.

241

242

```python { .api }

243

class OperationHolderMixin:

244

"""Mixin providing permission combination operators."""

245

def __and__(self, other):

246

return OperandHolder(AND, self, other)

247

248

def __or__(self, other):

249

return OperandHolder(OR, self, other)

250

251

def __invert__(self):

252

return SingleOperandHolder(NOT, self)

253

254

class SingleOperandHolder(OperationHolderMixin):

255

"""Hold single operand for unary operations like NOT."""

256

def __init__(self, operator_class, op1_class):

257

self.operator_class = operator_class

258

self.op1_class = op1_class

259

260

class OperandHolder(OperationHolderMixin):

261

"""Hold two operands for binary operations like AND/OR."""

262

def __init__(self, operator_class, op1_class, op2_class):

263

self.operator_class = operator_class

264

self.op1_class = op1_class

265

self.op2_class = op2_class

266

267

class AND:

268

"""Logical AND operation for permissions."""

269

def has_permission(self, request, view):

270

return (

271

self.op1.has_permission(request, view) and

272

self.op2.has_permission(request, view)

273

)

274

275

class OR:

276

"""Logical OR operation for permissions."""

277

def has_permission(self, request, view):

278

return (

279

self.op1.has_permission(request, view) or

280

self.op2.has_permission(request, view)

281

)

282

283

class NOT:

284

"""Logical NOT operation for permissions."""

285

def has_permission(self, request, view):

286

return not self.op1.has_permission(request, view)

287

```

288

289

## Usage Examples

290

291

### Basic Authentication Setup

292

293

```python

294

# settings.py

295

REST_FRAMEWORK = {

296

'DEFAULT_AUTHENTICATION_CLASSES': [

297

'rest_framework.authentication.SessionAuthentication',

298

'rest_framework.authentication.TokenAuthentication',

299

],

300

'DEFAULT_PERMISSION_CLASSES': [

301

'rest_framework.permissions.IsAuthenticated',

302

],

303

}

304

305

# View-level configuration

306

from rest_framework.authentication import TokenAuthentication

307

from rest_framework.permissions import IsAuthenticated

308

309

class BookViewSet(ModelViewSet):

310

authentication_classes = [TokenAuthentication]

311

permission_classes = [IsAuthenticated]

312

queryset = Book.objects.all()

313

serializer_class = BookSerializer

314

```

315

316

### Token Authentication Usage

317

318

```python

319

# Create token for user

320

from rest_framework.authtoken.models import Token

321

322

user = User.objects.get(username='testuser')

323

token, created = Token.objects.get_or_create(user=user)

324

print(f"Token: {token.key}")

325

326

# Client request with token

327

import requests

328

329

headers = {'Authorization': f'Token {token.key}'}

330

response = requests.get('http://api.example.com/books/', headers=headers)

331

```

332

333

### Custom Permission Class

334

335

```python

336

from rest_framework.permissions import BasePermission

337

338

class IsOwnerOrReadOnly(BasePermission):

339

"""

340

Custom permission to only allow owners to edit objects.

341

"""

342

def has_object_permission(self, request, view, obj):

343

# Read permissions for any request

344

if request.method in ['GET', 'HEAD', 'OPTIONS']:

345

return True

346

347

# Write permissions only to owner

348

return obj.owner == request.user

349

350

# Usage in view

351

class BookViewSet(ModelViewSet):

352

permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

353

queryset = Book.objects.all()

354

serializer_class = BookSerializer

355

```

356

357

### Permission Operators

358

359

```python

360

from rest_framework.permissions import IsAuthenticated, IsAdminUser

361

362

# Combine permissions with operators

363

class MyView(APIView):

364

# Must be admin OR (authenticated AND owner)

365

permission_classes = [IsAdminUser | (IsAuthenticated & IsOwnerOrReadOnly)]

366

367

# Must NOT be anonymous (same as IsAuthenticated)

368

permission_classes = [~AllowAny]

369

```

370

371

### Function-Based View Authentication

372

373

```python

374

from rest_framework.decorators import api_view, authentication_classes, permission_classes

375

from rest_framework.authentication import TokenAuthentication

376

from rest_framework.permissions import IsAuthenticated

377

378

@api_view(['GET'])

379

@authentication_classes([TokenAuthentication])

380

@permission_classes([IsAuthenticated])

381

def protected_view(request):

382

return Response({'message': f'Hello {request.user.username}!'})

383

```

384

385

### Custom Authentication Backend

386

387

```python

388

from rest_framework.authentication import BaseAuthentication

389

from rest_framework.exceptions import AuthenticationFailed

390

from django.contrib.auth import get_user_model

391

392

User = get_user_model()

393

394

class CustomTokenAuthentication(BaseAuthentication):

395

def authenticate(self, request):

396

token = request.META.get('HTTP_X_API_KEY')

397

if not token:

398

return None

399

400

try:

401

user = User.objects.get(api_key=token)

402

except User.DoesNotExist:

403

raise AuthenticationFailed('Invalid API key')

404

405

return (user, token)

406

```

407

408

## Throttling

409

410

Rate limiting classes for controlling request frequency and preventing API abuse. Throttling works alongside authentication and permissions to provide comprehensive access control.

411

412

### Throttling Classes

413

414

```python { .api }

415

class BaseThrottle:

416

"""

417

Base class for all throttling implementations.

418

"""

419

def allow_request(self, request, view):

420

"""

421

Return True if request should be allowed, False otherwise.

422

423

Args:

424

request: DRF Request object

425

view: API view being accessed

426

427

Returns:

428

bool: Whether request is allowed

429

"""

430

431

def wait(self):

432

"""

433

Return recommended number of seconds to wait before next request.

434

435

Returns:

436

int or None: Seconds to wait, or None if not available

437

"""

438

439

class SimpleRateThrottle(BaseThrottle):

440

"""

441

Cache-based rate throttling with configurable rates.

442

"""

443

cache = None # Cache backend

444

timer = None # Time function

445

rate = None # Rate string like "100/day"

446

scope = None # Throttle scope for settings lookup

447

448

def get_cache_key(self, request, view):

449

"""

450

Return unique cache key for throttling.

451

Must be overridden.

452

453

Returns:

454

str or None: Cache key or None to skip throttling

455

"""

456

457

class AnonRateThrottle(SimpleRateThrottle):

458

"""

459

Throttle anonymous requests by IP address.

460

"""

461

scope = 'anon'

462

463

class UserRateThrottle(SimpleRateThrottle):

464

"""

465

Throttle authenticated requests by user.

466

"""

467

scope = 'user'

468

469

class ScopedRateThrottle(SimpleRateThrottle):

470

"""

471

Throttle requests based on view-specific scope.

472

"""

473

scope_attr = 'throttle_scope'

474

```

475

476

### Throttling Configuration

477

478

Configure throttling in Django settings:

479

480

```python

481

# settings.py

482

REST_FRAMEWORK = {

483

'DEFAULT_THROTTLE_CLASSES': [

484

'rest_framework.throttling.AnonRateThrottle',

485

'rest_framework.throttling.UserRateThrottle'

486

],

487

'DEFAULT_THROTTLE_RATES': {

488

'anon': '100/day',

489

'user': '1000/day',

490

'burst': '60/min',

491

'sustained': '1000/day'

492

}

493

}

494

```

495

496

### View-Level Throttling

497

498

```python

499

from rest_framework.throttling import UserRateThrottle, AnonRateThrottle

500

501

class BookViewSet(ModelViewSet):

502

throttle_classes = [UserRateThrottle, AnonRateThrottle]

503

queryset = Book.objects.all()

504

serializer_class = BookSerializer

505

506

# Custom throttle scope

507

class ContactViewSet(ModelViewSet):

508

throttle_classes = [ScopedRateThrottle]

509

throttle_scope = 'contacts'

510

queryset = Contact.objects.all()

511

serializer_class = ContactSerializer

512

```

513

514

### Custom Throttle Class

515

516

```python

517

from rest_framework.throttling import UserRateThrottle

518

519

class BurstRateThrottle(UserRateThrottle):

520

scope = 'burst'

521

522

class LoginRateThrottle(AnonRateThrottle):

523

scope = 'login'

524

525

def get_cache_key(self, request, view):

526

if request.user.is_authenticated:

527

return None # Only throttle anonymous users

528

529

ident = self.get_ident(request)

530

return self.cache_format % {

531

'scope': self.scope,

532

'ident': ident

533

}

534

```

535

536

### Function-Based View Throttling

537

538

```python

539

from rest_framework.decorators import api_view, throttle_classes

540

from rest_framework.throttling import UserRateThrottle

541

542

@api_view(['POST'])

543

@throttle_classes([UserRateThrottle])

544

def send_message(request):

545

return Response({'message': 'Message sent'})

546

```

547

548

## Constants and Utilities

549

550

```python { .api }

551

# Safe HTTP methods (read-only)

552

SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')

553

554

# Utility functions

555

def get_authorization_header(request):

556

"""

557

Extract authorization header from request.

558

559

Args:

560

request: Django request object

561

562

Returns:

563

bytes: Authorization header value

564

"""

565

```