Browse Source

Security

imwald
Nuša Pukšič 8 months ago
parent
commit
9bb3c0ddb7
  1. 1
      config/packages/security.yaml
  2. 13
      config/routes/web_profiler.yaml
  3. 8
      src/Controller/LoginController.php
  4. 8
      src/Entity/User.php
  5. 7
      src/Security/NostrAuthenticator.php
  6. 48
      src/Security/UserDTOProvider.php

1
config/packages/security.yaml

@ -10,6 +10,7 @@ security: @@ -10,6 +10,7 @@ security:
security: false
main:
lazy: true
stateless: false
provider: user_dto_provider
custom_authenticators:
- App\Security\NostrAuthenticator

13
config/routes/web_profiler.yaml

@ -1,7 +1,8 @@ @@ -1,7 +1,8 @@
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
when@local:
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler

8
src/Controller/LoginController.php

@ -17,9 +17,13 @@ class LoginController extends AbstractController @@ -17,9 +17,13 @@ class LoginController extends AbstractController
public function index(#[CurrentUser] ?User $user): Response
{
if (null !== $user) {
return new JsonResponse('Authentication Successful', 200);
return new JsonResponse([
'message' => 'Authentication Successful',
], 200);
}
return new JsonResponse('Unauthenticated', 401);
return new JsonResponse([
'message' => 'Unauthenticated',
], 401);
}
}

8
src/Entity/User.php

@ -5,6 +5,7 @@ namespace App\Entity; @@ -5,6 +5,7 @@ namespace App\Entity;
use App\Repository\UserEntityRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
@ -12,7 +13,7 @@ use Symfony\Component\Security\Core\User\UserInterface; @@ -12,7 +13,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
*/
#[ORM\Entity(repositoryClass: UserEntityRepository::class)]
#[ORM\Table(name: "app_user")]
class User implements UserInterface
class User implements UserInterface, EquatableInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
@ -112,4 +113,9 @@ class User implements UserInterface @@ -112,4 +113,9 @@ class User implements UserInterface
{
return $this->relays;
}
public function isEqualTo(UserInterface $user): bool
{
return $this->getUserIdentifier() === $user->getUserIdentifier();
}
}

7
src/Security/NostrAuthenticator.php

@ -5,6 +5,8 @@ namespace App\Security; @@ -5,6 +5,8 @@ namespace App\Security;
use App\Entity\Event;
use Mdanter\Ecc\Crypto\Signature\SchnorrSignature;
use swentel\nostr\Key\Key;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
@ -20,6 +22,9 @@ use Symfony\Component\Serializer\Serializer; @@ -20,6 +22,9 @@ use Symfony\Component\Serializer\Serializer;
class NostrAuthenticator extends AbstractAuthenticator implements InteractiveAuthenticatorInterface
{
public function __construct(
private readonly Security $security
) {}
public function supports(Request $request): ?bool
{
@ -59,7 +64,7 @@ class NostrAuthenticator extends AbstractAuthenticator implements InteractiveAut @@ -59,7 +64,7 @@ class NostrAuthenticator extends AbstractAuthenticator implements InteractiveAut
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
return new Response('Authentication Successful', 200);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response

48
src/Security/UserDTOProvider.php

@ -6,6 +6,7 @@ use App\Entity\User; @@ -6,6 +6,7 @@ use App\Entity\User;
use App\Enum\KindsEnum;
use App\Service\NostrClient;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use swentel\nostr\Key\Key;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -14,7 +15,8 @@ readonly class UserDTOProvider implements UserProviderInterface @@ -14,7 +15,8 @@ readonly class UserDTOProvider implements UserProviderInterface
{
public function __construct(
private EntityManagerInterface $entityManager,
private NostrClient $nostrClient
private NostrClient $nostrClient,
private LoggerInterface $logger
) {}
/**
@ -42,42 +44,54 @@ readonly class UserDTOProvider implements UserProviderInterface @@ -42,42 +44,54 @@ readonly class UserDTOProvider implements UserProviderInterface
*/
public function loadUserByIdentifier(string $identifier): UserInterface
{
$user = $this->entityManager->getRepository(User::class)->findOneBy(['npub' => $identifier]);
$metadata = $relays = null;
if (!$user) {
// user
$user = new User();
$user->setNpub($identifier);
$this->entityManager->persist($user);
$this->entityManager->flush();
}
try {
$key = new Key();
$pubkey = $key->convertToHex($identifier);
$data = $this->nostrClient->getLoginData($pubkey);
$this->logger->info('Load user by identifier.', ['data' => $data]);
$metadata = null;
$relays = null;
foreach ($data as $d) {
$ev = $d->event;
if ($ev->kind === KindsEnum::METADATA) {
$this->logger->info('Load user by identifier event.', ['event' => $ev]);
if ($ev->kind === KindsEnum::METADATA->value) {
$metadata = json_decode($ev->content);
$this->logger->info('Load user by identifier event.', ['metadata' => $metadata]);
}
if ($ev->kind === KindsEnum::RELAY_LIST) {
if ($ev->kind === KindsEnum::RELAY_LIST->value) {
$relays = $ev->tags;
}
}
} catch (\Exception $e) {
// nothing to do here right now
$this->logger->error('Error getting user data.', ['exception' => $e]);
$metadata = null;
$relays = null;
}
// Fallback metadata if none fetched
if (is_null($metadata)) {
// if no metadata event, use what you have
$metadata = new \stdClass();
$metadata->name = substr($identifier, 0, 5) . ':' . substr($identifier, -5);
$metadata->name = substr($identifier, 0, 8) . '…' . substr($identifier, -4);
}
// Get or create user
$user = $this->entityManager->getRepository(User::class)->findOneBy(['npub' => $identifier]);
if (!$user) {
$user = new User();
$user->setNpub($identifier);
$this->entityManager->persist($user);
}
// Update with fresh metadata/relays
$user->setMetadata($metadata);
$user->setRelays($relays);
$this->entityManager->flush();
return $user;
}

Loading…
Cancel
Save