From 1c5294eddd033c11e5b4714ea3a09082b840ef0d Mon Sep 17 00:00:00 2001 From: "v.kuvaitsev" Date: Thu, 29 Aug 2024 13:20:24 +0300 Subject: [PATCH 1/4] BANG-468: Force change to https --- auth/views.py | 29 +++++++++++++++++++++++++++++ its_on/main.py | 22 ++++++++++++++++++++-- its_on/routes.py | 6 +++++- settings.yaml | 3 ++- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/auth/views.py b/auth/views.py index 497c403..5dd2658 100644 --- a/auth/views.py +++ b/auth/views.py @@ -4,10 +4,12 @@ import aiohttp_jinja2 from aiohttp.abc import StreamResponse from aiohttp.web import HTTPFound, View, Response +from aiohttp.web_exceptions import HTTPTemporaryRedirect from aiohttp_security import forget, remember from dynaconf import settings from marshmallow.exceptions import ValidationError from multidict import MultiDictProxy +from yarl import URL from auth.auth import check_credentials, get_login_context from auth.schemes import LoginPostRequestSchema @@ -16,6 +18,33 @@ from typing import Optional, Dict +def redirect_uri(request): + oauth_subbapp = request.app._subapps[0] + url = str(request.url.with_path(str(oauth_subbapp.router["callback"].url_for()))) + if settings.OAUTH.force_https: + return url.replace('http', 'https') + return url + + +class OauthViewForceHttps(View): + """Modified version of aiohttp_oauth2.client.views.AuthView to replace http by https""" + + async def get(self) -> Response: + params = { + "client_id": self.request.app["CLIENT_ID"], + "redirect_uri": redirect_uri(self.request), + "response_type": "code", + **self.request.app["AUTH_EXTRAS"], + } + + if self.request.app["SCOPES"]: + params["scope"] = " ".join(self.request.app["SCOPES"]) + + location = str(URL(self.request.app["AUTHORIZE_URL"]).with_query(params)) + + return HTTPTemporaryRedirect(location=location) + + class LoginView(View): @aiohttp_jinja2.template('users/login.html') async def get(self, error: Optional[str] = None) -> Dict[str, Union[str | bool]]: diff --git a/its_on/main.py b/its_on/main.py index de9cdf1..9521826 100644 --- a/its_on/main.py +++ b/its_on/main.py @@ -3,7 +3,7 @@ import asyncio import pathlib -from aiohttp import web +from aiohttp import web, ClientSession from aiohttp_oauth2 import oauth2_app from aiohttp_security import setup as setup_security from aiohttp_security import SessionIdentityPolicy @@ -21,7 +21,7 @@ from its_on.cache import setup_cache from its_on.db_utils import init_pg, close_pg from its_on.middlewares import setup_middlewares -from its_on.routes import setup_routes +from its_on.routes import setup_routes, setup_oauth_route BASE_DIR = pathlib.Path(__file__).parent.parent @@ -37,6 +37,12 @@ async def make_redis_pool() -> aioredis.ConnectionsPool: return await aioredis.create_redis_pool(redis_address, timeout=1) +async def client_session(app: web.Application): + async with ClientSession() as session: + app["session"] = session + yield + + def init_app( loop: asyncio.AbstractEventLoop, redis_pool: Optional[aioredis.ConnectionsPool] = None, @@ -44,6 +50,18 @@ def init_app( app = web.Application(loop=loop) if settings.OAUTH.IS_USED: + client_id = settings.OAUTH.CLIENT_ID + authorize_url = settings.OAUTH.AUTHORIZE_URL + scopes = None + auth_extras = None + app.update( # pylint: disable=no-member + CLIENT_ID=client_id, + AUTHORIZE_URL=authorize_url, + SCOPES=scopes, + AUTH_EXTRAS=auth_extras or {}, + ) + app.cleanup_ctx.append(client_session) + setup_oauth_route(app) app.add_subapp( '/oauth/', oauth2_app( diff --git a/its_on/routes.py b/its_on/routes.py index 1acf592..f1a4b4a 100644 --- a/its_on/routes.py +++ b/its_on/routes.py @@ -5,7 +5,7 @@ from aiohttp_cors import CorsConfig from dynaconf import settings -from auth.views import LoginView, LogoutView +from auth.views import LoginView, LogoutView, OauthViewForceHttps from its_on.views import SwitchFullListView, SwitchListView, SwitchSvgBadgeView from its_on.admin.views.switches import ( SwitchAddAdminView, @@ -20,6 +20,10 @@ from pathlib import Path +def setup_oauth_route(app: Application) -> None: + app.router.add_view('/oauth/auth', OauthViewForceHttps) + + def setup_routes(app: Application, base_dir: Path, cors_config: CorsConfig) -> None: app.router.add_view('/zbs/login', LoginView, name='login_view') app.router.add_view('/zbs/logout', LogoutView) diff --git a/settings.yaml b/settings.yaml index dc01c3c..bf11791 100644 --- a/settings.yaml +++ b/settings.yaml @@ -5,12 +5,13 @@ default: database: dsn: postgresql://bestdoctor:bestdoctor@localhost:5432/its_on oauth: - is_used: false + is_used: true only_oauth: false client_id: '@none' client_secret: '@none' authorize_url: '@none' token_url: '@none' + force_https: true sign_in_title: 'Sign in with "Bestdoctor ID"' enable_db_logging: false cache_url: redis://127.0.0.1:6379/1 From 87a2e7be4aa22bb15d222d43fd5b5d379bd31663 Mon Sep 17 00:00:00 2001 From: "v.kuvaitsev" Date: Thu, 29 Aug 2024 14:42:04 +0300 Subject: [PATCH 2/4] BANG-468: Fix quotes --- auth/views.py | 19 ++++++++++--------- its_on/main.py | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/auth/views.py b/auth/views.py index 5dd2658..3bd491a 100644 --- a/auth/views.py +++ b/auth/views.py @@ -5,6 +5,7 @@ from aiohttp.abc import StreamResponse from aiohttp.web import HTTPFound, View, Response from aiohttp.web_exceptions import HTTPTemporaryRedirect +from aiohttp.web_request import Request from aiohttp_security import forget, remember from dynaconf import settings from marshmallow.exceptions import ValidationError @@ -18,9 +19,9 @@ from typing import Optional, Dict -def redirect_uri(request): +def redirect_uri(request: Request) -> str: oauth_subbapp = request.app._subapps[0] - url = str(request.url.with_path(str(oauth_subbapp.router["callback"].url_for()))) + url = str(request.url.with_path(str(oauth_subbapp.router['callback'].url_for()))) if settings.OAUTH.force_https: return url.replace('http', 'https') return url @@ -31,16 +32,16 @@ class OauthViewForceHttps(View): async def get(self) -> Response: params = { - "client_id": self.request.app["CLIENT_ID"], - "redirect_uri": redirect_uri(self.request), - "response_type": "code", - **self.request.app["AUTH_EXTRAS"], + 'client_id': self.request.app['CLIENT_ID'], + 'redirect_uri': redirect_uri(self.request), + 'response_type': 'code', + **self.request.app['AUTH_EXTRAS'], } - if self.request.app["SCOPES"]: - params["scope"] = " ".join(self.request.app["SCOPES"]) + if self.request.app['SCOPES']: + params['scope'] = ' '.join(self.request.app['SCOPES']) - location = str(URL(self.request.app["AUTHORIZE_URL"]).with_query(params)) + location = str(URL(self.request.app['AUTHORIZE_URL']).with_query(params)) return HTTPTemporaryRedirect(location=location) diff --git a/its_on/main.py b/its_on/main.py index 9521826..7ea9f24 100644 --- a/its_on/main.py +++ b/its_on/main.py @@ -37,9 +37,9 @@ async def make_redis_pool() -> aioredis.ConnectionsPool: return await aioredis.create_redis_pool(redis_address, timeout=1) -async def client_session(app: web.Application): +async def client_session(app: web.Application) -> None: async with ClientSession() as session: - app["session"] = session + app['session'] = session yield From bd8a847c78ac51a050bc72271d60c229e7ffd2d1 Mon Sep 17 00:00:00 2001 From: "v.kuvaitsev" Date: Thu, 29 Aug 2024 14:46:52 +0300 Subject: [PATCH 3/4] BANG-468: Ignore types --- its_on/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/its_on/main.py b/its_on/main.py index 7ea9f24..2d1e94d 100644 --- a/its_on/main.py +++ b/its_on/main.py @@ -37,7 +37,7 @@ async def make_redis_pool() -> aioredis.ConnectionsPool: return await aioredis.create_redis_pool(redis_address, timeout=1) -async def client_session(app: web.Application) -> None: +async def client_session(app: web.Application): # type:ignore async with ClientSession() as session: app['session'] = session yield @@ -60,7 +60,7 @@ def init_app( SCOPES=scopes, AUTH_EXTRAS=auth_extras or {}, ) - app.cleanup_ctx.append(client_session) + app.cleanup_ctx.append(client_session) # type:ignore setup_oauth_route(app) app.add_subapp( '/oauth/', From 28fac35195d38b4d8897080ec63b4e0c44f38aa2 Mon Sep 17 00:00:00 2001 From: "v.kuvaitsev" Date: Thu, 29 Aug 2024 14:50:17 +0300 Subject: [PATCH 4/4] BANG-468: Fix types --- its_on/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/its_on/main.py b/its_on/main.py index 2d1e94d..ef1fd93 100644 --- a/its_on/main.py +++ b/its_on/main.py @@ -60,7 +60,7 @@ def init_app( SCOPES=scopes, AUTH_EXTRAS=auth_extras or {}, ) - app.cleanup_ctx.append(client_session) # type:ignore + app.cleanup_ctx.append(client_session) setup_oauth_route(app) app.add_subapp( '/oauth/',