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
|
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
|
||||||
|
|
0
app/authentik/__init__.py
Normal file
0
app/authentik/__init__.py
Normal 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
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 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
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)
|
|
|
@ -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
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