Skip to content

Commit

Permalink
UHF-10559: Add support for secondary key authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
tuutti committed Sep 3, 2024
1 parent 0b9012a commit aad7a9b
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 86 deletions.
6 changes: 0 additions & 6 deletions drush.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@ services:
- '@datetime.time'
tags:
- { name: drush.command }
helfi_api_base.pubsub_commands:
class: \Drupal\helfi_api_base\Commands\PubSubCommands
arguments:
- '@helfi_api_base.pubsub_manager'
tags:
- { name: drush.command }
helfi_api_base.deploy_commands:
class: \Drupal\helfi_api_base\Commands\DeployCommands
arguments: ['@event_dispatcher']
Expand Down
56 changes: 9 additions & 47 deletions helfi_api_base.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,53 +108,15 @@ services:
arguments:
- '@config.factory'

Drupal\helfi_api_base\Azure\PubSub\SettingsFactory: '@helfi_api_base.pubsub_settings_factory'
helfi_api_base.pubsub_settings_factory:
class: Drupal\helfi_api_base\Azure\PubSub\SettingsFactory
arguments:
- '@helfi_api_base.vault_manager'

Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory: '@helfi_api_base.pubsub_client_factory'
helfi_api_base.pubsub_client_factory:
class: Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory

helfi_api_base.pubsub_client:
public: false
class: \WebSocket\Client
factory: ['@helfi_api_base.pubsub_client_factory', 'create']
arguments:
- '@helfi_api_base.pubsub_settings'
- '@datetime.time'

Drupal\helfi_api_base\Azure\PubSub\Settings: '@helfi_api_base.pubsub_settings'
helfi_api_base.pubsub_settings:
class: Drupal\helfi_api_base\Azure\PubSub\Settings
factory: ['@helfi_api_base.pubsub_settings_factory', 'create']

Drupal\helfi_api_base\Azure\PubSub\PubSubManager: '@helfi_api_base.pubsub_manager'
helfi_api_base.pubsub_manager:
class: Drupal\helfi_api_base\Azure\PubSub\PubSubManager
arguments:
- '@helfi_api_base.pubsub_client'
- '@event_dispatcher'
- '@datetime.time'
- '@helfi_api_base.pubsub_settings'

Drupal\helfi_api_base\Cache\CacheTagInvalidatorInterface: '@helfi_api_base.cache_tag_invalidator'
Drupal\helfi_api_base\Cache\CacheTagInvalidator: '@helfi_api_base.cache_tag_invalidator'
helfi_api_base.cache_tag_invalidator:
class: Drupal\helfi_api_base\Cache\CacheTagInvalidator
arguments:
- '@helfi_api_base.pubsub_manager'

Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber: '@helfi_api_base.cache_tag_invalidator_subscriber'
helfi_api_base.cache_tag_invalidator_subscriber:
class: Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber
arguments:
- '@cache_tags.invalidator'
- '@helfi_api_base.environment_resolver'
tags:
- { name: event_subscriber }
Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory: ~
Drupal\helfi_api_base\Azure\PubSub\Settings:
factory: ['Drupal\helfi_api_base\Azure\PubSub\SettingsFactory', 'create']
Drupal\helfi_api_base\Azure\PubSub\PubSubManager: ~
Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface: '@Drupal\helfi_api_base\Azure\PubSub\PubSubManager'
Drupal\helfi_api_base\Cache\CacheTagInvalidatorInterface: '@Drupal\helfi_api_base\Cache\CacheTagInvalidator'
Drupal\helfi_api_base\Cache\CacheTagInvalidator: ~

Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber: ~

Drupal\helfi_api_base\Entity\Revision\RevisionManager: '@helfi_api_base.revision_manager'
helfi_api_base.revision_manager:
Expand Down
13 changes: 13 additions & 0 deletions src/Azure/PubSub/AccessTokenType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Drupal\helfi_api_base\Azure\PubSub;

/**
* The access token type.
*/
enum AccessTokenType {
case Primary;
case Secondary;
}
33 changes: 25 additions & 8 deletions src/Azure/PubSub/PubSubClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,45 @@
final class PubSubClientFactory {

/**
* Constructs a new websocket client object.
* Constructs a new instance.
*
* @param \Drupal\helfi_api_base\Azure\PubSub\Settings $settings
* The settings.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time interface.
* @param \Drupal\helfi_api_base\Azure\PubSub\Settings $settings
* The PubSub settings.
*/
public function __construct(
private readonly TimeInterface $time,
private readonly Settings $settings,
) {
}

/**
* Constructs a new websocket client object.
*
* @param \Drupal\helfi_api_base\Azure\PubSub\AccessTokenType $type
* The client type.
*
* @return \WebSocket\Client
* The client.
*/
public function create(Settings $settings, TimeInterface $time) : Client {
$url = sprintf('wss://%s/client/hubs/%s', rtrim($settings->endpoint, '/'), $settings->hub);
public function create(AccessTokenType $type) : Client {
$url = sprintf('wss://%s/client/hubs/%s', rtrim($this->settings->endpoint, '/'), $this->settings->hub);

$accessKey = $this->settings->accessKey;

if ($type === AccessTokenType::Secondary) {
$accessKey = $this->settings->secondaryAccessKey;
}
$authorizationToken = JWT::encode([
'aud' => $url,
'iat' => $time->getCurrentTime(),
'exp' => $time->getCurrentTime() + 3600,
'iat' => $this->time->getCurrentTime(),
'exp' => $this->time->getCurrentTime() + 3600,
'role' => [
'webpubsub.sendToGroup',
'webpubsub.joinLeaveGroup',
],
], $settings->accessKey, 'HS256');
], $accessKey, 'HS256');

return new Client($url, [
'headers' => [
Expand Down
67 changes: 44 additions & 23 deletions src/Azure/PubSub/PubSubManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Drupal\Component\Datetime\TimeInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use WebSocket\Client;
use WebSocket\ConnectionException;

/**
Expand All @@ -24,8 +23,6 @@ final class PubSubManager implements PubSubManagerInterface {
/**
* Constructs a new instance.
*
* @param \WebSocket\Client $client
* The websocket client.
* @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The event dispatcher service.
* @param \Drupal\Component\Datetime\TimeInterface $time
Expand All @@ -34,13 +31,41 @@ final class PubSubManager implements PubSubManagerInterface {
* The PubSub settings.
*/
public function __construct(
private readonly Client $client,
private readonly PubSubClientFactory $clientFactory,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly TimeInterface $time,
private readonly Settings $settings,
) {
}

private function clientReceive() : string {
try {
return (string) $this->clientFactory
->create(AccessTokenType::Primary)
->receive();
}
catch (ConnectionException) {
}
return (string) $this->clientFactory
->create(AccessTokenType::Secondary)
->receive();
}

private function clientText(array $message) : void {
$message = $this->encodeMessage($message);

try {
$this->clientFactory
->create(AccessTokenType::Primary)
->text($message);
}
catch (ConnectionException) {
}
$this->clientFactory
->create(AccessTokenType::Secondary)
->text($message);
}

/**
* Joins a group.
*
Expand All @@ -52,16 +77,14 @@ private function joinGroup() : void {
if ($this->joinedGroup) {
return;
}
$this->client->text(
$this->encodeMessage([
'type' => 'joinGroup',
'group' => $this->settings->group,
])
);
$this->clientText([
'type' => 'joinGroup',
'group' => $this->settings->group,
]);

try {
// Wait until we've actually joined the group.
$message = $this->decodeMessage((string) $this->client->receive());
$message = $this->decodeMessage($this->clientReceive());

if (isset($message['event']) && $message['event'] === 'connected') {
$this->joinedGroup = TRUE;
Expand Down Expand Up @@ -135,17 +158,15 @@ public function sendMessage(array $message) : self {
$this->assertSettings();
$this->joinGroup();

$this->client
->text(
$this->encodeMessage([
'type' => 'sendToGroup',
'group' => $this->settings->group,
'dataType' => 'json',
'data' => $message + [
'timestamp' => $this->time->getCurrentTime(),
],
])
);
$this
->clientText([
'type' => 'sendToGroup',
'group' => $this->settings->group,
'dataType' => 'json',
'data' => $message + [
'timestamp' => $this->time->getCurrentTime(),
],
]);

return $this;
}
Expand All @@ -157,7 +178,7 @@ public function receive() : string {
$this->assertSettings();
$this->joinGroup();

$message = (string) $this->client->receive();
$message = $this->clientReceive();
$json = $this->decodeMessage($message);

$this->eventDispatcher
Expand Down
5 changes: 4 additions & 1 deletion src/Azure/PubSub/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ final class Settings {
* @param string $endpoint
* The API endpoint.
* @param string $accessKey
* The API access token.
* The API access key.
* @param string $secondaryAccessKey
* The secondary API access key.
*/
public function __construct(
public readonly string $hub,
public readonly string $group,
public readonly string $endpoint,
public readonly string $accessKey,
public readonly string $secondaryAccessKey,
) {
}

Expand Down
4 changes: 3 additions & 1 deletion src/Azure/PubSub/SettingsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function create() : Settings {
'group' => '',
'endpoint' => '',
'access_key' => '',
'secondary_access_key' => '',
];

if ($settings = $this->vaultManager->get('pubsub')) {
Expand All @@ -50,7 +51,8 @@ public function create() : Settings {
$data->hub ?: '',
$data->group ?: '',
$data->endpoint ?: '',
$data->access_key ?: ''
$data->access_key ?: '',
$data->secondary_access_key ?: '',
);
}

Expand Down
3 changes: 3 additions & 0 deletions src/Commands/PubSubCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Drupal\helfi_api_base\Commands;

use Drupal\Core\DependencyInjection\AutowireTrait;
use Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface;
use Drush\Attributes\Command;
use Drush\Commands\DrushCommands;
Expand All @@ -20,6 +21,8 @@
*/
final class PubSubCommands extends DrushCommands {

use AutowireTrait;

public const MAX_MESSAGES = 100;
public const CLIENT_TIMEOUT = 120;

Expand Down

0 comments on commit aad7a9b

Please sign in to comment.