204 lines
6.1 KiB
PHP
204 lines
6.1 KiB
PHP
<?php
|
|
// includes/jupyter_helpers.php
|
|
// Centralised helpers for Jupyter configuration and display logic.
|
|
|
|
/**
|
|
* Returns the default configuration values documented on the Install & Configuration page.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
function dsp_jupyter_defaults(): array {
|
|
return [
|
|
'base_url' => 'https://localhost',
|
|
'token' => 'dsp-token',
|
|
'workspace_root' => 'datasources',
|
|
'port' => '443',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Determines the external base URL for the Jupyter service.
|
|
*
|
|
* @return string
|
|
*/
|
|
function dsp_jupyter_base_url(): string {
|
|
$configured = getenv('JUPYTER_EXTERNAL_URL');
|
|
if ($configured) {
|
|
return rtrim($configured, '/');
|
|
}
|
|
|
|
$defaults = dsp_jupyter_defaults();
|
|
$scheme = (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') ? 'https' : 'http';
|
|
$hostHeader = $_SERVER['HTTP_HOST'] ?? '';
|
|
|
|
if ($hostHeader) {
|
|
$hostname = explode(':', $hostHeader)[0];
|
|
$port = dsp_jupyter_port();
|
|
$authority = $hostname;
|
|
|
|
$portIsDefault = ($scheme === 'http' && $port === '80') || ($scheme === 'https' && $port === '443');
|
|
if (!$portIsDefault) {
|
|
$authority .= ':' . $port;
|
|
}
|
|
|
|
return sprintf('%s://%s', $scheme, $authority);
|
|
}
|
|
|
|
return rtrim($defaults['base_url'], '/');
|
|
}
|
|
|
|
/**
|
|
* Retrieves the token used to authenticate with Jupyter.
|
|
*
|
|
* @return string
|
|
*/
|
|
function dsp_jupyter_token(): string {
|
|
$authStrategy = strtolower((string) getenv('JUPYTERHUB_AUTH_STRATEGY'));
|
|
if ($authStrategy === 'oauth') {
|
|
return '';
|
|
}
|
|
|
|
$defaults = dsp_jupyter_defaults();
|
|
|
|
$envToken = getenv('JUPYTER_TOKEN');
|
|
if ($envToken !== false) {
|
|
return $envToken;
|
|
}
|
|
|
|
return $defaults['token'];
|
|
}
|
|
|
|
/**
|
|
* Resolves the username JupyterHub should use for the active user.
|
|
*
|
|
* The template can be customised via JUPYTERHUB_USERNAME_TEMPLATE.
|
|
* Supported placeholders:
|
|
* {person_id} - numeric person identifier from the session
|
|
* {username} - DSP username from the session
|
|
* {email} - Session email if available
|
|
*
|
|
* @param int|null $personId Optional explicit person ID
|
|
* @return string|null Sanitised username or null when it cannot be determined.
|
|
*/
|
|
function dsp_resolve_jupyterhub_username(?int $personId, ?string $username = null, ?string $email = null): ?string {
|
|
if ($personId === null || $personId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$template = getenv('JUPYTERHUB_USERNAME_TEMPLATE') ?: 'user_{person_id}';
|
|
$usernameRaw = str_replace(
|
|
['{person_id}', '{username}', '{email}'],
|
|
[
|
|
(string) $personId,
|
|
(string) $username,
|
|
(string) $email,
|
|
],
|
|
$template
|
|
);
|
|
|
|
$usernameSanitised = preg_replace('/[^a-zA-Z0-9._-]+/', '-', $usernameRaw);
|
|
$usernameSanitised = trim((string) $usernameSanitised, "-_.");
|
|
|
|
return $usernameSanitised !== '' ? $usernameSanitised : null;
|
|
}
|
|
|
|
function dsp_jupyterhub_username(?int $personId = null): ?string {
|
|
if ($personId === null) {
|
|
$personId = isset($_SESSION['person_id']) ? (int) $_SESSION['person_id'] : null;
|
|
}
|
|
|
|
$username = $_SESSION['username'] ?? null;
|
|
$email = $_SESSION['email'] ?? null;
|
|
|
|
return dsp_resolve_jupyterhub_username($personId, $username, $email);
|
|
}
|
|
|
|
/**
|
|
* Builds the per-user route served by JupyterHub.
|
|
*
|
|
* The path template can be overridden with JUPYTERHUB_USER_PATH (e.g. "user/{username}/lab").
|
|
*
|
|
* @param string $baseUrl Hub base URL.
|
|
* @param int|null $personId Optional explicit person ID.
|
|
* @return string Absolute path including the user segment, without trailing slash.
|
|
*/
|
|
function dsp_jupyterhub_user_route(string $baseUrl, ?int $personId = null): string {
|
|
$baseUrl = rtrim($baseUrl, '/');
|
|
|
|
$pathTemplate = getenv('JUPYTERHUB_USER_PATH');
|
|
if ($pathTemplate === false || $pathTemplate === '') {
|
|
return $baseUrl;
|
|
}
|
|
|
|
$personId = $personId ?? (isset($_SESSION['person_id']) ? (int) $_SESSION['person_id'] : null);
|
|
$username = dsp_jupyterhub_username($personId);
|
|
|
|
if ($username === null) {
|
|
return $baseUrl;
|
|
}
|
|
|
|
$relativePath = str_replace(
|
|
['{username}', '{person_id}'],
|
|
[$username, (string) $personId],
|
|
ltrim($pathTemplate, '/')
|
|
);
|
|
|
|
return rtrim($baseUrl . '/' . $relativePath, '/');
|
|
}
|
|
|
|
/**
|
|
* Builds the iframe URL using the resolved base URL and token.
|
|
*
|
|
* @param string|null $baseUrl Optional override of the base URL.
|
|
* @param string|null $token Optional override of the token.
|
|
* @param int|null $personId Optional override of the person ID used for the route.
|
|
*
|
|
* @return string
|
|
*/
|
|
function dsp_jupyter_iframe_url(?string $baseUrl = null, ?string $token = null, ?int $personId = null): string {
|
|
$resolvedBase = rtrim($baseUrl ?: dsp_jupyter_base_url(), '/');
|
|
$userRoute = dsp_jupyterhub_user_route($resolvedBase, $personId);
|
|
$finalToken = $token ?: dsp_jupyter_token();
|
|
|
|
return $finalToken
|
|
? sprintf('%s?token=%s', $userRoute, urlencode($finalToken))
|
|
: $userRoute;
|
|
}
|
|
|
|
/**
|
|
* Determines the host port the Jupyter service is published on.
|
|
*
|
|
* @return string
|
|
*/
|
|
function dsp_jupyter_port(): string {
|
|
$configured = getenv('JUPYTER_PORT');
|
|
if ($configured === false || $configured === '') {
|
|
$configured = getenv('JUPYTERHUB_PORT');
|
|
}
|
|
if ($configured) {
|
|
return (string) $configured;
|
|
}
|
|
|
|
$defaults = dsp_jupyter_defaults();
|
|
|
|
return $defaults['port'];
|
|
}
|
|
|
|
/**
|
|
* Captures the environment overrides impacting the embedded Jupyter configuration.
|
|
*
|
|
* @return array<string, string|null>
|
|
*/
|
|
function dsp_jupyter_env_overrides(): array {
|
|
return [
|
|
'JUPYTER_EXTERNAL_URL' => getenv('JUPYTER_EXTERNAL_URL') ?: null,
|
|
'JUPYTER_TOKEN' => getenv('JUPYTER_TOKEN') ?: null,
|
|
'JUPYTER_PORT' => getenv('JUPYTER_PORT') ?: null,
|
|
'JUPYTERHUB_PORT' => getenv('JUPYTERHUB_PORT') ?: null,
|
|
'DSP_APP_ORIGINS' => getenv('DSP_APP_ORIGINS') ?: null,
|
|
'DSP_FRAME_ANCESTORS' => getenv('DSP_FRAME_ANCESTORS') ?: null,
|
|
'JUPYTERHUB_USERNAME_TEMPLATE' => getenv('JUPYTERHUB_USERNAME_TEMPLATE') ?: null,
|
|
'JUPYTERHUB_USER_PATH' => getenv('JUPYTERHUB_USER_PATH') ?: null,
|
|
];
|
|
}
|