mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-19 21:22:50 +03:00
reject users whose role was removed from the config
A user whose role was removed from the config fell back to viewer, which has no camera restrictions, so a restricted role removed from yaml gained access to every camera. /auth now rejects that user's tokens, and login refuses them until an admin assigns a valid role.
This commit is contained in:
+12
-7
@@ -824,9 +824,11 @@ def auth(request: Request):
|
||||
logger.debug("jwt token issued before password change")
|
||||
return fail_response
|
||||
|
||||
# matches the fallback login applies to a role removed from the config
|
||||
if role not in auth_config.roles:
|
||||
role = "viewer"
|
||||
logger.debug(
|
||||
"jwt user %s has role %s, which is not in the config", user, role
|
||||
)
|
||||
return fail_response
|
||||
|
||||
current_time = int(time.time())
|
||||
|
||||
@@ -961,13 +963,16 @@ def login(request: Request, body: AppPostLoginBody):
|
||||
|
||||
password_hash = db_user.password_hash
|
||||
if verify_password(password, password_hash):
|
||||
role = getattr(db_user, "role", "viewer")
|
||||
config_roles_set = set(request.app.frigate_config.auth.roles.keys())
|
||||
if role not in config_roles_set:
|
||||
role = db_user.role
|
||||
if role not in request.app.frigate_config.auth.roles:
|
||||
logger.warning(
|
||||
f"User {db_user.username} has an invalid role {role}, falling back to 'viewer'."
|
||||
"Login refused for user '%s' with role '%s', which is not in the config",
|
||||
user,
|
||||
role,
|
||||
)
|
||||
role = "viewer"
|
||||
failed_logins.record(user, time.time(), known=True)
|
||||
return JSONResponse(content={"message": "Login failed"}, status_code=401)
|
||||
|
||||
expiration = int(time.time()) + JWT_SESSION_LENGTH
|
||||
encoded_jwt = create_encoded_jwt(user, role, expiration, request.app.jwt_token)
|
||||
response = Response("", 200)
|
||||
|
||||
@@ -115,14 +115,22 @@ class TestAuthJwtRole(BaseTestHttp):
|
||||
self.assertEqual(resp.status_code, 202)
|
||||
self.assertEqual(resp.headers["remote-role"], "garage")
|
||||
|
||||
def test_role_removed_from_config_falls_back_to_viewer(self):
|
||||
def test_role_removed_from_config_is_rejected(self):
|
||||
User.update(role="removed_role").where(User.username == "bob").execute()
|
||||
app = self._create_app()
|
||||
with AuthTestClient(app) as client:
|
||||
resp = self._auth(client, self._token(app, "bob", "removed_role"))
|
||||
for source in ("bearer", "cookie"):
|
||||
resp = self._auth(client, self._token(app, "bob", "garage"), source)
|
||||
self.assertEqual(resp.status_code, 401)
|
||||
self.assertNotIn("remote-role", resp.headers)
|
||||
|
||||
self.assertEqual(resp.status_code, 202)
|
||||
self.assertEqual(resp.headers["remote-role"], "viewer")
|
||||
def test_login_refused_for_role_removed_from_config(self):
|
||||
User.update(role="removed_role").where(User.username == "bob").execute()
|
||||
with AuthTestClient(self._create_app()) as client:
|
||||
resp = client.post("/login", json={"user": "bob", "password": PASSWORD})
|
||||
|
||||
self.assertEqual(resp.status_code, 401)
|
||||
self.assertNotIn("set-cookie", resp.headers)
|
||||
|
||||
def test_demoted_user_gets_new_role_on_next_request(self):
|
||||
app = self._create_app()
|
||||
|
||||
Reference in New Issue
Block a user