Add session store and login endpoint and auth middleware
This commit is contained in:
parent
714f3221f9
commit
ef08fe5997
|
@ -3,6 +3,8 @@ core:
|
|||
listen:
|
||||
host: 127.0.0.1
|
||||
port: 8080
|
||||
# generated with: openssl rand -hex 16
|
||||
cookiestore_secret: "e41133b5cfdd8660815b8d5cc2c74843"
|
||||
|
||||
|
||||
ldap:
|
||||
|
@ -14,11 +16,12 @@ ldap:
|
|||
validate: False # Can either be True or False. Default: False
|
||||
ca_certs: openldap/cert.pem
|
||||
|
||||
username: uid=phi,ou=Services,dc=unit,dc=macaomilano,dc=org
|
||||
password: phi
|
||||
# username: uid=phi,ou=Services,dc=unit,dc=macaomilano,dc=org
|
||||
# password: phi
|
||||
# username: cn=root,dc=unit,dc=macaomilano,dc=org
|
||||
# password: root
|
||||
|
||||
base_dn: dc=unit,dc=macaomilano,dc=org
|
||||
attribute_id: uid
|
||||
|
||||
|
||||
logging:
|
||||
|
|
|
@ -2,24 +2,19 @@
|
|||
from aiohttp import web
|
||||
|
||||
from phi.logging import get_logger
|
||||
from phi.async_ldap.client import AsyncClient
|
||||
from phi.async_ldap.model import Hackers, Robots, Congregations
|
||||
from phi.api.routes import api_routes
|
||||
from phi.auth_middleware import authenticated
|
||||
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
||||
|
||||
def api_app(config):
|
||||
def api_app(store):
|
||||
log.info("Initializing API sub-app.")
|
||||
|
||||
app = web.Application()
|
||||
app = web.Application(middlewares=[authenticated])
|
||||
|
||||
ldap_client = AsyncClient(**config.get("ldap", {}))
|
||||
app["ldap_client"] = ldap_client
|
||||
app["users"] = Hackers(ldap_client)
|
||||
app["services"] = Robots(ldap_client)
|
||||
app["groups"] = Congregations(ldap_client)
|
||||
app["store"] = store
|
||||
app["log"] = log
|
||||
|
||||
app.router.add_routes(api_routes)
|
||||
|
|
|
@ -9,8 +9,8 @@ from aiohttp.web import (
|
|||
)
|
||||
|
||||
from phi.logging import get_logger
|
||||
from phi.api.utils import serialize
|
||||
from phi.async_ldap.model import Hackers, User
|
||||
# from phi.api.utils import serialize
|
||||
from phi.async_ldap.model import User
|
||||
from phi.exceptions import (
|
||||
PhiEntryDoesNotExist,
|
||||
PhiUnexpectedRuntimeValue,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from aiohttp.web import route
|
||||
from aiohttp.web import view
|
||||
|
||||
from phi.api.rest import UserView
|
||||
|
||||
api_routes = [
|
||||
route("*", "/user", UserView),
|
||||
route("*", "/user/", UserView),
|
||||
route("*", "/user/{uid}", UserView),
|
||||
view("/user", UserView),
|
||||
view("/user/", UserView),
|
||||
view("/user/{uid}", UserView),
|
||||
]
|
||||
|
|
|
@ -1,23 +1,34 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
import pkg_resources
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp_session import setup
|
||||
from aiohttp_session.cookie_storage import EncryptedCookieStorage
|
||||
import click
|
||||
from pprint import pformat as pp
|
||||
import yaml
|
||||
|
||||
from phi.config import get_config, merge_config
|
||||
from phi.config import get_config, merge_config, extract_secret
|
||||
from phi.logging import setup_logging, get_logger
|
||||
from phi.api.app import api_app
|
||||
from phi.client_store import ClientStore
|
||||
from phi.login import login
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
||||
|
||||
LOGIN_ROUTE = "/login"
|
||||
|
||||
|
||||
def setup_app(config):
|
||||
app = web.Application()
|
||||
app["config"] = config
|
||||
setup(app, EncryptedCookieStorage(extract_secret(config)))
|
||||
|
||||
api = api_app(config)
|
||||
app["config"] = config
|
||||
app["store"] = ClientStore(LOGIN_ROUTE)
|
||||
app["log"] = log
|
||||
|
||||
app.add_routes([web.post(LOGIN_ROUTE, login)])
|
||||
|
||||
api = api_app(app["store"])
|
||||
app.add_subapp("/api", api)
|
||||
|
||||
return app
|
||||
|
@ -109,7 +120,11 @@ def run_app(app):
|
|||
help="Path to a yaml configuration for the logger.",
|
||||
)
|
||||
@click.option(
|
||||
"--debug", "debug", is_flag=True, default=False, help="Set the log level to debug.",
|
||||
"--debug",
|
||||
"debug",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Set the log level to debug.",
|
||||
)
|
||||
def cli(
|
||||
host,
|
||||
|
@ -141,6 +156,7 @@ def cli(
|
|||
debug,
|
||||
)
|
||||
config_file, file_config = get_config(config_path)
|
||||
log.debug(f"FILE: {pp(file_config)}")
|
||||
config = merge_config(cli_config, file_config)
|
||||
if debug:
|
||||
set_to_debug(config)
|
||||
|
|
|
@ -86,7 +86,7 @@ class AsyncClient(LDAPClient):
|
|||
host=None,
|
||||
port=None,
|
||||
encryption=None,
|
||||
cyphers=None,
|
||||
ciphers=None,
|
||||
validate=False,
|
||||
ca_cert=None,
|
||||
username=None,
|
||||
|
@ -130,3 +130,6 @@ class AsyncClient(LDAPClient):
|
|||
|
||||
self.set_auto_page_acquire(True)
|
||||
self.set_credentials(self.method, user=self.username, password=self.password)
|
||||
|
||||
def __repr__(self):
|
||||
return f"AsyncClient[{self.full_uri}]<{self.username}>"
|
||||
|
|
23
src/phi/auth_middleware.py
Normal file
23
src/phi/auth_middleware.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
from aiohttp.web import middleware, HTTPFound
|
||||
|
||||
from phi import app
|
||||
from phi.async_ldap.model import Hackers, Robots, Congregations
|
||||
|
||||
|
||||
@middleware
|
||||
async def authenticated(request, handler):
|
||||
try:
|
||||
store = request.app["store"]
|
||||
except KeyError:
|
||||
raise HTTPFound(app.LOGIN_ROUTE)
|
||||
|
||||
client = await store.get_client(request)
|
||||
|
||||
request.app["ldap_client"] = client
|
||||
request.app["users"] = Hackers(client)
|
||||
request.app["services"] = Robots(client)
|
||||
request.app["groups"] = Congregations(client)
|
||||
resp = await handler(request)
|
||||
|
||||
return resp
|
35
src/phi/client_store.py
Normal file
35
src/phi/client_store.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
import secrets
|
||||
|
||||
from aiohttp.web import HTTPFound
|
||||
from aiohttp_session import get_session
|
||||
|
||||
|
||||
class ClientStore(dict):
|
||||
"""
|
||||
This class is responsible to hold the clients used by the active connections.
|
||||
"""
|
||||
|
||||
def __init__(self, login_route):
|
||||
self.login_route = login_route
|
||||
self.store = dict()
|
||||
|
||||
async def get_client(self, request):
|
||||
session = await get_session(request)
|
||||
client_id = session.get("client_id")
|
||||
if client_id is None:
|
||||
raise HTTPFound(self.login_route)
|
||||
|
||||
client = self.store.get(client_id)
|
||||
if client is None:
|
||||
raise HTTPFound(self.login_route)
|
||||
|
||||
return client
|
||||
|
||||
async def set_client(self, request, client):
|
||||
session = await get_session(request)
|
||||
|
||||
client_id = secrets.token_hex(16)
|
||||
|
||||
self.store[client_id] = client
|
||||
session["client_id"] = client_id
|
|
@ -5,7 +5,7 @@ import yaml
|
|||
NAME = "phi"
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"core": {"listen": {"host": "localhost", "port": 8080}},
|
||||
"core": {"listen": {"host": "localhost", "port": 8080}, "cookiestore_secret": None},
|
||||
"ldap": {
|
||||
"host": "localhost",
|
||||
"port": 389,
|
||||
|
@ -13,10 +13,7 @@ DEFAULT_CONFIG = {
|
|||
"ciphers": "HIGH",
|
||||
"validate": True,
|
||||
"ca_certs": pkg_resources.resource_filename(NAME, "openldap/cert.pem"),
|
||||
"username": None,
|
||||
"password": None,
|
||||
"base_dn": None,
|
||||
"attribute_id": "uid",
|
||||
"attribute_mail": "mail",
|
||||
},
|
||||
"logging": {
|
||||
|
@ -35,9 +32,18 @@ DEFAULT_CONFIG = {
|
|||
},
|
||||
},
|
||||
"loggers": {
|
||||
"phi": {"level": "INFO", "handlers": ["console", "file"],},
|
||||
"aiohttp": {"level": "INFO", "handlers": ["console", "file"],},
|
||||
"bonsai": {"level": "WARNING", "handlers": ["console", "file"],},
|
||||
"phi": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console", "file"],
|
||||
},
|
||||
"aiohttp": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console", "file"],
|
||||
},
|
||||
"bonsai": {
|
||||
"level": "WARNING",
|
||||
"handlers": ["console", "file"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -123,3 +129,16 @@ def recursive_merge(main_config, aux_config):
|
|||
_recursive_merge(main_config, aux_config, DEFAULT_CONFIG, "ROOT", _config)
|
||||
|
||||
return _config["ROOT"]
|
||||
|
||||
|
||||
def extract_secret(config):
|
||||
try:
|
||||
secret = config["core"]["cookiestore_secret"].encode("utf-8")
|
||||
if len(secret) != 32:
|
||||
raise ValueError(
|
||||
"The provided core.cookiestore_secret must be 32 bytes long"
|
||||
)
|
||||
|
||||
return secret
|
||||
except KeyError:
|
||||
raise RuntimeError("You must provide a core.cookiestore_secret")
|
||||
|
|
38
src/phi/login.py
Normal file
38
src/phi/login.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
from aiohttp.web import HTTPBadRequest, HTTPOk
|
||||
|
||||
from phi.async_ldap.client import AsyncClient
|
||||
|
||||
|
||||
async def login(request):
|
||||
log = request.app["log"]
|
||||
log.debug("login")
|
||||
|
||||
store = request.app["store"]
|
||||
config = request.app["config"]
|
||||
|
||||
body = await request.json()
|
||||
|
||||
tag = body.get("tag", "uid")
|
||||
|
||||
ou = body.get("ou")
|
||||
if ou is not None:
|
||||
config["ldap"]["ou"] = ou
|
||||
|
||||
try:
|
||||
user = body["user"]
|
||||
password = body["password"]
|
||||
except KeyError as e:
|
||||
text = f"Missing key: {e}"
|
||||
log.warn(text)
|
||||
raise HTTPBadRequest(text=text)
|
||||
|
||||
client = AsyncClient(
|
||||
attribute_id=tag, username=user, password=password, **config.get("ldap")
|
||||
)
|
||||
|
||||
log.debug(f"Client: {client}")
|
||||
|
||||
await store.set_client(request, client)
|
||||
|
||||
raise HTTPOk
|
Loading…
Reference in New Issue
Block a user