mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 07:17:17 +00:00
add ability to read cookie secret from config
This commit is contained in:
parent
96f394bab0
commit
cbcfff27b8
@ -50,6 +50,7 @@ Base authorization settings. ``OAuth`` provider requires ``aioauth-client`` libr
|
|||||||
* ``allow_read_only`` - allow requesting status APIs without authorization, boolean, required.
|
* ``allow_read_only`` - allow requesting status APIs without authorization, boolean, required.
|
||||||
* ``client_id`` - OAuth2 application client ID, string, required in case if ``oauth`` is used.
|
* ``client_id`` - OAuth2 application client ID, string, required in case if ``oauth`` is used.
|
||||||
* ``client_secret`` - OAuth2 application client secret key, string, required in case if ``oauth`` is used.
|
* ``client_secret`` - OAuth2 application client secret key, string, required in case if ``oauth`` is used.
|
||||||
|
* ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 url-safe base64-encoded bytes and can be generated as following ``base64.urlsafe_b64encode(os.urandom(32)).decode("utf8")``. If not set, it will be generated automatically; note, however, that in this case, all sessions will be automatically expired during restart.
|
||||||
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service, integer, optional, default is 7 days.
|
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service, integer, optional, default is 7 days.
|
||||||
* ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used.
|
* ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used.
|
||||||
* ``oauth_scopes`` - scopes list for OAuth2 provider, which will allow retrieving user email (which is used for checking user permissions), e.g. ``https://www.googleapis.com/auth/userinfo.email`` for ``GoogleClient`` or ``user:email`` for ``GithubClient``, space separated list of strings, required in case if ``oauth`` is used.
|
* ``oauth_scopes`` - scopes list for OAuth2 provider, which will allow retrieving user email (which is used for checking user permissions), e.g. ``https://www.googleapis.com/auth/userinfo.email`` for ``GoogleClient`` or ``user:email`` for ``GithubClient``, space separated list of strings, required in case if ``oauth`` is used.
|
||||||
|
@ -109,6 +109,9 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
|
|||||||
"client_secret": {
|
"client_secret": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"cookie_secret_key": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"max_age": {
|
"max_age": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"coerce": "integer",
|
"coerce": "integer",
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_security # type: ignore
|
import aiohttp_security # type: ignore
|
||||||
import base64
|
|
||||||
import socket
|
import socket
|
||||||
import types
|
import types
|
||||||
|
|
||||||
@ -32,12 +31,13 @@ from cryptography import fernet
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from ahriman.core.auth import Auth
|
from ahriman.core.auth import Auth
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.models.user_identity import UserIdentity
|
from ahriman.models.user_identity import UserIdentity
|
||||||
from ahriman.web.middlewares import HandlerType, MiddlewareType
|
from ahriman.web.middlewares import HandlerType, MiddlewareType
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["AuthorizationPolicy", "auth_handler", "setup_auth"]
|
__all__ = ["AuthorizationPolicy", "auth_handler", "cookie_secret_key", "setup_auth"]
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationPolicy(aiohttp_security.AbstractAuthorizationPolicy): # type: ignore
|
class AuthorizationPolicy(aiohttp_security.AbstractAuthorizationPolicy): # type: ignore
|
||||||
@ -125,19 +125,36 @@ def auth_handler(allow_read_only: bool) -> MiddlewareType:
|
|||||||
return handle
|
return handle
|
||||||
|
|
||||||
|
|
||||||
def setup_auth(application: web.Application, validator: Auth) -> web.Application:
|
def cookie_secret_key(configuration: Configuration) -> fernet.Fernet:
|
||||||
|
"""
|
||||||
|
extract cookie secret key from configuration if set or generate new one
|
||||||
|
|
||||||
|
Args:
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
fernet.Fernet: fernet key instance
|
||||||
|
"""
|
||||||
|
if (secret_key := configuration.get("auth", "cookie_secret_key", fallback=None)) is not None:
|
||||||
|
return fernet.Fernet(secret_key)
|
||||||
|
|
||||||
|
secret_key = fernet.Fernet.generate_key()
|
||||||
|
return fernet.Fernet(secret_key)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_auth(application: web.Application, configuration: Configuration, validator: Auth) -> web.Application:
|
||||||
"""
|
"""
|
||||||
setup authorization policies for the application
|
setup authorization policies for the application
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
application(web.Application): web application instance
|
application(web.Application): web application instance
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
validator(Auth): authorization module instance
|
validator(Auth): authorization module instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
web.Application: configured web application
|
web.Application: configured web application
|
||||||
"""
|
"""
|
||||||
fernet_key = fernet.Fernet.generate_key()
|
secret_key = cookie_secret_key(configuration)
|
||||||
secret_key = base64.urlsafe_b64decode(fernet_key)
|
|
||||||
storage = EncryptedCookieStorage(secret_key, cookie_name="API_SESSION", max_age=validator.max_age)
|
storage = EncryptedCookieStorage(secret_key, cookie_name="API_SESSION", max_age=validator.max_age)
|
||||||
setup_session(application, storage)
|
setup_session(application, storage)
|
||||||
|
|
||||||
|
@ -168,6 +168,6 @@ def setup_service(architecture: str, configuration: Configuration, spawner: Spaw
|
|||||||
validator = application["validator"] = Auth.load(configuration, database)
|
validator = application["validator"] = Auth.load(configuration, database)
|
||||||
if validator.enabled:
|
if validator.enabled:
|
||||||
from ahriman.web.middlewares.auth_handler import setup_auth
|
from ahriman.web.middlewares.auth_handler import setup_auth
|
||||||
setup_auth(application, validator)
|
setup_auth(application, configuration, validator)
|
||||||
|
|
||||||
return application
|
return application
|
||||||
|
@ -3,14 +3,16 @@ import socket
|
|||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from aiohttp.test_utils import TestClient
|
from aiohttp.test_utils import TestClient
|
||||||
|
from cryptography import fernet
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from ahriman.core.auth import Auth
|
from ahriman.core.auth import Auth
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.models.user import User
|
from ahriman.models.user import User
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.models.user_identity import UserIdentity
|
from ahriman.models.user_identity import UserIdentity
|
||||||
from ahriman.web.middlewares.auth_handler import auth_handler, AuthorizationPolicy, setup_auth
|
from ahriman.web.middlewares.auth_handler import AuthorizationPolicy, auth_handler, cookie_secret_key, setup_auth
|
||||||
|
|
||||||
|
|
||||||
def _identity(username: str) -> str:
|
def _identity(username: str) -> str:
|
||||||
@ -175,11 +177,28 @@ async def test_auth_handler_write(mocker: MockerFixture) -> None:
|
|||||||
check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Full, aiohttp_request.path)
|
check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Full, aiohttp_request.path)
|
||||||
|
|
||||||
|
|
||||||
def test_setup_auth(application_with_auth: web.Application, auth: Auth, mocker: MockerFixture) -> None:
|
def test_cookie_secret_key(configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
must generate fernet key
|
||||||
|
"""
|
||||||
|
secret_key = cookie_secret_key(configuration)
|
||||||
|
assert isinstance(secret_key, fernet.Fernet)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cookie_secret_key_cached(configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
must use cookie key as set by configuration
|
||||||
|
"""
|
||||||
|
configuration.set_option("auth", "cookie_secret_key", fernet.Fernet.generate_key().decode("utf8"))
|
||||||
|
assert cookie_secret_key(configuration) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_auth(application_with_auth: web.Application, configuration: Configuration, auth: Auth,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must set up authorization
|
must set up authorization
|
||||||
"""
|
"""
|
||||||
setup_mock = mocker.patch("aiohttp_security.setup")
|
setup_mock = mocker.patch("aiohttp_security.setup")
|
||||||
application = setup_auth(application_with_auth, auth)
|
application = setup_auth(application_with_auth, configuration, auth)
|
||||||
assert application.get("validator") is not None
|
assert application.get("validator") is not None
|
||||||
setup_mock.assert_called_once_with(application_with_auth, pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
|
setup_mock.assert_called_once_with(application_with_auth, pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
|
||||||
|
Loading…
Reference in New Issue
Block a user