mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-01 11:48:12 +08:00
Refactor czi_hosted and server into backend directory, pull common code into backend/common, refactor tests (#2102)
* move local_server -> backend/server server-> backend/czi_hosted, pull common code into backend/common update imports, tests and make commands
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# import the built in auth types so they can be registered
|
||||
|
||||
import backend.server.auth.auth_none # noqa: F401
|
||||
import backend.server.auth.auth_test # noqa: F401
|
||||
import backend.server.auth.auth_session # noqa: F401
|
||||
@@ -0,0 +1,91 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class AuthTypeBase(ABC):
|
||||
"""Base type for all authentication types."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@abstractmethod
|
||||
def is_valid_authentication_type(self):
|
||||
"""Return True if the auth type is valid, e.g. it can return userinfo and username.
|
||||
(AuthTypeNone is the only one type that returns False)"""
|
||||
pass
|
||||
|
||||
def requires_client_login(self):
|
||||
"""Return True if the user needs to login from the client (e.g. Login button is shown)"""
|
||||
return False
|
||||
|
||||
@abstractmethod
|
||||
def complete_setup(self, app):
|
||||
"""complete any setup that may be needed by this auth type. The Flask app is passed in.
|
||||
This is the last auth function called before the server starts to run."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_user_authenticated(self):
|
||||
"""Return True if the user is authenticated"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_user_id(self):
|
||||
"""Return the id for this user (string)"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_user_name(self):
|
||||
"""Return the name of the user (string)"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_user_email(self):
|
||||
"""Return the name of the user (string)"""
|
||||
pass
|
||||
|
||||
def get_user_picture(self):
|
||||
"""Return the location to the user's picture"""
|
||||
return None
|
||||
|
||||
|
||||
class AuthTypeClientBase(AuthTypeBase):
|
||||
"""Base type for all authentication types that require the client to login"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def requires_client_login(self):
|
||||
return True
|
||||
|
||||
@abstractmethod
|
||||
def add_url_rules(self, selfapp):
|
||||
"""Add url rules to the app (like /login, /logout, etc)"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_login_url(self, data_adaptor):
|
||||
"""Return the url for the login route"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_logout_url(self, data_adaptor):
|
||||
"""Return the url for the logout route"""
|
||||
pass
|
||||
|
||||
|
||||
class AuthTypeFactory:
|
||||
"""Factory class to create an authentication type"""
|
||||
|
||||
auth_types = {}
|
||||
|
||||
@staticmethod
|
||||
def register(name, auth_type):
|
||||
assert issubclass(auth_type, AuthTypeBase)
|
||||
AuthTypeFactory.auth_types[name] = auth_type
|
||||
|
||||
@staticmethod
|
||||
def create(name, app_config):
|
||||
auth_type = AuthTypeFactory.auth_types.get(name)
|
||||
if auth_type is None:
|
||||
return None
|
||||
return auth_type(app_config)
|
||||
@@ -0,0 +1,27 @@
|
||||
from backend.server.auth.auth import AuthTypeBase, AuthTypeFactory
|
||||
|
||||
|
||||
class AuthTypeNone(AuthTypeBase):
|
||||
def __init__(self, app_config):
|
||||
super().__init__()
|
||||
|
||||
def is_valid_authentication_type(self):
|
||||
return False
|
||||
|
||||
def complete_setup(self, app):
|
||||
pass
|
||||
|
||||
def is_user_authenticated(self):
|
||||
return True
|
||||
|
||||
def get_user_id(self):
|
||||
return None
|
||||
|
||||
def get_user_name(self):
|
||||
return None
|
||||
|
||||
def get_user_email(self):
|
||||
return None
|
||||
|
||||
|
||||
AuthTypeFactory.register(None, AuthTypeNone)
|
||||
@@ -0,0 +1,39 @@
|
||||
from backend.server.auth.auth import AuthTypeBase, AuthTypeFactory
|
||||
from flask import session
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
class AuthTypeSession(AuthTypeBase):
|
||||
"""Session based authentication. The user is always logged. The user id is a random number
|
||||
associated with the session. This is a good choice for desktop servers."""
|
||||
|
||||
# key in the session token for userid
|
||||
CXGUID = "cxguid"
|
||||
|
||||
def __init__(self, app_config):
|
||||
super().__init__()
|
||||
|
||||
def is_valid_authentication_type(self):
|
||||
return True
|
||||
|
||||
def complete_setup(self, app):
|
||||
pass
|
||||
|
||||
def is_user_authenticated(self):
|
||||
# always authenticated
|
||||
return True
|
||||
|
||||
def get_user_id(self):
|
||||
if self.CXGUID not in session:
|
||||
session[self.CXGUID] = uuid4().hex
|
||||
session.permanent = True
|
||||
return session[self.CXGUID]
|
||||
|
||||
def get_user_name(self):
|
||||
return "anonymous"
|
||||
|
||||
def get_user_email(self):
|
||||
return None
|
||||
|
||||
|
||||
AuthTypeFactory.register("session", AuthTypeSession)
|
||||
@@ -0,0 +1,73 @@
|
||||
from backend.server.auth.auth import AuthTypeClientBase, AuthTypeFactory
|
||||
from flask import session, request, redirect
|
||||
|
||||
|
||||
class AuthTypeTest(AuthTypeClientBase):
|
||||
"""An authentication type for testing client based logins. When the login route is accessed
|
||||
the user is automatically logged in with a default or configured username"""
|
||||
|
||||
# key in session token with userid and username
|
||||
CXGUID = "cxguid_test"
|
||||
CXGUNAME = "cxguname_test"
|
||||
CXGUEMAIL = "cxguemail_test"
|
||||
CXGUPICTURE = "cxgupicture_test"
|
||||
|
||||
def __init__(self, app_config):
|
||||
super().__init__()
|
||||
self.user_name = "test_account"
|
||||
self.user_id = "id0001"
|
||||
self.user_email = "test_account@test.com"
|
||||
self.user_picture = None
|
||||
|
||||
def is_valid_authentication_type(self):
|
||||
return True
|
||||
|
||||
def requires_client_login(self):
|
||||
return True
|
||||
|
||||
def add_url_rules(self, app):
|
||||
app.add_url_rule("/login", "login", self.login, methods=["GET"])
|
||||
app.add_url_rule("/logout", "logout", self.logout, methods=["GET"])
|
||||
|
||||
def complete_setup(self, app):
|
||||
pass
|
||||
|
||||
def is_user_authenticated(self):
|
||||
return self.CXGUID in session
|
||||
|
||||
def get_user_id(self):
|
||||
return session.get(self.CXGUID)
|
||||
|
||||
def get_user_name(self):
|
||||
return session.get(self.CXGUNAME)
|
||||
|
||||
def get_user_email(self):
|
||||
return session.get(self.CXGUEMAIL)
|
||||
|
||||
def get_user_picture(self):
|
||||
return session.get(self.CXGUPICTURE)
|
||||
|
||||
def login(self):
|
||||
args = request.args
|
||||
return_to = args.get("dataset", "/")
|
||||
session[self.CXGUID] = args.get("userid", self.user_id)
|
||||
session[self.CXGUNAME] = args.get("username", self.user_name)
|
||||
session[self.CXGUEMAIL] = args.get("email", self.user_email)
|
||||
session[self.CXGUPICTURE] = args.get("picture", self.user_picture)
|
||||
return redirect(return_to)
|
||||
|
||||
def logout(self):
|
||||
session.clear()
|
||||
return_to = request.args.get("dataset", "/")
|
||||
return redirect(return_to)
|
||||
|
||||
def get_login_url(self, data_adaptor):
|
||||
"""Return the url for the login route"""
|
||||
return "/login"
|
||||
|
||||
def get_logout_url(self, data_adaptor):
|
||||
"""Return the url for the logout route"""
|
||||
return "/logout"
|
||||
|
||||
|
||||
AuthTypeFactory.register("test", AuthTypeTest)
|
||||
Reference in New Issue
Block a user