blakeblackshear.frigate/frigate/config/auth.py
Josh Hawkins ed1e3a7c9a
Enhance user roles to limit camera access (#20024)
* update config for roles and add validator

* ensure admin and viewer are never overridden

* add class method to user to retrieve all allowed cameras

* enforce config roles in auth api endpoints

* add camera access api dependency functions

* protect review endpoints

* protect preview endpoints

* rename param name for better fastapi injection matching

* remove unneeded

* protect export endpoints

* protect event endpoints

* protect media endpoints

* update auth hook for allowed cameras

* update default app view

* ensure anonymous user always returns all cameras

* limit cameras in explore

* cameras is already a list

* limit cameras in review/history

* limit cameras in live view

* limit cameras in camera groups

* only show face library and classification in sidebar for admin

* remove check in delete reviews

since admin role is required, no need to check camera access. fixes failing test

* pass request with camera access for tests

* more async

* camera access tests

* fix proxy auth tests

* allowed cameras for review tests

* combine event tests and refactor for camera access

* fix post validation for roles

* don't limit roles in create user dialog

* fix triggers endpoints

no need to run require camera access dep since the required role is admin

* fix type

* create and edit role dialogs

* delete role dialog

* fix role change dialog

* update settings view for roles

* i18n changes

* minor spacing tweaks

* docs

* use badges and camera name label component

* clarify docs

* display all cameras badge for admin and viewer

* i18n fix

* use validator to prevent reserved and empty roles from being assigned

* split users and roles into separate tabs in settings

* tweak docs

* clarify docs

* change icon

* don't memoize roles

always recalculate on component render
2025-09-12 05:19:29 -06:00

75 lines
2.6 KiB
Python

from typing import Dict, List, Optional
from pydantic import Field, field_validator, model_validator
from .base import FrigateBaseModel
__all__ = ["AuthConfig"]
class AuthConfig(FrigateBaseModel):
enabled: bool = Field(default=True, title="Enable authentication")
reset_admin_password: bool = Field(
default=False, title="Reset the admin password on startup"
)
cookie_name: str = Field(
default="frigate_token", title="Name for jwt token cookie", pattern=r"^[a-z_]+$"
)
cookie_secure: bool = Field(default=False, title="Set secure flag on cookie")
session_length: int = Field(
default=86400, title="Session length for jwt session tokens", ge=60
)
refresh_time: int = Field(
default=43200,
title="Refresh the session if it is going to expire in this many seconds",
ge=30,
)
failed_login_rate_limit: Optional[str] = Field(
default=None,
title="Rate limits for failed login attempts.",
)
trusted_proxies: list[str] = Field(
default=[],
title="Trusted proxies for determining IP address to rate limit",
)
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
hash_iterations: int = Field(default=600000, title="Password hash iterations")
roles: Dict[str, List[str]] = Field(
default_factory=dict,
title="Role to camera mappings. Empty list grants access to all cameras.",
)
@field_validator("roles")
@classmethod
def validate_roles(cls, v: Dict[str, List[str]]) -> Dict[str, List[str]]:
# Ensure role names are valid (alphanumeric with underscores)
for role in v.keys():
if not role.replace("_", "").isalnum():
raise ValueError(
f"Invalid role name '{role}'. Must be alphanumeric with underscores."
)
# Ensure 'admin' and 'viewer' are not used as custom role names
reserved_roles = {"admin", "viewer"}
if v.keys() & reserved_roles:
raise ValueError(
f"Reserved roles {reserved_roles} cannot be used as custom roles."
)
# Ensure no role has an empty camera list
for role, allowed_cameras in v.items():
if not allowed_cameras:
raise ValueError(
f"Role '{role}' has no cameras assigned. Custom roles must have at least one camera."
)
return v
@model_validator(mode="after")
def ensure_default_roles(self):
# Ensure admin and viewer are never overridden
self.roles["admin"] = []
self.roles["viewer"] = []
return self