From patchwork Mon Jul 20 14:25:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Mangano-Tarumi?= X-Patchwork-Id: 1726 Return-Path: Delivered-To: patchwork@archlinux.org Received: from apollo.archlinux.org (localhost [127.0.0.1]) by apollo.archlinux.org (Postfix) with ESMTP id CD9CB1A0B4658 for ; Mon, 20 Jul 2020 14:25:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on apollo.archlinux.org X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=DKIM_SIGNED=0.1, DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,MAILING_LIST_MULTI=-1, RCVD_IN_DNSWL_MED=-2.3,SPF_HELO_NONE=0.001,T_DMARC_POLICY_NONE=0.01 autolearn=ham autolearn_force=no version=3.4.4 X-Spam-BL-Results: [127.0.9.2] Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by apollo.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:26 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 6082B1D35F36EE; Mon, 20 Jul 2020 14:25:20 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [5.9.250.164]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits)) (No client certificate requested) (Authenticated sender: luna) by orion.archlinux.org (Postfix) with ESMTPSA id 408551D35F36EC; Mon, 20 Jul 2020 14:25:20 +0000 (UTC) Authentication-Results: orion.archlinux.org; dkim=pass (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=jznAevld Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 163C820F45; Mon, 20 Jul 2020 14:25:20 +0000 (UTC) Authentication-Results: luna.archlinux.org; dkim=pass (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=jznAevld Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 7D04220F45 for ; Mon, 20 Jul 2020 14:25:14 +0000 (UTC) Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by luna.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:14 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id CCCDE1D35F36E8 for ; Mon, 20 Jul 2020 14:25:12 +0000 (UTC) Received: from tsubame.mg0.fr (tsubame.mg0.fr [IPv6:2001:41d0:401:3100::402b]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mg0.fr; s=tsubame; h=Message-ID:Subject:To:From:Date:cc; bh=0latJFoi3QMm9UKgsb5Zq/mr5put8SfYWRXwAA9WoSU=; b=jznAevldbBLlyRig0XyzJk5v3O W6TSjNELS8MDGRIjbwO7EcAZ48LGyoMqZvXoL9NUew08T65hAkDG3Rys6PEUiYGUiYpPANBwvmRjF lTAdxykdwio6NZZv8AYPNDEaYI/caz2dstUwIwRfEZXctACoQ52ejUc0h5NTEiFWF7F8=; Received: from fmang by tsubame.mg0.fr with local (Exim 4.94) (envelope-from ) id 1jxWj9-00GtUk-Ov for aur-dev@archlinux.org; Mon, 20 Jul 2020 16:25:11 +0200 Date: Mon, 20 Jul 2020 16:25:11 +0200 From: =?utf-8?b?RnLDqWTDqXJpYw==?= Mangano-Tarumi To: aur-dev@archlinux.org Subject: [PATCH 1/3] SSO: Port IP ban checking Message-ID: <20200720142511.GA4026568@tsubame.mg0.fr> MIME-Version: 1.0 Content-Disposition: inline X-BeenThere: aur-dev@archlinux.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: "Arch User Repository \(AUR\) Development" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: aur-dev-bounces@archlinux.org Sender: "aur-dev" --- aurweb/routers/sso.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py index 04ecdca6..efd4462c 100644 --- a/aurweb/routers/sso.py +++ b/aurweb/routers/sso.py @@ -14,7 +14,7 @@ from starlette.requests import Request import aurweb.config import aurweb.db -from aurweb.schema import Sessions, Users +from aurweb.schema import Bans, Sessions, Users router = fastapi.APIRouter() @@ -57,13 +57,28 @@ def open_session(conn, user_id): return sid +def is_ip_banned(conn, ip): + """ + Check if an IP is banned. `ip` is a string and may be an IPv4 as well as an + IPv6, depending on the server’s configuration. + """ + result = conn.execute(Bans.select().where(Bans.c.IPAddress == ip)) + return result.fetchone() is not None + + @router.get("/sso/authenticate") async def authenticate(request: Request, conn=Depends(aurweb.db.connect)): """ Receive an OpenID Connect ID token, validate it, then process it to create an new AUR session. """ - # TODO check for banned IPs + # TODO Handle translations + if is_ip_banned(conn, request.client.host): + raise HTTPException( + status_code=403, + detail='The login form is currently disabled for your IP address, ' + 'probably due to sustained spam attacks. Sorry for the ' + 'inconvenience.') token = await oauth.sso.authorize_access_token(request) user = await oauth.sso.parse_id_token(request, token) sub = user.get("sub") # this is the SSO account ID in JWT terminology From patchwork Mon Jul 20 14:25:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Mangano-Tarumi?= X-Patchwork-Id: 1727 Return-Path: Delivered-To: patchwork@archlinux.org Received: from apollo.archlinux.org (localhost [127.0.0.1]) by apollo.archlinux.org (Postfix) with ESMTP id 3F6901A0B467A for ; Mon, 20 Jul 2020 14:25:33 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on apollo.archlinux.org X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=DKIM_SIGNED=0.1, DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,MAILING_LIST_MULTI=-1, RCVD_IN_DNSWL_MED=-2.3,SPF_HELO_NONE=0.001,T_DMARC_POLICY_NONE=0.01 autolearn=ham autolearn_force=no version=3.4.4 X-Spam-BL-Results: [127.0.9.2] Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by apollo.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:33 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 7A8CD1D35F371E; Mon, 20 Jul 2020 14:25:31 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [5.9.250.164]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits)) (No client certificate requested) (Authenticated sender: luna) by orion.archlinux.org (Postfix) with ESMTPSA id 516B61D35F371B; Mon, 20 Jul 2020 14:25:31 +0000 (UTC) Authentication-Results: orion.archlinux.org; dkim=pass (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=fIaS/2pp Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 9473520F45; Mon, 20 Jul 2020 14:25:30 +0000 (UTC) Authentication-Results: luna.archlinux.org; dkim=pass (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=fIaS/2pp Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 6D5AE20F23 for ; Mon, 20 Jul 2020 14:25:26 +0000 (UTC) Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by luna.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:26 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id A5B801D35F3710 for ; Mon, 20 Jul 2020 14:25:24 +0000 (UTC) Received: from tsubame.mg0.fr (tsubame.mg0.fr [IPv6:2001:41d0:401:3100::402b]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mg0.fr; s=tsubame; h=Message-ID:Subject:To:From:Date:cc; bh=r6bX+I+1l3WLxAj3Zx9Ru2Bo2BwkG5W8zb7xfVKRmcc=; b=fIaS/2ppO/rXQXN3mF3QnixbtF 8/FwLD4TcWz3uF4+CqxB1BhlgvgiAUA1v5otrTXm/5hO6jouOmV0HKNY6/Ma2OajW4m0TU8SXtU0Q hJ91I4wUU3y7DbKdpLRD2OJ0308Ax/a4oUWgpaxAmmdeSVhaKY1VFnRYo9XionsaufBA=; Received: from fmang by tsubame.mg0.fr with local (Exim 4.94) (envelope-from ) id 1jxWjK-00GtUv-Sc for aur-dev@archlinux.org; Mon, 20 Jul 2020 16:25:22 +0200 Date: Mon, 20 Jul 2020 16:25:22 +0200 From: =?utf-8?b?RnLDqWTDqXJpYw==?= Mangano-Tarumi To: aur-dev@archlinux.org Subject: [PATCH 2/3] SSO: Port account suspension Message-ID: <20200720142522.GA4026580@tsubame.mg0.fr> MIME-Version: 1.0 Content-Disposition: inline X-BeenThere: aur-dev@archlinux.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: "Arch User Repository \(AUR\) Development" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: aur-dev-bounces@archlinux.org Sender: "aur-dev" --- aurweb/routers/sso.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py index efd4462c..3e3b743d 100644 --- a/aurweb/routers/sso.py +++ b/aurweb/routers/sso.py @@ -41,11 +41,20 @@ async def login(request: Request): return await oauth.sso.authorize_redirect(request, redirect_uri, prompt="login") +def is_account_suspended(conn, user_id): + row = conn.execute(select([Users.c.Suspended]).where(Users.c.ID == user_id)).fetchone() + return row is not None and bool(row[0]) + + def open_session(conn, user_id): """ Create a new user session into the database. Return its SID. """ - # TODO check for account suspension + # TODO Handle translations. + if is_account_suspended(conn, user_id): + raise HTTPException(status_code=403, detail='Account suspended') + # TODO This is a terrible message because it could imply the attempt at + # logging in just caused the suspension. # TODO apply [options] max_sessions_per_user sid = uuid.uuid4().hex conn.execute(Sessions.insert().values( From patchwork Mon Jul 20 14:25:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Mangano-Tarumi?= X-Patchwork-Id: 1728 Return-Path: Delivered-To: patchwork@archlinux.org Received: from apollo.archlinux.org (localhost [127.0.0.1]) by apollo.archlinux.org (Postfix) with ESMTP id E5FFF1A0B4697 for ; Mon, 20 Jul 2020 14:25:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on apollo.archlinux.org X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=DKIM_SIGNED=0.1, DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,MAILING_LIST_MULTI=-1, RCVD_IN_DNSWL_MED=-2.3,SPF_HELO_NONE=0.001,T_DMARC_POLICY_NONE=0.01 autolearn=ham autolearn_force=no version=3.4.4 X-Spam-BL-Results: [127.0.9.2] Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by apollo.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:41 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 617901D35F373F; Mon, 20 Jul 2020 14:25:38 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [5.9.250.164]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits)) (No client certificate requested) (Authenticated sender: luna) by orion.archlinux.org (Postfix) with ESMTPSA id 380B31D35F373E; Mon, 20 Jul 2020 14:25:38 +0000 (UTC) Authentication-Results: orion.archlinux.org; dkim=pass (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=gcC1Y1T4 Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id CDFE220F45; Mon, 20 Jul 2020 14:25:36 +0000 (UTC) Authentication-Results: luna.archlinux.org; dkim=pass (1024-bit key) header.d=mg0.fr header.i=@mg0.fr header.b=gcC1Y1T4 Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 3CF2F20F23 for ; Mon, 20 Jul 2020 14:25:33 +0000 (UTC) Received: from orion.archlinux.org (orion.archlinux.org [88.198.91.70]) by luna.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:33 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 2C8C81D35F3715 for ; Mon, 20 Jul 2020 14:25:30 +0000 (UTC) Received: from tsubame.mg0.fr (tsubame.mg0.fr [IPv6:2001:41d0:401:3100::402b]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS for ; Mon, 20 Jul 2020 14:25:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mg0.fr; s=tsubame; h=Message-ID:Subject:To:From:Date:cc; bh=DQ5WlTDp7FH76BVbNZEAAar29AsTGjTWmq+eAYeLV3I=; b=gcC1Y1T4EtnLlmL/oPU7PJwz8A XXxzMMsGJw7BfdnG042HoKJmciixFSJ6j/8C6x0z3AXYpW53PqmvPAoLUdukhrC8eocbqTtpV8IEp m0DV78Q5bzJYOw5xtY97Y3oWMK9I/Gaxh4VvOmMVzUn5TthzUBLcCtRFFCjfR+JOqxSE=; Received: from fmang by tsubame.mg0.fr with local (Exim 4.94) (envelope-from ) id 1jxWjQ-00GtV3-PP for aur-dev@archlinux.org; Mon, 20 Jul 2020 16:25:28 +0200 Date: Mon, 20 Jul 2020 16:25:28 +0200 From: =?utf-8?b?RnLDqWTDqXJpYw==?= Mangano-Tarumi To: aur-dev@archlinux.org Subject: [PATCH 3/3] Build a translation facility for FastAPI Message-ID: <20200720142528.GA4026590@tsubame.mg0.fr> MIME-Version: 1.0 Content-Disposition: inline X-BeenThere: aur-dev@archlinux.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: "Arch User Repository \(AUR\) Development" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: aur-dev-bounces@archlinux.org Sender: "aur-dev" --- aurweb/l10n.py | 22 ++++++++++++++++++++++ aurweb/routers/sso.py | 20 +++++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/aurweb/l10n.py b/aurweb/l10n.py index 51b56abb..a476ecd8 100644 --- a/aurweb/l10n.py +++ b/aurweb/l10n.py @@ -16,3 +16,25 @@ class Translator: self._localedir, languages=[lang]) return self._translator[lang].gettext(s) + + +def get_translator_for_request(request): + """ + Determine the preferred language from a FastAPI request object and build a + translator function for it. + + Example: + ```python + _ = get_translator_for_request(request) + print(_("Hello")) + ``` + """ + lang = request.cookies.get("AURLANG") + if lang is None: + lang = aurweb.config.get("options", "default_lang") + translator = Translator() + + def translate(message): + return translator.translate(message, lang) + + return translate diff --git a/aurweb/routers/sso.py b/aurweb/routers/sso.py index 3e3b743d..7b9c67c8 100644 --- a/aurweb/routers/sso.py +++ b/aurweb/routers/sso.py @@ -14,6 +14,7 @@ from starlette.requests import Request import aurweb.config import aurweb.db +from aurweb.l10n import get_translator_for_request from aurweb.schema import Bans, Sessions, Users router = fastapi.APIRouter() @@ -46,13 +47,13 @@ def is_account_suspended(conn, user_id): return row is not None and bool(row[0]) -def open_session(conn, user_id): +def open_session(request, conn, user_id): """ Create a new user session into the database. Return its SID. """ - # TODO Handle translations. if is_account_suspended(conn, user_id): - raise HTTPException(status_code=403, detail='Account suspended') + _ = get_translator_for_request(request) + raise HTTPException(status_code=403, detail=_('Account suspended')) # TODO This is a terrible message because it could imply the attempt at # logging in just caused the suspension. # TODO apply [options] max_sessions_per_user @@ -81,25 +82,26 @@ async def authenticate(request: Request, conn=Depends(aurweb.db.connect)): Receive an OpenID Connect ID token, validate it, then process it to create an new AUR session. """ - # TODO Handle translations if is_ip_banned(conn, request.client.host): + _ = get_translator_for_request(request) raise HTTPException( status_code=403, - detail='The login form is currently disabled for your IP address, ' - 'probably due to sustained spam attacks. Sorry for the ' - 'inconvenience.') + detail=_('The login form is currently disabled for your IP address, ' + 'probably due to sustained spam attacks. Sorry for the ' + 'inconvenience.')) token = await oauth.sso.authorize_access_token(request) user = await oauth.sso.parse_id_token(request, token) sub = user.get("sub") # this is the SSO account ID in JWT terminology if not sub: - raise HTTPException(status_code=400, detail="JWT is missing its `sub` field.") + _ = get_translator_for_request(request) + raise HTTPException(status_code=400, detail=_("JWT is missing its `sub` field.")) aur_accounts = conn.execute(select([Users.c.ID]).where(Users.c.SSOAccountID == sub)) \ .fetchall() if not aur_accounts: return "Sorry, we don’t seem to know you Sir " + sub elif len(aur_accounts) == 1: - sid = open_session(conn, aur_accounts[0][Users.c.ID]) + sid = open_session(request, conn, aur_accounts[0][Users.c.ID]) response = RedirectResponse("/") # TODO redirect to the referrer response.set_cookie(key="AURSID", value=sid, httponly=True,