Skip to content

Commit

Permalink
Merge pull request #30 from corbado/feat/enhance-identifier-service
Browse files Browse the repository at this point in the history
Feature: Enhance identifierService
  • Loading branch information
corbadoman authored Sep 24, 2024
2 parents 8a45a87 + d3e08aa commit 1427140
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: tests

on:
push:
branches: '*'
# Matches all branches including / in name
branches: '**'

concurrency:
group: ${{ github.ref }}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The [Corbado](https://www.corbado.com) PHP SDK provides convenient access to the

### Requirements

- PHP 7.2 or later
- PHP 8.0 or later
- [Composer](https://getcomposer.org/)

### Installation
Expand Down
15 changes: 15 additions & 0 deletions src/Helper/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,19 @@ public static function arrayKeysExist(array $data, array $keys): void
}
}
}

/**
* Checks if given string exist in given string array
*
* @param array<string> $array
* @param string $string
* @return void
* @throws AssertException
*/
public static function arrayStringExist(array $array, string $string): void
{
if (!in_array($string, $array)) {
throw new AssertException('Assert failed: Given array has no string "' . $string . '"');
}
}
}
41 changes: 41 additions & 0 deletions src/Services/IdentifierInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,57 @@
use Corbado\Generated\Model\Identifier;
use Corbado\Generated\Model\IdentifierCreateReq;
use Corbado\Generated\Model\IdentifierList;
use Corbado\Generated\Model\IdentifierStatus;
use Corbado\Generated\Model\IdentifierType;
use Corbado\Generated\Model\IdentifierUpdateReq;

interface IdentifierInterface
{
public function create(string $userID, IdentifierCreateReq $req): Identifier;

public function delete(string $userID, string $identifierID): void;

public function update(string $userID, string $identifierID, IdentifierUpdateReq $req): Identifier;

/**
* @param string $userID
* @param string $identifierID
* @param string $status
* @return Identifier
*/
public function updateStatus(string $userID, string $identifierID, string $status): Identifier;

/**
* @param array<string> $filter
*/
public function list(string $sort = '', array $filter = [], int $page = 1, int $pageSize = 10): IdentifierList;

/**
* @param string $value
* @param string $type
* @param string $sort
* @param int $page
* @param int $pageSize
* @return IdentifierList
*/
public function listByValueAndType(string $value, string $type, string $sort = '', int $page = 1, int $pageSize = 10): IdentifierList;

/**
* @param string $userID
* @param string $sort
* @param int $page
* @param int $pageSize
* @return IdentifierList
*/
public function listByUserID(string $userID, string $sort = '', int $page = 1, int $pageSize = 10): IdentifierList;

/**
* @param string $userID
* @param string $type
* @param string $sort
* @param int $page
* @param int $pageSize
* @return IdentifierList
*/
public function listByUserIDAndType(string $userID, string $type, string $sort = '', int $page = 1, int $pageSize = 10): IdentifierList;
}
78 changes: 78 additions & 0 deletions src/Services/IdentifierService.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use Corbado\Generated\Model\ErrorRsp;
use Corbado\Generated\Model\Identifier;
use Corbado\Generated\Model\IdentifierCreateReq;
use Corbado\Generated\Model\IdentifierStatus;
use Corbado\Generated\Model\IdentifierType;
use Corbado\Generated\Model\IdentifierUpdateReq;
use Corbado\Generated\Model\IdentifierList;
use Corbado\Helper\Assert;
Expand Down Expand Up @@ -96,6 +98,25 @@ public function update(string $userID, string $identifierID, IdentifierUpdateReq
return $identifier;
}

/**
* @throws AssertException
* @throws StandardException
* @throws ServerException
*/
public function updateStatus(string $userID, string $identifierID, string $status): Identifier
{
Assert::stringNotEmpty($userID);
Assert::stringNotEmpty($userID);
Assert::notNull($status);
Assert::arrayStringExist(IdentifierStatus::getAllowableEnumValues(), $status);

$req = new IdentifierUpdateReq();
// @phpstan-ignore-next-line
$req->setStatus($status);

return $this->update($userID, $identifierID, $req);
}

/**
* @param array<string> $filter
* @throws ServerException
Expand All @@ -115,4 +136,61 @@ public function list(string $sort = '', array $filter = [], int $page = 1, int $

return $rsp;
}

/**
* @throws AssertException
* @throws ServerException
* @throws StandardException
*/
public function listByValueAndType(string $value, string $type, string $sort = '', int $page = 1, int $pageSize = 10): IdentifierList
{
Assert::stringNotEmpty($value);
Assert::arrayStringExist(IdentifierType::getAllowableEnumValues(), $type);
Assert::notNull($type);


$filter = ["identifierValue:eq:" . $value, "identifierType:eq:" . $type];

return $this->list($sort, $filter, $page, $pageSize);
}

/**
* @throws ServerException
* @throws AssertException
* @throws StandardException
*/
public function listByUserID(string $userID, string $sort = '', int $page = 1, int $pageSize = 10): IdentifierList
{
Assert::stringNotEmpty($userID);

$prefix = "usr-";
if (str_starts_with($userID, $prefix)) {
$userID = substr($userID, strlen($prefix));
}

$filter = ["userID:eq:" . $userID];

return $this->list($sort, $filter, $page, $pageSize);
}

/**
* @throws AssertException
* @throws ServerException
* @throws StandardException
*/
public function listByUserIDAndType(string $userID, string $type, string $sort = '', int $page = 1, int $pageSize = 10): IdentifierList
{
Assert::stringNotEmpty($userID);
Assert::arrayStringExist(IdentifierType::getAllowableEnumValues(), $type);
Assert::notNull($userID);

$prefix = "usr-";
if (str_starts_with($userID, $prefix)) {
$userID = substr($userID, strlen($prefix));
}

$filter = ["userID:eq:" . $userID, "identifierType:eq:" . $type];

return $this->list($sort, $filter, $page, $pageSize);
}
}
29 changes: 29 additions & 0 deletions tests/integration/Identifier/IdentifierCompleteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ public function testIdentifierComplete(): void
$this->assertEquals(400, $exception->getHttpStatusCode());
$this->assertEqualsCanonicalizing(['identifierID: does not exist'], $exception->getValidationMessages());

// Test updating Status of a non-existent identifier
try {
$status = IdentifierStatus::PENDING;

Utils::SDK()->identifiers()->updateStatus($existingUserID, 'ide-123456789', $status);
} catch (ServerException $e) {
$exception = $e;
}

$this->assertNotNull($exception);
$this->assertEquals(400, $exception->getHttpStatusCode());
$this->assertEqualsCanonicalizing(['identifierID: does not exist'], $exception->getValidationMessages());

// Test creating an identifier with invalid data
try {
$req = new IdentifierCreateReq();
Expand Down Expand Up @@ -95,6 +108,18 @@ public function testIdentifierComplete(): void
$rsp = Utils::SDK()->identifiers()->list('', ['identifierType:eq:email', 'identifierValue:eq:' . $existingIdentifierValue]);
$this->assertEquals(1, $rsp->getPaging()->getTotalItems());

// Test listing identifiers by Value and Type with data returned
$rsp = Utils::SDK()->identifiers()->listByValueAndType($existingIdentifierValue, IdentifierType::EMAIL);
$this->assertEquals(1, $rsp->getPaging()->getTotalItems());

// Test listing identifiers by UserId with data returned
$rsp = Utils::SDK()->identifiers()->listByUserID($existingUserID);
$this->assertEquals(1, $rsp->getPaging()->getTotalItems());

// Test listing identifiers by UserId and Type with data returned
$rsp = Utils::SDK()->identifiers()->listByUserIDAndType($existingUserID, IdentifierType::EMAIL);
$this->assertEquals(1, $rsp->getPaging()->getTotalItems());

// Test updating an existing identifier with valid data
$req = new IdentifierUpdateReq();
// @phpstan-ignore-next-line
Expand All @@ -103,6 +128,10 @@ public function testIdentifierComplete(): void
$identifier = Utils::SDK()->identifiers()->update($existingUserID, $existingIdentifierID, $req);
$this->assertEquals(IdentifierStatus::VERIFIED, $identifier->getStatus());

// Test updating identifier status
$identifier = Utils::SDK()->identifiers()->updateStatus($existingUserID, $existingIdentifierID, IdentifierStatus::PENDING);
$this->assertEquals(IdentifierStatus::PENDING, $identifier->getStatus());

// Test deleting an existing identifier
Utils::SDK()->identifiers()->delete($existingUserID, $existingIdentifierID);
}
Expand Down
43 changes: 29 additions & 14 deletions tests/unit/Services/SessionServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Corbado\Exceptions\ValidationException;
use Corbado\Exceptions\AssertException;
use Corbado\Services\SessionService;
use DateInterval;
use DateTimeInterface;
use Exception;
use Firebase\JWT\JWT;
use GuzzleHttp\Client;
Expand All @@ -14,6 +16,7 @@
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Throwable;

class SessionServiceTest extends TestCase
{
Expand All @@ -29,7 +32,7 @@ public function testValidateToken(string $shortSession, bool $success, int $code
try {
$session = self::createSession();
$user = $session->validateToken($shortSession);
} catch (\Throwable $e) {
} catch (Throwable $e) {
$exception = $e;
}

Expand All @@ -43,11 +46,22 @@ public function testValidateToken(string $shortSession, bool $success, int $code
}

/**
* @throws Exception
* @return array<int, array<int, string|bool|int>>
* @throws Exception
*/
public static function provideJWTs(): array
{

$privateKey = file_get_contents(dirname(__FILE__) . '/testdata/validPrivateKey.pem');
if ($privateKey === false) {
throw new Exception('file_get_contents() failed');
}

$invalidPrivateKey = file_get_contents(dirname(__FILE__) . '/testdata/invalidPrivateKey.pem');
if ($invalidPrivateKey === false) {
throw new Exception('file_get_contents() failed');
}

return [
[
// JWT with invalid format
Expand All @@ -61,27 +75,33 @@ public static function provideJWTs(): array
false,
ValidationException::CODE_JWT_INVALID_SIGNATURE
],
[
// JWT with invalid private key signed
self::generateJWT('https://auth.acme.com', time() + 100, time() + 100, $invalidPrivateKey),
false,
ValidationException::CODE_JWT_INVALID_SIGNATURE
],
[
// Not before (nfb) in future
self::generateJWT('https://auth.acme.com', time() + 100, time() + 100),
self::generateJWT('https://auth.acme.com', time() + 100, time() + 100, $privateKey),
false,
ValidationException::CODE_JWT_BEFORE
],
[
// Expired (exp)
self::generateJWT('https://auth.acme.com', time() - 100, time() - 100),
self::generateJWT('https://auth.acme.com', time() - 100, time() - 100, $privateKey),
false,
ValidationException::CODE_JWT_EXPIRED
],
[
// Invalid issuer (iss)
self::generateJWT('https://invalid.com', time() + 100, time() - 100),
self::generateJWT('https://invalid.com', time() + 100, time() - 100, $privateKey),
false,
ValidationException::CODE_JWT_ISSUER_MISMATCH
],
[
// Success
self::generateJWT('https://auth.acme.com', time() + 100, time() - 100),
self::generateJWT('https://auth.acme.com', time() + 100, time() - 100, $privateKey),
true,
0
]
Expand Down Expand Up @@ -154,12 +174,12 @@ public function set(mixed $value): static
return $this;
}

public function expiresAt(?\DateTimeInterface $expiration): static
public function expiresAt(?DateTimeInterface $expiration): static
{
return $this;
}

public function expiresAfter(\DateInterval|int|null $time): static
public function expiresAfter(DateInterval|int|null $time): static
{
return $this;
}
Expand Down Expand Up @@ -234,7 +254,7 @@ public function commit(): bool
/**
* @throws Exception
*/
private static function generateJWT(string $iss, int $exp, int $nbf): string
private static function generateJWT(string $iss, int $exp, int $nbf, string $privateKey): string
{
$payload = [
'iss' => $iss,
Expand All @@ -248,11 +268,6 @@ private static function generateJWT(string $iss, int $exp, int $nbf): string
'orig' => 'orig',
];

$privateKey = file_get_contents(dirname(__FILE__) . '/testdata/privateKey.pem');
if ($privateKey === false) {
throw new Exception('file_get_contents() failed');
}

return JWT::encode($payload, $privateKey, 'RS256', 'kid123');
}
}
Loading

0 comments on commit 1427140

Please sign in to comment.