From 1a3d5664318ce5af80dfe94a42a7f617a5cb4559 Mon Sep 17 00:00:00 2001 From: Carlos Herrero Date: Thu, 23 Sep 2021 19:23:55 +0200 Subject: [PATCH 1/8] Login page --- plugins/login/fps_login/__init__.py | 0 plugins/login/fps_login/routes.py | 25 ++++ plugins/login/fps_login/static/favicon.ico | Bin 0 -> 32038 bytes .../static/favicons/favicon-busy-1.ico | Bin 0 -> 1150 bytes .../static/favicons/favicon-busy-2.ico | Bin 0 -> 1150 bytes .../static/favicons/favicon-busy-3.ico | Bin 0 -> 1150 bytes .../static/favicons/favicon-file.ico | Bin 0 -> 1150 bytes .../static/favicons/favicon-notebook.ico | Bin 0 -> 1150 bytes .../static/favicons/favicon-terminal.ico | Bin 0 -> 1150 bytes .../fps_login/static/favicons/favicon.ico | Bin 0 -> 32038 bytes plugins/login/fps_login/static/index.html | 122 ++++++++++++++++++ .../login/fps_login/static/logo/github.svg | 3 + plugins/login/fps_login/static/logo/logo.png | Bin 0 -> 5922 bytes .../login/fps_login/static/style/index.css | 93 +++++++++++++ plugins/login/setup.py | 13 ++ 15 files changed, 256 insertions(+) create mode 100644 plugins/login/fps_login/__init__.py create mode 100644 plugins/login/fps_login/routes.py create mode 100644 plugins/login/fps_login/static/favicon.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon-busy-1.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon-busy-2.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon-busy-3.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon-file.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon-notebook.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon-terminal.ico create mode 100644 plugins/login/fps_login/static/favicons/favicon.ico create mode 100644 plugins/login/fps_login/static/index.html create mode 100644 plugins/login/fps_login/static/logo/github.svg create mode 100644 plugins/login/fps_login/static/logo/logo.png create mode 100644 plugins/login/fps_login/static/style/index.css create mode 100644 plugins/login/setup.py diff --git a/plugins/login/fps_login/__init__.py b/plugins/login/fps_login/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/login/fps_login/routes.py b/plugins/login/fps_login/routes.py new file mode 100644 index 00000000..9be2cc20 --- /dev/null +++ b/plugins/login/fps_login/routes.py @@ -0,0 +1,25 @@ +from pathlib import Path +from fastapi import APIRouter +from fastapi.responses import FileResponse +from fastapi.staticfiles import StaticFiles +from fps.hooks import register_router # type: ignore +from fps.logging import get_configured_logger # type: ignore + +logger = get_configured_logger("login") + +router = APIRouter() +prefix_static = Path(__file__).parent / "static" + +router.mount( + "/static/login_page", + StaticFiles(directory=prefix_static), + name="static", +) + +@router.get("/login_page") +async def api_login(): + logger.warn("LOGIN PAGE REQUESTED") + logger.warn(prefix_static) + return FileResponse(prefix_static / "index.html") + +r = register_router(router) diff --git a/plugins/login/fps_login/static/favicon.ico b/plugins/login/fps_login/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2d1bcff7ca0ed6c77fdf1b016986f9137a43d31a GIT binary patch literal 32038 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 literal 0 HcmV?d00001 diff --git a/plugins/login/fps_login/static/favicons/favicon-busy-3.ico b/plugins/login/fps_login/static/favicons/favicon-busy-3.ico new file mode 100644 index 0000000000000000000000000000000000000000..b5edce573303a1d750106afa0a3b17bb2e6f2fb7 GIT binary patch literal 1150 zcmb_bv1$TA5PiXDk|Jo8+9n_Y3x)Vi`2fLkKEXB-v{wTWzrfB?k~)Dv1Y6U^YVwA4 zPh|JbExhC1&b>Ep=D3{&E_l~!;IodWOW+PbgCIr5@mv6E_Y@6hzeINs)kOS7`=`~2 zd##}8S3>{KXm95qCHRL0{!W6wlk0ci@XQ)-L^;c|*l{lzmludum^&s`M4M>s<0;ol zo-N~@f5O}k!u*DJ;+078+xO3pa$U_Cxk1(^7KqyaTNAf|Y@E{`1I-hnwtrp!8myu{ z>P7o!^Yak+JR#boUNk>;2CjX2u!`PLkI!$~?ECW-uMspnP4L8GQ3AfNj`7zbAWlw`wvCu45I)5 literal 0 HcmV?d00001 diff --git a/plugins/login/fps_login/static/favicons/favicon-file.ico b/plugins/login/fps_login/static/favicons/favicon-file.ico new file mode 100644 index 0000000000000000000000000000000000000000..8167018cd005ff4a24d8287c620539e23c69aac9 GIT binary patch literal 1150 zcmeHGu?@m75Ih7usc1=q#Ar}3g9{1u5G9Vze%<}~9qSED z_*E4+*VxQ}1%Nd{icIFY0Mu3#9X}t*S|qcPXdmjkckEEPihH)7%2nLouWvxzmEyD; z_Z}H)ul%aFKJ%B}O15MDW@wjZiljJOvMuwx&zB)}Yvgo9FK{i_b#L8MN~r!Z`{GSC cvToVotH@v7c?w*vZ?LC+B96p?o;l#&8_znXx&QzG literal 0 HcmV?d00001 diff --git a/plugins/login/fps_login/static/favicons/favicon-notebook.ico b/plugins/login/fps_login/static/favicons/favicon-notebook.ico new file mode 100644 index 0000000000000000000000000000000000000000..4537e2d989843ae1a96a0548aa0a7066a22e2698 GIT binary patch literal 1150 zcmd6kJxT;Y5QX2WpqU$)xPggaBK}}1>McBhsCWizK|@m`1#e)g;lhS0Vx(Z8XsF1B zf@t|QGc63$HWTg)-xU2`y`G|5YVo$);ya`5F=+Oz;Y!U7Un(3%^tjC*nw^$zt$lCLQe;7 z68RSTXM;T!>A8mbS(zU-$nVo*u$!1+qs)I$$1A@12wJ$KD_DZdiXRr{6L1H)bNs;% z_xMoD7kliL`IDM&zEXNy)YY0_vYhz#zuJz@P!dKp~(AL>x#lFaYILfs!CRtU&#LehmNA|NqHQ z|NlP@`~yXO{r??6b?qo(q;Ts0O91s>q0kHwp#F0dnjr+#{|<-?NpU`0ZTkWVxAGzYSw`z#PH=!+V$+80G`{{Xl#ih#x~Shz$}0 J$-&gZXaKG5Ui|<7 literal 0 HcmV?d00001 diff --git a/plugins/login/fps_login/static/favicons/favicon.ico b/plugins/login/fps_login/static/favicons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2d1bcff7ca0ed6c77fdf1b016986f9137a43d31a GIT binary patch literal 32038 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 + + + + Jupyverse + + + + + + + + + + + + + +
+
+
+ +
+
+
+ + + + + + diff --git a/plugins/login/fps_login/static/logo/github.svg b/plugins/login/fps_login/static/logo/github.svg new file mode 100644 index 00000000..bf0ff1a2 --- /dev/null +++ b/plugins/login/fps_login/static/logo/github.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/login/fps_login/static/logo/logo.png b/plugins/login/fps_login/static/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3f3f57ff39432a80c15dabceb99141bc7ec4318e GIT binary patch literal 5922 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 Eg5lktVgLXD literal 0 HcmV?d00001 diff --git a/plugins/login/fps_login/static/style/index.css b/plugins/login/fps_login/static/style/index.css new file mode 100644 index 00000000..681953c2 --- /dev/null +++ b/plugins/login/fps_login/static/style/index.css @@ -0,0 +1,93 @@ +#jupyter_server { + padding-left: 0px; + padding-top: 10px; + padding-bottom: 1px; +} + +#jupyter_server img { + height: 28px; +} + +#jupyter-main-app { + padding-top: 50px; + text-align: center; +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 1.42857143; + color: #000; +} + +body > #header { + display: block; + background-color: #fff; + position: relative; + z-index: 100; +} + +body > #header #header-container { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 5px; + padding-top: 5px; + padding-bottom: 5px; + padding-bottom: 5px; + padding-top: 5px; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +body > #header .header-bar { + width: 100%; + height: 1px; + background: #e7e7e7; + margin-bottom: -1px; +} + +.navbar-brand { + float: left; + height: 30px; + padding: 6px 0px; + padding-top: 6px; + padding-bottom: 6px; + padding-left: 0px; + font-size: 17px; + line-height: 18px; +} + +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.center-nav { + display: inline-block; + margin-bottom: -4px; +} + +.navigation ul { + list-style: none; + padding: 0; + margin: 0; +} + + +li { + text-align: left; +} + +.github { + background-color: #FFFF; + color: #FFFF; + border: none; +} \ No newline at end of file diff --git a/plugins/login/setup.py b/plugins/login/setup.py new file mode 100644 index 00000000..837f65cc --- /dev/null +++ b/plugins/login/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup, find_packages # type: ignore + +setup( + name="fps_login", + version="0.0.1", + packages=find_packages(), + install_requires=[ + "fps", + ], + entry_points={ + "fps_router": ["fps-login = fps_login.routes"], + }, +) From 6e7bd5e8da628d8670e52352e83b1d0d694b8969 Mon Sep 17 00:00:00 2001 From: Carlos Herrero Date: Fri, 24 Sep 2021 16:04:11 +0200 Subject: [PATCH 2/8] Update scope when requesting github access and include static dir --- .github/workflows/main.yml | 2 ++ plugins/login/MANIFEST.in | 1 + plugins/login/fps_login/routes.py | 2 ++ plugins/login/fps_login/static/index.html | 2 +- plugins/login/setup.py | 4 ++++ 5 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 plugins/login/MANIFEST.in diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e1f4b5e8..7c02ead8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,6 +43,7 @@ jobs: pip install ./plugins/kernels pip install ./plugins/lab pip install ./plugins/jupyterlab + pip install ./plugins/login pip install flake8 black mypy pytest pytest-asyncio requests ipykernel - name: Check style @@ -59,6 +60,7 @@ jobs: mypy plugins/nbconvert/fps_nbconvert mypy plugins/yjs/fps_yjs mypy plugins/terminals/fps_terminals + mypy plugins/login/fps_login - name: Run tests run: | diff --git a/plugins/login/MANIFEST.in b/plugins/login/MANIFEST.in new file mode 100644 index 00000000..c242957b --- /dev/null +++ b/plugins/login/MANIFEST.in @@ -0,0 +1 @@ +graft fps_login/static \ No newline at end of file diff --git a/plugins/login/fps_login/routes.py b/plugins/login/fps_login/routes.py index 9be2cc20..8213bd92 100644 --- a/plugins/login/fps_login/routes.py +++ b/plugins/login/fps_login/routes.py @@ -16,10 +16,12 @@ name="static", ) + @router.get("/login_page") async def api_login(): logger.warn("LOGIN PAGE REQUESTED") logger.warn(prefix_static) return FileResponse(prefix_static / "index.html") + r = register_router(router) diff --git a/plugins/login/fps_login/static/index.html b/plugins/login/fps_login/static/index.html index 6d4fd225..8ab6aaf3 100644 --- a/plugins/login/fps_login/static/index.html +++ b/plugins/login/fps_login/static/index.html @@ -84,7 +84,7 @@ method: 'GET', headers: { accept: 'application/json' } }; - fetch('/auth/github/authorize?authentication_backend=cookie', init) + fetch('/auth/github/authorize?authentication_backend=cookie&scopes=user:email', init) .then( async resp => { const data = await resp.json(); console.debug("rsp:", data); diff --git a/plugins/login/setup.py b/plugins/login/setup.py index 837f65cc..ff7663f0 100644 --- a/plugins/login/setup.py +++ b/plugins/login/setup.py @@ -4,6 +4,10 @@ name="fps_login", version="0.0.1", packages=find_packages(), + include_package_data=True, + package_data={ + "static": ["*"], + }, install_requires=[ "fps", ], From 37194e8c44641ce39be1910f40e88efbe3d942d8 Mon Sep 17 00:00:00 2001 From: Carlos Herrero Date: Fri, 24 Sep 2021 22:16:15 +0200 Subject: [PATCH 3/8] Add token auth to login page --- plugins/auth/fps_auth/backends.py | 128 ++++++++------------ plugins/auth/fps_auth/config.py | 9 +- plugins/auth/fps_auth/db.py | 3 +- plugins/auth/fps_auth/models.py | 1 + plugins/auth/fps_auth/routes.py | 120 +++++++++--------- plugins/contents/fps_contents/routes.py | 12 +- plugins/jupyterlab/fps_jupyterlab/routes.py | 10 +- plugins/kernels/fps_kernels/routes.py | 8 +- plugins/lab/fps_lab/routes.py | 26 +--- plugins/login/fps_login/routes.py | 2 - plugins/login/fps_login/static/index.html | 101 +++++++-------- plugins/nbconvert/fps_nbconvert/routes.py | 2 +- plugins/retrolab/fps_retrolab/routes.py | 6 +- plugins/terminals/fps_terminals/routes.py | 4 +- plugins/yjs/fps_yjs/routes.py | 2 +- tests/test_auth.py | 5 +- 16 files changed, 198 insertions(+), 241 deletions(-) diff --git a/plugins/auth/fps_auth/backends.py b/plugins/auth/fps_auth/backends.py index e81e569d..274953cf 100644 --- a/plugins/auth/fps_auth/backends.py +++ b/plugins/auth/fps_auth/backends.py @@ -1,43 +1,37 @@ from typing import Optional +from fps.exceptions import RedirectException # type: ignore + import httpx -from fastapi import Depends, HTTPException, status -from fastapi_users.authentication import BaseAuthentication, CookieAuthentication # type: ignore +from httpx_oauth.clients.github import GitHubOAuth2 # type: ignore +from fastapi import Depends, Response, HTTPException, status + +from fastapi_users.authentication import CookieAuthentication, JWTAuthentication # type: ignore from fastapi_users import FastAPIUsers, BaseUserManager # type: ignore from starlette.requests import Request -from fps.exceptions import RedirectException # type: ignore +from .config import get_auth_config +from .db import secret, get_user_db from .models import ( User, + UserDB, UserCreate, UserUpdate, - UserDB, + UserDB ) -from .config import get_auth_config -from .db import secret, get_user_db +auth_config = get_auth_config() -NOAUTH_EMAIL = "noauth_user@jupyter.com" -NOAUTH_USER = UserDB( - id="d4ded46b-a4df-4b51-8d83-ae19010272a7", - email=NOAUTH_EMAIL, - hashed_password="", -) - - -class NoAuthAuthentication(BaseAuthentication): - def __init__(self, user: UserDB, name: str = "noauth"): - super().__init__(name, logout=False) - self.user = user - self.scheme = None # type: ignore - - async def __call__(self, credentials, user_manager): - noauth_user = await user_manager.user_db.get_by_email(NOAUTH_EMAIL) - return noauth_user or self.user - - -noauth_authentication = NoAuthAuthentication(NOAUTH_USER) +class LoginCookieAuthentication(CookieAuthentication): + async def get_login_response(self, user, response, user_manager): + await super().get_login_response(user, response, user_manager) + # auto redirect + response.status_code = status.HTTP_302_FOUND + response.headers["Location"] = "/lab" +cookie_authentication = LoginCookieAuthentication(secret=secret, name='cookie') +jwt_authentication = JWTAuthentication(secret=secret, lifetime_seconds=3600) +github_authentication = GitHubOAuth2(auth_config.client_id, auth_config.client_secret.get_secret_value()) class UserManager(BaseUserManager[UserCreate, UserDB]): user_db_model = UserDB @@ -53,68 +47,52 @@ async def on_after_register(self, user: UserDB, request: Optional[Request] = Non ) ).json() user.anonymous = False + user.token = "" user.username = r["login"] user.name = r["name"] user.color = None user.avatar = r["avatar_url"] await self.user_db.update(user) - -class LoginCookieAuthentication(CookieAuthentication): - async def get_login_response(self, user, response, user_manager): - await super().get_login_response(user, response, user_manager) - # set user as logged in - user.logged_in = True - await user_manager.user_db.update(user) - # auto redirect - response.status_code = status.HTTP_302_FOUND - response.headers["Location"] = "/lab" - - async def get_logout_response(self, user, response, user_manager): - await super().get_logout_response(user, response, user_manager) - # set user as logged out - user.logged_in = False - await user_manager.user_db.update(user) - - -cookie_authentication = LoginCookieAuthentication( - cookie_secure=get_auth_config().cookie_secure, secret=secret -) - - def get_user_manager(user_db=Depends(get_user_db)): yield UserManager(user_db) - -users = FastAPIUsers( +fapi_users = FastAPIUsers( get_user_manager, - [noauth_authentication, cookie_authentication], + [cookie_authentication, jwt_authentication], User, UserCreate, UserUpdate, - UserDB, + UserDB ) +async def current_user( + response: Response, + token: Optional[str] = None, + user: User = Depends(fapi_users.current_user(optional=True)), + user_db = Depends(get_user_db), + user_manager = Depends(get_user_manager) +): + active_user = user + + if active_user : + return active_user + + elif auth_config.mode == 'noauth' : + active_user = await user_db.get_by_email(auth_config.guest_email) + + elif auth_config.mode == 'token' : + tmp = await user_db.get_by_email(auth_config.guest_email) + if tmp.token == token : + active_user = tmp + await cookie_authentication.get_login_response(tmp, response, user_manager) + + + if active_user : + return active_user + + elif auth_config.login_url : + raise RedirectException(auth_config.login_url) -async def get_enabled_backends(auth_config=Depends(get_auth_config)): - if auth_config.mode == "noauth": - return [noauth_authentication] - return [cookie_authentication] - - -def current_user(): - async def _( - user: User = Depends( - users.current_user(optional=True, get_enabled_backends=get_enabled_backends) - ), - auth_config=Depends(get_auth_config), - ): - if user is None: - if auth_config.login_url: - raise RedirectException(auth_config.login_url) - else: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) - else: - return user - - return _ + else : + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) diff --git a/plugins/auth/fps_auth/config.py b/plugins/auth/fps_auth/config.py index 2fda5ea8..7d22f214 100644 --- a/plugins/auth/fps_auth/config.py +++ b/plugins/auth/fps_auth/config.py @@ -1,7 +1,10 @@ +from uuid import uuid4 +from typing import Literal +from pydantic import SecretStr + from fps.config import PluginModel, get_config # type: ignore from fps.hooks import register_config, register_plugin_name # type: ignore -from pydantic import SecretStr -from typing import Literal + class AuthConfig(PluginModel): @@ -9,6 +12,8 @@ class AuthConfig(PluginModel): client_secret: SecretStr = SecretStr("") redirect_uri: str = "" mode: Literal["noauth", "token", "user"] = "token" + token = str(uuid4()) + guest_email = "guest@jupyter.com" cookie_secure: bool = ( False # FIXME: should default to True, and set to False for tests ) diff --git a/plugins/auth/fps_auth/db.py b/plugins/auth/fps_auth/db.py index a27d7709..f56fb66d 100644 --- a/plugins/auth/fps_auth/db.py +++ b/plugins/auth/fps_auth/db.py @@ -45,6 +45,7 @@ class UserTable(Base, SQLAlchemyBaseUserTable): initialized = Column(Boolean, default=False, nullable=False) anonymous = Column(Boolean, default=False, nullable=False) + token = Column(String(length=32), nullable=True) name = Column(String(length=32), nullable=True) username = Column(String(length=32), nullable=True) color = Column(String(length=32), nullable=True) @@ -70,5 +71,5 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTable, Base): user_db = SQLAlchemyUserDatabase(UserDB, database, users, oauth_accounts) -def get_user_db(): +def get_user_db() -> SQLAlchemyUserDatabase[UserDB]: yield user_db diff --git a/plugins/auth/fps_auth/models.py b/plugins/auth/fps_auth/models.py index 36b1c2f1..9b5a754b 100644 --- a/plugins/auth/fps_auth/models.py +++ b/plugins/auth/fps_auth/models.py @@ -7,6 +7,7 @@ class JupyterUser(BaseModel): initialized: bool = False anonymous: bool = True + token: Optional[str] = None name: Optional[str] = None username: Optional[str] = None color: Optional[str] = None diff --git a/plugins/auth/fps_auth/routes.py b/plugins/auth/fps_auth/routes.py index f2aaf9b8..557d0254 100644 --- a/plugins/auth/fps_auth/routes.py +++ b/plugins/auth/fps_auth/routes.py @@ -1,25 +1,20 @@ from uuid import uuid4 +from fastapi_users.authentication import jwt -from httpx_oauth.clients.github import GitHubOAuth2 # type: ignore from fps.hooks import register_router # type: ignore from fps.config import get_config # type: ignore from fps.logging import get_configured_logger # type: ignore from fps_uvicorn.config import UvicornConfig # type: ignore -from fastapi import APIRouter, Depends + +from fastapi import APIRouter, Depends, Response from sqlalchemy.orm import sessionmaker # type: ignore from .config import get_auth_config from .db import user_db, secret, database, engine, UserTable -from .backends import ( - users, - current_user, - cookie_authentication, - NOAUTH_USER, - NOAUTH_EMAIL, -) +from .backends import fapi_users, current_user, cookie_authentication, jwt_authentication, github_authentication from .models import ( User, - UserDB, + UserDB ) logger = get_configured_logger("auth") @@ -30,77 +25,70 @@ uvicorn_config = get_config(UvicornConfig) auth_config = get_auth_config() - -github_oauth_client = GitHubOAuth2( - auth_config.client_id, auth_config.client_secret.get_secret_value() -) - - -github_oauth_router = users.get_oauth_router( - github_oauth_client, secret # type: ignore -) -auth_router = users.get_auth_router(cookie_authentication) -user_register_router = users.get_register_router() # type: ignore -users_router = users.get_users_router() - router = APIRouter() -TOKEN_USER = None -USER_TOKEN = None - - -def set_user_token(user_token): - global USER_TOKEN - USER_TOKEN = user_token - - -def get_user_token(): - return USER_TOKEN - - @router.on_event("startup") async def startup(): await database.connect() - if auth_config.mode == "noauth": - await create_noauth_user() - elif auth_config.mode == "token": - set_user_token(str(uuid4())) - await create_token_user() + guest = await user_db.get_by_email(auth_config.guest_email) + + if guest : + guest.token = auth_config.token + await user_db.update(guest) + + else : + guest = UserDB( + id=str(uuid4()), + email=auth_config.guest_email, + token=auth_config.token, + hashed_password="" + ) + await user_db.create(guest) + + logger.info("") + logger.info("To access the server, copy and paste this URL:") + logger.info(f"http://{uvicorn_config.host}:{uvicorn_config.port}/?token={auth_config.token}") + logger.info("") @router.on_event("shutdown") async def shutdown(): - global TOKEN_USER await database.disconnect() - if auth_config.mode == "token": - await user_db.delete(TOKEN_USER) - - -async def create_noauth_user(): - noauth_user = await user_db.get_by_email(NOAUTH_EMAIL) - if noauth_user is None: - await user_db.create(NOAUTH_USER) - - -async def create_token_user(): - global TOKEN_USER - logger.info("To access the server, copy and paste this URL:") - logger.info( - f"http://{uvicorn_config.host}:{uvicorn_config.port}/?token={USER_TOKEN}" - ) - token_email = f"{USER_TOKEN}_user@jupyter.com" - TOKEN_USER = UserDB(id=USER_TOKEN, email=token_email, hashed_password="") - await user_db.create(TOKEN_USER) @router.get("/auth/users") -async def get_users(user: User = Depends(current_user())): +async def get_users(user: User = Depends(current_user)): users = session.query(UserTable).all() return [user for user in users if user.logged_in] +@router.post("/auth/jwt/refresh") +async def refresh_jwt(response: Response, user=Depends(current_user)): + return await jwt_authentication.get_login_response(user, response) + +# Cookie based auth login, logout and register routes +r_cookie_auth = register_router( + fapi_users.get_auth_router(cookie_authentication), + prefix="/auth" +) +r_register = register_router( + fapi_users.get_register_router(), + prefix="/auth" +) +r_users = register_router( + fapi_users.get_users_router(), + prefix="/auth/users" +) + +# JWT token based auth login, logout +r_jwt_auth = register_router( + fapi_users.get_auth_router(jwt_authentication), + prefix="/auth/jwt" +) + +# GitHub OAuth register router +r_github = register_router( + fapi_users.get_oauth_router(github_authentication, secret), + prefix="/auth/github" +) -r_auth = register_router(auth_router) -r_register = register_router(user_register_router) -r_users = register_router(users_router, prefix="/auth/users") -r_github = register_router(github_oauth_router, prefix="/auth/github") r = register_router(router) diff --git a/plugins/contents/fps_contents/routes.py b/plugins/contents/fps_contents/routes.py index 092c2874..f5d1f519 100644 --- a/plugins/contents/fps_contents/routes.py +++ b/plugins/contents/fps_contents/routes.py @@ -23,7 +23,7 @@ ) async def create_content( request: Request, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): create_content = await request.json() path = Path(create_content["path"]) @@ -53,13 +53,13 @@ async def create_content( @router.get("/api/contents") async def get_root_content( content: int, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): return Content(**get_path_content(Path(""), bool(content))) @router.get("/api/contents/{path:path}/checkpoints") -async def get_checkpoint(path, user: User = Depends(current_user())): +async def get_checkpoint(path, user: User = Depends(current_user)): src_path = Path(path) dst_path = ( Path(".ipynb_checkpoints") / f"{src_path.stem}-checkpoint{src_path.suffix}" @@ -74,7 +74,7 @@ async def get_checkpoint(path, user: User = Depends(current_user())): async def get_content( path: str, content: int, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): return Content(**get_path_content(Path(path), bool(content))) @@ -82,7 +82,7 @@ async def get_content( @router.put("/api/contents/{path:path}") async def save_content( request: Request, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): save_content = SaveContent(**(await request.json())) try: @@ -110,7 +110,7 @@ async def save_content( "/api/contents/{path:path}/checkpoints", status_code=201, ) -async def create_checkpoint(path, user: User = Depends(current_user())): +async def create_checkpoint(path, user: User = Depends(current_user)): src_path = Path(path) dst_path = ( Path(".ipynb_checkpoints") / f"{src_path.stem}-checkpoint{src_path.suffix}" diff --git a/plugins/jupyterlab/fps_jupyterlab/routes.py b/plugins/jupyterlab/fps_jupyterlab/routes.py index 1fbf68b4..764cc2fe 100644 --- a/plugins/jupyterlab/fps_jupyterlab/routes.py +++ b/plugins/jupyterlab/fps_jupyterlab/routes.py @@ -11,8 +11,8 @@ from fps_auth.db import get_user_db # type: ignore from fps_auth.backends import current_user # type: ignore from fps_auth.models import User # type: ignore -from fps_lab.routes import init_router # type: ignore +from fps_lab.routes import init_router # type: ignore from fps_lab.config import get_lab_config # type: ignore router = APIRouter() @@ -27,7 +27,7 @@ @router.get("/lab") async def get_lab( - user: User = Depends(current_user()), lab_config=Depends(get_lab_config) + user: User = Depends(current_user), lab_config=Depends(get_lab_config) ): return HTMLResponse( get_index("default", lab_config.collaborative, lab_config.base_url) @@ -42,7 +42,7 @@ async def load_workspace(path, lab_config=Depends(get_lab_config)): @router.get("/lab/api/workspaces/{name}") -async def get_workspace_data(user: User = Depends(current_user())): +async def get_workspace_data(user: User = Depends(current_user)): if user: return json.loads(user.workspace) return {} @@ -54,7 +54,7 @@ async def get_workspace_data(user: User = Depends(current_user())): ) async def set_workspace( request: Request, - user: User = Depends(current_user()), + user: User = Depends(current_user), user_db=Depends(get_user_db), ): user.workspace = await request.body() @@ -64,7 +64,7 @@ async def set_workspace( @router.get("/lab/workspaces/{name}", response_class=HTMLResponse) async def get_workspace( - name, user: User = Depends(current_user()), lab_config=Depends(get_lab_config) + name, user: User = Depends(current_user), lab_config=Depends(get_lab_config) ): return get_index(name, lab_config.collaborative, lab_config.base_url) diff --git a/plugins/kernels/fps_kernels/routes.py b/plugins/kernels/fps_kernels/routes.py index 5deae8c4..f8fbfb45 100644 --- a/plugins/kernels/fps_kernels/routes.py +++ b/plugins/kernels/fps_kernels/routes.py @@ -74,7 +74,7 @@ async def get_kernels(): @router.delete("/api/sessions/{session_id}", status_code=204) async def delete_session( session_id: str, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): kernel_id = sessions[session_id]["kernel"]["id"] kernel_server = kernels[kernel_id]["server"] @@ -87,7 +87,7 @@ async def delete_session( @router.patch("/api/sessions/{session_id}") async def rename_session( request: Request, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): rename_session = await request.json() session_id = rename_session.pop("id") @@ -115,7 +115,7 @@ async def get_sessions(): ) async def create_session( request: Request, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): create_session = await request.json() kernel_name = create_session["kernel"]["name"] @@ -149,7 +149,7 @@ async def create_session( @router.post("/api/kernels/{kernel_id}/restart") async def restart_kernel( kernel_id, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): if kernel_id in kernels: kernel = kernels[kernel_id] diff --git a/plugins/lab/fps_lab/routes.py b/plugins/lab/fps_lab/routes.py index 972e3dca..38403e3d 100644 --- a/plugins/lab/fps_lab/routes.py +++ b/plugins/lab/fps_lab/routes.py @@ -14,13 +14,8 @@ from starlette.requests import Request # type: ignore from fps_auth.db import get_user_db # type: ignore -from fps_auth.backends import ( # type: ignore - current_user, - cookie_authentication, - LoginCookieAuthentication, - get_user_manager, -) -from fps_auth.models import User # type: ignore +from fps_auth.backends import current_user # type: ignore +from fps_auth.models import User # type: ignore from fps_auth.config import get_auth_config # type: ignore from .config import get_lab_config # type: ignore @@ -64,18 +59,9 @@ def init_router(router, redirect_after_root): @router.get("/") async def get_root( response: Response, - token: Optional[str] = None, - auth_config=Depends(get_auth_config), lab_config=Depends(get_lab_config), - user_db=Depends(get_user_db), - user_manager=Depends(get_user_manager), + user=Depends(current_user), ): - if token and auth_config.mode == "token": - user = await user_db.get(token) - if user: - await super( - LoginCookieAuthentication, cookie_authentication - ).get_login_response(user, response, user_manager) # auto redirect response.status_code = status.HTTP_302_FOUND response.headers["Location"] = lab_config.base_url + redirect_after_root @@ -146,7 +132,7 @@ async def get_setting( name0, name1, name2, - user: User = Depends(current_user()), + user: User = Depends(current_user), ): with open( prefix_dir / "share" / "jupyter" / "lab" / "static" / "package.json" @@ -191,7 +177,7 @@ async def change_setting( request: Request, name0, name1, - user: User = Depends(current_user()), + user: User = Depends(current_user), user_db=Depends(get_user_db), ): settings = json.loads(user.settings) @@ -201,7 +187,7 @@ async def change_setting( return Response(status_code=HTTPStatus.NO_CONTENT.value) @router.get("/lab/api/settings") - async def get_settings(user: User = Depends(current_user())): + async def get_settings(user: User = Depends(current_user)): with open( prefix_dir / "share" / "jupyter" / "lab" / "static" / "package.json" ) as f: diff --git a/plugins/login/fps_login/routes.py b/plugins/login/fps_login/routes.py index 8213bd92..4101fdcb 100644 --- a/plugins/login/fps_login/routes.py +++ b/plugins/login/fps_login/routes.py @@ -19,8 +19,6 @@ @router.get("/login_page") async def api_login(): - logger.warn("LOGIN PAGE REQUESTED") - logger.warn(prefix_static) return FileResponse(prefix_static / "index.html") diff --git a/plugins/login/fps_login/static/index.html b/plugins/login/fps_login/static/index.html index 8ab6aaf3..25a31046 100644 --- a/plugins/login/fps_login/static/index.html +++ b/plugins/login/fps_login/static/index.html @@ -42,44 +42,66 @@
-