Skip to content

Add application fetching from Keycloak #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies = [
"pydantic-settings>=2.6.1",
"waitress>=3.0.1",
"authentik-client>=2024.10.4.post1733219872",
"mantelo>=2.1.1",
]

[build-system]
Expand Down
8 changes: 8 additions & 0 deletions src/blobdash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def create_app():
auth_provider = AuthentikAuthProvider(
settings.auth.fetch.host, settings.auth.fetch.token
)
case "keycloak":
auth_provider = KeycloakAuthProvider(
settings.auth.fetch.host,
settings.auth.fetch.realm,
settings.auth.fetch.auth_realm,
settings.auth.fetch.id,
settings.auth.fetch.secret,
)
case None:
auth_provider = None

Expand Down
40 changes: 40 additions & 0 deletions src/blobdash/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from abc import ABC, abstractmethod

import authentik_client
from mantelo import KeycloakAdmin

from .applications import Application

Expand All @@ -13,6 +14,45 @@ def get_applications(self, username):
pass


class KeycloakAuthProvider(AuthProvider):
def __init__(self, host, realm, auth_realm, id, secret):
self.host = host
self.realm = realm
self.auth_realm = realm if auth_realm is None else auth_realm
self.id = id
self.secret = secret

self._api = KeycloakAdmin.from_client_credentials(
server_url=self.host,
realm_name=self.realm,
authentication_realm_name=self.auth_realm,
client_id=self.id,
client_secret=self.secret,
)

def get_applications(self, username):
# Get user ID from username. Returns a list of user objects, so choose the first (and only)
# entry and grab its ID.
user = self._api.users.get(username=username, exact=True)[0]["id"]

# Get a list of all clients the user has logged into, by cycling through consents for the
# user ID and grabbing the client object associated with that client ID
clients = [
self._api.clients.get(clientId=consent["clientId"])[0]
for consent in self._api.users(user).consents.get()
]

return {
client["clientId"]: Application(
name=client["name"],
url=client["baseUrl"],
icon=client["attributes"]["logoUri"],
desc=client["description"],
)
for client in clients
}


class AuthentikAuthProvider(AuthProvider):
def __init__(self, host, token):
# Base path for Authentik API
Expand Down
11 changes: 10 additions & 1 deletion src/blobdash/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,21 @@ class AuthentikSettings(BaseModel):
token: str


class KeycloakSettings(BaseModel):
provider: Literal["keycloak"]
host: str
auth_realm: Optional[str] = None
realm: str
id: str
secret: str


class AuthSettings(BaseModel):
enabled: bool = False
header: str = "X-App-User"
logout_url: str = "/flows/-/default/invalidation"
default_user: Optional[str] = None
fetch: Optional[AuthentikSettings] = None
fetch: Optional[AuthentikSettings | KeycloakSettings] = None


class DashdotSettings(BaseModel):
Expand Down
106 changes: 106 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading