Skip to content

Commit

Permalink
Add admin option to disable SSO registrations (#604)
Browse files Browse the repository at this point in the history
  • Loading branch information
e-five256 committed Mar 19, 2024
1 parent e5dd511 commit af46166
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 45 deletions.
5 changes: 4 additions & 1 deletion src/DTO/SettingsDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public function __construct(
public bool $KBIN_FEDERATION_PAGE_ENABLED,
public bool $KBIN_ADMIN_ONLY_OAUTH_CLIENTS,
public bool $KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN,
public bool $MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY
public bool $MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY,
public bool $MBIN_SSO_REGISTRATIONS_ENABLED
) {
}

Expand All @@ -56,6 +57,7 @@ public function mergeIntoDto(SettingsDto $dto): SettingsDto
$dto->KBIN_ADMIN_ONLY_OAUTH_CLIENTS = $this->KBIN_ADMIN_ONLY_OAUTH_CLIENTS ?? $dto->KBIN_ADMIN_ONLY_OAUTH_CLIENTS;
$dto->KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN = $this->KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN ?? $dto->KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN;
$dto->MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY = $this->MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY ?? $dto->MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY;
$dto->MBIN_SSO_REGISTRATIONS_ENABLED = $this->MBIN_SSO_REGISTRATIONS_ENABLED ?? $dto->MBIN_SSO_REGISTRATIONS_ENABLED;

return $dto;
}
Expand Down Expand Up @@ -83,6 +85,7 @@ public function jsonSerialize(): mixed
'KBIN_ADMIN_ONLY_OAUTH_CLIENTS' => $this->KBIN_ADMIN_ONLY_OAUTH_CLIENTS,
'KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN' => $this->KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN,
'MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY' => $this->MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY,
'MBIN_SSO_REGISTRATIONS_ENABLED' => $this->MBIN_SSO_REGISTRATIONS_ENABLED,
];
}
}
1 change: 1 addition & 0 deletions src/Form/SettingsType.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
])
->add('KBIN_HEADER_LOGO', CheckboxType::class, ['required' => false])
->add('KBIN_REGISTRATIONS_ENABLED', CheckboxType::class, ['required' => false])
->add('MBIN_SSO_REGISTRATIONS_ENABLED', CheckboxType::class, ['required' => false])
->add('KBIN_CAPTCHA_ENABLED', CheckboxType::class, ['required' => false])
->add('KBIN_FEDERATION_ENABLED', CheckboxType::class, ['required' => false])
->add('KBIN_MERCURE_ENABLED', CheckboxType::class, ['required' => false])
Expand Down
51 changes: 35 additions & 16 deletions src/Security/FacebookAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use App\Repository\ImageRepository;
use App\Service\ImageManager;
use App\Service\IpResolver;
use App\Service\SettingsManager;
use App\Service\UserManager;
use App\Utils\Slugger;
use Doctrine\ORM\EntityManagerInterface;
Expand All @@ -23,6 +24,7 @@
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
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;
Expand All @@ -39,7 +41,8 @@ public function __construct(
private readonly ImageFactory $imageFactory,
private readonly ImageRepository $imageRepository,
private readonly IpResolver $ipResolver,
private readonly Slugger $slugger
private readonly Slugger $slugger,
private readonly SettingsManager $settingsManager
) {
}

Expand Down Expand Up @@ -83,27 +86,36 @@ public function authenticate(Request $request): Passport

if ($user) {
$user->oauthFacebookId = $facebookUser->getId();
} else {
$dto = (new UserDto())->create(
$slugger->slug($facebookUser->getName()).rand(1, 999),
$facebookUser->getEmail()
);

$avatar = $this->getAvatar($facebookUser->getPictureUrl());
$this->entityManager->persist($user);
$this->entityManager->flush();

if ($avatar) {
$dto->avatar = $this->imageFactory->createDto($avatar);
}
return $user;
}

$dto->plainPassword = bin2hex(random_bytes(20));
$dto->ip = $this->ipResolver->resolve();
if (false === $this->settingsManager->get('MBIN_SSO_REGISTRATIONS_ENABLED')) {
throw new CustomUserMessageAuthenticationException('MBIN_SSO_REGISTRATIONS_ENABLED');
}

$user = $this->userManager->create($dto, false);
$user->oauthFacebookId = $facebookUser->getId();
$user->avatar = $this->getAvatar($facebookUser->getPictureUrl());
$user->isVerified = true;
$dto = (new UserDto())->create(
$slugger->slug($facebookUser->getName()).rand(1, 999),
$facebookUser->getEmail()
);

$avatar = $this->getAvatar($facebookUser->getPictureUrl());

if ($avatar) {
$dto->avatar = $this->imageFactory->createDto($avatar);
}

$dto->plainPassword = bin2hex(random_bytes(20));
$dto->ip = $this->ipResolver->resolve();

$user = $this->userManager->create($dto, false);
$user->oauthFacebookId = $facebookUser->getId();
$user->avatar = $this->getAvatar($facebookUser->getPictureUrl());
$user->isVerified = true;

$this->entityManager->persist($user);
$this->entityManager->flush();

Expand Down Expand Up @@ -149,6 +161,13 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());

if ('MBIN_SSO_REGISTRATIONS_ENABLED' === $message) {
$session = $request->getSession();
$session->getFlashBag()->add('error', 'sso_registrations_enabled.error');

return new RedirectResponse($this->router->generate('app_login'));
}

return new Response($message, Response::HTTP_FORBIDDEN);
}
}
41 changes: 30 additions & 11 deletions src/Security/GithubAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use App\DTO\UserDto;
use App\Entity\User;
use App\Service\SettingsManager;
use App\Service\UserManager;
use App\Utils\Slugger;
use Doctrine\ORM\EntityManagerInterface;
Expand All @@ -18,6 +19,7 @@
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
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;
Expand All @@ -29,7 +31,8 @@ public function __construct(
private readonly RouterInterface $router,
private readonly EntityManagerInterface $entityManager,
private readonly UserManager $userManager,
private readonly Slugger $slugger
private readonly Slugger $slugger,
private readonly SettingsManager $settingsManager
) {
}

Expand Down Expand Up @@ -63,20 +66,29 @@ public function authenticate(Request $request): Passport

if ($user) {
$user->oauthGithubId = \strval($githubUser->getId());
} else {
$dto = (new UserDto())->create(
$slugger->slug($githubUser->getNickname()).rand(1, 999),
$githubUser->getEmail(),
null
);

$dto->plainPassword = bin2hex(random_bytes(20));
$this->entityManager->persist($user);
$this->entityManager->flush();

$user = $this->userManager->create($dto, false);
$user->oauthGithubId = \strval($githubUser->getId());
$user->isVerified = true;
return $user;
}

if (false === $this->settingsManager->get('MBIN_SSO_REGISTRATIONS_ENABLED')) {
throw new CustomUserMessageAuthenticationException('MBIN_SSO_REGISTRATIONS_ENABLED');
}

$dto = (new UserDto())->create(
$slugger->slug($githubUser->getNickname()).rand(1, 999),
$githubUser->getEmail(),
null
);

$dto->plainPassword = bin2hex(random_bytes(20));

$user = $this->userManager->create($dto, false);
$user->oauthGithubId = \strval($githubUser->getId());
$user->isVerified = true;

$this->entityManager->persist($user);
$this->entityManager->flush();

Expand All @@ -99,6 +111,13 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());

if ('MBIN_SSO_REGISTRATIONS_ENABLED' === $message) {
$session = $request->getSession();
$session->getFlashBag()->add('error', 'sso_registrations_enabled.error');

return new RedirectResponse($this->router->generate('app_login'));
}

return new Response($message, Response::HTTP_FORBIDDEN);
}
}
49 changes: 34 additions & 15 deletions src/Security/GoogleAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use App\Repository\ImageRepository;
use App\Service\ImageManager;
use App\Service\IpResolver;
use App\Service\SettingsManager;
use App\Service\UserManager;
use App\Utils\Slugger;
use Doctrine\ORM\EntityManagerInterface;
Expand All @@ -24,6 +25,7 @@
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
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;
Expand All @@ -41,7 +43,8 @@ public function __construct(
private readonly ImageRepository $imageRepository,
private readonly RequestStack $requestStack,
private readonly IpResolver $ipResolver,
private readonly Slugger $slugger
private readonly Slugger $slugger,
private readonly SettingsManager $settingsManager
) {
}

Expand Down Expand Up @@ -87,26 +90,35 @@ public function authenticate(Request $request): Passport

if ($user) {
$user->oauthGoogleId = $googleUser->getId();
} else {
$dto = (new UserDto())->create(
$slugger->slug($googleUser->getName()).rand(1, 999),
$googleUser->getEmail()
);

$avatar = $this->getAvatar($googleUser->getAvatar());
$this->entityManager->persist($user);
$this->entityManager->flush();

if ($avatar) {
$dto->avatar = $this->imageFactory->createDto($avatar);
}
return $user;
}

$dto->plainPassword = bin2hex(random_bytes(20));
$dto->ip = $this->ipResolver->resolve();
if (false === $this->settingsManager->get('MBIN_SSO_REGISTRATIONS_ENABLED')) {
throw new CustomUserMessageAuthenticationException('MBIN_SSO_REGISTRATIONS_ENABLED');
}

$user = $this->userManager->create($dto, false);
$user->oauthGoogleId = $googleUser->getId();
$user->isVerified = true;
$dto = (new UserDto())->create(
$slugger->slug($googleUser->getName()).rand(1, 999),
$googleUser->getEmail()
);

$avatar = $this->getAvatar($googleUser->getAvatar());

if ($avatar) {
$dto->avatar = $this->imageFactory->createDto($avatar);
}

$dto->plainPassword = bin2hex(random_bytes(20));
$dto->ip = $this->ipResolver->resolve();

$user = $this->userManager->create($dto, false);
$user->oauthGoogleId = $googleUser->getId();
$user->isVerified = true;

$this->entityManager->persist($user);
$this->entityManager->flush();

Expand Down Expand Up @@ -155,6 +167,13 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());

if ('MBIN_SSO_REGISTRATIONS_ENABLED' === $message) {
$session = $request->getSession();
$session->getFlashBag()->add('error', 'sso_registrations_enabled.error');

return new RedirectResponse($this->router->generate('app_login'));
}

return new Response($message, Response::HTTP_FORBIDDEN);
}
}
15 changes: 15 additions & 0 deletions src/Security/KeycloakAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use App\Entity\User;
use App\Repository\UserRepository;
use App\Service\IpResolver;
use App\Service\SettingsManager;
use App\Service\UserManager;
use App\Utils\Slugger;
use Doctrine\ORM\EntityManagerInterface;
Expand All @@ -20,6 +21,7 @@
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
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;
Expand All @@ -35,6 +37,7 @@ public function __construct(
private readonly IpResolver $ipResolver,
private readonly Slugger $slugger,
private readonly UserRepository $userRepository,
private readonly SettingsManager $settingsManager
) {
}

Expand Down Expand Up @@ -75,11 +78,16 @@ public function authenticate(Request $request): Passport
if ($user) {
$user->oauthKeycloakId = $keycloakUser->getId();

$this->entityManager->persist($user);
$this->entityManager->flush();

return $user;
}

if (false === $this->settingsManager->get('MBIN_SSO_REGISTRATIONS_ENABLED')) {
throw new CustomUserMessageAuthenticationException('MBIN_SSO_REGISTRATIONS_ENABLED');
}

$username = $slugger->slug($keycloakUser->toArray()['preferred_username']);

if ($this->userRepository->count(['username' => $username]) > 0) {
Expand Down Expand Up @@ -120,6 +128,13 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());

if ('MBIN_SSO_REGISTRATIONS_ENABLED' === $message) {
$session = $request->getSession();
$session->getFlashBag()->add('error', 'sso_registrations_enabled.error');

return new RedirectResponse($this->router->generate('app_login'));
}

return new Response($message, Response::HTTP_FORBIDDEN);
}
}
3 changes: 2 additions & 1 deletion src/Service/SettingsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public function __construct(
$this->find($results, 'KBIN_FEDERATION_PAGE_ENABLED', FILTER_VALIDATE_BOOLEAN) ?? $this->kbinFederationPageEnabled,
$this->find($results, 'KBIN_ADMIN_ONLY_OAUTH_CLIENTS', FILTER_VALIDATE_BOOLEAN) ?? $this->kbinAdminOnlyOauthClients,
$this->find($results, 'KBIN_FEDERATED_SEARCH_ONLY_LOGGEDIN', FILTER_VALIDATE_BOOLEAN) ?? true,
$this->find($results, 'MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY', FILTER_VALIDATE_BOOLEAN) ?? false
$this->find($results, 'MBIN_SIDEBAR_SECTIONS_LOCAL_ONLY', FILTER_VALIDATE_BOOLEAN) ?? false,
$this->find($results, 'MBIN_SSO_REGISTRATIONS_ENABLED', FILTER_VALIDATE_BOOLEAN) ?? true
);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Twig/Extension/SettingsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function getFunctions(): array
new TwigFunction('kbin_default_lang', [SettingsExtensionRuntime::class, 'kbinDefaultLang']),
new TwigFunction('mbin_default_theme', [SettingsExtensionRuntime::class, 'mbinDefaultTheme']),
new TwigFunction('kbin_registrations_enabled', [SettingsExtensionRuntime::class, 'kbinRegistrationsEnabled']),
new TwigFunction('mbin_sso_registrations_enabled', [SettingsExtensionRuntime::class, 'mbinSsoRegistrationsEnabled']),
new TwigFunction('kbin_header_logo', [SettingsExtensionRuntime::class, 'kbinHeaderLogo']),
new TwigFunction('kbin_captcha_enabled', [SettingsExtensionRuntime::class, 'kbinCaptchaEnabled']),
new TwigFunction('kbin_mercure_enabled', [SettingsExtensionRuntime::class, 'kbinMercureEnabled']),
Expand Down
6 changes: 6 additions & 0 deletions src/Twig/Runtime/SettingsExtensionRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public function kbinRegistrationsEnabled(): bool
return $this->settings->get('KBIN_REGISTRATIONS_ENABLED');
}

#[Pure]
public function mbinSsoRegistrationsEnabled(): bool
{
return $this->settings->get('MBIN_SSO_REGISTRATIONS_ENABLED');
}

public function kbinDefaultLang(): string
{
return $this->settings->get('KBIN_DEFAULT_LANG');
Expand Down
4 changes: 4 additions & 0 deletions templates/admin/settings.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
{{ form_label(form.KBIN_REGISTRATIONS_ENABLED, 'registrations_enabled') }}
{{ form_widget(form.KBIN_REGISTRATIONS_ENABLED) }}
</div>
<div class="checkbox">
{{ form_label(form.MBIN_SSO_REGISTRATIONS_ENABLED, 'sso_registrations_enabled') }}
{{ form_widget(form.MBIN_SSO_REGISTRATIONS_ENABLED) }}
</div>
<div class="checkbox">
{{ form_label(form.KBIN_CAPTCHA_ENABLED, 'captcha_enabled') }}
{{ form_widget(form.KBIN_CAPTCHA_ENABLED) }}
Expand Down
Loading

0 comments on commit af46166

Please sign in to comment.