class LTIToolProvider in LTI Tool Provider 2.x
Same name and namespace in other branches
- 8 src/Authentication/Provider/LTIToolProvider.php \Drupal\lti_tool_provider\Authentication\Provider\LTIToolProvider
Oauth authentication provider for LTI Tool Provider.
Hierarchy
- class \Drupal\lti_tool_provider\Authentication\Provider\LTIToolProvider implements AuthenticationProviderInterface
Expanded class hierarchy of LTIToolProvider
1 file declares its use of LTIToolProvider
- LTIToolProviderTest.php in tests/
src/ Unit/ LTIToolProviderTest.php
1 string reference to 'LTIToolProvider'
1 service uses LTIToolProvider
File
- src/
Authentication/ Provider/ LTIToolProvider.php, line 28
Namespace
Drupal\lti_tool_provider\Authentication\ProviderView source
class LTIToolProvider implements AuthenticationProviderInterface {
/**
* The configuration factory.
*
* @var ConfigFactoryInterface
*/
protected $configFactory;
/**
* The entity type manager.
*
* @var EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* A logger instance.
*
* @var LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* The event dispatcher.
*
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The consumer entity matching the LTI request.
*
* @var LtiToolProviderConsumer
*/
protected $consumerEntity;
/**
* The LTI context, i.e. the request parameters.
*
* @var array
*/
protected $context;
/**
* The LTI provisioned user.
*
* @var UserInterface
*
*/
protected $user;
/**
* Constructs a HTTP basic authentication provider object.
*
* @param ConfigFactoryInterface $config_factory
* The configuration factory.
* @param EntityTypeManagerInterface $entity_type_manager
* The entity manager.
* @param LoggerChannelFactoryInterface $logger_factory
* A logger instance.
* @param EventDispatcherInterface $eventDispatcher
* The event dispatcher.
*/
public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, LoggerChannelFactoryInterface $logger_factory, EventDispatcherInterface $eventDispatcher) {
$this->configFactory = $config_factory;
$this->entityTypeManager = $entity_type_manager;
$this->loggerFactory = $logger_factory
->get('lti_tool_provider');
$this->eventDispatcher = $eventDispatcher;
}
/**
* {@inheritdoc}
*
* @see https://www.imsglobal.org/wiki/step-1-lti-launch-request
*/
public function applies(Request $request) : bool {
$lti_message_type = $request->request
->get('lti_message_type');
$lti_version = $request->request
->get('lti_version');
$oauth_consumer_key = $request->request
->get('oauth_consumer_key');
$resource_link_id = $request->request
->get('resource_link_id');
if (!$request
->isMethod('POST')) {
return FALSE;
}
if ($lti_message_type !== 'basic-lti-launch-request') {
return FALSE;
}
if (!in_array($lti_version, [
'LTI-1p0',
'LTI-1p2',
])) {
return FALSE;
}
if (empty($oauth_consumer_key)) {
return FALSE;
}
if (empty($resource_link_id)) {
return FALSE;
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function authenticate(Request $request) {
try {
$this->context = $request->request
->all();
$event = new LtiToolProviderLaunchEvent($this->context);
LtiToolProviderEvent::dispatchEvent($this->eventDispatcher, $event);
if ($event
->isCancelled()) {
throw new Exception($event
->getMessage());
}
$this->context = $event
->getContext();
$this
->validateOauthRequest();
$this
->provisionUser();
$event = new LtiToolProviderAuthenticatedEvent($this->context, $this->user);
LtiToolProviderEvent::dispatchEvent($this->eventDispatcher, $event);
if ($event
->isCancelled()) {
throw new Exception($event
->getMessage());
}
$this->context = $event
->getContext();
$this->user = $event
->getUser();
$this
->userLoginFinalize();
$this->context['consumer_id'] = $this->consumerEntity
->id();
$this->context['consumer_label'] = $this->consumerEntity
->label();
$session = $request
->getSession();
$session
->set('lti_tool_provider_context', $this->context);
return $this->user;
} catch (Exception $e) {
$this->loggerFactory
->warning($e
->getMessage());
$this
->sendLtiError($e
->getMessage());
return NULL;
}
}
/**
* Validate the OAuth request.
*/
private function validateOauthRequest() {
$provider = new OAuthProvider([
"oauth_signature_method" => OAUTH_SIG_METHOD_HMACSHA1,
]);
$provider
->consumerHandler([
$this,
'consumerHandler',
]);
$provider
->timestampNonceHandler([
$this,
'timestampNonceHandler',
]);
$provider
->isRequestTokenEndpoint(FALSE);
$provider
->is2LeggedEndpoint(TRUE);
$provider
->checkOAuthRequest();
}
/**
* Provision a user that matches the LTI request context info.
*
* @throws Exception
*/
protected function provisionUser() {
try {
$name = 'ltiuser';
$mail = 'ltiuser@invalid';
$name_param = $this->consumerEntity
->get('name')
->getValue()[0]['value'];
if (isset($this->context[$name_param]) && !empty($this->context[$name_param])) {
$name = $this->context[$name_param];
}
$mail_param = $this->consumerEntity
->get('mail')
->getValue()[0]['value'];
if (isset($this->context[$mail_param]) && !empty($this->context[$mail_param])) {
$mail = $this->context[$mail_param];
}
if ($users = $this->entityTypeManager
->getStorage('user')
->loadByProperties([
'name' => $name,
'status' => 1,
])) {
$this->user = reset($users);
}
elseif ($users = $this->entityTypeManager
->getStorage('user')
->loadByProperties([
'mail' => $mail,
'status' => 1,
])) {
$this->user = reset($users);
}
else {
$this->user = User::create();
$this->user
->setUsername($name);
$this->user
->setEmail($mail);
$this->user
->setPassword(user_password());
$this->user
->enforceIsNew();
$this->user
->activate();
$event = new LtiToolProviderProvisionUserEvent($this->context, $this->user);
LtiToolProviderEvent::dispatchEvent($this->eventDispatcher, $event);
if ($event
->isCancelled()) {
throw new Exception($event
->getMessage());
}
$this->context = $event
->getContext();
$this->user = $event
->getUser();
$this->user
->save();
}
} catch (Exception $e) {
$this->loggerFactory
->warning($e
->getMessage());
$this
->sendLtiError($e
->getMessage());
}
}
/**
* Send an error back to the LMS.
*
* @param string $message
* The error message to send.
*/
protected function sendLtiError(string $message) {
if (isset($this->context['launch_presentation_return_url']) && !empty($this->context['launch_presentation_return_url'])) {
$url = Url::fromUri($this->context['launch_presentation_return_url'])
->setOption('query', [
'lti_errormsg' => $message,
])
->setAbsolute(TRUE)
->toString();
$response = new RedirectResponse($url);
$response
->send();
}
}
/**
* Finalizes the user login.
*/
protected function userLoginFinalize() {
user_login_finalize($this->user);
}
/**
* Looks up the consumer entity that matches the consumer key.
*
* @param $provider
*
* @return int
* - OAUTH_OK if validated.
* - OAUTH_CONSUMER_KEY_UNKNOWN if not.
*/
public function consumerHandler($provider) : int {
try {
$ids = $this->entityTypeManager
->getStorage('lti_tool_provider_consumer')
->getQuery()
->condition('consumer_key', $provider->consumer_key, '=')
->execute();
if (!count($ids)) {
return OAUTH_CONSUMER_KEY_UNKNOWN;
}
$this->consumerEntity = $this->entityTypeManager
->getStorage('lti_tool_provider_consumer')
->load(key($ids));
$provider->consumer_secret = $this->consumerEntity
->get('consumer_secret')
->getValue()[0]['value'];
} catch (InvalidPluginDefinitionException|PluginNotFoundException $e) {
return OAUTH_CONSUMER_KEY_UNKNOWN;
}
return OAUTH_OK;
}
/**
* Validate nonce.
*
* @param $provider
*
* @return int
* - OAUTH_OK if validated.
* - OAUTH_BAD_TIMESTAMP if timestamp too old.
* - OAUTH_BAD_NONCE if nonce has been used.
*/
public function timestampNonceHandler($provider) : int {
// Verify timestamp has been set.
if (!isset($provider->timestamp)) {
return OAUTH_BAD_TIMESTAMP;
}
// Verify nonce timestamp is not older than now - nonce interval.
if ($provider->timestamp < time() - LTI_TOOL_PROVIDER_NONCE_INTERVAL) {
return OAUTH_BAD_TIMESTAMP;
}
// Verify nonce timestamp is not newer than now + nonce interval.
if ($provider->timestamp > time() + LTI_TOOL_PROVIDER_NONCE_INTERVAL) {
return OAUTH_BAD_TIMESTAMP;
}
// Verify nonce and consumer_key has been set.
if (!isset($provider->nonce) || !isset($provider->consumer_key)) {
return OAUTH_BAD_NONCE;
}
try {
$storage = $this->entityTypeManager
->getStorage('lti_tool_provider_nonce');
// Verify that current nonce is not a duplicate.
$nonce_exists = $storage
->getQuery()
->condition('nonce', $provider->nonce, '=')
->execute();
if (count($nonce_exists)) {
return OAUTH_BAD_NONCE;
}
// Store nonce in database.
$storage
->create([
'nonce' => $provider->nonce,
'consumer_key' => $provider->consumer_key,
'timestamp' => $provider->timestamp,
])
->save();
} catch (Exception $e) {
$this->loggerFactory
->warning($e
->getMessage());
return OAUTH_BAD_NONCE;
}
return OAUTH_OK;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
LTIToolProvider:: |
protected | property | The configuration factory. | |
LTIToolProvider:: |
protected | property | The consumer entity matching the LTI request. | |
LTIToolProvider:: |
protected | property | The LTI context, i.e. the request parameters. | |
LTIToolProvider:: |
protected | property | The entity type manager. | |
LTIToolProvider:: |
protected | property | The event dispatcher. | |
LTIToolProvider:: |
protected | property | A logger instance. | |
LTIToolProvider:: |
protected | property | The LTI provisioned user. | |
LTIToolProvider:: |
public | function |
Overrides AuthenticationProviderInterface:: |
|
LTIToolProvider:: |
public | function |
Authenticates the user. Overrides AuthenticationProviderInterface:: |
|
LTIToolProvider:: |
public | function | Looks up the consumer entity that matches the consumer key. | |
LTIToolProvider:: |
protected | function | Provision a user that matches the LTI request context info. | |
LTIToolProvider:: |
protected | function | Send an error back to the LMS. | |
LTIToolProvider:: |
public | function | Validate nonce. | |
LTIToolProvider:: |
protected | function | Finalizes the user login. | |
LTIToolProvider:: |
private | function | Validate the OAuth request. | |
LTIToolProvider:: |
public | function | Constructs a HTTP basic authentication provider object. |