Skip to content

Commit

Permalink
Merge pull request #89 from oat-sa/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
ekkinox authored Mar 17, 2021
2 parents ecfc197 + d995a07 commit 19919e3
Show file tree
Hide file tree
Showing 17 changed files with 1,495 additions and 2 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
CHANGELOG
=========

4.2.0
-----

* Added enhanced role management: type (system, institution, context), core / non core, long / short names & automatic validation
* Updated LtiMessagePayloadInterface with getValidatedRoleCollection() method (allows easy access to validated roles from launches)
* Updated documentation

4.1.0
-----

Expand Down
36 changes: 36 additions & 0 deletions doc/message/platform-originating-messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,39 @@ if (!$result->hasError()) {
}
}
```

### Working with launch roles

The [LtiMessagePayloadInterface](../../src/Message/Payload/LtiMessagePayloadInterface.php) provides the `getValidatedRoleCollection()` getter to allow you to work easily with [the LTI specification roles](http://www.imsglobal.org/spec/lti/v1p3/#role-vocabularies) as a [RoleCollection](../../src/Role/Collection/RoleCollection.php).

You can base yourself on this collection if you need to perform RBAC on tool side, for example:

```php
<?php

use OAT\Library\Lti1p3Core\Message\Launch\Validator\Result\LaunchValidationResult;
use OAT\Library\Lti1p3Core\Role\RoleInterface;

/** @var LaunchValidationResult $result */
$result = $validator->validatePlatformOriginatingLaunch(...);

// Result exploitation
if (!$result->hasError()) {

// Access the validated role collection
$roles = $result->getPayload()->getValidatedRoleCollection();

// Check if a role of type context (core or not) has been provided (our case for http://purl.imsglobal.org/vocab/lis/v2/membership#Learner)
if ($roles->canFindBy(RoleInterface::TYPE_CONTEXT)) {
// Authorized launch
...
} else {
// Unauthorized launch
...
}
}
```

**Notes**:
- if the launch contains invalid (non respecting LTI specification) roles, the getter will throw an [LtiException](../../src/Exception/LtiException.php)
- the [LtiMessagePayloadInterface](../../src/Message/Payload/LtiMessagePayloadInterface.php) offers the `getRoles()` getter to work with plain roles values (no validation)
17 changes: 17 additions & 0 deletions src/Message/Payload/LtiMessagePayload.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
use OAT\Library\Lti1p3Core\Message\Payload\Claim\ProctoringSettingsClaim;
use OAT\Library\Lti1p3Core\Message\Payload\Claim\ProctoringVerifiedUserClaim;
use OAT\Library\Lti1p3Core\Message\Payload\Claim\ResourceLinkClaim;
use OAT\Library\Lti1p3Core\Role\Collection\RoleCollection;
use OAT\Library\Lti1p3Core\Role\Collection\RoleCollectionInterface;
use OAT\Library\Lti1p3Core\Role\Factory\RoleFactory;
use OAT\Library\Lti1p3Core\User\UserIdentity;
use OAT\Library\Lti1p3Core\User\UserIdentityInterface;

Expand Down Expand Up @@ -84,6 +87,20 @@ public function getRoles(): array
return $this->getMandatoryClaim(static::CLAIM_LTI_ROLES);
}

/**
* @throws LtiExceptionInterface
*/
public function getValidatedRoleCollection(): RoleCollection
{
$collection = new RoleCollection();

foreach ($this->getMandatoryClaim(static::CLAIM_LTI_ROLES) as $role) {
$collection->add(RoleFactory::create($role));
}

return $collection;
}

public function getResourceLink(): ?ResourceLinkClaim
{
return $this->getClaim(ResourceLinkClaim::class);
Expand Down
3 changes: 3 additions & 0 deletions src/Message/Payload/LtiMessagePayloadInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use OAT\Library\Lti1p3Core\Message\Payload\Claim\ProctoringSettingsClaim;
use OAT\Library\Lti1p3Core\Message\Payload\Claim\ProctoringVerifiedUserClaim;
use OAT\Library\Lti1p3Core\Message\Payload\Claim\ResourceLinkClaim;
use OAT\Library\Lti1p3Core\Role\Collection\RoleCollection;
use OAT\Library\Lti1p3Core\User\UserIdentityInterface;

/**
Expand Down Expand Up @@ -95,6 +96,8 @@ public function getTargetLinkUri(): string;

public function getRoles(): array;

public function getValidatedRoleCollection(): RoleCollection;

public function getRoleScopeMentor(): array;

public function getCustom(): array;
Expand Down
74 changes: 74 additions & 0 deletions src/Role/AbstractRole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?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) 2021 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace OAT\Library\Lti1p3Core\Role;

use OAT\Library\Lti1p3Core\Exception\LtiException;
use OAT\Library\Lti1p3Core\Exception\LtiExceptionInterface;

abstract class AbstractRole implements RoleInterface
{
/** @var string */
protected $name;

/**
* @throws LtiExceptionInterface
*/
public function __construct(string $name)
{
$this->name = $name;

if (!$this->isValid()) {
throw new LtiException(sprintf('Role %s is invalid for type %s', $this->name, static::getType()));
}
}

public function getName(): string
{
return $this->name;
}

public function getSubName(): ?string
{
return null;
}

public function isCore(): bool
{
$exp = explode('#', $this->name);

return $this->getMap()[end($exp)];
}

protected function isValid(): bool
{
if (strpos($this->name, static::getNamespace()) !== 0) {
return false;
}

$exp = explode('#', $this->name);

return array_key_exists(end($exp), $this->getMap());
}

abstract protected function getMap(): array;
}
100 changes: 100 additions & 0 deletions src/Role/Collection/RoleCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?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) 2021 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace OAT\Library\Lti1p3Core\Role\Collection;

use OAT\Library\Lti1p3Core\Role\RoleInterface;
use OAT\Library\Lti1p3Core\Util\Collection\Collection;
use OAT\Library\Lti1p3Core\Util\Collection\CollectionInterface;

class RoleCollection
{
/** @var RoleInterface[]|CollectionInterface */
private $roles;

public function __construct(array $roles = [])
{
$this->roles = new Collection();

foreach ($roles as $role) {
$this->add($role);
}
}

public function all(): array
{
return $this->roles->all();
}

public function count(): int
{
return $this->roles->count();
}

public function add(RoleInterface $role): self
{
$this->roles->set($role->getName(), $role);

return $this;
}

public function has(string $name): bool
{
return $this->roles->has($name);
}

public function get(string $name): RoleInterface
{
return $this->roles->getMandatory($name);
}

public function canFindBy(?string $type = null, ?bool $core = null): bool
{
return !empty($this->findBy($type, $core));
}

/**
* @return RoleInterface[]
*/
public function findBy(?string $type = null, ?bool $core = null): array
{
$roles = [];

foreach ($this->roles as $name => $role) {
$add = true;

if (null !== $type) {
$add = $add && $role::getType() === $type;
}

if (null !== $core) {
$add = $add && $role->isCore() === $core;
}

if ($add) {
$roles[$name] = $role;
}
}

return $roles;
}
}
48 changes: 48 additions & 0 deletions src/Role/Factory/RoleFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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) 2021 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace OAT\Library\Lti1p3Core\Role\Factory;

use OAT\Library\Lti1p3Core\Exception\LtiExceptionInterface;
use OAT\Library\Lti1p3Core\Role\RoleInterface;
use OAT\Library\Lti1p3Core\Role\Type\ContextRole;
use OAT\Library\Lti1p3Core\Role\Type\InstitutionRole;
use OAT\Library\Lti1p3Core\Role\Type\SystemRole;

class RoleFactory
{
/**
* @throws LtiExceptionInterface
*/
public static function create(string $name): RoleInterface
{
if (strpos($name, SystemRole::getNameSpace()) === 0) {
return new SystemRole($name);
}

if (strpos($name, InstitutionRole::getNameSpace()) === 0) {
return new InstitutionRole($name);
}

return new ContextRole($name);
}
}
47 changes: 47 additions & 0 deletions src/Role/RoleInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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) 2021 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace OAT\Library\Lti1p3Core\Role;

/**
* @see http://www.imsglobal.org/spec/lti/v1p3/#role-vocabularies
*/
interface RoleInterface
{
public const TYPE_SYSTEM = 'system';
public const TYPE_INSTITUTION = 'institution';
public const TYPE_CONTEXT = 'context';

public const NAMESPACE_SYSTEM = 'http://purl.imsglobal.org/vocab/lis/v2/system/person';
public const NAMESPACE_INSTITUTION = 'http://purl.imsglobal.org/vocab/lis/v2/institution/person';
public const NAMESPACE_CONTEXT = 'http://purl.imsglobal.org/vocab/lis/v2/membership';

public static function getType(): string;

public static function getNamespace(): string;

public function getName(): string;

public function getSubName(): ?string;

public function isCore(): bool;
}
Loading

0 comments on commit 19919e3

Please sign in to comment.