Skip to content

Commit

Permalink
feat: add WAMP-CRA
Browse files Browse the repository at this point in the history
  • Loading branch information
cydrickn committed Jul 19, 2024
1 parent f6068b1 commit 743fea4
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 25 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ That will now run the server
|------------|---------|---------|
| Anonymous | ✓ | ✓ |
| Ticket | ✓ | ✓ |
| Wamp-CRA | ✗ | ✗ |
| Wamp-CRA | ✓ | ✓ |
| Wamp-SCRA | ✗ | ✗ |
| Cryptosign | ✗ | ✗ |
| TLS | ✗ | ✗ |
Expand Down
13 changes: 11 additions & 2 deletions bin/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,18 @@
'realms' => ['realm1']
],
[
'method' => 'anonymous',
'method' => 'wampcra',
'type' => 'static',
'role' => 'auth'
'users' => [
[
'authid' => 'auth',
'secret' => 'qa2/QVmmjSx1JJuyH5EI2gMDQf+ARnfwMcLOpUfln74=',
'role' => 'auth',
'salt' => 'salt1',
'keylen' => 32,
'iterations' => 1000
],
],
]
],
);
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"symfony/event-dispatcher": "^2.8",
"octamp/client": "^1.3",
"symfony/dotenv": "^7.1",
"rybakit/msgpack": "^0.9.1"
"rybakit/msgpack": "^0.9.1",
"ext-openssl": "*"
},
"require-dev": {
"openswoole/ide-helper": "^22.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<?php

namespace Octamp\Wamp\Auth;
namespace Octamp\Wamp\Auth\Anonymous;

use Octamp\Wamp\Auth\AbstractDynamicAuthenticator;
use Octamp\Wamp\Auth\Response\AuthErrorResponse;
use Octamp\Wamp\Auth\Response\AuthSuccessResponse;
use Octamp\Wamp\Auth\Response\HelloErrorResponse;
use Octamp\Wamp\Auth\Response\HelloSuccessResponse;
use Octamp\Wamp\Promise\Promise;
use Octamp\Wamp\Promise\PromiseInterrupted;
use Octamp\Wamp\Promise\PromiseErrorException;
use Octamp\Wamp\Promise\PromiseInterface;
use Octamp\Wamp\Promise\PromiseInterrupted;
use Octamp\Wamp\Realm\RealmManager;
use Octamp\Wamp\Session\Session;
use Thruway\Message\AuthenticateMessage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

declare(strict_types=1);

namespace Octamp\Wamp\Auth;
namespace Octamp\Wamp\Auth\Anonymous;

use Octamp\Wamp\Auth\AbstractAuthenticator;
use Octamp\Wamp\Auth\Response\AuthErrorResponse;
use Octamp\Wamp\Auth\Response\AuthSuccessResponse;
use Octamp\Wamp\Auth\Response\HelloErrorResponse;
use Octamp\Wamp\Auth\Response\HelloSuccessResponse;
use Octamp\Wamp\Promise\Promise;
use Octamp\Wamp\Promise\PromiseInterface;
use Octamp\Wamp\Session\Session;
use Thruway\Message\AuthenticateMessage;
use Thruway\Message\HelloMessage;
Expand Down
34 changes: 29 additions & 5 deletions src/Auth/AuthManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

namespace Octamp\Wamp\Auth;

use Octamp\Wamp\Auth\Anonymous\AnonymousDynamicAuthenticator;
use Octamp\Wamp\Auth\Anonymous\AnonymousStaticAuthenticator;
use Octamp\Wamp\Auth\Response\HelloErrorResponse;
use Octamp\Wamp\Auth\Response\HelloSuccessResponse;
use Octamp\Wamp\Promise\PromiseErrorException;
use Octamp\Wamp\Auth\Ticket\TicketDynamicAuthenticator;
use Octamp\Wamp\Auth\Ticket\TicketStaticAuthenticator;
use Octamp\Wamp\Auth\WampCra\WampCraDynamicAuthenticator;
use Octamp\Wamp\Auth\WampCra\WampCraStaticAuthenticator;
use Octamp\Wamp\Realm\RealmManager;
use Octamp\Wamp\Session\Session;
use OpenSwoole\Coroutine;
Expand All @@ -18,6 +23,21 @@

class AuthManager implements WithRealmManagerInterface
{
protected static array $authenticatorClasses = [
'anonymous' => [
'static' => AnonymousStaticAuthenticator::class,
'dynamic' => AnonymousDynamicAuthenticator::class,
],
'ticket' => [
'static' => TicketStaticAuthenticator::class,
'dynamic' => TicketDynamicAuthenticator::class,
],
'wampcra' => [
'static' => WampCraStaticAuthenticator::class,
'dynamic' => WampCraDynamicAuthenticator::class,
],
];

public const STATUS_CHALLENGE = 1;
public const STATUS_NO_CHALLENGE = 2;
public const STATUS_SUCCESS = 3;
Expand Down Expand Up @@ -56,12 +76,13 @@ public function addAuthenticator(AuthenticatorInterface $authenticator): void

public function generateAuthenticator(array $data): ?AuthenticatorInterface
{
$class = '\\Octamp\\Wamp\\Auth\\' . ucwords($data['method']) . ucwords($data['type']) . 'Authenticator';
if (!class_exists($class)) {
$className = self::$authenticatorClasses[$data['method']][$data['type']];

if (!class_exists($className)) {
throw new \Exception($data['method'] . ' with type "' . $data['type'] . '" is not known authenticator');
}

return new $class($data);
return new $className($data);
}

public function processHelloMessage(Session $session, HelloMessage $message): void
Expand Down Expand Up @@ -114,12 +135,15 @@ public function processHelloMessage(Session $session, HelloMessage $message): vo
if (isset($authDetailsRaw->authextra)) {
$authDetails->setAuthExtra($authDetailsRaw->authextra);
}
if (isset($authDetailsRaw->authprovider)) {
$authDetails->setAuthProvider($authDetailsRaw->authprovider);
}
$session->setAuthenticationDetails($authDetails);

if ($status === self::STATUS_CHALLENGE) {
$challengeDetails = $res->challengeDetails;
$authMethod = $res->challengeMethod;
$challenge = $challengeDetails?->challenge ?? [];
$challenge = $challengeDetails?->challenge ?? '{}';

$session->getAuthenticationDetails()->setChallengeDetails($challengeDetails ?: []);
$session->getAuthenticationDetails()->setChallenge($challenge);
Expand Down
2 changes: 1 addition & 1 deletion src/Auth/AuthenticationDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function setChallenge(mixed $challenge): void
$this->challenge = $challenge;
}

public function getChallenge(): ?array
public function getChallenge(): mixed
{
return $this->challenge;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

declare(strict_types=1);

namespace Octamp\Wamp\Auth;
namespace Octamp\Wamp\Auth\Ticket;

use Octamp\Wamp\Auth\AbstractDynamicAuthenticator;
use Octamp\Wamp\Auth\Response\AuthErrorResponse;
use Octamp\Wamp\Auth\Response\AuthSuccessResponse;
use Octamp\Wamp\Auth\Response\HelloErrorResponse;
use Octamp\Wamp\Auth\Response\HelloSuccessResponse;
use Octamp\Wamp\Promise\PromiseInterrupted;
use Octamp\Wamp\Promise\PromiseErrorException;
use Octamp\Wamp\Promise\PromiseInterrupted;
use Octamp\Wamp\Session\Session;
use Thruway\Message\AuthenticateMessage;
use Thruway\Message\HelloMessage;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<?php

namespace Octamp\Wamp\Auth;
namespace Octamp\Wamp\Auth\Ticket;

use Octamp\Wamp\Auth\AbstractAuthenticator;
use Octamp\Wamp\Auth\Response\AuthErrorResponse;
use Octamp\Wamp\Auth\Response\AuthSuccessResponse;
use Octamp\Wamp\Auth\Response\HelloErrorResponse;
use Octamp\Wamp\Auth\Response\HelloSuccessResponse;
use Octamp\Wamp\Promise\Promise;
use Octamp\Wamp\Promise\PromiseInterface;
use Octamp\Wamp\Session\Session;
use OpenSwoole\Table;
use Thruway\Message\AuthenticateMessage;
use Thruway\Message\ChallengeMessage;
use Thruway\Message\HelloMessage;

class TicketStaticAuthenticator extends AbstractAuthenticator
Expand All @@ -20,8 +18,6 @@ class TicketStaticAuthenticator extends AbstractAuthenticator

protected function init(): void
{
parent::__construct($this->config);

$principals = $this->config['principals'] ?? [];

$maxAuthIdLen = max(array_column($principals, 'authid'));
Expand Down
104 changes: 104 additions & 0 deletions src/Auth/WampCra/WampCraDynamicAuthenticator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace Octamp\Wamp\Auth\WampCra;

use DateTimeInterface;
use Octamp\Wamp\Auth\AbstractDynamicAuthenticator;
use Octamp\Wamp\Auth\Response\AuthErrorResponse;
use Octamp\Wamp\Auth\Response\AuthSuccessResponse;
use Octamp\Wamp\Auth\Response\HelloErrorResponse;
use Octamp\Wamp\Auth\Response\HelloSuccessResponse;
use Octamp\Wamp\Promise\PromiseErrorException;
use Octamp\Wamp\Promise\PromiseInterrupted;
use Octamp\Wamp\Session\Session;
use Thruway\Message\AuthenticateMessage;
use Thruway\Message\HelloMessage;

class WampCraDynamicAuthenticator extends AbstractDynamicAuthenticator
{
public function processHello(Session $session, HelloMessage $message): HelloSuccessResponse|HelloErrorResponse
{
$helloDetails = $message->getDetails();
$authId = $helloDetails->authid ?? null;

if ($authId === null) {
return $this->generateFailureResponse('wamp.error.authentication_required', ['message' => 'authid required']);
}

try {
$result = $this->sendMessageToAuthenticator($message, ['authid' => $authId])->wait();
$authDetails = [];
if (isset($result->authid)) {
$authDetails['authid'] = $result->authid;
} else {
$authDetails['authid'] = $authId;
}

$challengeDetails = [
'challenge' => json_encode([
'authid' => $authId,
'authrole' => $result->role ?? '',
'authprovider' => $result->authprovider ?? 'dynamic',
'authmethod' => $this->getMethod(),
'nonce' => bin2hex(random_bytes(16)),
'timestamp' => (new \DateTime())->format(DateTimeInterface::ATOM),
'sesssion' => $session->getSessionId(),
]),
];

if (isset($result->salt)) {
$challengeDetails['salt'] = $result->salt;
$challengeDetails['keylen'] = $result->keylen ?? 32;
$challengeDetails['iterations'] = $result->iteration ?? 1000;
}

return $this->generateChallengeResponse($authDetails, $result, $challengeDetails);
} catch (PromiseErrorException $exception) {
if ($exception->getData() instanceof PromiseInterrupted) {
return $this->generateFailureResponse('wamp.error.unknown', []);
}
return $this->generateFailureResponse($exception->getData()['error_uri'] ?? '', $exception->getData()['error_details'] ?? []);
}
}

public function processAuthenticate(Session $session, AuthenticateMessage $message): AuthSuccessResponse|AuthErrorResponse
{
$challenge = $session->getAuthenticationDetails()->getChallenge();
if ($challenge === null) {
return $this->generatedErrorResponse('wamp.error.authentication_failed', []);
}
$authId = $session->getAuthenticationDetails()->getAuthId();
if ($authId === null) {
return $this->generatedErrorResponse('wamp.error.authentication_required', ['message' => 'authid required']);
}
$verificationDetails = $session->getAuthenticationDetails()->getVerificationDetails();
$signature = $message->getSignature();
$secret = $verificationDetails->secret;
$token = base64_encode(hash_hmac('sha256', $challenge, $secret, true));
if ($token !== $signature) {
return $this->generatedErrorResponse('wamp.error.authentication_denied', ['message' => 'Invalid signature']);
}

$authDetails = [
'authid' => $verificationDetails->authid,
'authprovider' => $verificationDetails->authprovider ?? 'dynamic',
];

if (isset($verificationDetails->role)) {
$authDetails['authrole'] = $verificationDetails->role;
}
if (isset($verificationDetails->extra)) {
$authDetails['authextra'] = $verificationDetails->extra;
}
if (isset($verificationDetails->roles)) {
$authDetails['authroles'] = $verificationDetails->roles;
}

return $this->generateSuccessResponse($authDetails);
}

public function getMethod(): string
{
return 'wampcra';
}
}
Loading

0 comments on commit 743fea4

Please sign in to comment.