Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/AUT-3400/launching authoring by new configurable roles #396

13 changes: 12 additions & 1 deletion controller/AuthoringTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
use oat\taoLti\models\classes\LtiException;
use oat\taoLti\models\classes\LtiMessages\LtiErrorMessage;
use oat\taoLti\models\classes\LtiService;
use oat\taoLti\models\classes\Tool\Exception\WrongLtiRolesException;
use oat\taoLti\models\classes\Tool\Service\AuthoringLtiRoleService;
use oat\taoLti\models\classes\Tool\Validation\Lti1p3Validator;
use oat\taoLti\models\classes\user\UserService;
use Psr\Container\ContainerExceptionInterface;
Expand Down Expand Up @@ -79,6 +81,7 @@ protected function getValidatedLtiMessagePayload(): LtiMessagePayloadInterface
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws common_exception_Error
* @throws WrongLtiRolesException
*/
public function launch(): void
{
Expand All @@ -90,8 +93,11 @@ public function launch(): void
->addUser(
$ltiMessage->getUserIdentity()->getIdentifier(),
helpers_Random::generateString(UserService::PASSWORD_LENGTH),
new core_kernel_classes_Resource(current($ltiMessage->getRoles()))
new core_kernel_classes_Resource(
$this->getAuthoringRoleService()->getValidRole($ltiMessage->getRoles())
)
);

$this->getServiceLocator()
->getContainer()
->get(LtiService::class)
Expand Down Expand Up @@ -145,4 +151,9 @@ private function getLtiMessageOrRedirectToLogin(): LtiMessagePayloadInterface

return $message;
}

private function getAuthoringRoleService(): AuthoringLtiRoleService
{
return $this->getPsrContainer()->get(AuthoringLtiRoleService::class);
}
}
2 changes: 1 addition & 1 deletion install/ontology/ltiroles_membership.rdf
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
<rdfs:label xml:lang="en-US"><![CDATA[LTI 1p3 Instructor]]></rdfs:label>
<rdfs:comment xml:lang="en-US"><![CDATA[The LTI 1p3 Context Instructor Role]]></rdfs:comment>
<generis:includesRole rdf:resource="http://www.tao.lu/Ontologies/TAOLTI.rdf#LtiBaseRole"/>
<generis:includesRole rdf:resource="http://www.tao.lu/Ontologies/TAO.rdf#DeliveryRole"/>
<generis:includesRole rdf:resource="http://purl.imsglobal.org/vocab/lis/v2/membership/ContentDeveloper#ContentDeveloper"/>
<generis:isSystem rdf:resource="http://www.tao.lu/Ontologies/generis.rdf#True"/>
</rdf:Description>
<rdf:Description rdf:about="http://purl.imsglobal.org/vocab/lis/v2/membership/Instructor#ExternalInstructor">
Expand Down
9 changes: 9 additions & 0 deletions install/ontology/ltiroles_person.rdf
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@
<generis:isSystem rdf:resource="http://www.tao.lu/Ontologies/generis.rdf#True"/>
</rdf:Description>

<rdf:Description rdf:about="http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator">
<rdf:type rdf:resource="http://www.tao.lu/Ontologies/TAOLTI.rdf#LTIRole"/>
<rdfs:label xml:lang="en-US"><![CDATA[LTI Institution Administrator (System)]]></rdfs:label>
<rdfs:comment xml:lang="en-US"><![CDATA[The LTI Institution Administrator Role]]></rdfs:comment>
<taolti:RoleURN><![CDATA[urn:lti:sysrole:ims/lis/Administrator]]></taolti:RoleURN>
<generis:includesRole rdf:resource="http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#Developer"/>
<generis:isSystem rdf:resource="http://www.tao.lu/Ontologies/generis.rdf#True"/>
</rdf:Description>

</rdf:RDF>
82 changes: 82 additions & 0 deletions migrations/Version202312051317263774_taoLti.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace oat\taoLti\migrations;

use core_kernel_classes_Resource;
use core_kernel_users_Cache;
use Doctrine\DBAL\Schema\Schema;
use oat\oatbox\reporting\Report;
use oat\tao\model\accessControl\func\AccessRule;
use oat\tao\model\accessControl\func\AclProxy;
use oat\tao\model\user\TaoRoles;
use oat\tao\scripts\tools\migrations\AbstractMigration;
use oat\tao\scripts\update\OntologyUpdater;
use oat\taoLti\models\classes\LtiRoles;

/**
* Auto-generated Migration: Please modify to your needs!
*
* phpcs:disable Squiz.Classes.ValidClassName
*/
final class Version202312051317263774_taoLti extends AbstractMigration
{
public function getDescription(): string
{
return 'Apply new http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor and http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator permission for AuthoringTool';
}

public function up(Schema $schema): void
{
AclProxy::applyRule($this->getLaunchActionRule());
AclProxy::applyRule($this->getRunActionRule(LtiRoles::CONTEXT_LTI1P3_INSTRUCTOR));
AclProxy::applyRule($this->getRunActionRule(LtiRoles::CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR));

$this->addReport(
Report::createInfo(
sprintf(
'Clearing the Generis cache for roles %s',
LtiRoles::CONTEXT_LTI1P3_INSTRUCTOR,
)
)
);
core_kernel_users_Cache::removeIncludedRoles(
new core_kernel_classes_Resource(LtiRoles::CONTEXT_LTI1P3_INSTRUCTOR)
);
core_kernel_users_Cache::removeIncludedRoles(
new core_kernel_classes_Resource(LtiRoles::CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR)
);

OntologyUpdater::syncModels();

$this->addReport(Report::createInfo('Apply new permission for AuthoringTool'));
}

public function down(Schema $schema): void
{
AclProxy::revokeRule($this->getLaunchActionRule());
AclProxy::revokeRule($this->getRunActionRule(LtiRoles::CONTEXT_LTI1P3_INSTRUCTOR));
AclProxy::revokeRule($this->getRunActionRule(LtiRoles::CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR));

$this->addReport(Report::createInfo('Revoke CONTEXT_INSTRUCTOR, CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR permission for AuthoringTool'));
}

private function getLaunchActionRule(): AccessRule
{
return new AccessRule(
AccessRule::GRANT,
TaoRoles::ANONYMOUS,
['ext' => 'taoLti', 'mod' => 'AuthoringTool', 'act' => 'launch']
);
}

private function getRunActionRule(string $role): AccessRule
{
return new AccessRule(
AccessRule::GRANT,
$role,
['ext' => 'taoLti', 'mod' => 'AuthoringTool', 'act' => 'run']
);
}
}
2 changes: 1 addition & 1 deletion models/classes/LtiLaunchData.php
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ public function getReturnUrl()
* which is a value of any type other than a resource.
* @since 5.4.0
*/
public function jsonSerialize()
public function jsonSerialize(): array
{
return [
'variables' => array_map(
Expand Down
2 changes: 2 additions & 0 deletions models/classes/LtiRoles.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,6 @@ interface LtiRoles
public const CONTEXT_LTI1P3_ADMINISTRATOR_SUB_EXTERNAL_SYSTEM_ADMINISTRATOR = 'http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#ExternalSystemAdministrator';
public const CONTEXT_LTI1P3_ADMINISTRATOR_SUB_SUPPORT = 'http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#Support';
public const CONTEXT_LTI1P3_ADMINISTRATOR_SUB_SYSTEM_ADMINISTRATOR = 'http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#SystemAdministrator';

public const CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator';
}
21 changes: 21 additions & 0 deletions models/classes/ServiceProvider/LtiServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@
use oat\taoLti\models\classes\Client\LtiClientFactory;
use oat\taoLti\models\classes\LtiAgs\LtiAgsScoreService;
use oat\taoLti\models\classes\LtiAgs\LtiAgsScoreServiceInterface;
use oat\taoLti\models\classes\LtiRoles;
use oat\taoLti\models\classes\Platform\Repository\DefaultToolConfig;
use oat\taoLti\models\classes\Platform\Repository\Lti1p3RegistrationRepository;
use oat\taoLti\models\classes\Platform\Repository\Lti1p3RegistrationSnapshotRepository;
use oat\taoLti\models\classes\Platform\Repository\LtiPlatformFactory;
use oat\taoLti\models\classes\Platform\Service\UpdatePlatformRegistrationSnapshotListener;
use oat\taoLti\models\classes\Security\DataAccess\Repository\CachedPlatformKeyChainRepository;
use oat\taoLti\models\classes\Security\DataAccess\Repository\PlatformKeyChainRepository;
use oat\taoLti\models\classes\Tool\Service\AuthoringLtiRoleService;
use oat\taoLti\models\classes\Tool\Validation\AuthoringToolValidator;
use oat\taoLti\models\classes\Tool\Validation\Lti1p3Validator;
use Psr\Cache\CacheItemPoolInterface;
Expand All @@ -79,6 +81,16 @@ public function __invoke(ContainerConfigurator $configurator): void
$_ENV['LTI_DEFAULT_SCOPE'] ?? 'https://purl.imsglobal.org/spec/lti-bo/scope/basicoutcome'
);

$parameters->set(
'rolesAllowed',
[
LtiRoles::CONTEXT_LTI1P3_ADMINISTRATOR_SUB_DEVELOPER,
LtiRoles::CONTEXT_LTI1P3_CONTENT_DEVELOPER_SUB_CONTENT_DEVELOPER,
LTIRoles::CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR,
LtiRoles::CONTEXT_LTI1P3_INSTRUCTOR
]
);

$services
->set(LtiClientFactory::class)
->args(
Expand Down Expand Up @@ -238,5 +250,14 @@ public function __invoke(ContainerConfigurator $configurator): void
service(AuthoringToolValidator::class),
]
);

$services
->set(AuthoringLtiRoleService::class, AuthoringLtiRoleService::class)
->public()
->args(
[
param('rolesAllowed')
]
);
}
}
34 changes: 34 additions & 0 deletions models/classes/Tool/Exception/WrongLtiRolesException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2023 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoLti\models\classes\Tool\Exception;

use Exception;
use Throwable;

class WrongLtiRolesException extends Exception
{
public function __construct(string $message = "Role not allowed", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
46 changes: 46 additions & 0 deletions models/classes/Tool/Service/AuthoringLtiRoleService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2023 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoLti\models\classes\Tool\Service;

use oat\taoLti\models\classes\Tool\Exception\WrongLtiRolesException;

class AuthoringLtiRoleService
{
public function __construct(array $roleAllowed)
{
$this->roleAllowed = $roleAllowed;
}

/**
* @throws WrongLtiRolesException
*/
public function getValidRole(array $roles): string
{
$commonRoles = array_intersect($roles, $this->roleAllowed);

if (empty($commonRoles)) {
throw new WrongLtiRolesException();
}
return current($commonRoles);
}
}
2 changes: 1 addition & 1 deletion models/classes/user/LtiUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public function refresh()
// nothing to do
}

public function jsonSerialize()
public function jsonSerialize(): array
{
return [
self::USER_IDENTIFIER => $this->userUri,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2023 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoLti\test\unit\models\classes\Tool\Service;

use oat\taoLti\models\classes\LtiRoles;
use oat\taoLti\models\classes\Tool\Exception\WrongLtiRolesException;
use oat\taoLti\models\classes\Tool\Service\AuthoringLtiRoleService;
use PHPUnit\Framework\TestCase;

class AuthoringLtiRoleServiceTest extends TestCase
{
public function setUp(): void
{
$this->subject = new AuthoringLtiRoleService(
[
LtiRoles::CONTEXT_LTI1P3_ADMINISTRATOR_SUB_DEVELOPER,
LtiRoles::CONTEXT_LTI1P3_CONTENT_DEVELOPER_SUB_CONTENT_DEVELOPER,
LTIRoles::CONTEXT_INSTITUTION_LTI1P3_ADMINISTRATOR,
LtiRoles::CONTEXT_LTI1P3_INSTRUCTOR
]
);
}

/**
* @dataProvider ltiMessageRolesProvider
*/
public function testValidRole(array $rolesProvided, string $expected): void
{
self::assertEquals($expected, $this->subject->getValidRole($rolesProvided));
}

/**
* @dataProvider invalidRolesProvider
* @throws WrongLtiRolesException
*/
public function testExpectException(array $roles): void
{
$this->expectException(WrongLtiRolesException::class);
$this->subject->getValidRole($roles);
}

public function invalidRolesProvider(): array
{
return [
'Empty array' => [
'roles' => [],
],
'UnsupportedRole' => [
'roles' => ['http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#Support']
]
];
}

public function ltiMessageRolesProvider(): array
{
return [
'When one valid roles' => [
'rolesProvided' => [
'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator',
'http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#Support'
],
'expected' => 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator'
],
'When more than one valid roles' => [
'rolesProvided' => [
'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator',
'http://purl.imsglobal.org/vocab/lis/v2/membership/Administrator#Support',
'http://purl.imsglobal.org/vocab/lis/v2/membership/ContentDeveloper#ContentDeveloper'
],
'expected' => 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator'
]
];
}
}
Loading