Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Firewall;
|
||||
|
||||
use App\Saml\SamlAuthFactory;
|
||||
use App\Saml\Token\SamlToken;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
|
||||
|
||||
class SamlListener extends AbstractAuthenticationListener
|
||||
{
|
||||
/**
|
||||
* @var SamlAuthFactory
|
||||
*/
|
||||
protected $authFactory;
|
||||
|
||||
public function setAuth(SamlAuthFactory $authFactory): void
|
||||
{
|
||||
$this->authFactory = $authFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs authentication.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @return TokenInterface|Response|null The authenticated token, null if full authentication is not possible, or a Response
|
||||
*
|
||||
* @throws AuthenticationException if the authentication fails
|
||||
* @throws \Exception if attribute set by "username_attribute" option not found
|
||||
*/
|
||||
protected function attemptAuthentication(Request $request)
|
||||
{
|
||||
$oneLoginAuth = $this->authFactory->create();
|
||||
|
||||
$oneLoginAuth->processResponse();
|
||||
if ($oneLoginAuth->getErrors()) {
|
||||
$this->logger->error($oneLoginAuth->getLastErrorReason());
|
||||
throw new AuthenticationException($oneLoginAuth->getLastErrorReason());
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
if (isset($this->options['use_attribute_friendly_name']) && $this->options['use_attribute_friendly_name']) {
|
||||
$attributes = $oneLoginAuth->getAttributesWithFriendlyName();
|
||||
} else {
|
||||
$attributes = $oneLoginAuth->getAttributes();
|
||||
}
|
||||
$attributes['sessionIndex'] = $oneLoginAuth->getSessionIndex();
|
||||
$token = new SamlToken();
|
||||
$token->setAttributes($attributes);
|
||||
|
||||
if (isset($this->options['username_attribute'])) {
|
||||
if (!\array_key_exists($this->options['username_attribute'], $attributes)) {
|
||||
$this->logger->error(sprintf('Found attributes: %s', print_r($attributes, true)));
|
||||
throw new \Exception(sprintf("Attribute '%s' not found in SAML data", $this->options['username_attribute']));
|
||||
}
|
||||
|
||||
$username = $attributes[$this->options['username_attribute']][0];
|
||||
} else {
|
||||
$username = $oneLoginAuth->getNameId();
|
||||
}
|
||||
$token->setUser($username);
|
||||
|
||||
return $this->authenticationManager->authenticate($token);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Logout;
|
||||
|
||||
use App\Saml\SamlAuthFactory;
|
||||
use App\Saml\Token\SamlTokenInterface;
|
||||
use OneLogin\Saml2\Error;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
|
||||
|
||||
final class SamlLogoutHandler implements LogoutHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var SamlAuthFactory
|
||||
*/
|
||||
private $samlAuth;
|
||||
|
||||
public function __construct(SamlAuthFactory $samlAuth)
|
||||
{
|
||||
$this->samlAuth = $samlAuth;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by the LogoutListener when a user has requested
|
||||
* to be logged out. Usually, you would unset session variables, or remove
|
||||
* cookies, etc.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token
|
||||
*/
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if (!$token instanceof SamlTokenInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$samlAuth = $this->samlAuth->create();
|
||||
|
||||
try {
|
||||
$samlAuth->processSLO();
|
||||
} catch (Error $e) {
|
||||
if (!empty($samlAuth->getSLOurl())) {
|
||||
$sessionIndex = $token->hasAttribute('sessionIndex') ? $token->getAttribute('sessionIndex') : null;
|
||||
$samlAuth->logout(null, [], $token->getUsername(), $sessionIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Provider;
|
||||
|
||||
use App\Configuration\SamlConfigurationInterface;
|
||||
use App\Entity\User;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Saml\SamlTokenFactory;
|
||||
use App\Saml\Token\SamlTokenInterface;
|
||||
use App\Saml\User\SamlUserFactory;
|
||||
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
|
||||
final class SamlProvider implements AuthenticationProviderInterface
|
||||
{
|
||||
private $userProvider;
|
||||
private $userFactory;
|
||||
private $tokenFactory;
|
||||
private $repository;
|
||||
private $configuration;
|
||||
|
||||
public function __construct(UserRepository $repository, UserProviderInterface $userProvider, SamlTokenFactory $tokenFactory, SamlUserFactory $userFactory, SamlConfigurationInterface $configuration)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->userProvider = $userProvider;
|
||||
$this->tokenFactory = $tokenFactory;
|
||||
$this->userFactory = $userFactory;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SamlTokenInterface $token
|
||||
* @return SamlTokenInterface
|
||||
*/
|
||||
public function authenticate(TokenInterface $token)
|
||||
{
|
||||
$user = null;
|
||||
|
||||
try {
|
||||
/** @var User $user */
|
||||
$user = $this->userProvider->loadUserByUsername($token->getUsername());
|
||||
} catch (UsernameNotFoundException $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $user) {
|
||||
$user = $this->userFactory->createUser($token);
|
||||
} else {
|
||||
$this->userFactory->hydrateUser($user, $token);
|
||||
}
|
||||
|
||||
$this->repository->saveUser($user);
|
||||
} catch (\Exception $ex) {
|
||||
throw new AuthenticationException(
|
||||
sprintf('Failed creating or hydrating user "%s": %s', $token->getUsername(), $ex->getMessage())
|
||||
);
|
||||
}
|
||||
|
||||
$authenticatedToken = $this->tokenFactory->createToken($user, $token->getAttributes(), $user->getRoles());
|
||||
$authenticatedToken->setAuthenticated(true);
|
||||
|
||||
return $authenticatedToken;
|
||||
}
|
||||
|
||||
public function supports(TokenInterface $token)
|
||||
{
|
||||
if (!$this->configuration->isActivated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $token instanceof SamlTokenInterface;
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,15 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
||||
*/
|
||||
class SamlAuthFactory
|
||||
{
|
||||
private $request;
|
||||
private $configuration;
|
||||
|
||||
public function __construct(RequestStack $request, SamlConfigurationInterface $configuration)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->configuration = $configuration;
|
||||
public function __construct(
|
||||
private RequestStack $request,
|
||||
private SamlConfigurationInterface $configuration
|
||||
) {
|
||||
}
|
||||
|
||||
public function create(): Auth
|
||||
{
|
||||
if (null !== $this->request->getMasterRequest() && $this->request->getMasterRequest()->isFromTrustedProxy()) {
|
||||
if (null !== $this->request->getMainRequest() && $this->request->getMainRequest()->isFromTrustedProxy()) {
|
||||
Utils::setProxyVars(true);
|
||||
}
|
||||
|
||||
|
||||
129
src/Saml/SamlAuthenticator.php
Normal file
129
src/Saml/SamlAuthenticator.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml;
|
||||
|
||||
use App\Configuration\SamlConfiguration;
|
||||
use App\Saml\Security\SamlAuthenticationFailureHandler;
|
||||
use App\Saml\Security\SamlAuthenticationSuccessHandler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||
use Symfony\Component\Security\Http\HttpUtils;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SamlAuthenticator extends AbstractAuthenticator
|
||||
{
|
||||
private array $options = [
|
||||
'check_path' => 'saml_acs',
|
||||
'login_path' => 'saml_login',
|
||||
'use_attribute_friendly_name' => false,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private HttpUtils $httpUtils,
|
||||
private SamlAuthenticationSuccessHandler $successHandler,
|
||||
private SamlAuthenticationFailureHandler $failureHandler,
|
||||
private SamlAuthFactory $samlAuthFactory,
|
||||
private SamlProvider $samlProvider,
|
||||
private SamlConfiguration $configuration
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(Request $request): bool
|
||||
{
|
||||
if (!$this->configuration->isActivated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->httpUtils->checkRequestPath($request, $this->options['check_path'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function createToken(Passport $passport, string $firewallName): TokenInterface
|
||||
{
|
||||
$user = $passport->getUser();
|
||||
|
||||
$token = new SamlToken($user, $firewallName, $user->getRoles());
|
||||
$token->setUser($user);
|
||||
|
||||
foreach ($passport->getBadges() as $badge) {
|
||||
if ($badge instanceof SamlBadge) {
|
||||
$token->setAttributes($badge->getSamlLoginAttributes()->getAttributes());
|
||||
}
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function authenticate(Request $request): Passport
|
||||
{
|
||||
$oneLoginAuth = $this->samlAuthFactory->create();
|
||||
|
||||
$oneLoginAuth->processResponse();
|
||||
|
||||
// $this->logger->debug('Received SAML response: ' . $oneLoginAuth->getLastResponseXML());
|
||||
|
||||
if ($oneLoginAuth->getErrors()) {
|
||||
throw new AuthenticationException($oneLoginAuth->getLastErrorReason());
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
if (isset($this->options['use_attribute_friendly_name']) && $this->options['use_attribute_friendly_name']) {
|
||||
$attributes = $oneLoginAuth->getAttributesWithFriendlyName();
|
||||
} else {
|
||||
$attributes = $oneLoginAuth->getAttributes();
|
||||
}
|
||||
$attributes['sessionIndex'] = $oneLoginAuth->getSessionIndex();
|
||||
|
||||
$loginAttributes = new SamlLoginAttributes();
|
||||
$loginAttributes->setAttributes($attributes);
|
||||
|
||||
if (isset($this->options['username_attribute'])) {
|
||||
if (!\array_key_exists($this->options['username_attribute'], $attributes)) {
|
||||
throw new \Exception(sprintf("Attribute '%s' not found in SAML data", $this->options['username_attribute']));
|
||||
}
|
||||
|
||||
$username = $attributes[$this->options['username_attribute']][0];
|
||||
} else {
|
||||
$username = $oneLoginAuth->getNameId();
|
||||
}
|
||||
$loginAttributes->setUserIdentifier($username);
|
||||
|
||||
$passport = new SelfValidatingPassport(
|
||||
new UserBadge($loginAttributes->getUserIdentifier(), function () use ($loginAttributes) {
|
||||
return $this->samlProvider->findUser($loginAttributes);
|
||||
}),
|
||||
[new RememberMeBadge(), new SamlBadge($loginAttributes)]
|
||||
);
|
||||
|
||||
return $passport;
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
|
||||
{
|
||||
return $this->successHandler->onAuthenticationSuccess($request, $token);
|
||||
}
|
||||
|
||||
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
|
||||
{
|
||||
return $this->failureHandler->onAuthenticationFailure($request, $exception);
|
||||
}
|
||||
}
|
||||
29
src/Saml/SamlBadge.php
Normal file
29
src/Saml/SamlBadge.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml;
|
||||
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
||||
|
||||
final class SamlBadge implements BadgeInterface
|
||||
{
|
||||
public function __construct(private SamlLoginAttributes $samlToken)
|
||||
{
|
||||
}
|
||||
|
||||
public function getSamlLoginAttributes(): SamlLoginAttributes
|
||||
{
|
||||
return $this->samlToken;
|
||||
}
|
||||
|
||||
public function isResolved(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
50
src/Saml/SamlLoginAttributes.php
Normal file
50
src/Saml/SamlLoginAttributes.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml;
|
||||
|
||||
final class SamlLoginAttributes
|
||||
{
|
||||
private array $attributes = [];
|
||||
private ?string $userIdentifier = null;
|
||||
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
public function setAttributes(array $attributes): void
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
public function hasAttribute(string $name): bool
|
||||
{
|
||||
return \array_key_exists($name, $this->attributes);
|
||||
}
|
||||
|
||||
public function getAttribute(string $name): mixed
|
||||
{
|
||||
if (!\array_key_exists($name, $this->attributes)) {
|
||||
throw new \InvalidArgumentException(sprintf('This SAML login has no "%s" attribute.', $name));
|
||||
}
|
||||
|
||||
return $this->attributes[$name];
|
||||
}
|
||||
|
||||
public function getUserIdentifier(): ?string
|
||||
{
|
||||
return $this->userIdentifier;
|
||||
}
|
||||
|
||||
public function setUserIdentifier(?string $userIdentifier): void
|
||||
{
|
||||
$this->userIdentifier = $userIdentifier;
|
||||
}
|
||||
}
|
||||
48
src/Saml/SamlLogoutSubscriber.php
Normal file
48
src/Saml/SamlLogoutSubscriber.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml;
|
||||
|
||||
use OneLogin\Saml2\Error;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Http\Event\LogoutEvent;
|
||||
|
||||
final class SamlLogoutSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private SamlAuthFactory $samlAuth)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
LogoutEvent::class => 'logout',
|
||||
];
|
||||
}
|
||||
|
||||
public function logout(LogoutEvent $event)
|
||||
{
|
||||
$token = $event->getToken();
|
||||
|
||||
if (!$token instanceof SamlToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
$samlAuth = $this->samlAuth->create();
|
||||
|
||||
try {
|
||||
$samlAuth->processSLO();
|
||||
} catch (Error $e) {
|
||||
if (!empty($samlAuth->getSLOurl())) {
|
||||
$sessionIndex = $token->hasAttribute('sessionIndex') ? $token->getAttribute('sessionIndex') : null;
|
||||
$samlAuth->logout(null, [], $token->getUserIdentifier(), $sessionIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,27 +7,57 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\User;
|
||||
namespace App\Saml;
|
||||
|
||||
use App\Configuration\SamlConfigurationInterface;
|
||||
use App\Entity\User;
|
||||
use App\Saml\Token\SamlTokenInterface;
|
||||
use App\Repository\UserRepository;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
|
||||
final class SamlUserFactory
|
||||
final class SamlProvider
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SamlConfigurationInterface $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
public function __construct(
|
||||
private UserRepository $repository,
|
||||
private UserProviderInterface $userProvider,
|
||||
private SamlConfigurationInterface $configuration
|
||||
) {
|
||||
}
|
||||
|
||||
public function createUser(SamlTokenInterface $token): User
|
||||
public function findUser(SamlLoginAttributes $token): User
|
||||
{
|
||||
$user = null;
|
||||
|
||||
try {
|
||||
/** @var User $user */
|
||||
$user = $this->userProvider->loadUserByIdentifier($token->getUserIdentifier());
|
||||
} catch (UserNotFoundException $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $user) {
|
||||
$user = $this->createUser($token);
|
||||
} else {
|
||||
$this->hydrateUser($user, $token);
|
||||
}
|
||||
|
||||
$this->repository->saveUser($user);
|
||||
} catch (\Exception $ex) {
|
||||
throw new AuthenticationException(
|
||||
sprintf('Failed creating or hydrating user "%s": %s', $token->getUserIdentifier(), $ex->getMessage())
|
||||
);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function createUser(SamlLoginAttributes $token): User
|
||||
{
|
||||
// Not using UserService: user settings should be set via SAML attributes
|
||||
$user = new User();
|
||||
$user->setEnabled(true);
|
||||
$user->setUsername($token->getUsername());
|
||||
$user->setUserIdentifier($token->getUserIdentifier());
|
||||
$user->setPassword('');
|
||||
|
||||
$this->hydrateUser($user, $token);
|
||||
@@ -35,7 +65,7 @@ final class SamlUserFactory
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function hydrateUser(User $user, SamlTokenInterface $token): void
|
||||
private function hydrateUser(User $user, SamlLoginAttributes $token): void
|
||||
{
|
||||
$groupAttribute = $this->configuration->getRolesAttribute();
|
||||
$groupMapping = $this->configuration->getRolesMapping();
|
||||
@@ -84,11 +114,11 @@ final class SamlUserFactory
|
||||
if ($user->getId() === null) {
|
||||
$user->setPassword('');
|
||||
}
|
||||
$user->setUsername($token->getUsername());
|
||||
$user->setUserIdentifier($token->getUserIdentifier());
|
||||
$user->setAuth(User::AUTH_SAML);
|
||||
}
|
||||
|
||||
private function getPropertyValue(SamlTokenInterface $token, $attribute)
|
||||
private function getPropertyValue(SamlLoginAttributes $token, $attribute)
|
||||
{
|
||||
$results = [];
|
||||
$attributes = $token->getAttributes();
|
||||
@@ -7,10 +7,10 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Token;
|
||||
namespace App\Saml;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken;
|
||||
|
||||
interface SamlTokenInterface extends TokenInterface
|
||||
final class SamlToken extends PostAuthenticationToken
|
||||
{
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml;
|
||||
|
||||
use App\Saml\Token\SamlToken;
|
||||
|
||||
final class SamlTokenFactory
|
||||
{
|
||||
public function createToken($user, array $attributes, array $roles): SamlToken
|
||||
{
|
||||
$token = new SamlToken($roles);
|
||||
$token->setUser($user);
|
||||
$token->setAttributes($attributes);
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
22
src/Saml/Security/SamlAuthenticationFailureHandler.php
Normal file
22
src/Saml/Security/SamlAuthenticationFailureHandler.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Security;
|
||||
|
||||
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
|
||||
|
||||
final class SamlAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
|
||||
{
|
||||
protected $defaultOptions = [
|
||||
'failure_path' => 'login',
|
||||
'failure_forward' => false,
|
||||
'login_path' => 'saml_login',
|
||||
'failure_path_parameter' => '_failure_path',
|
||||
];
|
||||
}
|
||||
@@ -14,7 +14,15 @@ use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessH
|
||||
|
||||
final class SamlAuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
|
||||
{
|
||||
protected function determineTargetUrl(Request $request)
|
||||
protected $defaultOptions = [
|
||||
'always_use_default_target_path' => false,
|
||||
'default_target_path' => '/',
|
||||
'login_path' => 'saml_login',
|
||||
'target_path_parameter' => '_target_path',
|
||||
'use_referer' => false,
|
||||
];
|
||||
|
||||
protected function determineTargetUrl(Request $request): string
|
||||
{
|
||||
if ($this->options['always_use_default_target_path']) {
|
||||
return $this->options['default_target_path'];
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Security;
|
||||
|
||||
use App\Saml\Logout\SamlLogoutHandler;
|
||||
use App\Saml\Provider\SamlProvider;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
final class SamlFactory extends AbstractFactory
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addOption('check_path', 'saml_acs');
|
||||
$this->addOption('failure_path', 'fos_user_security_login');
|
||||
$this->addOption('success_handler', SamlAuthenticationSuccessHandler::class);
|
||||
$this->defaultFailureHandlerOptions['login_path'] = 'saml_login';
|
||||
}
|
||||
|
||||
protected function isRememberMeAware($config)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPosition()
|
||||
{
|
||||
return 'pre_auth';
|
||||
}
|
||||
|
||||
public function getKey()
|
||||
{
|
||||
return 'kimai_saml';
|
||||
}
|
||||
|
||||
protected function getListenerId()
|
||||
{
|
||||
return 'kimai.saml_listener';
|
||||
}
|
||||
|
||||
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
|
||||
{
|
||||
$providerId = 'security.authentication.provider.saml.' . $id;
|
||||
$definition = $container->setDefinition($providerId, new ChildDefinition(SamlProvider::class));
|
||||
$definition->replaceArgument(1, new Reference($userProviderId));
|
||||
|
||||
return $providerId;
|
||||
}
|
||||
|
||||
protected function createListener($container, $id, $config, $userProvider)
|
||||
{
|
||||
$listenerId = parent::createListener($container, $id, $config, $userProvider);
|
||||
$this->createLogoutHandler($container, $id, $config);
|
||||
|
||||
return $listenerId;
|
||||
}
|
||||
|
||||
private function createLogoutHandler(ContainerBuilder $container, $id, $config)
|
||||
{
|
||||
if ($container->hasDefinition('security.logout_listener.' . $id)) {
|
||||
$logoutListener = $container->getDefinition('security.logout_listener.' . $id);
|
||||
|
||||
$container
|
||||
->setDefinition(SamlLogoutHandler::class, new ChildDefinition('saml.security.http.logout'))
|
||||
->replaceArgument(2, array_intersect_key($config, $this->options));
|
||||
$logoutListener->addMethodCall('addHandler', [new Reference(SamlLogoutHandler::class)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Saml\Token;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
|
||||
|
||||
class SamlToken extends AbstractToken implements SamlTokenInterface
|
||||
{
|
||||
public function getCredentials()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user