2022-03-06 15:29:00 +01:00
|
|
|
from http.client import HTTPException
|
2022-03-06 12:31:12 +01:00
|
|
|
from typing import Dict, Optional
|
2022-03-03 16:36:08 +01:00
|
|
|
import requests
|
|
|
|
from requests import Request
|
2022-03-06 15:29:00 +01:00
|
|
|
import structlog
|
2022-04-29 14:13:54 +02:00
|
|
|
|
|
|
|
from app.authentik.settings import AuthentikSettings
|
2022-03-06 12:31:12 +01:00
|
|
|
from .models import User
|
2022-03-04 18:51:33 +01:00
|
|
|
|
2022-03-06 15:29:00 +01:00
|
|
|
logging = structlog.get_logger()
|
2022-03-03 16:36:08 +01:00
|
|
|
|
2022-04-29 14:13:54 +02:00
|
|
|
|
2022-03-03 16:36:08 +01:00
|
|
|
class Authentik:
|
|
|
|
|
2022-04-29 14:13:54 +02:00
|
|
|
def __init__(self, settings: AuthentikSettings):
|
|
|
|
self.base = f"{settings.baseurl}api/v3/"
|
|
|
|
self.token = settings.token
|
|
|
|
self.headers = {"Authorization": f"Bearer {self.token}"}
|
2022-03-04 18:51:33 +01:00
|
|
|
self.hook_endpoint = None
|
2022-03-03 16:36:08 +01:00
|
|
|
self.property_mapping = None
|
|
|
|
self.event_matcher_policy = None
|
|
|
|
self.event_transport = None
|
|
|
|
self.admin_group = None
|
|
|
|
self.event_rule = None
|
|
|
|
self.event_rule_link = None
|
|
|
|
|
2022-04-22 18:34:48 +02:00
|
|
|
# check connection
|
2022-04-29 14:13:54 +02:00
|
|
|
r = requests.get(
|
|
|
|
url=f"{self.base}/admin/version", headers=self.headers)
|
2022-04-22 18:34:48 +02:00
|
|
|
assert r.status_code == 200
|
|
|
|
|
2022-03-04 18:51:33 +01:00
|
|
|
def get(self, endpoint: str, params: Dict = {}) -> Request:
|
2022-03-03 16:36:08 +01:00
|
|
|
return requests.get(url=f"{self.base}{endpoint}", params=params, headers=self.headers)
|
|
|
|
|
2022-03-04 18:51:33 +01:00
|
|
|
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
|
2022-03-03 16:36:08 +01:00
|
|
|
self.event_matcher_policy = self.create_event_matcher_policy()
|
|
|
|
self.property_mapping = self.create_property_mapping()
|
2022-03-06 15:29:00 +01:00
|
|
|
self.event_transport = self.create_event_transport(
|
|
|
|
self.hook_endpoint, self.property_mapping["pk"])
|
2022-03-03 16:36:08 +01:00
|
|
|
self.admin_group = self.get_admin_group()
|
2022-03-06 15:29:00 +01:00
|
|
|
self.event_rule = self.create_event_rule(
|
|
|
|
self.admin_group, self.event_transport["pk"])
|
|
|
|
self.event_rule_link = self.add_event_rule_link(
|
|
|
|
self.event_matcher_policy["pk"], self.event_rule["pk"])
|
2022-03-03 16:36:08 +01:00
|
|
|
return self
|
|
|
|
|
|
|
|
def create_event_transport(self, hook_endpoint, property_mapping_pk):
|
|
|
|
url = "events/transports/"
|
|
|
|
data = {
|
|
|
|
"mode": "webhook",
|
|
|
|
"name": "my hook",
|
|
|
|
"send_once": False,
|
|
|
|
"webhook_mapping": property_mapping_pk,
|
|
|
|
"webhook_url": hook_endpoint
|
|
|
|
}
|
2022-04-29 14:13:54 +02:00
|
|
|
|
|
|
|
for result in self.get(url).json()["results"]:
|
|
|
|
if result["name"] == "my hook":
|
|
|
|
raise Exception("Event Rule already exists")
|
|
|
|
|
2022-03-03 16:36:08 +01:00
|
|
|
r: Request = self.post(url, data)
|
|
|
|
if r.status_code == 201:
|
|
|
|
return r.json()
|
|
|
|
raise Exception(r.status_code, r.url, r.text)
|
|
|
|
|
2022-03-06 15:29:00 +01:00
|
|
|
def create_event_rule(self, admin_group, event_transport):
|
2022-03-03 16:36:08 +01:00
|
|
|
url = "events/rules/"
|
|
|
|
data = {
|
|
|
|
"group": admin_group["pk"],
|
|
|
|
"name": "event-rule",
|
|
|
|
"severity": "notice",
|
|
|
|
"transports": [
|
|
|
|
event_transport
|
|
|
|
]
|
|
|
|
}
|
2022-04-29 14:13:54 +02:00
|
|
|
|
|
|
|
for result in self.get(url).json()["results"]:
|
|
|
|
if result["name"] == "event-rule":
|
|
|
|
raise Exception("Event Rule already exists")
|
|
|
|
|
2022-03-03 16:36:08 +01:00
|
|
|
r = self.post(url, data)
|
|
|
|
if r.status_code == 201:
|
|
|
|
return r.json()
|
|
|
|
raise Exception(r.status_code, r.url, r.text)
|
|
|
|
|
|
|
|
def add_event_rule_link(self, policy_pk, target_pk):
|
|
|
|
url = "policies/bindings/"
|
|
|
|
data = {
|
|
|
|
"enabled": True,
|
|
|
|
"group": "",
|
|
|
|
"negate": False,
|
|
|
|
"order": "0",
|
|
|
|
"policy": policy_pk,
|
|
|
|
"target": target_pk,
|
|
|
|
"timeout": "1",
|
|
|
|
"user": ""
|
|
|
|
}
|
|
|
|
r = self.post(url, data)
|
|
|
|
if r.status_code == 201:
|
|
|
|
return r.json()
|
|
|
|
raise Exception(r.status_code, r.url, r.text)
|
|
|
|
|
|
|
|
def get_admin_group(self):
|
|
|
|
url = "core/groups/"
|
|
|
|
args = {
|
|
|
|
"is_superuser": True,
|
|
|
|
"name": "authentik Admins"
|
|
|
|
}
|
|
|
|
r = self.get(url, args)
|
|
|
|
if r.status_code == 200:
|
|
|
|
groups = r.json()["results"]
|
|
|
|
if len(groups) == 1:
|
|
|
|
self.admin_group = groups[0]
|
|
|
|
return self.admin_group
|
|
|
|
raise Exception(r.status_code, r.url, r.text)
|
|
|
|
|
|
|
|
def create_event_matcher_policy(self):
|
|
|
|
url = "policies/event_matcher/"
|
|
|
|
data = {
|
|
|
|
"action": "model_created",
|
|
|
|
"app": "authentik.core",
|
|
|
|
"client_ip": "",
|
|
|
|
"execution_logging": True,
|
|
|
|
"name": "model created"
|
|
|
|
}
|
|
|
|
r = self.post(url, data)
|
|
|
|
if r.status_code == 201:
|
|
|
|
self.event_matcher_policy = r.json()
|
|
|
|
return r.json()
|
|
|
|
raise Exception(r.status_code, r.url, r.text)
|
|
|
|
|
|
|
|
def create_property_mapping(self):
|
|
|
|
url = "propertymappings/notification/"
|
|
|
|
data = {
|
|
|
|
"name": "new-mapper",
|
|
|
|
"expression": "fields = {}\nif notification:\n if notification.event:\n for k, v in notification.event.context.items():\n fields[k] = v\nreturn fields"
|
|
|
|
}
|
|
|
|
r = self.post(url, data)
|
|
|
|
if r.status_code == 201:
|
|
|
|
return r.json()
|
|
|
|
raise Exception(r.status_code, r.url, r.text)
|
|
|
|
|
2022-04-22 18:34:48 +02:00
|
|
|
def get_user_by_pk(self, pk: str) -> Optional[User]:
|
|
|
|
r = self.get(f"core/users/{pk}").json()
|
|
|
|
if "pk" in r:
|
|
|
|
return User(**r)
|
|
|
|
raise Exception(r)
|
|
|
|
|
2022-03-06 12:31:12 +01:00
|
|
|
def get_user(self, user: User) -> Optional[User]:
|
2022-03-04 18:51:33 +01:00
|
|
|
if user.pk:
|
|
|
|
r = self.get(f"core/users/{user.pk}").json()
|
|
|
|
else:
|
|
|
|
r = self.get(f"core/users/?search={user.username}").json()
|
2022-03-06 15:29:00 +01:00
|
|
|
if len(r["results"]) == 0:
|
2022-03-06 12:31:12 +01:00
|
|
|
return None
|
2022-03-06 15:29:00 +01:00
|
|
|
if len(r["results"]) > 1:
|
2022-03-04 18:51:33 +01:00
|
|
|
raise Exception("More than one user with that username", r)
|
|
|
|
r = r["results"][0]
|
|
|
|
|
|
|
|
if "pk" in r:
|
2022-04-22 18:34:48 +02:00
|
|
|
print(r)
|
2022-03-04 18:51:33 +01:00
|
|
|
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)
|
|
|
|
|
2022-03-06 15:29:00 +01:00
|
|
|
def delete_user(self, user: User) -> bool:
|
|
|
|
if user == None or user.pk == None:
|
|
|
|
raise Exception("Not a valid user")
|
2022-03-06 12:31:12 +01:00
|
|
|
|
2022-03-06 15:29:00 +01:00
|
|
|
r = self.delete(f"core/users/{user.pk}")
|
|
|
|
if r.status_code == 204:
|
|
|
|
return True
|
|
|
|
r = r.json()
|
2022-03-06 12:31:12 +01:00
|
|
|
if("detail" in r):
|
|
|
|
if r["detail"] == "Not found.":
|
2022-03-06 15:29:00 +01:00
|
|
|
return False
|