authentik api
This commit is contained in:
parent
5891616eb4
commit
00de186ad4
10 changed files with 206 additions and 32 deletions
2
Makefile
2
Makefile
|
@ -5,7 +5,7 @@ init:
|
|||
pip3 install -r requirements.txt
|
||||
|
||||
run:
|
||||
./env/bin/uvicorn app.main:app --reload
|
||||
./env/bin/uvicorn app.main:app --reload --host 0.0.0.0
|
||||
|
||||
test:
|
||||
./env/bin/pytest app
|
||||
|
|
0
app/authentik/__init__.py
Normal file
0
app/authentik/__init__.py
Normal file
|
@ -2,14 +2,16 @@ from typing import Dict
|
|||
import requests
|
||||
from requests import Request
|
||||
|
||||
from app.authentik.models import User
|
||||
|
||||
|
||||
class Authentik:
|
||||
|
||||
def __init__(self, token, base="https://sso.lit.yksflip.de/"):
|
||||
def __init__(self, token, base):
|
||||
self.base = f"{base}api/v3/"
|
||||
self.hook_endpoint = "https://webhook.site/0caf7e4d-c853-4e33-995e-dc12f82481f0"
|
||||
self.token = token
|
||||
self.headers = {"Authorization": f"Bearer {token}"}
|
||||
self.hook_endpoint = None
|
||||
self.property_mapping = None
|
||||
self.event_matcher_policy = None
|
||||
self.event_transport = None
|
||||
|
@ -17,13 +19,18 @@ class Authentik:
|
|||
self.event_rule = None
|
||||
self.event_rule_link = None
|
||||
|
||||
def post(self, endpoint: str, data: Dict) -> Request:
|
||||
return requests.post(url= f"{self.base}{endpoint}", json=data, headers=self.headers)
|
||||
|
||||
def get(self, endpoint: str, params: Dict) -> Request:
|
||||
def get(self, endpoint: str, params: Dict = {}) -> Request:
|
||||
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.property_mapping = self.create_property_mapping()
|
||||
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)
|
||||
|
||||
|
||||
def get_user(self, user_pk):
|
||||
pass
|
||||
def get_user(self, user: User):
|
||||
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
41
app/authentik/models.py
Normal 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
|
25
app/authentik/test_authentik.py
Normal file
25
app/authentik/test_authentik.py
Normal 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
|
||||
|
13
app/main.py
13
app/main.py
|
@ -1,4 +1,6 @@
|
|||
from fastapi import FastAPI, Request
|
||||
|
||||
from app.authentik.authentik import Authentik
|
||||
from .wekan.api import Wekan
|
||||
import json
|
||||
|
||||
|
@ -8,8 +10,15 @@ app = FastAPI()
|
|||
async def root():
|
||||
return {'message': 'Hello World'}
|
||||
|
||||
@app.post("/hook")
|
||||
@app.post("/authentik/hook")
|
||||
async def hook(request: Request):
|
||||
# print(await request.body())
|
||||
r = await request.json()
|
||||
# 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
11
app/settings.py
Normal 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
|
|
@ -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)
|
|
@ -26,15 +26,10 @@ def test_hook():
|
|||
|
||||
def test_hook_model_created(mocker):
|
||||
mock = mocker.patch('app.wekan.api.Wekan.create_user',
|
||||
return_value='fake user')
|
||||
return_value='fake user')
|
||||
print(mock.mock_calls)
|
||||
d = """
|
||||
{
|
||||
"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"
|
||||
}
|
||||
{"model": {"pk": 5, "app": "authentik_core", "name": "asd", "model_name": "user"}, "http_request": {"args": {}, "path": "/api/v3/core/users/", "method": "POST"}}
|
||||
"""
|
||||
response = client.post("/hook", data=d, )
|
||||
assert response.status_code == 200
|
||||
|
@ -42,4 +37,3 @@ def test_hook_model_created(mocker):
|
|||
kall = mock.call_args
|
||||
assert kall.args[0] == "18"
|
||||
# assert str(response.text) == 'fake user'
|
||||
|
||||
|
|
77
compose.authentik.yml
Normal file
77
compose.authentik.yml
Normal 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
|
Loading…
Reference in a new issue