mirror of
https://github.com/apache/superset.git
synced 2026-06-30 20:05:36 +00:00
Compare commits
1 Commits
chore/ci/s
...
feat/logou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ed8c1df59 |
@@ -671,6 +671,13 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods
|
|||||||
"""Register app-level request handlers"""
|
"""Register app-level request handlers"""
|
||||||
from flask import request, Response
|
from flask import request, Response
|
||||||
|
|
||||||
|
from superset.security.session_validation import (
|
||||||
|
register_inactive_user_logout,
|
||||||
|
)
|
||||||
|
|
||||||
|
# End the session of any user who is deactivated mid-session.
|
||||||
|
register_inactive_user_logout(self.superset_app)
|
||||||
|
|
||||||
@self.superset_app.after_request
|
@self.superset_app.after_request
|
||||||
def apply_http_headers(response: Response) -> Response:
|
def apply_http_headers(response: Response) -> Response:
|
||||||
"""Applies the configuration's http headers to all responses"""
|
"""Applies the configuration's http headers to all responses"""
|
||||||
|
|||||||
74
superset/security/session_validation.py
Normal file
74
superset/security/session_validation.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
"""Terminate the session of a user who has been deactivated mid-session.
|
||||||
|
|
||||||
|
Flask-Login only consults ``is_active`` when establishing a login; for an
|
||||||
|
already-authenticated user it does not re-check it on subsequent requests. So a
|
||||||
|
user disabled by an administrator keeps their session until it expires. This
|
||||||
|
hook re-checks ``is_active`` on each request and logs the user out as soon as
|
||||||
|
their account is deactivated. (Deleted users are already handled: the user
|
||||||
|
loader returns ``None`` and the request is anonymous.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from flask import request
|
||||||
|
from flask_login import current_user, logout_user
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Endpoints that must stay reachable for an anonymous/logging-out user.
|
||||||
|
_EXEMPT_ENDPOINT_TOKENS = (
|
||||||
|
"static",
|
||||||
|
"appbuilder",
|
||||||
|
"login",
|
||||||
|
"logout",
|
||||||
|
"auth",
|
||||||
|
"health",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_exempt_endpoint(endpoint: str | None) -> bool:
|
||||||
|
if not endpoint:
|
||||||
|
return True
|
||||||
|
lowered = endpoint.lower()
|
||||||
|
return any(token in lowered for token in _EXEMPT_ENDPOINT_TOKENS)
|
||||||
|
|
||||||
|
|
||||||
|
def register_inactive_user_logout(app: Any) -> None:
|
||||||
|
"""Register the before-request hook that logs out deactivated users."""
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def _logout_inactive_user() -> None: # pylint: disable=unused-variable
|
||||||
|
if _is_exempt_endpoint(request.endpoint):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not getattr(current_user, "is_authenticated", False):
|
||||||
|
return
|
||||||
|
|
||||||
|
# ``is_active`` is False once an admin deactivates the account. End the
|
||||||
|
# session now; the request then proceeds as anonymous and the normal
|
||||||
|
# access controls deny protected views.
|
||||||
|
if not current_user.is_active:
|
||||||
|
logger.info(
|
||||||
|
"Logging out deactivated user (id=%s)",
|
||||||
|
getattr(current_user, "id", None),
|
||||||
|
)
|
||||||
|
logout_user()
|
||||||
36
tests/unit_tests/security/test_session_validation.py
Normal file
36
tests/unit_tests/security/test_session_validation.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from superset.security.session_validation import _is_exempt_endpoint
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"endpoint,expected",
|
||||||
|
[
|
||||||
|
(None, True),
|
||||||
|
("AuthDBView.login", True),
|
||||||
|
("AuthDBView.logout", True),
|
||||||
|
("appbuilder.static", True),
|
||||||
|
("SupersetIndexView.index", False),
|
||||||
|
("Superset.dashboard", False),
|
||||||
|
("ChartRestApi.get_list", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_exempt_endpoint(endpoint, expected) -> None:
|
||||||
|
assert _is_exempt_endpoint(endpoint) is expected
|
||||||
Reference in New Issue
Block a user