Skip to content

Commit

Permalink
improve multi tenancy (#37)
Browse files Browse the repository at this point in the history
* improve multi tenancy

* move token to top level

* fix test cases

* fix psalm

* fix method name

* add framework config

* update config

* order scope by name

* update code style

* add alias
  • Loading branch information
chriskapp authored Mar 12, 2024
1 parent 702476e commit 96ee711
Show file tree
Hide file tree
Showing 180 changed files with 1,746 additions and 1,422 deletions.
14 changes: 7 additions & 7 deletions src/Authorization/Action/Revoke.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@
*/
class Revoke implements ActionInterface
{
private Service\App\Token $appTokenService;
private Table\App\Token $table;
private Service\Token $tokenService;
private Table\Token $table;

public function __construct(Service\App\Token $appTokenService, Table\App\Token $table)
public function __construct(Service\Token $tokenService, Table\Token $table)
{
$this->appTokenService = $appTokenService;
$this->tokenService = $tokenService;
$this->table = $table;
}

Expand All @@ -61,11 +61,11 @@ public function handle(RequestInterface $request, ParametersInterface $configura
throw new StatusCode\BadRequestException('No token provided');
}

$row = $this->table->getTokenByToken($context->getApp()->getId(), $token);
$row = $this->table->getTokenByToken($context->getTenantId(), $token);

// the token must be assigned to the user
if ($row instanceof Table\Generated\AppTokenRow && $row->getAppId() == $context->getApp()->getId() && $row->getUserId() == $context->getUser()->getId()) {
$this->appTokenService->removeToken($row->getAppId(), $row->getId(), UserContext::newActionContext($context));
if ($row instanceof Table\Generated\TokenRow && $row->getUserId() == $context->getUser()->getId()) {
$this->tokenService->removeToken($row->getId(), UserContext::newActionContext($context));

return [
'success' => true
Expand Down
34 changes: 12 additions & 22 deletions src/Authorization/GrantType/AuthorizationCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

use Fusio\Impl\Service;
use Fusio\Impl\Table;
use PSX\Framework\Config\ConfigInterface;
use PSX\Framework\OAuth2\Credentials;
use PSX\Framework\OAuth2\GrantType\AuthorizationCodeAbstract;
use PSX\OAuth2\Exception\InvalidClientException;
Expand All @@ -39,17 +38,17 @@
*/
class AuthorizationCode extends AuthorizationCodeAbstract
{
private Service\App\Token $appTokenService;
private Service\Token $tokenService;
private Service\Scope $scopeService;
private Service\System\FrameworkConfig $frameworkConfig;
private Table\App\Code $appCodeTable;
private ConfigInterface $config;

public function __construct(Service\App\Token $appTokenService, Service\Scope $scopeService, Table\App\Code $appCodeTable, ConfigInterface $config)
public function __construct(Service\Token $tokenService, Service\Scope $scopeService, Service\System\FrameworkConfig $frameworkConfig, Table\App\Code $appCodeTable)
{
$this->appTokenService = $appTokenService;
$this->scopeService = $scopeService;
$this->appCodeTable = $appCodeTable;
$this->config = $config;
$this->tokenService = $tokenService;
$this->scopeService = $scopeService;
$this->frameworkConfig = $frameworkConfig;
$this->appCodeTable = $appCodeTable;
}

protected function generate(Credentials $credentials, Grant\AuthorizationCode $grant)
Expand All @@ -59,7 +58,7 @@ protected function generate(Credentials $credentials, Grant\AuthorizationCode $g
$credentials->getClientSecret(),
$grant->getCode(),
$grant->getRedirectUri(),
$this->getTenantId(),
$this->frameworkConfig->getTenantId(),
);

if (empty($code)) {
Expand All @@ -72,28 +71,19 @@ protected function generate(Credentials $credentials, Grant\AuthorizationCode $g
}

// scopes
$scopes = $this->scopeService->getValidScopes($code['scope'], (int) $code['app_id'], (int) $code['user_id']);
$scopes = $this->scopeService->getValidScopes($this->frameworkConfig->getTenantId(), $code['scope'], (int) $code['app_id'], (int) $code['user_id']);
if (empty($scopes)) {
throw new InvalidScopeException('No valid scope given');
}

// generate access token
return $this->appTokenService->generateAccessToken(
return $this->tokenService->generateAccessToken(
$this->frameworkConfig->getTenantId(),
$code['app_id'],
$code['user_id'],
$scopes,
$_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
new \DateInterval($this->config->get('fusio_expire_token'))
$this->frameworkConfig->getExpireTokenInterval()
);
}

private function getTenantId(): ?string
{
$tenantId = $this->config->get('fusio_tenant_id');
if (empty($tenantId)) {
return null;
}

return $tenantId;
}
}
35 changes: 12 additions & 23 deletions src/Authorization/GrantType/ClientCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@

use Fusio\Impl\Service;
use Fusio\Impl\Table;
use PSX\Framework\Config\ConfigInterface;
use PSX\Framework\OAuth2\Credentials;
use PSX\Framework\OAuth2\GrantType\ClientCredentialsAbstract;
use PSX\OAuth2\AccessToken;
use PSX\OAuth2\Exception\InvalidClientException;
use PSX\OAuth2\Exception\InvalidScopeException;
use PSX\OAuth2\Grant;
use PSX\Sql\Condition;

/**
* ClientCredentials
Expand All @@ -41,24 +39,24 @@
class ClientCredentials extends ClientCredentialsAbstract
{
private Service\User\Authenticator $authenticatorService;
private Service\App\Token $appTokenService;
private Service\Token $tokenService;
private Service\Scope $scopeService;
private Service\System\FrameworkConfig $frameworkConfig;
private Table\App $appTable;
private ConfigInterface $config;

public function __construct(Service\User\Authenticator $authenticatorService, Service\App\Token $appTokenService, Service\Scope $scopeService, Table\App $appTable, ConfigInterface $config)
public function __construct(Service\User\Authenticator $authenticatorService, Service\Token $tokenService, Service\Scope $scopeService, Service\System\FrameworkConfig $frameworkConfig, Table\App $appTable)
{
$this->authenticatorService = $authenticatorService;
$this->appTokenService = $appTokenService;
$this->tokenService = $tokenService;
$this->scopeService = $scopeService;
$this->frameworkConfig = $frameworkConfig;
$this->appTable = $appTable;
$this->config = $config;
}

protected function generate(Credentials $credentials, Grant\ClientCredentials $grant): AccessToken
{
// check whether the credentials contain an app key and secret
$app = $this->appTable->findOneByAppKeyAndSecret($credentials->getClientId(), $credentials->getClientSecret(), $this->getTenantId());
$app = $this->appTable->findOneByAppKeyAndSecret($this->frameworkConfig->getTenantId(), $credentials->getClientId(), $credentials->getClientSecret());
if (!empty($app)) {
$appId = $app->getId();
$userId = $app->getUserId();
Expand All @@ -75,32 +73,23 @@ protected function generate(Credentials $credentials, Grant\ClientCredentials $g
$scope = $grant->getScope();
if (empty($scope)) {
// as fallback simply use all scopes assigned to the user
$scope = implode(',', $this->authenticatorService->getAvailableScopes($userId));
$scope = implode(',', $this->authenticatorService->getAvailableScopes($this->frameworkConfig->getTenantId(), $userId));
}

// validate scopes
$scopes = $this->scopeService->getValidScopes($scope, $appId, $userId);
$scopes = $this->scopeService->getValidScopes($this->frameworkConfig->getTenantId(), $scope, $appId, $userId);
if (empty($scopes)) {
throw new InvalidScopeException('No valid scope given');
}

// generate access token
return $this->appTokenService->generateAccessToken(
$appId === null ? 1 : $appId,
return $this->tokenService->generateAccessToken(
$this->frameworkConfig->getTenantId(),
$appId,
$userId,
$scopes,
$_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
new \DateInterval($this->config->get('fusio_expire_token'))
$this->frameworkConfig->getExpireTokenInterval()
);
}

private function getTenantId(): ?string
{
$tenantId = $this->config->get('fusio_tenant_id');
if (empty($tenantId)) {
return null;
}

return $tenantId;
}
}
33 changes: 11 additions & 22 deletions src/Authorization/GrantType/Password.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@

use Fusio\Impl\Service;
use Fusio\Impl\Table;
use PSX\Framework\Config\ConfigInterface;
use PSX\Framework\OAuth2\Credentials;
use PSX\Framework\OAuth2\GrantType\PasswordAbstract;
use PSX\OAuth2\AccessToken;
use PSX\OAuth2\Exception\InvalidClientException;
use PSX\OAuth2\Exception\InvalidGrantException;
use PSX\OAuth2\Exception\InvalidScopeException;
use PSX\OAuth2\Grant;
use PSX\Sql\Condition;

/**
* Password
Expand All @@ -42,23 +40,23 @@
class Password extends PasswordAbstract
{
private Service\User\Authenticator $authenticatorService;
private Service\App\Token $appTokenService;
private Service\Token $tokenService;
private Service\Scope $scopeService;
private Service\System\FrameworkConfig $frameworkConfig;
private Table\App $appTable;
private ConfigInterface $config;

public function __construct(Service\User\Authenticator $authenticatorService, Service\App\Token $appTokenService, Service\Scope $scopeService, Table\App $appTable, ConfigInterface $config)
public function __construct(Service\User\Authenticator $authenticatorService, Service\Token $tokenService, Service\Scope $scopeService, Service\System\FrameworkConfig $frameworkConfig, Table\App $appTable)
{
$this->authenticatorService = $authenticatorService;
$this->appTokenService = $appTokenService;
$this->tokenService = $tokenService;
$this->scopeService = $scopeService;
$this->frameworkConfig = $frameworkConfig;
$this->appTable = $appTable;
$this->config = $config;
}

protected function generate(Credentials $credentials, Grant\Password $grant): AccessToken
{
$app = $this->appTable->findOneByAppKeyAndSecret($credentials->getClientId(), $credentials->getClientSecret(), $this->getTenantId());
$app = $this->appTable->findOneByAppKeyAndSecret($this->frameworkConfig->getTenantId(), $credentials->getClientId(), $credentials->getClientSecret());
if (empty($app)) {
throw new InvalidClientException('Unknown credentials');
}
Expand All @@ -72,32 +70,23 @@ protected function generate(Credentials $credentials, Grant\Password $grant): Ac
$scope = $grant->getScope();
if (empty($scope)) {
// as fallback simply use all scopes assigned to the user
$scope = implode(',', $this->authenticatorService->getAvailableScopes($userId));
$scope = implode(',', $this->authenticatorService->getAvailableScopes($this->frameworkConfig->getTenantId(), $userId));
}

// validate scopes
$scopes = $this->scopeService->getValidScopes($scope, $app->getId(), $userId);
$scopes = $this->scopeService->getValidScopes($this->frameworkConfig->getTenantId(), $scope, $app->getId(), $userId);
if (empty($scopes)) {
throw new InvalidScopeException('No valid scope given');
}

// generate access token
return $this->appTokenService->generateAccessToken(
return $this->tokenService->generateAccessToken(
$this->frameworkConfig->getTenantId(),
$app->getId(),
$userId,
$scopes,
$_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
new \DateInterval($this->config->get('fusio_expire_token'))
$this->frameworkConfig->getExpireTokenInterval()
);
}

private function getTenantId(): ?string
{
$tenantId = $this->config->get('fusio_tenant_id');
if (empty($tenantId)) {
return null;
}

return $tenantId;
}
}
40 changes: 9 additions & 31 deletions src/Authorization/GrantType/RefreshToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,10 @@
namespace Fusio\Impl\Authorization\GrantType;

use Fusio\Impl\Service;
use Fusio\Impl\Table;
use PSX\Framework\Config\ConfigInterface;
use PSX\Framework\OAuth2\Credentials;
use PSX\Framework\OAuth2\GrantType\RefreshTokenAbstract;
use PSX\OAuth2\AccessToken;
use PSX\OAuth2\Exception\ServerErrorException;
use PSX\OAuth2\Grant;
use PSX\Sql\Condition;

/**
* RefreshToken
Expand All @@ -39,41 +35,23 @@
*/
class RefreshToken extends RefreshTokenAbstract
{
private Service\App\Token $appTokenService;
private Table\App $appTable;
private ConfigInterface $config;
private Service\Token $tokenService;
private Service\System\FrameworkConfig $frameworkConfig;

public function __construct(Service\App\Token $appTokenService, Table\App $appTable, ConfigInterface $config)
public function __construct(Service\Token $tokenService, Service\System\FrameworkConfig $frameworkConfig)
{
$this->appTokenService = $appTokenService;
$this->appTable = $appTable;
$this->config = $config;
$this->tokenService = $tokenService;
$this->frameworkConfig = $frameworkConfig;
}

protected function generate(Credentials $credentials, Grant\RefreshToken $grant): AccessToken
{
$app = $this->appTable->findOneByAppKeyAndSecret($credentials->getClientId(), $credentials->getClientSecret(), $this->getTenantId());
if (empty($app)) {
throw new ServerErrorException('Unknown credentials');
}

// refresh access token
return $this->appTokenService->refreshAccessToken(
$app->getId(),
return $this->tokenService->refreshAccessToken(
$this->frameworkConfig->getTenantId(),
$grant->getRefreshToken(),
$_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
new \DateInterval($this->config->get('fusio_expire_token')),
new \DateInterval($this->config->get('fusio_expire_refresh') ?? 'P3D')
$this->frameworkConfig->getExpireTokenInterval(),
$this->frameworkConfig->getExpireRefreshInterval()
);
}

private function getTenantId(): ?string
{
$tenantId = $this->config->get('fusio_tenant_id');
if (empty($tenantId)) {
return null;
}

return $tenantId;
}
}
15 changes: 10 additions & 5 deletions src/Authorization/UserContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
class UserContext
{
private int $userId;
private int $appId;
private ?int $appId;
private string $ip;
private ?string $tenantId;

public function __construct(int $userId, int $appId, string $ip, ?string $tenantId = null)
public function __construct(int $userId, ?int $appId, string $ip, ?string $tenantId = null)
{
$this->userId = $userId;
$this->appId = $appId;
Expand All @@ -49,7 +49,7 @@ public function getUserId(): int
return $this->userId;
}

public function getAppId(): int
public function getAppId(): ?int
{
return $this->appId;
}
Expand All @@ -66,7 +66,7 @@ public function getTenantId(): ?string

public static function newContext(int $userId, ?int $appId = null, ?string $tenantId = null): self
{
return new UserContext($userId, $appId ?? 1, $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1', $tenantId);
return new UserContext($userId, $appId, $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1', $tenantId);
}

public static function newAnonymousContext(): self
Expand All @@ -81,6 +81,11 @@ public static function newCommandContext(): self

public static function newActionContext(ContextInterface $context): self
{
return self::newContext($context->getUser()->getId(), $context->getApp()->getId(), $context->getTenantId());
$appId = null;
if (!$context->getApp()->isAnonymous()) {
$appId = $context->getApp()->getId();
}

return self::newContext($context->getUser()->getId(), $appId, $context->getTenantId());
}
}
Loading

0 comments on commit 96ee711

Please sign in to comment.