-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Creating a configuration template for extensions and corresponding cl…
…asses for config loading and extension management.
- Loading branch information
1 parent
45fb957
commit 3c490a5
Showing
8 changed files
with
248 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
namespace App\Exceptions; | ||
|
||
use Exception; | ||
use Nette\Http\IResponse; | ||
|
||
/** | ||
* Exception concerning core module confugration. | ||
*/ | ||
class ConfigException extends ApiException | ||
{ | ||
/** | ||
* Creates instance with further description. | ||
* @param string $msg description | ||
* @param Exception|null $previous | ||
* @param string $frontendErrorCode | ||
* @param array|null $frontendErrorParams | ||
*/ | ||
public function __construct( | ||
$msg, | ||
$previous = null, | ||
string $frontendErrorCode = FrontendErrorMappings::E500_000__INTERNAL_SERVER_ERROR, | ||
$frontendErrorParams = null | ||
) { | ||
parent::__construct( | ||
"Internal configuration error - $msg", | ||
IResponse::S500_InternalServerError, | ||
$frontendErrorCode, | ||
$frontendErrorParams, | ||
$previous | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
<?php | ||
|
||
namespace App\Helpers; | ||
|
||
use App\Model\Entity\Instance; | ||
use App\Model\Entity\User; | ||
use App\Exceptions\ConfigException; | ||
use Nette; | ||
use Nette\Utils\Arrays; | ||
|
||
/** | ||
* This holds a configuration and help handle tokens for a single extension. | ||
*/ | ||
class ExtensionConfig | ||
{ | ||
use Nette\SmartObject; | ||
|
||
/** | ||
* Internal identifier. | ||
*/ | ||
private string $id; | ||
|
||
/** | ||
* Caption as a string or localized strings (array locale => caption). | ||
* @var string|string[] | ||
*/ | ||
private string|array $caption; | ||
|
||
/** | ||
* URL template for the external service. The template may hold the following placeholders: | ||
* - {token} - will be replaced with URL-encoded temporary token | ||
* - {locale} - will be replaced with a language identifier ('en', 'cs', ...) based on currently selected language | ||
*/ | ||
private string $url; | ||
|
||
/** | ||
* A scope that will be set to (full) access tokens generated after tmp-token verification. | ||
*/ | ||
private string $tokenScope; | ||
Check failure on line 39 in app/helpers/Extensions/ExtensionConfig.php GitHub Actions / phpstan (8.2)
|
||
|
||
/** | ||
* User override for (full) access tokens. This user will be used instead of user ID passed in tmp token. | ||
* This is a way how to safely provide more powerful full tokens (without compromising tmp tokens). | ||
* If null, the (logged in) user from tmp token is passed to the full token. | ||
*/ | ||
private string|null $tokenUserId = null; | ||
Check failure on line 46 in app/helpers/Extensions/ExtensionConfig.php GitHub Actions / phpstan (8.2)
|
||
|
||
/** | ||
* List of instances in which the extension should appear. | ||
* Empty list = all instances. | ||
* @var string[] | ||
*/ | ||
private array $instances = []; | ||
|
||
/** | ||
* List of user roles for which this extensions should appear. | ||
* Empty list = all roles. | ||
* @var string[] | ||
*/ | ||
private array $userRoles = []; | ||
|
||
/** | ||
* List of eligible user external login types. A user must hava at least one of these logins to see the extension. | ||
* Empty list = no external logins are required. | ||
*/ | ||
private array $userExternalLogins = []; | ||
|
||
public function __construct(array $config) | ||
{ | ||
$this->id = (string)Arrays::get($config, "id"); | ||
|
||
$this->caption = Arrays::get($config, "caption"); | ||
if (is_array($this->caption)) { | ||
foreach ($this->caption as $locale => $caption) { | ||
if (!is_string($locale) || !is_string($caption)) { | ||
throw new ConfigException("Invalid extension caption format."); | ||
} | ||
} | ||
} | ||
|
||
$this->url = Arrays::get($config, "url"); | ||
$this->tokenScope = Arrays::get($config, ["token", "scope"], "master"); | ||
$this->tokenUserId = Arrays::get($config, ["token", "user"], null); | ||
$this->instances = Arrays::get($config, "instances", []); | ||
$this->userRoles = Arrays::get($config, ["user", "roles"], []); | ||
$this->userExternalLogins = Arrays::get($config, ["user", "externalLogins"], []); | ||
} | ||
|
||
public function getId(): string | ||
{ | ||
return $this->id; | ||
} | ||
|
||
public function getCaption(): string|array | ||
{ | ||
return $this->caption; | ||
} | ||
|
||
/** | ||
* Get formatted URL. A template is injected a token and current locale. | ||
* @param string $token already serialized JWT | ||
* @param string $locale language identification ('en', 'cs', ...) | ||
* @return string an instantiated URL template | ||
*/ | ||
public function getUrl(string $token, string $locale): string | ||
{ | ||
$url = $this->url; | ||
$url = str_replace('{token}', urlencode($token), $url); | ||
$url = str_replace('{locale}', urlencode($locale), $url); | ||
return $url; | ||
} | ||
|
||
/** | ||
* Check whether this extension is accessible by given user in given instance. | ||
* @param Instance $instance | ||
* @param User $user | ||
* @return bool true if the extension is accessible | ||
*/ | ||
public function isAccessible(Instance $instance, User $user): bool | ||
{ | ||
if ($this->instances && !in_array($instance->getId(), $this->instances)) { | ||
return false; | ||
} | ||
|
||
if ($this->userRoles && !in_array($user->getRole(), $this->userRoles)) { | ||
return false; | ||
} | ||
|
||
if ($this->userExternalLogins) { | ||
$logins = $user->getConsolidatedExternalLogins(); | ||
foreach ($this->userExternalLogins as $service) { | ||
if (array_key_exists($service, $logins)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
namespace App\Helpers; | ||
|
||
use App\Model\Entity\Instance; | ||
use App\Model\Entity\User; | ||
use Nette; | ||
|
||
/** | ||
* Configuration and related management of ReCodEx extensions. An extension is a 3rd party webapp that can be used | ||
* to cooperate with ReCodEx (e.g., for user-membership management based on external university system). | ||
* An extension has a URL which is injected with tmp token (holding the ID of currently logged user). | ||
* The tmp token can be used by the extension to fetch a full token which can be used to access the API on behalf | ||
* of the logged in user. | ||
*/ | ||
class Extensions | ||
{ | ||
use Nette\SmartObject; | ||
|
||
protected array $extensions = []; | ||
|
||
public function __construct(array $extensions) | ||
{ | ||
foreach ($extensions as $config) { | ||
$extension = new ExtensionConfig($config); | ||
$this->extensions[$extension->getId()] = $extension; | ||
} | ||
} | ||
|
||
/** | ||
* Retrieve the extension by its ID. | ||
* @param string $id | ||
* @return ExtensionConfig|null null is returned if no such extension exists | ||
*/ | ||
public function getExtension(string $id): ?ExtensionConfig | ||
{ | ||
return $this->extensions[$id] ?? null; | ||
} | ||
|
||
/** | ||
* Filter out extensions that are accessible by given user in given instance. | ||
* @param Instance $instance | ||
* @param User $user | ||
* @return ExtensionConfig[] array indexed by extension IDs | ||
*/ | ||
public function getAccessibleExtensions(Instance $instance, User $user): array | ||
{ | ||
$res = []; | ||
foreach ($this->extensions as $id => $extension) { | ||
if ($extension->isAccessible($instance, $user)) { | ||
$res[$id] = $extension; | ||
} | ||
} | ||
return $res; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,6 @@ | |
*/ | ||
class InstanceViewFactory | ||
{ | ||
|
||
/** @var GroupViewFactory */ | ||
private $groupViewFactory; | ||
|
||
|