-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Parse and insert keycloak roles scopes into JupyterHub (#2471)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Michał Krassowski <[email protected]>
- Loading branch information
1 parent
3c2d26d
commit 363cb0d
Showing
5 changed files
with
286 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import pytest | ||
|
||
from tests.tests_deployment.keycloak_utils import delete_client_keycloak_test_roles | ||
|
||
|
||
@pytest.fixture() | ||
def cleanup_keycloak_roles(): | ||
# setup | ||
yield | ||
# teardown | ||
delete_client_keycloak_test_roles(client_name="jupyterhub") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import os | ||
import pathlib | ||
|
||
from _nebari.config import read_configuration | ||
from _nebari.keycloak import get_keycloak_admin_from_config | ||
from nebari.plugins import nebari_plugin_manager | ||
|
||
|
||
def get_keycloak_client_details_by_name(client_name, keycloak_admin=None): | ||
if not keycloak_admin: | ||
keycloak_admin = get_keycloak_admin() | ||
clients = keycloak_admin.get_clients() | ||
for client in clients: | ||
if client["clientId"] == client_name: | ||
return client | ||
|
||
|
||
def get_keycloak_user_details_by_name(username, keycloak_admin=None): | ||
if not keycloak_admin: | ||
keycloak_admin = get_keycloak_admin() | ||
users = keycloak_admin.get_users() | ||
for user in users: | ||
if user["username"] == username: | ||
return user | ||
|
||
|
||
def get_keycloak_role_details_by_name(roles, role_name): | ||
for role in roles: | ||
if role["name"] == role_name: | ||
return role | ||
|
||
|
||
def get_keycloak_admin(): | ||
config_schema = nebari_plugin_manager.config_schema | ||
config_filepath = os.environ.get("NEBARI_CONFIG_PATH", "nebari-config.yaml") | ||
assert pathlib.Path(config_filepath).exists() | ||
config = read_configuration(config_filepath, config_schema) | ||
return get_keycloak_admin_from_config(config) | ||
|
||
|
||
def create_keycloak_client_role( | ||
client_id: str, role_name: str, scopes: str, component: str | ||
): | ||
keycloak_admin = get_keycloak_admin() | ||
keycloak_admin.create_client_role( | ||
client_id, | ||
payload={ | ||
"name": role_name, | ||
"description": f"{role_name} description", | ||
"attributes": {"scopes": [scopes], "component": [component]}, | ||
}, | ||
) | ||
client_roles = keycloak_admin.get_client_roles(client_id=client_id) | ||
return get_keycloak_role_details_by_name(client_roles, role_name) | ||
|
||
|
||
def assign_keycloak_client_role_to_user(username: str, client_name: str, role: dict): | ||
"""Given a keycloak role and client name, assign that to the user""" | ||
keycloak_admin = get_keycloak_admin() | ||
user_details = get_keycloak_user_details_by_name( | ||
username=username, keycloak_admin=keycloak_admin | ||
) | ||
client_details = get_keycloak_client_details_by_name( | ||
client_name=client_name, keycloak_admin=keycloak_admin | ||
) | ||
keycloak_admin.assign_client_role( | ||
user_id=user_details["id"], client_id=client_details["id"], roles=[role] | ||
) | ||
|
||
|
||
def create_keycloak_role(client_name: str, role_name: str, scopes: str, component: str): | ||
"""Create a role keycloak role for the given client with scopes and | ||
component set in attributes | ||
""" | ||
keycloak_admin = get_keycloak_admin() | ||
client_details = get_keycloak_client_details_by_name( | ||
client_name=client_name, keycloak_admin=keycloak_admin | ||
) | ||
return create_keycloak_client_role( | ||
client_details["id"], role_name=role_name, scopes=scopes, component=component | ||
) | ||
|
||
|
||
def delete_client_keycloak_test_roles(client_name): | ||
keycloak_admin = get_keycloak_admin() | ||
client_details = get_keycloak_client_details_by_name( | ||
client_name=client_name, keycloak_admin=keycloak_admin | ||
) | ||
client_roles = keycloak_admin.get_client_roles(client_id=client_details["id"]) | ||
for role in client_roles: | ||
if not role["name"].startswith("test"): | ||
continue | ||
keycloak_admin.delete_client_role( | ||
client_role_id=client_details["id"], | ||
role_name=role["name"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters