mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-20 21:52: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")
|
logger.debug("jwt token issued before password change")
|
||||||
return fail_response
|
return fail_response
|
||||||
|
|
||||||
# matches the fallback login applies to a role removed from the config
|
|
||||||
if role not in auth_config.roles:
|
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())
|
current_time = int(time.time())
|
||||||
|
|
||||||
@@ -961,13 +963,16 @@ def login(request: Request, body: AppPostLoginBody):
|
|||||||
|
|
||||||
password_hash = db_user.password_hash
|
password_hash = db_user.password_hash
|
||||||
if verify_password(password, password_hash):
|
if verify_password(password, password_hash):
|
||||||
role = getattr(db_user, "role", "viewer")
|
role = db_user.role
|
||||||
config_roles_set = set(request.app.frigate_config.auth.roles.keys())
|
if role not in request.app.frigate_config.auth.roles:
|
||||||
if role not in config_roles_set:
|
|
||||||
logger.warning(
|
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
|
expiration = int(time.time()) + JWT_SESSION_LENGTH
|
||||||
encoded_jwt = create_encoded_jwt(user, role, expiration, request.app.jwt_token)
|
encoded_jwt = create_encoded_jwt(user, role, expiration, request.app.jwt_token)
|
||||||
response = Response("", 200)
|
response = Response("", 200)
|
||||||
|
|||||||
@@ -115,14 +115,22 @@ class TestAuthJwtRole(BaseTestHttp):
|
|||||||
self.assertEqual(resp.status_code, 202)
|
self.assertEqual(resp.status_code, 202)
|
||||||
self.assertEqual(resp.headers["remote-role"], "garage")
|
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()
|
User.update(role="removed_role").where(User.username == "bob").execute()
|
||||||
app = self._create_app()
|
app = self._create_app()
|
||||||
with AuthTestClient(app) as client:
|
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)
|
def test_login_refused_for_role_removed_from_config(self):
|
||||||
self.assertEqual(resp.headers["remote-role"], "viewer")
|
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):
|
def test_demoted_user_gets_new_role_on_next_request(self):
|
||||||
app = self._create_app()
|
app = self._create_app()
|
||||||
|
|||||||
Reference in New Issue
Block a user