the direction it serves; "job" ops take the direction of the job they are bound to. */ private const OPS = [ 'hello' => 'any', 'db.describe' => 'any', 'subsites.list' => 'any', 'users.list' => 'pull', 'users.preview' => 'push', 'job.open' => 'job', 'db.read' => 'pull', 'db.write' => 'push', 'files.manifest' => 'pull', 'files.read' => 'pull', 'files.plan' => 'push', 'files.write' => 'push', 'job.finalize' => 'job', 'job.cancel' => 'job', 'job.restore' => 'push', ]; public static function known_op(string $op): bool { return isset(self::OPS[$op]); } /** @return string[] directions ("pull", "push") this key may use on a site in $mode */ public static function effective(string $key_permission, string $mode): array { $out = []; foreach (['pull', 'push'] as $direction) { if (self::key_allows($key_permission, $direction) && self::mode_allows($mode, $direction)) { $out[] = $direction; } } return $out; } public static function allows(string $op, string $key_permission, string $mode, ?string $job_direction): bool { if (!isset(self::OPS[$op])) { return false; } $direction = self::OPS[$op]; $effective = self::effective($key_permission, $mode); if ($direction === 'any') { return $effective !== []; } if ($direction === 'job') { return $job_direction !== null && in_array($job_direction, $effective, true); } return in_array($direction, $effective, true) && ($job_direction === null || $job_direction === $direction); } private static function key_allows(string $permission, string $direction): bool { return $permission === 'both' || $permission === $direction; } private static function mode_allows(string $mode, string $direction): bool { return $mode === 'open' || $mode === $direction . '-only'; } }