Skip to content

Commit

Permalink
✨Add Postfix API
Browse files Browse the repository at this point in the history
  • Loading branch information
0x46616c6b committed Sep 29, 2024
1 parent 6c10a48 commit 7cfeb40
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ KEYCLOAK_API_IP_ALLOWLIST="127.0.0.1, ::1"
# Warning: set a secure access token
KEYCLOAK_API_ACCESS_TOKEN="insecure"

### Enable postfix API ###
# Set to `true` to enable postfix API
POSTFIX_API_ENABLED=false
# Access is restricted to these IPs (supports subnets like `10.0.0.1/24`)
POSTFIX_API_IP_ALLOWLIST="127.0.0.1, ::1"
# Warning: set a secure access token
POSTFIX_API_ACCESS_TOKEN="insecure"

### Enable roundcube API ###
# Set to `true` to enable roundcube API
ROUNDCUBE_API_ENABLED=false
Expand Down
3 changes: 3 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ WKD_FORMAT="advanced"
KEYCLOAK_API_ENABLED=true
KEYCLOAK_API_IP_ALLOWLIST="127.0.0.1, ::1"
KEYCLOAK_API_ACCESS_TOKEN="insecure"
POSTFIX_API_ENABLED=true
POSTFIX_API_IP_ALLOWLIST="127.0.0.1, ::1"
POSTFIX_API_ACCESS_TOKEN="insecure"
ROUNDCUBE_API_ENABLED=true
ROUNDCUBE_API_IP_ALLOWLIST="127.0.0.1, ::1"
17 changes: 17 additions & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ security:
users:
- identifier: keycloak
roles: ['ROLE_KEYCLOAK']
postfix:
memory:
users:
- identifier: postfix
roles: ['ROLE_POSTFIX']

role_hierarchy:
# User
Expand Down Expand Up @@ -116,6 +121,12 @@ security:
provider: keycloak
access_token:
token_handler: App\Security\KeycloakAccessTokenHandler
postfix:
pattern: ^/api/postfix
stateless: true
provider: postfix
access_token:
token_handler: App\Security\PostfixAccessTokenHandler
roundcube:
pattern: ^/api/roundcube
stateless: true
Expand Down Expand Up @@ -169,6 +180,12 @@ security:
allow_if: "'%env(KEYCLOAK_API_ENABLED)%' == 'true' and is_granted('ROLE_KEYCLOAK')",
}
- { path: "^/api/keycloak", roles: ROLE_NO_ACCESS }
- {
path: "^/api/postfix",
ips: "%env(POSTFIX_API_IP_ALLOWLIST)%",
allow_if: "'%env(POSTFIX_API_ENABLED)%' == 'true' and is_granted('ROLE_POSTFIX')",
}
- { path: "^/api/postfix", roles: ROLE_NO_ACCESS }
- {
path: "^/api/roundcube",
ips: "%env(ROUNDCUBE_API_IP_ALLOWLIST)%",
Expand Down
4 changes: 4 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ services:
arguments:
$keycloakApiAccessToken: "%env(KEYCLOAK_API_ACCESS_TOKEN)%"

App\Security\PostfixAccessTokenHandler:
arguments:
$postfixApiAccessToken: "%env(POSTFIX_API_ACCESS_TOKEN)%"

App\Sender\WelcomeMessageSender:
public: true

Expand Down
26 changes: 26 additions & 0 deletions src/Controller/PostfixController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Controller;

use App\Entity\Alias;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class PostfixController extends AbstractController
{
public function __construct(readonly private EntityManagerInterface $manager)
{
}

#[Route(path: '/api/postfix/alias/{alias}', name: 'api_postfix_alias', methods: ['GET'], stateless: true)]
public function getAliasUsers(string $alias): Response
{
$users = $this->manager->getRepository(Alias::class)->findBy(['deleted' => false, 'source' => $alias]);

return $this->json(array_map(function (Alias $alias) {
return $alias->getDestination();
}, $users));
}
}
23 changes: 23 additions & 0 deletions src/Security/PostfixAccessTokenHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Security;

use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;

readonly class PostfixAccessTokenHandler implements AccessTokenHandlerInterface
{
public function __construct(private string $postfixApiAccessToken)
{
}

public function getUserBadgeFrom(#[\SensitiveParameter] string $accessToken): UserBadge
{
if ($accessToken !== $this->postfixApiAccessToken) {
throw new BadCredentialsException('Invalid access token');
}

return new UserBadge('postfix');
}
}
33 changes: 33 additions & 0 deletions tests/Controller/PostfixControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class PostfixControllerTest extends WebTestCase
{
public function testGetAliasUsersWrongApiToken(): void
{
$client = static::createClient([], [
'HTTP_Authorization' => 'Bearer wrong',
]);
$client->request('GET', '/api/postfix/alias/alias@example.org');

self::assertResponseStatusCodeSame(401);
}

public function testGetAliasUsers(): void
{
$client = static::createClient([], [
'HTTP_Authorization' => 'Bearer insecure',
]);

$client->request('GET', '/api/postfix/alias/alias@example.org');
self::assertResponseIsSuccessful();
self::assertJsonStringEqualsJsonString('["user2@example.org"]', $client->getResponse()->getContent());

$client->request('GET', '/api/postfix/alias/user@example.org');
self::assertResponseIsSuccessful();
self::assertJsonStringEqualsJsonString('[]', $client->getResponse()->getContent());
}
}

0 comments on commit 7cfeb40

Please sign in to comment.