OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
122
app/Domain/Status/Controllers/Index.php
Normal file
122
app/Domain/Status/Controllers/Index.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Status\Controllers;
|
||||
|
||||
use Leantime\Core\Configuration\AppSettings;
|
||||
use Leantime\Core\Configuration\Environment;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Core\Http\IncomingRequest;
|
||||
use Leantime\Domain\Plugins\Services\Plugins;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Public, unauthenticated instance status / discovery endpoint — GET /status.
|
||||
*
|
||||
* The mobile app calls this at connect time to discover which login methods the
|
||||
* instance offers (password / ldap / oidc), so it can show the right affordances
|
||||
* — notably the OIDC "Sign in with SSO" redirect, which stays dormant in the app
|
||||
* until a backend advertises it here. See the mobile client's
|
||||
* connectionService::fetchPublicStatus and
|
||||
* docs/backend-mobile-auth-bridge-plan.md for the contract.
|
||||
*
|
||||
* SECURITY — this endpoint is UNAUTHENTICATED, so it returns ONLY the safe,
|
||||
* minimal tier: auth methods, the core version + instance name, provider labels,
|
||||
* and the min app version. It deliberately does NOT list installed plugins,
|
||||
* plugin versions, or the db version — an unauthenticated inventory of those is a
|
||||
* recon gift (CVE matching). Those stay behind auth (the authenticated
|
||||
* mobileStatus). Keep any `publicStatus` filter additions to this safe tier.
|
||||
*
|
||||
* Route 'status.index' is allow-listed public in AuthCheck.
|
||||
*/
|
||||
class Index extends Controller
|
||||
{
|
||||
/**
|
||||
* Keys that must NEVER appear in the unauthenticated response, even if a
|
||||
* publicStatus filter (or a future edit) adds them — a recon-risk inventory.
|
||||
* Stripped after the filter runs, as defense in depth.
|
||||
*/
|
||||
private const SENSITIVE_KEYS = ['plugins', 'pluginVersions', 'installedPlugins', 'dbVersion', 'db_version'];
|
||||
|
||||
private Environment $config;
|
||||
|
||||
private AppSettings $appSettings;
|
||||
|
||||
private IncomingRequest $request;
|
||||
|
||||
private Plugins $plugins;
|
||||
|
||||
/**
|
||||
* init - inject config, app settings, the incoming request, and the plugin
|
||||
* service (used to gate mobile-auth advertising on AdvancedAuth).
|
||||
*/
|
||||
public function init(Environment $config, AppSettings $appSettings, IncomingRequest $request, Plugins $plugins): void
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->appSettings = $appSettings;
|
||||
$this->request = $request;
|
||||
$this->plugins = $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the public discovery payload: enabled auth methods, the OIDC login
|
||||
* URL (when enabled), instance name, core version, and min app version — the
|
||||
* safe unauthenticated tier only. Never plugin inventory / versions / db
|
||||
* version (see the class-level security note).
|
||||
*/
|
||||
public function get(array $params): Response
|
||||
{
|
||||
$oidcEnabled = (bool) $this->config->oidcEnable;
|
||||
$ldapEnabled = $this->config->useLdap === true && extension_loaded('ldap');
|
||||
|
||||
// Mobile auth is an AdvancedAuth capability — the mobile connection points
|
||||
// (getToken, and the OIDC mint bridge) require the plugin. Only advertise
|
||||
// mobile OIDC when AdvancedAuth is installed, so a core-only instance never
|
||||
// offers a login the mint endpoint (Oidc\Controllers\Mobile) would refuse.
|
||||
// NOTE: Cloud is assumed to ship AdvancedAuth, so this predicate covers it;
|
||||
// confirm before release.
|
||||
$mobileGate = $this->plugins->isEnabled('AdvancedAuth');
|
||||
|
||||
// password is always available; ldap/oidc only when configured.
|
||||
$authMethods = ['password'];
|
||||
if ($ldapEnabled) {
|
||||
$authMethods[] = 'ldap';
|
||||
}
|
||||
if ($oidcEnabled && $mobileGate) {
|
||||
$authMethods[] = 'oidc';
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'mobileAuthEnabled' => $mobileGate,
|
||||
'instanceName' => (string) ($this->config->sitename ?: 'Leantime'),
|
||||
'version' => $this->appSettings->appVersion,
|
||||
'minAppVersion' => null,
|
||||
'authMethods' => $authMethods,
|
||||
'ssoProviders' => [],
|
||||
];
|
||||
|
||||
if ($oidcEnabled && $mobileGate) {
|
||||
// Generic-OIDC login initiation URL — advertised for mobile only when
|
||||
// AdvancedAuth is installed. The app opens this in the system auth
|
||||
// browser; the mobile branch is triggered by its own query params
|
||||
// (see Oidc\Controllers\Login).
|
||||
$payload['oidcLoginUrl'] = $this->request->getSchemeAndHttpHost().'/oidc/login';
|
||||
}
|
||||
|
||||
// AdvancedAuth (or other plugins) can append named SSO providers to the
|
||||
// PUBLIC-safe payload (labels + login URLs only) via this filter, without
|
||||
// core knowing about them. Filter handlers MUST preserve the public-safe
|
||||
// contract — never add secrets, plugin inventory, or versions here.
|
||||
$payload = self::dispatchFilter('publicStatus', $payload, ['request' => $this->request]);
|
||||
|
||||
// Defense in depth: this endpoint is unauthenticated, so strip any
|
||||
// known-sensitive keys a misbehaving filter (or a future edit) might have
|
||||
// added. The recon-risk inventory must NEVER reach an unauthenticated
|
||||
// caller, even if a plugin gets the contract wrong.
|
||||
foreach (self::SENSITIVE_KEYS as $sensitive) {
|
||||
unset($payload[$sensitive]);
|
||||
}
|
||||
|
||||
return new JsonResponse($payload);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user