The repository that defines the extensions for keycloak is located at https://github.com/lsc-sde/awms-keycloak-extensions
This is a java extension that runs inside of a customised keycloak docker image.
What the Code Does
WorkspaceKubernetesClient.java
, is a client for interacting with Kubernetes custom resources related to analytics workspaces. Specifically:
- It connects to a Kubernetes cluster and interacts with custom resources:
analyticsworkspaces
- Workspace definitionsanalyticsworkspacebindings
- Bindings between users and workspaces
- The client provides methods to:
- Get workspace bindings for a specific user
- Get all workspaces for a user
- Patch workspace bindings to change their scale (replicas)
- Set the active workspace for a user by scaling up one binding and scaling down others
This appears to be part of a larger Keycloak “required action” extension that makes users select or configure their workspace before they can access the system.
Project Structure
The project follows a standard Maven structure and includes:
- A Keycloak required action extension (in the
requiredaction
folder) - Custom templates for the UI (
update-workspace.ftl
) - Custom message properties
- GitHub workflows for CI/CD
- Docker configuration for containerizing the extension
This extension likely integrates with the CR8TOR system seen in other parts of the workspace, providing authentication and workspace management functionality for the Secure Data Environment.
Key Functionality
The most important functionality in this code is the ability to activate/deactivate workspaces for users by scaling Kubernetes resources. When a user selects a workspace, this client:
- Finds all workspace bindings for that user
- Sets the selected workspace binding to scale=1 (active)
- Sets all other workspace bindings to scale=0 (inactive)
This creates a seamless experience where the user can choose which workspace to use through the Keycloak authentication flow.
vvcb: Document all the code in this folder.
GitHub Copilot: # Keycloak Extension for Workspace Management
This project is a custom Keycloak extension that implements a “Required Action” for users to select and activate analytics workspaces in the Lancashire and South Cumbria Secure Data Environment (LSC SDE).
Project Overview
The awms-keycloak-extensions
project integrates Keycloak authentication with Kubernetes-based workspace management. It forces users to select a workspace before completing their login, then activates that workspace by scaling the corresponding Kubernetes resources.
Key Components
1. WorkspaceRequiredAction
This is the central component that implements Keycloak’s RequiredActionProvider
and RequiredActionFactory
interfaces:
- Registers itself as a required action with ID “workspace”
- Presents a form to users during login to select their workspace
- Stores the selected workspace in user attributes
- Activates the selected workspace by scaling Kubernetes resources
- Forces reselection when users log in to certain clients (e.g., Guacamole)
The action flow:
- Evaluates if a workspace selection is needed
- Shows a form to select from available workspaces
- Processes the selection, storing it in user attributes
- Calls the Kubernetes client to scale up the selected workspace
- Completes the authentication process
2. WorkspaceKubernetesClient
A client for interacting with the Kubernetes API to manage analytics workspaces:
public class WorkspaceKubernetesClient {
// Connects to a Kubernetes cluster and accesses custom resources
// for analytics workspaces and workspace bindings
// Methods for:
// - Getting workspace bindings for a user (by label and username)
// - Retrieving all workspaces accessible to a user
// - Patching workspace bindings to change their scale
// - Setting the active workspace by scaling one up and others down
}
This client handles:
- Connecting to the Kubernetes cluster
- Retrieving workspace bindings by username or label
- Scaling workspaces up (replicas=1) or down (replicas=0)
- Finding all workspaces available to a user
3. BoundWorkspace
A simple data class representing a workspace and its binding:
public class BoundWorkspace {
protected V1AnalyticsWorkspace _workspace;
protected V1AnalyticsWorkspaceBinding _binding;
// Constructor and getters
}
4. UI and Localization
update-workspace.ftl
- Freemarker template for the workspace selection form- messages_en.properties - Localized strings for the UI
How It Works
- During login, Keycloak evaluates if the user needs to select a workspace
- If required, the user is presented with a form showing available workspaces
- After selection, the workspace info is stored in the user’s attributes
- The Kubernetes client activates the selected workspace by scaling it to 1 replica
- All other workspaces for the user are scaled to 0 replicas
- The login process completes and the user can access their environment
This creates a just-in-time activation of workspaces, ensuring resources are only allocated when needed and that users can easily switch between different workspaces.
Technical Details
- Uses the Kubernetes Java client to interact with custom resources
- Implements Keycloak’s required action interfaces
- Leverages JSON patch operations for efficient Kubernetes resource updates
- Stores workspace metadata in user attributes for persistence
This extension enables a seamless integration between Keycloak’s authentication system and the Kubernetes-based workspace management of the LSC Secure Data Environment.
vvcb: @workspace /explain cannot infer type arguments for GenericKubernetesApi<>
Workspace: # Error Analysis: Type Inference Problem with GenericKubernetesApi
The error you’re encountering stems from the Java compiler not being able to automatically determine the correct type arguments for the GenericKubernetesApi<>
instantiation in your code. This is a common issue when working with complex generic types in Java.
The Issue
In your constructor:
public WorkspaceKubernetesClient() throws IOException {
apiClient = ClientBuilder.cluster().build();
Configuration.setDefaultApiClient(apiClient);
workspaceApi = new GenericKubernetesApi<>(V1AnalyticsWorkspace.class, V1AnalyticsWorkspaceList.class, API_GROUP, API_VERSION, WORKSPACE_PLURAL, apiClient);
workspaceBindingApi = new GenericKubernetesApi<>(V1AnalyticsWorkspaceBinding.class, V1AnalyticsWorkspaceBindingList.class, API_GROUP, API_VERSION, WORKSPACE_BINDING_PLURAL, apiClient);
}
The compiler cannot infer the complete type signature for the GenericKubernetesApi<>
constructor.
Solution
You need to explicitly specify all the type parameters for the GenericKubernetesApi
constructor. The full type signature requires specifying the resource type, list type, and status type.
Here’s how to fix it:
public WorkspaceKubernetesClient() throws IOException {
apiClient = ClientBuilder.cluster().build();
Configuration.setDefaultApiClient(apiClient);
// Explicitly specify all type parameters
workspaceApi = new GenericKubernetesApi<V1AnalyticsWorkspace, V1AnalyticsWorkspaceList, Object>(
V1AnalyticsWorkspace.class,
V1AnalyticsWorkspaceList.class,
API_GROUP,
API_VERSION,
WORKSPACE_PLURAL,
apiClient
);
workspaceBindingApi = new GenericKubernetesApi<V1AnalyticsWorkspaceBinding, V1AnalyticsWorkspaceBindingList, Object>(
V1AnalyticsWorkspaceBinding.class,
V1AnalyticsWorkspaceBindingList.class,
API_GROUP,
API_VERSION,
WORKSPACE_BINDING_PLURAL,
apiClient
);
}
The key change is adding Object
as the third type parameter, which represents the status type of the Kubernetes resources. If your custom resources have specific status types, you should use those types instead of Object
.
Additional Note
There is also a logical error in your code that you should fix. In the getAllWorkspaceBindingsForUser
method:
if (binding.getSpec().getUsername() == username) {
You’re using ==
to compare strings, which checks for reference equality, not value equality. You should use .equals()
instead:
if (username.equals(binding.getSpec().getUsername())) {
This will ensure the string values are properly compared.
vvcb: What does this python library do?
GitHub Copilot: # kubespawner-keycloak Python Library Analysis
Based on the code you’ve shared, kubespawner-keycloak
is a Python library that bridges JupyterHub’s KubeSpawner with Keycloak authentication, specifically tailored for the Lancashire and South Cumbria Secure Data Environment (LSC SDE).
Primary Functions
This library serves to:
-
Integrate JupyterHub with Keycloak authentication: It enables JupyterHub environments running on Kubernetes to authenticate against a Keycloak identity server.
-
Manage workspace volumes: It creates and mounts persistent volumes for user workspaces in Kubernetes based on user identity and permissions from Keycloak.
-
Map Keycloak groups to workspaces: The library appears to translate Keycloak group memberships into workspace access permissions.
Core Components
The library consists of several modules:
1. VolumeManager (k8sio.py)
Creates and manages Kubernetes persistent volumes for Jupyter workspaces:
class VolumeManager:
async def create_workspace_volume_if_not_exists(self, workspace_name, namespace):
# Creates PVCs for workspaces with appropriate labels
async def mount_volume(self, pod, storage_name, namespace, read_only=False):
# Mounts workspace volumes to Jupyter pods
2. Keycloak Integration (keycloak.py)
Interacts with Keycloak to retrieve user information and group memberships, mapping these to workspace permissions.
3. Custom Exceptions (exceptions.py)
Defines specialized exceptions for various error conditions:
class InvalidKeycloakGroupPath(Exception): ...
class NoAssignedValidWorkspaces(Exception): ...
How It Works Together
- When a user logs into JupyterHub, their credentials are validated through Keycloak
- The library fetches the user’s group memberships from Keycloak
- Based on these groups, it determines which workspaces the user has access to
- When spawning a Jupyter notebook server, it creates/mounts the appropriate workspace volumes
- It applies correct permissions through volume mounts (read-only vs read-write)
Implementation Details
- Uses the Kubernetes async client for interacting with the K8s API
- Creates PVCs with standardized naming and labeling (
jupyter-{workspace_name}
) - Mounts volumes to specific paths in the Jupyter container (
/home/jovyan/{storage_name}
) - Sets specific workspace labels for tracking and management (
workspace.xlscsde.nhs.uk/workspace
) - Provides status objects for tracking volume existence and state
This library essentially acts as middleware that connects identity management (Keycloak) with workspace management (Kubernetes PVCs) for JupyterHub instances, allowing for secure, dynamic provisioning of data science workspaces based on user identity and group membership.