authentik api

This commit is contained in:
Philipp Rothmann 2022-03-04 18:51:33 +01:00
parent 5891616eb4
commit 00de186ad4
10 changed files with 206 additions and 32 deletions

View file

@ -5,7 +5,7 @@ init:
pip3 install -r requirements.txt pip3 install -r requirements.txt
run: run:
./env/bin/uvicorn app.main:app --reload ./env/bin/uvicorn app.main:app --reload --host 0.0.0.0
test: test:
./env/bin/pytest app ./env/bin/pytest app

View file

View file

@ -2,14 +2,16 @@ from typing import Dict
import requests import requests
from requests import Request from requests import Request
from app.authentik.models import User
class Authentik: class Authentik:
def __init__(self, token, base="https://sso.lit.yksflip.de/"): def __init__(self, token, base):
self.base = f"{base}api/v3/" self.base = f"{base}api/v3/"
self.hook_endpoint = "https://webhook.site/0caf7e4d-c853-4e33-995e-dc12f82481f0"
self.token = token self.token = token
self.headers = {"Authorization": f"Bearer {token}"} self.headers = {"Authorization": f"Bearer {token}"}
self.hook_endpoint = None
self.property_mapping = None self.property_mapping = None
self.event_matcher_policy = None self.event_matcher_policy = None
self.event_transport = None self.event_transport = None
@ -17,13 +19,18 @@ class Authentik:
self.event_rule = None self.event_rule = None
self.event_rule_link = None self.event_rule_link = None
def post(self, endpoint: str, data: Dict) -> Request: def get(self, endpoint: str, params: Dict = {}) -> Request:
return requests.post(url= f"{self.base}{endpoint}", json=data, headers=self.headers)
def get(self, endpoint: str, params: Dict) -> Request:
return requests.get(url=f"{self.base}{endpoint}", params=params, headers=self.headers) return requests.get(url=f"{self.base}{endpoint}", params=params, headers=self.headers)
def create_web_hook(self): def post(self, endpoint: str, data: Dict = {}) -> Request:
return requests.post(url=f"{self.base}{endpoint}", json=data, headers=self.headers)
def delete(self, endpoint: str) -> Request:
return requests.delete(url=f"{self.base}{endpoint}", headers=self.headers)
def create_web_hook(self, hook_endpoint):
self.hook_endpoint = hook_endpoint
self.event_matcher_policy = self.create_event_matcher_policy() self.event_matcher_policy = self.create_event_matcher_policy()
self.property_mapping = self.create_property_mapping() self.property_mapping = self.create_property_mapping()
self.event_transport = self.create_event_transport(self.hook_endpoint, self.property_mapping["pk"]) self.event_transport = self.create_event_transport(self.hook_endpoint, self.property_mapping["pk"])
@ -125,6 +132,28 @@ class Authentik:
raise Exception(r.status_code, r.url, r.text) raise Exception(r.status_code, r.url, r.text)
def get_user(self, user_pk): def get_user(self, user: User):
pass if user.pk:
r = self.get(f"core/users/{user.pk}").json()
else:
r = self.get(f"core/users/?search={user.username}").json()
if len(r["results"]) > 1 :
raise Exception("More than one user with that username", r)
if len(r["results"]) == 0 :
raise Exception("No user with that username", r)
r = r["results"][0]
if "pk" in r:
return User(**r)
raise Exception(r)
def create_user(self, user: User) -> User:
r = self.post("core/users/", user.dict()).json()
if "pk" in r:
return User(**r)
raise Exception(r)
def delete_user(self, user: User):
r = self.delete(f"core/users/{user.username}")
print(r.json())

41
app/authentik/models.py Normal file
View file

@ -0,0 +1,41 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel
class UsersObjItem(BaseModel):
pk: int
username: str
name: str
is_active: bool
last_login: str
email: str
avatar: str
attributes: Dict[str, Any]
uid: str
class GroupsObjItem(BaseModel):
pk: str
name: str
is_superuser: bool
parent: str
parent_name: str
users: List[int]
attributes: Dict[str, Any]
users_obj: List[UsersObjItem]
class User(BaseModel):
pk: Optional[str]
username: str
name: str
is_active: bool = None
last_login: Optional[str] = None
is_superuser: Optional[bool] = None
groups: List[str]
groups_obj: Optional[List[GroupsObjItem]] = None
email: str
avatar: Optional[str] = None
attributes: Dict[str, Any] = None
uid: Optional[str] = None

View file

@ -0,0 +1,25 @@
import email
from app.authentik.models import User
from .authentik import Authentik
def test_connection():
a = Authentik(base="http://localhost:9000/", token="foobar123", )
# res = a.create_web_hook(hook_endpoint="http://172.17.0.1:8000")
u = User(username="banane",
name="banane",
groups=[],
email="foo@example.org",
is_active=True,
attributes={}
)
if(a.get_user(u)):
print("delete")
a.delete(u)
assert False
c = a.create_user(u)
assert u.username == c.username
assert not c.pk == None

View file

@ -1,4 +1,6 @@
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from app.authentik.authentik import Authentik
from .wekan.api import Wekan from .wekan.api import Wekan
import json import json
@ -8,8 +10,15 @@ app = FastAPI()
async def root(): async def root():
return {'message': 'Hello World'} return {'message': 'Hello World'}
@app.post("/hook") @app.post("/authentik/hook")
async def hook(request: Request): async def hook(request: Request):
# print(await request.body())
r = await request.json() r = await request.json()
# model_created = json.loads(r['body'].split("model_created: ")[1])["model"] # model_created = json.loads(r['body'].split("model_created: ")[1])["model"]
# return wekan.create_user(model_created["pk"]) # hook wekan.create_user(model_created["pk"])
@app.get("/authentik/create_hook")
async def hook(request: Request):
a = Authentik(base="http://localhost:9000/", token="foobar123", hook_endpoint="http://172.17.0.1:8000/authentik/hook")
res = a.create_web_hook()
print(res)

11
app/settings.py Normal file
View file

@ -0,0 +1,11 @@
from pydantic import BaseSettings, Field
class Settings(BaseSettings):
wekan_baseurl: str = Field(..., env="WEKAN_URL")
wekan_user: str = Field(..., env="WEKAN_USER")
wekan_pw: str = Field(..., env="WEKAN_PASS")
class Config:
env_file = '.env'
nc

View file

@ -1,12 +0,0 @@
from .authentik import Authentik
def test_connection():
a = Authentik(token="foobar123")
# res = a.create_property_mapping()
# res = a.create_event_matcher_policy()
# res = a.create_event_transport(hook_endpoint="http://localhost:8000")
# res = a.get_admin_group()
res = a.create_web_hook()
print(res)

View file

@ -29,12 +29,7 @@ def test_hook_model_created(mocker):
return_value='fake user') return_value='fake user')
print(mock.mock_calls) print(mock.mock_calls)
d = """ d = """
{ {"model": {"pk": 5, "app": "authentik_core", "name": "asd", "model_name": "user"}, "http_request": {"args": {}, "path": "/api/v3/core/users/", "method": "POST"}}
"body": "model_created: {'model': {'pk': 18, 'app': 'authentik_core', 'name': 'bernd', 'model_name': 'user'}, 'http_request': {'args': {}, 'path': '/api/v3/core/users/', 'method': 'POST'}}",
"severity": "alert",
"user_email": "flip@yksflip.de",
"user_username": "akadmin"
}
""" """
response = client.post("/hook", data=d, ) response = client.post("/hook", data=d, )
assert response.status_code == 200 assert response.status_code == 200
@ -42,4 +37,3 @@ def test_hook_model_created(mocker):
kall = mock.call_args kall = mock.call_args
assert kall.args[0] == "18" assert kall.args[0] == "18"
# assert str(response.text) == 'fake user' # assert str(response.text) == 'fake user'

77
compose.authentik.yml Normal file
View file

@ -0,0 +1,77 @@
---
version: '3.2'
services:
postgresql:
image: postgres:12-alpine
restart: unless-stopped
volumes:
- database:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secret
- POSTGRES_USER=${PG_USER:-authentik}
- POSTGRES_DB=${PG_DB:-authentik}
redis:
image: redis:alpine
restart: unless-stopped
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.2.1}
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: secret
AUTHENTIK_SECRET_KEY: secret
AK_ADMIN_TOKEN: foobar123
AK_ADMIN_PASS: foobar123
# AUTHENTIK_ERROR_REPORTING__ENABLED: "true"
# WORKERS: 2
volumes:
- ./media:/media
- ./custom-templates:/templates
- geoip:/geoip
ports:
- "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000"
- "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.2.1}
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: secret
AUTHENTIK_SECRET_KEY: secret
AK_ADMIN_TOKEN: foobar123
AK_ADMIN_PASS: foobar123
# AUTHENTIK_ERROR_REPORTING__ENABLED: "true"
# This is optional, and can be removed. If you remove this, the following will happen
# - The permissions for the /backups and /media folders aren't fixed, so make sure they are 1000:1000
# - The docker socket can't be accessed anymore
user: root
volumes:
- ./backups:/backups
- ./media:/media
- ./certs:/certs
- /var/run/docker.sock:/var/run/docker.sock
- ./custom-templates:/templates
- geoip:/geoip
geoipupdate:
image: "maxmindinc/geoipupdate:latest"
volumes:
- "geoip:/usr/share/GeoIP"
environment:
GEOIPUPDATE_EDITION_IDS: "GeoLite2-City"
GEOIPUPDATE_FREQUENCY: "8"
volumes:
database:
driver: local
geoip:
driver: local