From 8963781ca31cca2966b6a33e463e7497c5312d04 Mon Sep 17 00:00:00 2001 From: Igor Lobanov Date: Tue, 22 Nov 2022 04:11:26 +0100 Subject: [PATCH] auth and minor fixes --- README.md | 4 ++-- auth/authenticate.py | 7 +++++-- auth/email.py | 2 ++ nginx.conf.sigil | 6 +++--- resolvers/auth.py | 4 ++-- resolvers/zine/load.py | 5 +++-- services/auth/users.py | 13 ++++++++++--- services/stat/reacted.py | 3 ++- settings.py | 1 + 9 files changed, 30 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b572042e..fa9448cc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Tech stack: - ariadne - starlette -# Local development +# Local development Install deps first @@ -40,5 +40,5 @@ python3 server.py dev # How to do an authorized request -Put the header 'Auth' with token from signInQuery or registerQuery. +Put the header 'Authorization' with token from signInQuery or registerQuery. diff --git a/auth/authenticate.py b/auth/authenticate.py index 95e55789..4e8248c7 100644 --- a/auth/authenticate.py +++ b/auth/authenticate.py @@ -11,6 +11,7 @@ from auth.jwtcodec import JWTCodec from auth.tokenstorage import TokenStorage from base.exceptions import InvalidToken from services.auth.users import UserStorage +from settings import SESSION_TOKEN_HEADER class SessionToken: @@ -48,10 +49,12 @@ class JWTAuthenticate(AuthenticationBackend): async def authenticate( self, request: HTTPConnection ) -> Optional[Tuple[AuthCredentials, AuthUser]]: - if "Auth" not in request.headers: + + if SESSION_TOKEN_HEADER not in request.headers: return AuthCredentials(scopes=[]), AuthUser(user_id=None) - token = request.headers.get("Auth", "") + token = request.headers.get(SESSION_TOKEN_HEADER, "") + try: payload = await SessionToken.verify(token) except Exception as exc: diff --git a/auth/email.py b/auth/email.py index f711dd56..c0e2286a 100644 --- a/auth/email.py +++ b/auth/email.py @@ -25,6 +25,8 @@ async def send_auth_email(user, token, lang="ru"): "h:X-Mailgun-Variables": "{ \"token\": \"%s\" }" % token } print('[auth.email] payload: %r' % payload) + # debug + # print('http://localhost:3000/?modal=auth&mode=confirm-email&token=%s' % token) response = requests.post( api_url, auth=("api", MAILGUN_API_KEY), diff --git a/nginx.conf.sigil b/nginx.conf.sigil index eb458974..76f91cd6 100644 --- a/nginx.conf.sigil +++ b/nginx.conf.sigil @@ -119,7 +119,7 @@ server { # # Custom headers and headers various browsers *should* be OK with but aren't # - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; add_header 'Access-Control-Allow-Credentials' 'true'; # # Tell client that this pre-flight info is valid for 20 days @@ -133,7 +133,7 @@ server { if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '$allow_origin' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth' always; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; add_header 'Access-Control-Allow-Credentials' 'true' always; } @@ -141,7 +141,7 @@ server { if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '$allow_origin' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth' always; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; add_header 'Access-Control-Allow-Credentials' 'true' always; } diff --git a/resolvers/auth.py b/resolvers/auth.py index 02bff8b1..b2923ce3 100644 --- a/resolvers/auth.py +++ b/resolvers/auth.py @@ -18,6 +18,7 @@ from base.orm import local_session from base.resolvers import mutation, query from orm import Role, User from resolvers.zine.profile import user_subscriptions +from settings import SESSION_TOKEN_HEADER @mutation.field("refreshSession") @@ -143,7 +144,6 @@ async def auth_send_link(_, _info, email, lang="ru"): @query.field("signIn") async def login(_, info, email: str, password: str = "", lang: str = "ru"): - with local_session() as session: orm_user = session.query(User).filter(User.email == email).first() if orm_user is None: @@ -182,7 +182,7 @@ async def login(_, info, email: str, password: str = "", lang: str = "ru"): @query.field("signOut") @login_required async def sign_out(_, info: GraphQLResolveInfo): - token = info.context["request"].headers.get("Auth", "") + token = info.context["request"].headers.get(SESSION_TOKEN_HEADER, "") status = await TokenStorage.revoke(token) return status diff --git a/resolvers/zine/load.py b/resolvers/zine/load.py index cb95c88d..eea0408f 100644 --- a/resolvers/zine/load.py +++ b/resolvers/zine/load.py @@ -10,7 +10,8 @@ from services.zine.shoutauthor import ShoutAuthorStorage from services.stat.reacted import ReactedStorage -def apply_filters(filters, q, user=None): +def apply_filters(q, filters, user=None): + filters = {} if filters is None else filters if filters.get("reacted") and user: q.join(Reaction, Reaction.createdBy == user.slug) if filters.get("visibility"): @@ -106,7 +107,7 @@ async def load_shouts_by(_, info, options): Shout.deletedAt.is_(None) ) user = info.context["request"].user - q = apply_filters(options.get("filters"), q, user) + q = apply_filters(q, options.get("filters"), user) order_by = extract_order(options.get("order_by"), q) diff --git a/services/auth/users.py b/services/auth/users.py index dad50043..0d76dfa8 100644 --- a/services/auth/users.py +++ b/services/auth/users.py @@ -1,6 +1,7 @@ import asyncio from sqlalchemy.orm import selectinload from orm.user import User +from base.orm import local_session class UserStorage: @@ -20,9 +21,15 @@ class UserStorage: @staticmethod async def get_user(id): - self = UserStorage - async with self.lock: - return self.users.get(id) + with local_session() as session: + user = ( + session.query(User) + .options(selectinload(User.roles), selectinload(User.ratings)) + .filter(User.id == id) + .one() + ) + + return user @staticmethod async def get_all_users(): diff --git a/services/stat/reacted.py b/services/stat/reacted.py index 00f4fb66..6258bfe7 100644 --- a/services/stat/reacted.py +++ b/services/stat/reacted.py @@ -36,7 +36,8 @@ class ReactedStorage: @staticmethod async def get_shout_stat(slug): return { - "viewed": await ViewedStorage.get_shout(slug), + # TODO + "viewed": 0, # await ViewedStorage.get_shout(slug), "reacted": len(await ReactedStorage.get_shout(slug)), "commented": len(await ReactedStorage.get_comments(slug)), "rating": await ReactedStorage.get_rating(slug), diff --git a/settings.py b/settings.py index 7712303d..6649b8ea 100644 --- a/settings.py +++ b/settings.py @@ -24,3 +24,4 @@ for provider in OAUTH_PROVIDERS: } SHOUTS_REPO = "content" +SESSION_TOKEN_HEADER = "Authorization"