fix(iam): keep revocation core active when new flows are disabled
This commit is contained in:
@@ -17,6 +17,9 @@ final class IamV2Controller extends BaseAdminController
|
||||
{
|
||||
try {
|
||||
if (!IamV2Client::enabled()) { throw new RuntimeException('FEATURE_DISABLED'); }
|
||||
$action = strtolower((string) $this->request->action());
|
||||
if (in_array($action, ['operation','preflight','batch','retry','selfstart','passwordsetup'], true) && !IamV2Client::feature('management')) { throw new RuntimeException('FEATURE_DISABLED'); }
|
||||
if (in_array($action, ['launchstart','targetprepare','targetexchange'], true) && !IamV2Client::feature('launch')) { throw new RuntimeException('FEATURE_DISABLED'); }
|
||||
$reads = ['status','accounts','identities','operationresult','audit','job','targetconfig'];
|
||||
$read = in_array(strtolower((string) $this->request->action()), $reads, true);
|
||||
if (($read && !$this->request->isGet()) || (!$read && !$this->request->isPost())) { throw new RuntimeException('METHOD_NOT_ALLOWED'); }
|
||||
@@ -48,7 +51,7 @@ final class IamV2Controller extends BaseAdminController
|
||||
}
|
||||
public function status()
|
||||
{
|
||||
if (!IamV2Client::enabled()) { return $this->data(['enabled' => false]); }
|
||||
if (!IamV2Client::enabled()) { return $this->data(['enabled' => false, 'management_enabled' => false, 'launch_enabled' => false]); }
|
||||
return $this->invokeV2(function () {
|
||||
$token = (string) $this->request->header('token', '');
|
||||
$legacy = Db::name('admin_session')->where('token', $token)->where('expire_time', '>', time())->find();
|
||||
@@ -56,7 +59,7 @@ final class IamV2Controller extends BaseAdminController
|
||||
IamV2Source::admin((int) $legacy['admin_id']);
|
||||
$fresh = false; try { $s = IamV2Source::current($token); $fresh = $s['auth_time'] >= time() - 300; } catch (\Throwable $e) { $s = null; }
|
||||
$b = (new IamV2Manager())->binding((int) $legacy['admin_id']);
|
||||
return ['enabled' => true, 'session_ready' => $s !== null, 'reauth_required' => !$fresh,
|
||||
return ['enabled' => true, 'management_enabled' => IamV2Client::feature('management'), 'launch_enabled' => IamV2Client::feature('launch'), 'session_ready' => $s !== null, 'reauth_required' => !$fresh,
|
||||
'management_capabilities' => IamV2Source::capabilities((int) $legacy['admin_id']),
|
||||
'protected_review_operator' => IamV2Source::reviewOperator((int) $legacy['admin_id']),
|
||||
'protected_account' => IamV2Source::protectedAccount((int) $legacy['admin_id'])] + $b;
|
||||
@@ -117,6 +120,7 @@ final class IamV2Controller extends BaseAdminController
|
||||
public function launchContinue()
|
||||
{
|
||||
try {
|
||||
IamV2Source::requirePurpose('launch');
|
||||
$client = new IamV2Client(); $s = IamV2Source::check((string) ($_COOKIE[IamV2Source::COOKIE] ?? ''));
|
||||
$intent = (string) $this->request->get('intent', ''); $cookie = (string) ($_COOKIE['ZYT_IAM_V2_INTENT'] ?? '');
|
||||
if ($intent === '' || !hash_equals($cookie, $intent)) { throw new RuntimeException('ACCOUNT_MISMATCH'); }
|
||||
@@ -147,6 +151,7 @@ final class IamV2Controller extends BaseAdminController
|
||||
public function selfCallback()
|
||||
{
|
||||
try {
|
||||
IamV2Source::requirePurpose('self_link');
|
||||
$central = new IamV2Client(); $s = IamV2Source::check((string) ($_COOKIE[IamV2Source::COOKIE] ?? ''));
|
||||
$flow = IamV2Flow::take('self_link', (string) $this->request->get('state', ''), $s['session_id']);
|
||||
$oidc = new IamOidcClient($this->selfConfig()); $oidc->exchange((string) $this->request->get('code', ''), $flow['verifier'], $flow['nonce']);
|
||||
@@ -172,6 +177,7 @@ final class IamV2Controller extends BaseAdminController
|
||||
public function passwordContinue()
|
||||
{
|
||||
try {
|
||||
IamV2Source::requirePurpose('password_setup');
|
||||
$client = new IamV2Client(); $s = IamV2Source::check((string) ($_COOKIE[IamV2Source::COOKIE] ?? ''));
|
||||
$f = IamV2Flow::take('password_setup', (string) ($_COOKIE['ZYT_IAM_PASSWORD_INTENT'] ?? ''), $s['session_id']);
|
||||
$id = (string) $this->request->get('request_id', ''); $state = (string) $this->request->get('state', '');
|
||||
@@ -187,10 +193,10 @@ final class IamV2Controller extends BaseAdminController
|
||||
}
|
||||
public function targetConfig()
|
||||
{
|
||||
if (!IamV2Client::enabled()) { return $this->data(['enabled' => false]); }
|
||||
if (!IamV2Client::enabled()) { return $this->data(['enabled' => false, 'management_enabled' => false, 'launch_enabled' => false]); }
|
||||
return $this->invokeV2(function () {
|
||||
$source = (string) $this->request->get('source', 'iam-portal');
|
||||
return ['enabled' => true, 'restart_url' => IamV2Client::origin((string) Config::get('iam.portal_url')) . ($source === 'iam-admin' ? '/admin' : '/portal')];
|
||||
return ['enabled' => IamV2Client::feature('launch'), 'global_enabled' => true, 'management_enabled' => IamV2Client::feature('management'), 'launch_enabled' => IamV2Client::feature('launch'), 'restart_url' => IamV2Client::origin((string) Config::get('iam.portal_url')) . ($source === 'iam-admin' ? '/admin' : '/portal')];
|
||||
});
|
||||
}
|
||||
public function targetPrepare()
|
||||
|
||||
@@ -22,6 +22,12 @@ final class IamV2Client
|
||||
$settings ??= (array) Config::get('iam', []);
|
||||
return filter_var($settings['v2_enabled'] ?? false, FILTER_VALIDATE_BOOL);
|
||||
}
|
||||
public static function feature(string $feature, ?array $settings = null): bool
|
||||
{
|
||||
$settings ??= (array) Config::get('iam', []);
|
||||
if (!in_array($feature, ['management', 'launch'], true)) { return false; }
|
||||
return self::enabled($settings) && filter_var($settings['v2_' . $feature . '_enabled'] ?? false, FILTER_VALIDATE_BOOL);
|
||||
}
|
||||
public static function https(string $url): string
|
||||
{
|
||||
$p = parse_url($url);
|
||||
|
||||
@@ -13,6 +13,11 @@ use app\common\cache\AdminAccountSafeCache;
|
||||
final class IamV2Source
|
||||
{
|
||||
public const COOKIE = 'ZYT_IAM_V2_SESSION';
|
||||
public static function requirePurpose(string $purpose): void
|
||||
{
|
||||
$feature = in_array($purpose, ['management', 'self_link', 'password_setup'], true) ? 'management' : ($purpose === 'launch' ? 'launch' : null);
|
||||
if ($feature !== null && !IamV2Client::feature($feature)) { throw new RuntimeException('FEATURE_DISABLED'); }
|
||||
}
|
||||
public static function enroll(string $token, string $method, bool $cookie = true, ?int $authTime = null): array
|
||||
{
|
||||
if (!in_array($method, ['password', 'wecom', 'oidc', 'iam_launch'], true)) { throw new RuntimeException('REAUTH_REQUIRED'); }
|
||||
@@ -70,10 +75,11 @@ final class IamV2Source
|
||||
/** Service callback: local-only live authorization, safe while central holds its state lock. */
|
||||
public static function liveStatus(string $id, int $version, string $purpose = '', array $targets = []): array
|
||||
{
|
||||
self::requirePurpose($purpose);
|
||||
$s = self::check($id, $version);
|
||||
$out = ['active' => true, 'actorLocalId' => (string) $s['admin_id'], 'sessionId' => $s['session_id'], 'sessionVersion' => (int) $s['version'],
|
||||
'authMethod' => $s['auth_method'], 'authTime' => (int) $s['auth_time'], 'expiresAt' => (int) $s['expires_at']];
|
||||
if ($purpose !== 'management') { return $out; }
|
||||
if ($purpose !== 'management') { return $out + ['protectedAccount' => self::protectedAccount((int) $s['admin_id'])]; }
|
||||
if ($s['auth_time'] < time() - 300) { throw new RuntimeException('REAUTH_REQUIRED'); }
|
||||
if (!$targets || count($targets) > 100) { throw new RuntimeException('INVALID_INPUT'); }
|
||||
foreach ($targets as $target) {
|
||||
@@ -147,6 +153,7 @@ final class IamV2Source
|
||||
}
|
||||
public static function proof(array $session, string $purpose, array $targets = [], array $capabilities = [], ?string $transaction = null, array $extra = []): string
|
||||
{
|
||||
self::requirePurpose($purpose);
|
||||
self::check($session['session_id'], (int) $session['version']);
|
||||
$secret = bin2hex(random_bytes(32));
|
||||
$payload = ['applicationId' => (string) Config::get('iam.application_id', 'zyt'), 'actorLocalId' => (string) $session['admin_id'],
|
||||
@@ -160,6 +167,7 @@ final class IamV2Source
|
||||
}
|
||||
public static function introspect(string $proof, string $purpose, ?string $transaction = null): array
|
||||
{
|
||||
self::requirePurpose($purpose);
|
||||
// The central Go attestor serializes an absent transaction as an empty string.
|
||||
$transaction = $transaction === '' ? null : $transaction;
|
||||
return Db::transaction(function () use ($proof, $purpose, $transaction) {
|
||||
|
||||
@@ -19,6 +19,7 @@ final class IamV2Target
|
||||
public function __construct(private ?IamV2Client $client = null, private $resolve = null, private $login = null)
|
||||
{
|
||||
$this->settings = (array) Config::get('iam');
|
||||
if (!IamV2Client::feature('launch', $this->settings)) { throw new RuntimeException('FEATURE_DISABLED'); }
|
||||
$this->client ??= new IamV2Client();
|
||||
$this->resolve ??= fn(array $result) => $this->resolveAdmin($result);
|
||||
$this->login ??= function (int $id, array $authentication): array {
|
||||
|
||||
Reference in New Issue
Block a user