2024-12-30 09:44:47 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Core\Objects\DatabaseEntity;
|
|
|
|
|
2024-12-31 13:53:57 +01:00
|
|
|
use Core\Driver\Logger\Logger;
|
|
|
|
use Core\Driver\SQL\Column\Column;
|
|
|
|
use Core\Driver\SQL\Condition\CondIn;
|
2024-12-30 09:44:47 +01:00
|
|
|
use Core\Objects\Context;
|
2024-12-31 13:53:57 +01:00
|
|
|
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
2024-12-30 09:44:47 +01:00
|
|
|
use Core\Objects\DatabaseEntity\Attribute\ExtendingEnum;
|
2024-12-31 13:53:57 +01:00
|
|
|
use Core\Objects\DatabaseEntity\Attribute\Json;
|
2024-12-30 09:44:47 +01:00
|
|
|
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
|
|
|
use Core\Objects\DatabaseEntity\Attribute\Unique;
|
|
|
|
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
|
|
|
use Core\Objects\SSO\SSOProviderOAuth2;
|
|
|
|
use Core\Objects\SSO\SSOProviderOIDC;
|
|
|
|
use Core\Objects\SSO\SSOProviderSAML;
|
|
|
|
|
|
|
|
abstract class SsoProvider extends DatabaseEntity {
|
|
|
|
|
|
|
|
const PROTOCOLS = [
|
|
|
|
"oidc" => SSOProviderOIDC::class,
|
|
|
|
"oauth2" => SSOProviderOAuth2::class,
|
|
|
|
"saml" => SSOProviderSAML::class,
|
|
|
|
];
|
|
|
|
|
|
|
|
#[MaxLength(64)]
|
|
|
|
private string $name;
|
|
|
|
|
|
|
|
#[MaxLength(36)]
|
|
|
|
#[Unique]
|
|
|
|
private string $identifier;
|
|
|
|
|
|
|
|
private bool $active;
|
|
|
|
|
|
|
|
#[ExtendingEnum(self::PROTOCOLS)]
|
|
|
|
private string $protocol;
|
|
|
|
|
2024-12-31 13:53:57 +01:00
|
|
|
#[MaxLength(256)]
|
2024-12-30 09:44:47 +01:00
|
|
|
protected string $ssoUrl;
|
|
|
|
|
2024-12-31 13:53:57 +01:00
|
|
|
#[MaxLength(128)]
|
|
|
|
protected string $clientId;
|
|
|
|
|
|
|
|
#[Json]
|
|
|
|
#[DefaultValue('{}')]
|
|
|
|
protected array $groupMapping;
|
|
|
|
|
|
|
|
protected string $certificate;
|
|
|
|
|
2024-12-30 09:44:47 +01:00
|
|
|
public function __construct(string $protocol, ?int $id = null) {
|
|
|
|
parent::__construct($id);
|
|
|
|
$this->protocol = $protocol;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function newInstance(\ReflectionClass $reflectionClass, array $row) {
|
|
|
|
$type = $row["protocol"] ?? null;
|
|
|
|
if ($type === "saml") {
|
|
|
|
return (new \ReflectionClass(SSOProviderSAML::class))->newInstanceWithoutConstructor();
|
|
|
|
} else if ($type === "oauth2") {
|
|
|
|
return (new \ReflectionClass(SSOProviderOAuth2::class))->newInstanceWithoutConstructor();
|
|
|
|
} else if ($type === "oidc") {
|
|
|
|
return (new \ReflectionClass(SSOProviderOIDC::class))->newInstanceWithoutConstructor();
|
|
|
|
} else {
|
|
|
|
return parent::newInstance($reflectionClass, $row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildUrl(string $url, array $params): ?string {
|
|
|
|
$parts = parse_url($url);
|
|
|
|
if ($parts === false || !isset($parts["host"])) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset($parts["query"])) {
|
|
|
|
$parts["query"] = http_build_query($params);
|
|
|
|
} else {
|
|
|
|
$parts["query"] .= "&" . http_build_query($params);
|
|
|
|
}
|
|
|
|
|
|
|
|
$parts["scheme"] = $parts["scheme"] ?? "https";
|
|
|
|
return unparse_url($parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getIdentifier(): string {
|
|
|
|
return $this->identifier;
|
|
|
|
}
|
|
|
|
|
2024-12-31 13:53:57 +01:00
|
|
|
public function getGroupMapping(): array {
|
|
|
|
return $this->groupMapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function createUser(Context $context, string $email, array $groupNames) : User {
|
|
|
|
$sql = $context->getSQL();
|
|
|
|
$loggerName = "SSO-" . strtoupper($this->protocol);
|
|
|
|
$logger = new Logger($loggerName, $sql);
|
|
|
|
|
|
|
|
if (empty($groupNames)) {
|
|
|
|
$groups = [];
|
|
|
|
} else {
|
|
|
|
$groups = Group::findAll($sql, new CondIn(new Column("name"), $groupNames));
|
|
|
|
if ($groups === false) {
|
|
|
|
throw new \Exception("Error fetching available groups: " . $sql->getLastError());
|
|
|
|
} else if (count($groups) !== count($groupNames)) {
|
|
|
|
$availableGroups = array_map(function (Group $group) {
|
|
|
|
return $group->getName();
|
|
|
|
}, $groups);
|
|
|
|
$nonExistentGroups = array_diff($groupNames, $availableGroups);
|
|
|
|
$logger->warning("Could not resolve group names: " . implode(', ', $nonExistentGroups));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: create a possibility to map attribute values to user properties
|
|
|
|
$user = new User();
|
|
|
|
$user->email = $email;
|
|
|
|
$user->name = $email;
|
|
|
|
$user->password = null;
|
|
|
|
$user->fullName = "";
|
|
|
|
$user->ssoProvider = $this;
|
|
|
|
$user->confirmed = true;
|
|
|
|
$user->active = true;
|
|
|
|
$user->groups = $groups;
|
|
|
|
return $user;
|
|
|
|
}
|
|
|
|
|
2024-12-30 09:44:47 +01:00
|
|
|
public abstract function login(Context $context, ?string $redirectUrl);
|
|
|
|
}
|