From 8a7a253ea6c623317eebe255c1a14b634f606a8b Mon Sep 17 00:00:00 2001 From: Yannick Warnier Date: Thu, 15 Nov 2018 14:12:39 -0500 Subject: [PATCH 01/14] Update language terms --- main/lang/brazilian/trad4all.inc.php | 12 ++++++------ main/lang/galician/trad4all.inc.php | 18 ++++++++++++++++-- main/lang/polish/trad4all.inc.php | 2 +- main/lang/tagalog/trad4all.inc.php | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/main/lang/brazilian/trad4all.inc.php b/main/lang/brazilian/trad4all.inc.php index ca79d679d2..845f5ae3c3 100644 --- a/main/lang/brazilian/trad4all.inc.php +++ b/main/lang/brazilian/trad4all.inc.php @@ -79,7 +79,7 @@ $HyperbolicArctangentArctanh = "Arco tangente hiperbólica: \t\t arctanh (x)"; $SessionDurationTitle = "Duração da sessão"; $ArctangentArctan = "Arctangent:\t\t\tarctan(x)"; $HyperbolicTangentTanh = "Tangente hiperbólica:\t\ttanh(x)"; -$TangentTan = "Tangent:\t\t\ TTAN (x)"; +$TangentTan = "Tangent:\t\t\tTAN (x)"; $CoachAndStudent = "Treinador e aluno"; $Serie = "Series"; $HyperbolicArccosineArccosh = "Arccosine hiperbólica:\t\tarccosh(x)"; @@ -90,13 +90,13 @@ $TeacherTimeReport = "Professores relatório de tempo"; $HyperbolicArcsineArcsinh = "arcsine hiperbólica:\t\tarcsinh(x)"; $YourLanguageNotThereContactUs = "Não é possível encontrar o seu idioma na lista? Contacte-nos em info@chamilo.org contribuir como um tradutor."; $ArcsineArcsin = "arcsine:\t\t\tarcsin(x)"; -$HyperbolicSineSinh = "Seno hiperbólico:\t\ sinh(x)"; +$HyperbolicSineSinh = "Seno hiperbólico:\t\tsinh(x)"; $SineSin = "Sine:\t\t\t\tsin(x)"; $PiNumberPi = "Número Pi:\t\t\tpi"; $ENumberE = "Número E:\t\t\te"; $LogarithmLog = "Logaritmo:\t\t\tlog(x)"; $NaturalLogarithmLn = "Logaritmo natural:\t\tln(x)"; -$AbsoluteValueAbs = "Valor absoluto:\t\t\abas(x)"; +$AbsoluteValueAbs = "Valor absoluto:\t\t\tabas(x)"; $SquareRootSqrt = "Raiz quadrada:\t\t\tsqrt(x)"; $ExponentiationCircumflex = "Exponenciação:\t\t\t^"; $DivisionSlash = "Divisão:\t\t\t/"; @@ -2348,7 +2348,7 @@ $GroupDescription = "Descrição do grupo"; $GroupMembers = "Membros do grupo"; $EditGroup = "Editar este grupo"; $GroupSettingsModified = "Configurações do grupo modificado"; -$GroupTooMuchMembers = "Número proposta excede max que você permitiu que (você pode modificá-lo abaixo) \ t \ t \ t \ composição tgroup não foi modificado."; +$GroupTooMuchMembers = "Número proposta excede max que você permitiu que (você pode modificá-lo abaixo) \t\t\t\t composição tgroup não foi modificado."; $GroupTutor = "Tutor Grupo"; $GroupNoTutor = "(nenhum)"; $GroupNone = "(nenhum)"; @@ -5934,7 +5934,7 @@ $ContactInformationHasBeenSent = "As informações de contato foram enviadas"; $EditExtraFieldOptions = "Editar opções de campos extra"; $ExerciseDescriptionLabel = "Descrição"; $UserInactivedSinceX = "Usuário inativo desde %s"; -$ContactInformationDescription = "Prezado usuário,
\ n
Você está prestes a começar a usar um dos melhores de fonte aberta plataforma e-learning no mercado. Como muitos outros projetos de código aberto, este projecto é apoiado -se por uma grande comunidade de estudantes, professores, desenvolvedores e criadores de conteúdo que gostariam de promover melhor o projeto.
\n
\nAo saber um pouco mais sobre você, um dos nossos usuários mais importantes, quem vai gerir este chamilo de e-learning, que será capaz de deixar as pessoas sabem que o nosso software é usado e que você saiba quando nós organizamos eventos que possam ser relevantes para você.
\ n
\ nAo enchimento Desta forma, você aceita que a associação Chamilo ou seus membros possam enviar informações por e-mail sobre eventos importantes ou atualizações do software Chamilo ou comunidade. Isto irá ajudar a comunidade a crescer como uma entidade organizada, onde o fluxo de informações, com um respeito permanente de seu tempo e sua privacidade.
\ n
\ nPor favor nota que você é não é necessário para preencher este formulário. Se você quiser permanecer anônimo, vamos perder a oportunidade de oferecer-lhe todos os privilégios de ser um administrador do portal registrado, mas nós vamos respeitar sua decisão. Basta deixar esta forma vazia e clique em \"Avançar\"

."; +$ContactInformationDescription = "Prezado usuário,
\n
Você está prestes a começar a usar um dos melhores de fonte aberta plataforma e-learning no mercado. Como muitos outros projetos de código aberto, este projecto é apoiado -se por uma grande comunidade de estudantes, professores, desenvolvedores e criadores de conteúdo que gostariam de promover melhor o projeto.
\n
\nAo saber um pouco mais sobre você, um dos nossos usuários mais importantes, quem vai gerir este chamilo de e-learning, que será capaz de deixar as pessoas sabem que o nosso software é usado e que você saiba quando nós organizamos eventos que possam ser relevantes para você.
\n
\nAo enchimento Desta forma, você aceita que a associação Chamilo ou seus membros possam enviar informações por e-mail sobre eventos importantes ou atualizações do software Chamilo ou comunidade. Isto irá ajudar a comunidade a crescer como uma entidade organizada, onde o fluxo de informações, com um respeito permanente de seu tempo e sua privacidade.
\n
\nPor favor nota que você é não é necessário para preencher este formulário. Se você quiser permanecer anônimo, vamos perder a oportunidade de oferecer-lhe todos os privilégios de ser um administrador do portal registrado, mas nós vamos respeitar sua decisão. Basta deixar esta forma vazia e clique em \"Avançar\"

."; $CompanyActivity = "atividade da sua empresa"; $PleaseAllowUsALittleTimeToSubscribeYouToOneOfOurCourses = "Por favor, permita-nos um pouco de tempo para se inscrever você para um dos nossos cursos Se você acha que esqueci de você, entre em contato com os administradores do portal normalmente você pode encontrar os detalhes de contato no rodapé desta página..."; $ManageSessionFields = "Gerenciar campos de sessão"; @@ -6124,7 +6124,7 @@ $OnlyAccessFromYourGroup = "Só acessível a partir de seu grupo"; $CreateAssignmentPage = "Isto irá criar uma página wiki especial em que o professor pode descrever a tarefa e que serão ligadas automaticamente para as páginas wiki onde os alunos realizam a tarefa. Tanto o professor e páginas dos alunos são criados automaticamente. Nestas tarefas , os alunos só podem editar e ver DELE páginas, mas isso pode ser mudado facilmente se você precisar."; $UserFolders = "Pastas de usuários"; $UserFolder = "Pasta do usuário"; -$HelpUsersFolder = "informação visível para o professor SOMENTE:.\NA pasta usuários contém uma pasta para cada usuário que acessou-o através da ferramenta de documentos, ou quando qualquer arquivo foi enviado no curso através do editor on-line Se nem circunstâncias ocorreu, em seguida, nenhuma pasta do usuário terá sido criado. No caso de grupos, os arquivos que são enviados através do editor será adicionado na pasta de cada grupo, que só é acessível por estudantes deste grupo.\n

\nOs usuários pasta e cada uma das pastas incluídas ficarão ocultas por padrão no para todos os alunos, mas cada aluno pode ver o conteúdo do seu/sua diretório através do editor online. No entanto, se um estudante conhece o endereço de um arquivo ou pasta de um outro estudante, ele pode ser capaz de acessá-lo.\n

\nSe a pasta de um estudante é visível, outros alunos podem ver o que ele contém. Neste caso, o aluno que possui a pasta pode também (a partir da ferramenta documentos e somente em sua pasta/ela): criar e editar documentos web, converter um documento em um modelo para uso pessoal, criar e editar desenhos em SVG e PNG formatos, arquivos de áudio em formato WAV gravação, faça arquivos de áudio em MP3 a partir de um texto, faça snapshops a partir de uma webcam, enviar documentos, criar pastas, mover pastas e arquivos, excluir pastas e arquivos, e faça o download de backup de sua pasta/ela.\n

\nAlém disso, a ferramenta documentos está sincronizado com o gerente do editor on-line de arquivos, pelo que as alterações nos documentos acionado em qualquer um deles afetará ambos.\n

\nComo tal, a pasta de usuário é não só um lugar para depositar os arquivos, torna-se um gerente completo dos documentos estudantes usam durante o curso. Além disso, lembre-se que qualquer usuário pode copiar um arquivo que é visível a partir de qualquer pasta na ferramenta de documentos (ou não, ele é o proprietário) para seu/sua carteira ou documentos pessoais área de rede social, que estará disponível para ele / ela para uso em outros cursos."; +$HelpUsersFolder = "informação visível para o professor SOMENTE:.\nA pasta usuários contém uma pasta para cada usuário que acessou-o através da ferramenta de documentos, ou quando qualquer arquivo foi enviado no curso através do editor on-line Se nem circunstâncias ocorreu, em seguida, nenhuma pasta do usuário terá sido criado. No caso de grupos, os arquivos que são enviados através do editor será adicionado na pasta de cada grupo, que só é acessível por estudantes deste grupo.\n

\nOs usuários pasta e cada uma das pastas incluídas ficarão ocultas por padrão no para todos os alunos, mas cada aluno pode ver o conteúdo do seu/sua diretório através do editor online. No entanto, se um estudante conhece o endereço de um arquivo ou pasta de um outro estudante, ele pode ser capaz de acessá-lo.\n

\nSe a pasta de um estudante é visível, outros alunos podem ver o que ele contém. Neste caso, o aluno que possui a pasta pode também (a partir da ferramenta documentos e somente em sua pasta/ela): criar e editar documentos web, converter um documento em um modelo para uso pessoal, criar e editar desenhos em SVG e PNG formatos, arquivos de áudio em formato WAV gravação, faça arquivos de áudio em MP3 a partir de um texto, faça snapshops a partir de uma webcam, enviar documentos, criar pastas, mover pastas e arquivos, excluir pastas e arquivos, e faça o download de backup de sua pasta/ela.\n

\nAlém disso, a ferramenta documentos está sincronizado com o gerente do editor on-line de arquivos, pelo que as alterações nos documentos acionado em qualquer um deles afetará ambos.\n

\nComo tal, a pasta de usuário é não só um lugar para depositar os arquivos, torna-se um gerente completo dos documentos estudantes usam durante o curso. Além disso, lembre-se que qualquer usuário pode copiar um arquivo que é visível a partir de qualquer pasta na ferramenta de documentos (ou não, ele é o proprietário) para seu/sua carteira ou documentos pessoais área de rede social, que estará disponível para ele/ela para uso em outros cursos."; $HelpFolderChat = "INFORMAÇÕES VISÍVEL SOMENTE PARA O PROFESSOR: pasta\n Está contém todas as sessões que foram abertas no chat. Embora as sessões de chat pode muitas vezes ser trivial, outros pode ser realmente interessante e digno de ser incorporada como um documento de trabalho adicional. Para fazer isso sem alterar a visibilidade desta pasta, tornar o arquivo visível e vinculá-lo a partir de onde você considerem adequadas. Não é recomendado fazer esta pasta visível a todos."; $HelpFolderCertificates = "Informação visível para o professor SOMENTE:.. Pasta\nEste contém os vários modelos de certificados que foram criados para a ferramenta de classificação não é recomendável fazer esta pasta visível a todos"; $DestinationDirectory = "Pasta de destino"; diff --git a/main/lang/galician/trad4all.inc.php b/main/lang/galician/trad4all.inc.php index f0ef5ef06d..3c240ee427 100644 --- a/main/lang/galician/trad4all.inc.php +++ b/main/lang/galician/trad4all.inc.php @@ -7003,10 +7003,24 @@ $GradebookEnableLockingTitle = "Activar bloqueo de Avaliacións para os profesor $GradebookEnableLockingComment = "Unha vez activada, esta opción permitirá aos profesores bloquear calquera avaliación dentro do seu curso. Isto prohibirá ao profesor calquera modificación posterior dos resultados dos seus alumnos nos recursos usados para esta avaliación: exames, leccións, tarefas, etc. O único rol autorizado a desbloquear unha avaliación é o administrador. O profesor estará informado desta posibilidade ao intentar desbloquear a avaliación. Tanto o bloqueo como o desbloqueo estarán gardados no rexistro de actividades importantes do sistema."; $LdapDescriptionComment = "

  • Autentificación LDAP :
    Ver I. embaixo para configurar LDAP
    Ver II. embaixo para activar a autentificación LDAP


  • Actualizar atributos de usuario, con datos LDAP, despois de autentificación CAS (ver CAS configuration ) :
    Ver I. embaixo para configurar LDAP
    Para autentificación con manexo de usuarios CAS, a activación de LDAP non é requerida.


I. Configuración LDAP

Editar arquivo main/auth/external_login/ldap.conf.php
-> Editar valores do array $extldap_config

Os parámetros son
-> Para actualizar as correspondencias entre usuario e atributos LDAP, editar o array $extldap_user_correspondance
Os valores do array son <chamilo_field> => >ldap_field>
A estrutura do array está explciada no arquivo main/auth/external_login/ldap.conf.php


II. Activar autenticación LDAP

Editar arquivo main/inc/conf/configuration.php
-> Descomentar as liñas
$extAuthSource["extldap"]["login"] =$_configuration['root_sys'].$_configuration['code_append']."auth/external_login/login.ldap.php";
$extAuthSource["extldap"]["newUser"] =$_configuration['root_sys'].$_configuration['code_append']."auth/external_login/newUser.ldap.php";

N.B. : Os usuarios LDAP empregan os mesmos campos ca os usuarios da plataforma para facer login.
N.B. : A activación LDAP engade un menú Autentificación externa [LDAP] nas páxinas de usuario "engadir ou modificar"."; $ShibbolethMainActivateTitle = "Autenticación Shibboleth"; -$ShibbolethMainActivateComment = "En primeiro lugar, ten que configurar Shibboleth para o seu servidor web. Para configuralo en Chamilo: editar o arquivo main/auth/shibboleth/config/aai.class.php Modificar valores de \$result co nome dos atributos de Shibboleth \$result->unique_id = 'mail'; \$result->firstname = 'cn'; \$result->lastname = 'uid'; \$result->email = 'mail'; \$result->language = '-'; \$result->gender = '-'; \$result->address = '-'; \$result->staff_category = '-'; \$result->home_organization_type = '-'; \$result->home_organization = '-'; \$result->affiliation = '-'; \$result->persistent_id = '-'; ... Ir a Plug-in para engadir o botón 'Shibboleth Login' no seu campus de Chamilo."; +$ShibbolethMainActivateComment = "En primeiro lugar, ten que configurar Shibboleth para o seu servidor web. Para configuralo en Chamilo: editar o arquivo main/auth/shibboleth/config/aai.class.php Modificar valores de $result co nome dos atributos de Shibboleth + +... +Ir a Plug-in para engadir o botón 'Shibboleth Login' no seu campus de Chamilo."; $LdapDescriptionTitle = "Autenticación LDAP"; $FacebookMainActivateTitle = "Autenticación Facebook"; -$FacebookMainActivateComment = "En primeiro lugar, tense que crear unha aplicación de Facebook (ver https://developers.facebook.com/apps) cunha conta de Facebook. Nos parámetros de aplicacións de Facebook, o valor de dirección URL do sitio debe tener \"unha acción = fbconnect\" un parámetro GET (http://mychamilo.com/?action=fbconnect, por ejemplo). Entón, editar o arquivo main/auth/external_login/facebook.conf.php e ingresar en \"appId\" e \"secret\" os valores de \$facebook_config. Ir a Plug-in para engadir un botón configurable \"Facebook Login\" para o campus de Chamilo."; +$FacebookMainActivateComment = "En primeiro lugar, tense que crear unha aplicación de Facebook (ver https://developers.facebook.com/apps) cunha conta de Facebook. Nos parámetros de aplicacións de Facebook, o valor de dirección URL do sitio debe tener \"unha acción = fbconnect\" un parámetro GET (http://mychamilo.com/?action=fbconnect, por ejemplo). Entón, editar o arquivo main/auth/external_login/facebook.conf.php e ingresar en \"appId\" e \"secret\" os valores de $facebook_config. Ir a Plug-in para engadir un botón configurable \"Facebook Login\" para o campus de Chamilo."; $AnnouncementForGroup = "Anuncio para un grupo"; $AllGroups = "Todos os grupos"; $LanguagePriority1Title = "Prioridade do idioma 1"; diff --git a/main/lang/polish/trad4all.inc.php b/main/lang/polish/trad4all.inc.php index 1a96b9bb7a..e4851e113b 100644 --- a/main/lang/polish/trad4all.inc.php +++ b/main/lang/polish/trad4all.inc.php @@ -3458,7 +3458,7 @@ $Preserved = "Chroniony"; $ConfirmUnsubscribe = "Potwierdź usunięcie użytkownika"; $See = "Idź do"; $LastVisits = "Moje ostatnie logowania"; -$IfYouWantToAddManyUsers = "Jeśli chcesz dodać listę użytkowników do szkolenia \t\t\, skontaktuj się z administratorem."; +$IfYouWantToAddManyUsers = "Jeśli chcesz dodać listę użytkowników do szkolenia, skontaktuj się z administratorem."; $PassTooEasy = "to hasło jest zbyt proste. Zobacz przykład poniżej"; $AddedToCourse = "jest już zarejestrowany w kursie"; $UserAlreadyRegistered = "Użytkownik o tej nazwie jest już zarejestrowany w tym kursie."; diff --git a/main/lang/tagalog/trad4all.inc.php b/main/lang/tagalog/trad4all.inc.php index 8fdd4297a0..c12c53edaa 100644 --- a/main/lang/tagalog/trad4all.inc.php +++ b/main/lang/tagalog/trad4all.inc.php @@ -1340,7 +1340,7 @@ $ExamPassX = "Pasado sa minimum na %s"; $ExamFail = "Bagsak"; $ExamTracking = "Pagsusubaybay sa pagsusulit"; $EnableCourseValidation = "Balidasyon ng mga kurso"; -$EnableCourseValidationComment = "Kapag ang tampok na \Balidasyon ng kurso\" ay pinagana, ang isang guro ay hindi maaaring makapaglikha ng isang kurso nang nag-iisa. Siya ay dapat magsulat ng isang kahilingan sa kurso. Ang tagapangasiwa ng platform ay niriripaso ang kahilingan at aaprubahan ito o tatanggihan.
Ang tampok na ito ay umaasa sa 'automated e-mail messaing'; i-set ang Chamilo upang mag-access sa isang server ng email at gumamit ng isang nakatalagang e-mail account."; +$EnableCourseValidationComment = "Kapag ang tampok na 'Balidasyon ng kurso' ay pinagana, ang isang guro ay hindi maaaring makapaglikha ng isang kurso nang nag-iisa. Siya ay dapat magsulat ng isang kahilingan sa kurso. Ang tagapangasiwa ng platform ay niriripaso ang kahilingan at aaprubahan ito o tatanggihan.
Ang tampok na ito ay umaasa sa 'automated e-mail messaing'; i-set ang Chamilo upang mag-access sa isang server ng email at gumamit ng isang nakatalagang e-mail account."; $CourseValidationTermsAndConditionsLink = "Balidasyon ng kurso - isang link sa mga takda at mga kondisyon"; $CourseValidationTermsAndConditionsLinkComment = "Ito ang URL sa \"Mga Takda at Mga Kondisyon\" na dokumento na balido sa pagagawa ng kahilingan sa kurso. Kung ang address ay naitakda rito, ang gumagamit ay dapat basahin at sumang-ayon sa mga takda at mga kondisyon bago ipadala ang isang kahilingan sa kurso.
Kung iyong pagganahin ang module ng \"Mga Takda at Mga Kondisyon\" at kung nais mong gamitin ang URL nito, hayaang walang laman ang setting na ito."; $EnabledSVGTitle = "Lumikha at mag-edit ng mga SVG file"; From b8bb53d154e097cb4bfaf81c39529d0609701c9a Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Thu, 15 Nov 2018 14:15:47 -0500 Subject: [PATCH 02/14] LTI Allow create tools when there is no key/secret available for launch - refs BT#13469 --- plugin/ims_lti/Entity/ImsLtiTool.php | 6 ++-- plugin/ims_lti/ImsLtiPlugin.php | 44 ++++++++++++++++++++++++++-- plugin/ims_lti/README.md | 12 +++++++- plugin/ims_lti/configure.php | 36 +++++++++++++++++------ plugin/ims_lti/create.php | 35 ++++++++++++++++++---- plugin/ims_lti/edit.php | 16 +++++++--- plugin/ims_lti/form.php | 32 +++++++++++--------- plugin/ims_lti/lang/english.php | 2 ++ plugin/ims_lti/lang/spanish.php | 2 ++ plugin/ims_lti/src/Form/FrmAdd.php | 4 +-- plugin/ims_lti/src/Form/FrmEdit.php | 4 +-- 11 files changed, 152 insertions(+), 41 deletions(-) diff --git a/plugin/ims_lti/Entity/ImsLtiTool.php b/plugin/ims_lti/Entity/ImsLtiTool.php index 1d4e05f25c..efc4f3c4de 100644 --- a/plugin/ims_lti/Entity/ImsLtiTool.php +++ b/plugin/ims_lti/Entity/ImsLtiTool.php @@ -45,13 +45,13 @@ class ImsLtiTool /** * @var string * - * @ORM\Column(name="consumer_key", type="string") + * @ORM\Column(name="consumer_key", type="string", nullable=true) */ private $consumerKey = ''; /** * @var string * - * @ORM\Column(name="shared_secret", type="string") + * @ORM\Column(name="shared_secret", type="string", nullable=true) */ private $sharedSecret = ''; /** @@ -117,6 +117,8 @@ class ImsLtiTool $this->gradebookEval =null; $this->privacy = null; $this->children = new ArrayCollection(); + $this->consumerKey = null; + $this->sharedSecret = null; } /** diff --git a/plugin/ims_lti/ImsLtiPlugin.php b/plugin/ims_lti/ImsLtiPlugin.php index 1f85736fe5..3af28bda0c 100644 --- a/plugin/ims_lti/ImsLtiPlugin.php +++ b/plugin/ims_lti/ImsLtiPlugin.php @@ -125,8 +125,8 @@ class ImsLtiPlugin extends Plugin name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, launch_url VARCHAR(255) NOT NULL, - consumer_key VARCHAR(255) NOT NULL, - shared_secret VARCHAR(255) NOT NULL, + consumer_key VARCHAR(255) DEFAULT NULL, + shared_secret VARCHAR(255) DEFAULT NULL, custom_params LONGTEXT DEFAULT NULL, active_deep_linking TINYINT(1) DEFAULT \'0\' NOT NULL, privacy LONGTEXT DEFAULT NULL, @@ -466,4 +466,44 @@ class ImsLtiPlugin extends Plugin return !empty($tool); } + + /** + * @param string $configUrl + * + * @return string + * @throws Exception + */ + public function getLaunchUrlFromCartridge($configUrl) + { + $options = [ + CURLOPT_CUSTOMREQUEST => 'GET', + CURLOPT_POST => false, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_ENCODING => '', + CURLOPT_SSL_VERIFYPEER => false, + ]; + + $ch = curl_init($configUrl); + curl_setopt_array($ch, $options); + $content = curl_exec($ch); + $errno = curl_errno($ch); + curl_close($ch); + + if ($errno !== 0) { + throw new Exception($this->get_lang('NoAccessToUrl')); + } + + $xml = new SimpleXMLElement($content); + $result = $xml->xpath('blti:launch_url'); + + if (empty($result)) { + throw new Exception($this->get_lang('LaunchUrlNotFound')); + } + + $launchUrl = $result[0]; + + return (string) $launchUrl; + } } diff --git a/plugin/ims_lti/README.md b/plugin/ims_lti/README.md index 410de4d699..fb0e907952 100644 --- a/plugin/ims_lti/README.md +++ b/plugin/ims_lti/README.md @@ -1,7 +1,7 @@ IMS/LTI plugin === -Version 1.3 (beta) +Version 1.4 (beta) This plugin is meant to be later integrated into Chamilo (in a major version release). @@ -33,6 +33,9 @@ external tool. **v1.3** * Privacy settings added. Allow to indicate id the launcher's data should be sent in request. + +**v1.4** +* Allow create external tools when there is no key/secret available for launch # Installation @@ -75,3 +78,10 @@ CREATE INDEX IDX_C5E47F7C727ACA70 ON plugin_ims_lti_tool (parent_id); ```sql ALTER TABLE plugin_ims_lti_tool ADD privacy LONGTEXT DEFAULT NULL; ``` + +**To v.4** +```sql +ALTER TABLE plugin_ims_lti_tool + CHANGE consumer_key consumer_key VARCHAR(255) DEFAULT NULL, + CHANGE shared_secret shared_secret VARCHAR(255) DEFAULT NULL; +``` diff --git a/plugin/ims_lti/configure.php b/plugin/ims_lti/configure.php index f9eb22f986..67d253beae 100644 --- a/plugin/ims_lti/configure.php +++ b/plugin/ims_lti/configure.php @@ -49,15 +49,6 @@ switch ($action) { ->setDescription( empty($formValues['description']) ? null : $formValues['description'] ) - ->setLaunchUrl( - $baseTool ? $baseTool->getLaunchUrl() : $formValues['launch_url'] - ) - ->setConsumerKey( - $baseTool ? $baseTool->getConsumerKey() : $formValues['consumer_key'] - ) - ->setSharedSecret( - $baseTool ? $baseTool->getSharedSecret() : $formValues['shared_secret'] - ) ->setCustomParams( empty($formValues['custom_params']) ? null : $formValues['custom_params'] ) @@ -69,6 +60,33 @@ switch ($action) { !empty($formValues['share_picture']) ); + if ($baseTool) { + $tool + ->setLaunchUrl($baseTool->getLaunchUrl()) + ->setConsumerKey($baseTool->getConsumerKey()) + ->setSharedSecret($baseTool->getSharedSecret()); + } else { + if (empty($formValues['consumer_key']) && empty($formValues['shared_secret'])) { + try { + $launchUrl = $plugin->getLaunchUrlFromCartridge($formValues['launch_url']); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + header('Location: '.api_get_self().'?'.api_get_cidreq()); + exit; + } + + $tool->setLaunchUrl($launchUrl); + } else { + $tool + ->setLaunchUrl($formValues['launch_url']) + ->setConsumerKey($formValues['consumer_key']) + ->setSharedSecret($formValues['shared_secret']); + } + } + if (null === $baseTool || ($baseTool && !$baseTool->isActiveDeepLinking()) ) { diff --git a/plugin/ims_lti/create.php b/plugin/ims_lti/create.php index 50f14042b3..7f754d76b3 100644 --- a/plugin/ims_lti/create.php +++ b/plugin/ims_lti/create.php @@ -21,11 +21,12 @@ if ($form->validate()) { $externalTool = new ImsLtiTool(); $externalTool ->setName($formValues['name']) - ->setDescription($formValues['description']) - ->setLaunchUrl($formValues['launch_url']) - ->setConsumerKey($formValues['consumer_key']) - ->setSharedSecret($formValues['shared_secret']) - ->setCustomParams($formValues['custom_params']) + ->setDescription( + empty($formValues['description']) ? null : $formValues['description'] + ) + ->setCustomParams( + empty($formValues['custom_params']) ? null : $formValues['custom_params'] + ) ->setCourse(null) ->setActiveDeepLinking( isset($formValues['deep_linking']) @@ -36,6 +37,30 @@ if ($form->validate()) { isset($formValues['share_picture']) ); + if (empty($formValues['consumer_key']) && empty($formValues['shared_secret'])) { + try { + $launchUrl = $plugin->getLaunchUrlFromCartridge($formValues['launch_url']); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'ims_lti/admin.php'); + exit; + } + + $externalTool->setLaunchUrl($launchUrl); + } else { + $externalTool + ->setLaunchUrl($formValues['launch_url']) + ->setConsumerKey( + empty($formValues['consumer_key']) ? null : $formValues['consumer_key'] + ) + ->setSharedSecret( + empty($formValues['shared_secret']) ? null : $formValues['shared_secret'] + ); + } + $em->persist($externalTool); $em->flush(); diff --git a/plugin/ims_lti/edit.php b/plugin/ims_lti/edit.php index 7d562cc5dd..47b279b36b 100644 --- a/plugin/ims_lti/edit.php +++ b/plugin/ims_lti/edit.php @@ -37,8 +37,12 @@ if ($form->validate()) { $tool ->setName($formValues['name']) - ->setDescription($formValues['description']) - ->setCustomParams($formValues['custom_params']) + ->setDescription( + empty($formValues['description']) ? null : $formValues['description'] + ) + ->setCustomParams( + empty($formValues['custom_params']) ? null : $formValues['custom_params'] + ) ->setPrivacy( !empty($formValues['share_name']), !empty($formValues['share_email']), @@ -48,8 +52,12 @@ if ($form->validate()) { if (null === $tool->getParent()) { $tool ->setLaunchUrl($formValues['launch_url']) - ->setConsumerKey($formValues['consumer_key']) - ->setSharedSecret($formValues['shared_secret']); + ->setConsumerKey( + empty($formValues['consumer_key']) ? null : $formValues['consumer_key'] + ) + ->setSharedSecret( + empty($formValues['shared_secret']) ? null : $formValues['shared_secret'] + ); } if (null === $tool->getParent() || diff --git a/plugin/ims_lti/form.php b/plugin/ims_lti/form.php index ded7e2a42b..2d2ca63d87 100644 --- a/plugin/ims_lti/form.php +++ b/plugin/ims_lti/form.php @@ -112,19 +112,23 @@ $params['tool_consumer_instance_contact_email'] = api_get_setting('emailAdminist $params += $tool->parseCustomParams(); -$oauth = new OAuthSimple( - $tool->getConsumerKey(), - $tool->getSharedSecret() -); -$oauth->setAction('post'); -$oauth->setSignatureMethod('HMAC-SHA1'); -$oauth->setParameters($params); -$result = $oauth->sign(array( - 'path' => $tool->getLaunchUrl(), - 'parameters' => array( - 'oauth_callback' => 'about:blank' - ) -)); +if (!empty($tool->getConsumerKey()) && !empty($tool->getSharedSecret())) { + $oauth = new OAuthSimple( + $tool->getConsumerKey(), + $tool->getSharedSecret() + ); + $oauth->setAction('post'); + $oauth->setSignatureMethod('HMAC-SHA1'); + $oauth->setParameters($params); + $result = $oauth->sign(array( + 'path' => $tool->getLaunchUrl(), + 'parameters' => array( + 'oauth_callback' => 'about:blank' + ) + )); + + $params = $result['parameters']; +} ?> @@ -135,7 +139,7 @@ $result = $oauth->sign(array(
$values) { //Dump parameters + foreach ($params as $key => $values) { echo ''.PHP_EOL; } ?> diff --git a/plugin/ims_lti/lang/english.php b/plugin/ims_lti/lang/english.php index a675cf0f5b..80496ef89e 100644 --- a/plugin/ims_lti/lang/english.php +++ b/plugin/ims_lti/lang/english.php @@ -36,3 +36,5 @@ $strings['ShareLauncherPicture'] = 'Share launcher\'s picture'; $strings['NoTool'] = 'Tool not exists'; $strings['ToolAddedOnCourseX'] = 'Tool addeed on course %s.'; $strings['SupportDeppLinkingHelp'] = 'Contact your Tool Provider to verify if Deep Linking support is mandatory'; +$strings['NoAccessToUrl'] = 'No access to URL'; +$strings['LaunchUrlNotFound'] = 'Launch URL not found'; diff --git a/plugin/ims_lti/lang/spanish.php b/plugin/ims_lti/lang/spanish.php index e74433c0e6..08f33b71c9 100644 --- a/plugin/ims_lti/lang/spanish.php +++ b/plugin/ims_lti/lang/spanish.php @@ -36,3 +36,5 @@ $strings['ShareLauncherPicture'] = 'Enviar la foto del usuario'; $strings['NoTool'] = 'La herramienta no existe'; $strings['ToolAddedOnCourseX'] = 'Herramienta agregada en el curso %s.'; $strings['SupportDeppLinkingHelp'] = 'Contacte a su Proveedor de Herramienta para verificar si el soporte a Deep Linking es obligatorio'; +$strings['NoAccessToUrl'] = 'Sin acceso a la URL'; +$strings['LaunchUrlNotFound'] = 'URL de lanzamiento no encontrada'; \ No newline at end of file diff --git a/plugin/ims_lti/src/Form/FrmAdd.php b/plugin/ims_lti/src/Form/FrmAdd.php index db0886a13f..b4c0d46fb0 100644 --- a/plugin/ims_lti/src/Form/FrmAdd.php +++ b/plugin/ims_lti/src/Form/FrmAdd.php @@ -43,8 +43,8 @@ class FrmAdd extends FormValidator if (null === $this->baseTool) { $this->addUrl('launch_url', $plugin->get_lang('LaunchUrl'), true); - $this->addText('consumer_key', $plugin->get_lang('ConsumerKey')); - $this->addText('shared_secret', $plugin->get_lang('SharedSecret')); + $this->addText('consumer_key', $plugin->get_lang('ConsumerKey'), false); + $this->addText('shared_secret', $plugin->get_lang('SharedSecret'), false); } $this->addButtonAdvancedSettings('lti_adv'); diff --git a/plugin/ims_lti/src/Form/FrmEdit.php b/plugin/ims_lti/src/Form/FrmEdit.php index 14a29e4e01..b6db5d9c21 100644 --- a/plugin/ims_lti/src/Form/FrmEdit.php +++ b/plugin/ims_lti/src/Form/FrmEdit.php @@ -60,8 +60,8 @@ class FrmEdit extends FormValidator if (null === $parent) { $this->addUrl('launch_url', $plugin->get_lang('LaunchUrl'), true); - $this->addText('consumer_key', $plugin->get_lang('ConsumerKey')); - $this->addText('shared_secret', $plugin->get_lang('SharedSecret')); + $this->addText('consumer_key', $plugin->get_lang('ConsumerKey'), false); + $this->addText('shared_secret', $plugin->get_lang('SharedSecret'), false); } $this->addButtonAdvancedSettings('lti_adv'); From 099ec4117ed4aa6bd966f1928718fe69a0773723 Mon Sep 17 00:00:00 2001 From: Yannick Warnier Date: Thu, 15 Nov 2018 17:33:44 -0500 Subject: [PATCH 03/14] Security: Fix XSS vulnerability in agenda - see security report 28 - additions --- main/inc/ajax/agenda.ajax.php | 14 +++++++++----- main/inc/lib/agenda.lib.php | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/main/inc/ajax/agenda.ajax.php b/main/inc/ajax/agenda.ajax.php index 390623f605..fdd6164037 100755 --- a/main/inc/ajax/agenda.ajax.php +++ b/main/inc/ajax/agenda.ajax.php @@ -19,6 +19,8 @@ if ($type == 'course') { } $agenda = new Agenda($type); +// get filtered type +$type = $agenda->getType(); switch ($action) { case 'add_event': @@ -26,15 +28,17 @@ switch ($action) { break; } $add_as_announcement = isset($_REQUEST['add_as_annonuncement']) ? $_REQUEST['add_as_annonuncement'] : null; - $comment = isset($_REQUEST['comment']) ? $_REQUEST['comment'] : null; + $title = isset($_REQUEST['title']) ? Security::remove_XSS($_REQUEST['title']) : null; + $content = isset($_REQUEST['content']) ? Security::remove_XSS($_REQUEST['content']) : null; + $comment = isset($_REQUEST['comment']) ? Security::remove_XSS($_REQUEST['comment']) : null; $userToSend = isset($_REQUEST['users_to_send']) ? $_REQUEST['users_to_send'] : []; echo $agenda->addEvent( $_REQUEST['start'], $_REQUEST['end'], $_REQUEST['all_day'], - $_REQUEST['title'], - $_REQUEST['content'], + $title, + $content, $userToSend, $add_as_announcement, null, //$parentEventId = null, @@ -54,8 +58,8 @@ switch ($action) { $_REQUEST['start'], $_REQUEST['end'], $_REQUEST['all_day'], - $_REQUEST['title'], - $_REQUEST['content'] + $title, + $content ); break; case 'delete_event': diff --git a/main/inc/lib/agenda.lib.php b/main/inc/lib/agenda.lib.php index 13ac374840..ce383db97a 100644 --- a/main/inc/lib/agenda.lib.php +++ b/main/inc/lib/agenda.lib.php @@ -160,6 +160,18 @@ class Agenda } } + /** + * Returns the type previously set (and filtered) through setType + * If setType() was not called, then type defaults to "personal" as + * set in the class definition. + */ + public function getType() + { + if (isset($this->type)) { + return $this->type; + } + } + /** * @param int $id */ From 50fa9dc194ec4a3b0d965db504c2e99307c49724 Mon Sep 17 00:00:00 2001 From: Nicolas Ducoulombier Date: Fri, 16 Nov 2018 09:37:50 +0100 Subject: [PATCH 04/14] Minor : change translation variable -refs BT#15033 --- main/survey/surveyUtil.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/survey/surveyUtil.class.php b/main/survey/surveyUtil.class.php index 7f340a2d88..37e365d53f 100755 --- a/main/survey/surveyUtil.class.php +++ b/main/survey/surveyUtil.class.php @@ -2848,7 +2848,7 @@ class SurveyUtil ); $actions[] = Display::url( - Display::return_icon('multiplicate_survey.png', get_lang('MultiplicateSurvey')), + Display::return_icon('multiplicate_survey.png', get_lang('MultiplicateQuestions')), $codePath.'survey/survey_list.php?' .http_build_query($params + ['action' => 'multiplicate', 'survey_id' => $survey_id]) ); From a2e63833aeeee637839dbb5c9c1bdcd1567a78f9 Mon Sep 17 00:00:00 2001 From: Julio Montoya Date: Fri, 16 Nov 2018 12:09:36 +0100 Subject: [PATCH 05/14] Fix survey export to xls/csv see BT#15033 --- main/survey/reporting.php | 12 +- main/survey/surveyUtil.class.php | 253 +++++++++++++++++-------------- 2 files changed, 143 insertions(+), 122 deletions(-) diff --git a/main/survey/reporting.php b/main/survey/reporting.php index 8ce4b1fac0..b9eea6ca35 100755 --- a/main/survey/reporting.php +++ b/main/survey/reporting.php @@ -62,21 +62,13 @@ if (!empty($exportReport) && !empty($format)) { switch ($format) { case 'xls': $filename = 'survey_results_'.$survey_id.'.xlsx'; - $data = SurveyUtil::export_complete_report_xls( - $survey_data, - $filename, - $userId - ); + $data = SurveyUtil::export_complete_report_xls($survey_data, $filename, $userId); exit; break; case 'csv': default: - $data = SurveyUtil::export_complete_report( - $survey_data, - $userId - ); + $data = SurveyUtil::export_complete_report($survey_data, $userId); $filename = 'survey_results_'.$survey_id.'.csv'; - header('Content-type: application/octet-stream'); header('Content-Type: application/force-download'); diff --git a/main/survey/surveyUtil.class.php b/main/survey/surveyUtil.class.php index 37e365d53f..c99310402a 100755 --- a/main/survey/surveyUtil.class.php +++ b/main/survey/surveyUtil.class.php @@ -225,7 +225,7 @@ class SurveyUtil */ public static function handle_reporting_actions($survey_data, $people_filled) { - $action = isset($_GET['action']) ? $_GET['action'] : null; + $action = isset($_GET['action']) ? $_GET['action'] : ''; // Getting the number of question $temp_questions_data = SurveyManager::get_questions($_GET['survey_id']); @@ -243,20 +243,22 @@ class SurveyUtil // Counting the number of questions that are relevant for the reporting $survey_data['number_of_questions'] = count($questions_data); - if ($action == 'questionreport') { - self::display_question_report($survey_data); - } - if ($action == 'userreport') { - self::display_user_report($people_filled, $survey_data); - } - if ($action == 'comparativereport') { - self::display_comparative_report(); - } - if ($action == 'completereport') { - self::display_complete_report($survey_data); - } - if ($action == 'deleteuserreport') { - self::delete_user_report($_GET['survey_id'], $_GET['user']); + switch ($action) { + case 'questionreport': + self::display_question_report($survey_data); + break; + case 'userreport': + self::display_user_report($people_filled, $survey_data); + break; + case 'comparativereport': + self::display_comparative_report(); + break; + case 'completereport': + self::display_complete_report($survey_data); + break; + case 'deleteuserreport': + self::delete_user_report($_GET['survey_id'], $_GET['user']); + break; } } @@ -373,7 +375,7 @@ class SurveyUtil echo ' + $method) { + $selected = ""; + if ($name == $sig_method->get_name()) { + $selected = " selected='selected'"; + } + print "\n"; +} +?> + +

Enter The Endpoint to Test

+endpoint:
+Note: You can include query parameters in there to have them parsed in and signed too +

Enter Your Consumer Key / Secret

+consumer key:
+consumer secret:
+dump request, don't redirect: />
+make a token request (don't forget to copy down the values you get) + +

Enter Your Request Token / Secret

+token:
+token secret:
+

Don't forget to update your endpoint to point at the auth or access token url

+try to authorize this token:
+try to get an access token:
+ +

Currently Supported Signature Methods

+

Current signing method is: get_name() ?>

+
    + $method) { + + print "
  • $key"; + if ($key != $sig_method->get_name()) { + print "(switch)"; + } + print "
  • \n"; +} +?> +
+ +get_name()) { + // passing test_server as a dummy referecne + print "
" . $sig_method->fetch_private_cert($test_server). "
\n"; + print "
" . $sig_method->fetch_public_cert($test_server) . "
\n"; +} +?> + +

Further Resources

+

There is also a test server implementation in here.

+

The code running this example can be downloaded from the PHP section of the OAuth google code project: http://code.google.com/p/oauth/ + diff --git a/plugin/ims_lti/vendor/oauth1/example/common.inc.php b/plugin/ims_lti/vendor/oauth1/example/common.inc.php new file mode 100644 index 0000000000..39a7c4de4d --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/example/common.inc.php @@ -0,0 +1,26 @@ +add_signature_method($hmac_method); +$test_server->add_signature_method($plaintext_method); +$test_server->add_signature_method($rsa_method); + +$sig_methods = $test_server->get_signature_methods(); +?> diff --git a/plugin/ims_lti/vendor/oauth1/example/echo_api.php b/plugin/ims_lti/vendor/oauth1/example/echo_api.php new file mode 100644 index 0000000000..a79173f268 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/example/echo_api.php @@ -0,0 +1,21 @@ +verify_request($req); + + // lsit back the non-OAuth params + $total = array(); + foreach($req->get_parameters() as $k => $v) { + if (substr($k, 0, 5) == "oauth") continue; + $total[] = urlencode($k) . "=" . urlencode($v); + } + print implode("&", $total); +} catch (OAuthException $e) { + print($e->getMessage() . "\n


\n"); + print_r($req); + die(); +} + +?> diff --git a/plugin/ims_lti/vendor/oauth1/example/index.php b/plugin/ims_lti/vendor/oauth1/example/index.php new file mode 100644 index 0000000000..11feacadbe --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/example/index.php @@ -0,0 +1,108 @@ +sign_request($sig_method, $test_consumer, NULL); + +$acc_req = OAuthRequest::from_consumer_and_token($test_consumer, $req_token, "GET", $base_url . "/access_token.php"); +$acc_req->sign_request($sig_method, $test_consumer, $req_token); + +$echo_req = OAuthRequest::from_consumer_and_token($test_consumer, $acc_token, "GET", $base_url . "/echo_api.php", array("method"=> "foo%20bar", "bar" => "baz")); +$echo_req->sign_request($sig_method, $test_consumer, $acc_token); + +?> + + +OAuth Test Server + + + +

OAuth Test Server

+

Instructions for Use

+

This is a test server with a predefined static set of keys and tokens, you can make your requests using them to test your code (and mine ;)).

+

Your Consumer Key / Secret

+
    +
  • consumer key: key
  • +
  • consumer secret: secret
  • +
+

Use this key and secret for all your requests.

+

Getting a Request Token

+ +
    +
  • request token endpoint:
  • +
+ +

A successful request will return the following:

+

oauth_token=requestkey&oauth_token_secret=requestsecret

+ +

An unsuccessful request will attempt to describe what went wrong.

+ +

Example

+ + +

Getting an Access Token

+

The Request Token provided above is already authorized, you may use it to request an Access Token right away.

+ +
    +
  • access token endpoint:
  • +
+ +

A successful request will return the following:

+

oauth_token=accesskey&oauth_token_secret=accesssecret

+ +

An unsuccessful request will attempt to describe what went wrong.

+ +

Example

+ + +

Making Authenticated Calls

+

Using your Access Token you can make authenticated calls.

+ +
    +
  • api endpoint:
  • +
+

+A successful request will echo the non-OAuth parameters sent to it, for example:

+

method=foo&bar=baz

+

An unsuccessful request will attempt to describe what went wrong.

+ +

Example

+ + +

Currently Supported Signature Methods

+

Current signing method is:

+
    +get_signature_methods(); +foreach ($sig_methods as $key => $method) { + print "
  • $key"; + if ($key != $sig_method->get_name()) { + print "(switch)"; + } + print "
  • \n"; +} +?> +
+ +get_name()) { + print "
" . $sig_method->fetch_private_cert($req_req) . "
\n"; + print "
" . $sig_method->fetch_public_cert($req_req) . "
\n"; +} +?> + +

Further Resources

+

There is also a test client implementation in here.

+

The code running this example can be downloaded from the PHP section of the OAuth google code project: http://code.google.com/p/oauth/ + diff --git a/plugin/ims_lti/vendor/oauth1/example/request_token.php b/plugin/ims_lti/vendor/oauth1/example/request_token.php new file mode 100644 index 0000000000..f6c02f488e --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/example/request_token.php @@ -0,0 +1,14 @@ +fetch_request_token($req); + print $token; +} catch (OAuthException $e) { + print($e->getMessage() . "\n


\n"); + print_r($req); + die(); +} + +?> diff --git a/plugin/ims_lti/vendor/oauth1/init.php b/plugin/ims_lti/vendor/oauth1/init.php new file mode 100644 index 0000000000..c744d7491a --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/init.php @@ -0,0 +1,5 @@ +provided_base_string = $bs; } + public function get_signature_base_string() { return $this->provided_base_string; } +} diff --git a/plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthDataStore.php b/plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthDataStore.php new file mode 100644 index 0000000000..cd50e97901 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthDataStore.php @@ -0,0 +1,57 @@ +consumer = new OAuthConsumer("key", "secret", NULL); + $this->request_token = new OAuthToken("requestkey", "requestsecret", 1); + $this->access_token = new OAuthToken("accesskey", "accesssecret", 1); + $this->nonce = "nonce"; + } + + function lookup_consumer($consumer_key) { + if ($consumer_key == $this->consumer->key) return $this->consumer; + return NULL; + } + + function lookup_token($consumer, $token_type, $token) { + $token_attrib = $token_type . "_token"; + if ($consumer->key == $this->consumer->key + && $token == $this->$token_attrib->key) { + return $this->$token_attrib; + } + return NULL; + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + if ($consumer->key == $this->consumer->key + && (($token && $token->key == $this->request_token->key) + || ($token && $token->key == $this->access_token->key)) + && $nonce == $this->nonce) { + return $this->nonce; + } + return NULL; + } + + function new_request_token($consumer, $callback = null) { + if ($consumer->key == $this->consumer->key) { + return $this->request_token; + } + return NULL; + } + + function new_access_token($token, $consumer, $verifier = null) { + if ($consumer->key == $this->consumer->key + && $token->key == $this->request_token->key) { + return $this->access_token; + } + return NULL; + } +} \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthSignatureMethod_RSA_SHA1.php b/plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthSignatureMethod_RSA_SHA1.php new file mode 100644 index 0000000000..aa893c8ec7 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthSignatureMethod_RSA_SHA1.php @@ -0,0 +1,47 @@ +assertEquals('OAuthConsumer[key=key,secret=secret]', (string) $consumer); + } +} \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthRequestTest.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthRequestTest.php new file mode 100644 index 0000000000..83e2c5b3cf --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthRequestTest.php @@ -0,0 +1,329 @@ +'foo')); + $this->assertEquals( 'foo', $request->get_parameter('test'), 'Failed to read back parameter'); + + $request = new OAuthRequest('', '', array('test'=>array('foo', 'bar'))); + $this->assertEquals( array('foo', 'bar'), $request->get_parameter('test'), 'Failed to read back parameter'); + + + $request = new OAuthRequest('', '', array('test'=>'foo', 'bar'=>'baz')); + $this->assertEquals( 'foo', $request->get_parameter('test'), 'Failed to read back parameter'); + $this->assertEquals( 'baz', $request->get_parameter('bar'), 'Failed to read back parameter'); + } + + public function testGetAllParameters() { + // Yes, a awesomely boring test.. But if this doesn't work, the other tests is unreliable + $request = new OAuthRequest('', '', array('test'=>'foo')); + $this->assertEquals( array('test'=>'foo'), $request->get_parameters(), 'Failed to read back parameters'); + + $request = new OAuthRequest('', '', array('test'=>'foo', 'bar'=>'baz')); + $this->assertEquals( array('test'=>'foo', 'bar'=>'baz'), $request->get_parameters(), 'Failed to read back parameters'); + + $request = new OAuthRequest('', '', array('test'=>array('foo', 'bar'))); + $this->assertEquals( array('test'=>array('foo', 'bar')), $request->get_parameters(), 'Failed to read back parameters'); + } + + public function testSetParameters() { + $request = new OAuthRequest('', ''); + $this->assertEquals( NULL, $request->get_parameter('test'), 'Failed to assert that non-existing parameter is NULL'); + + $request->set_parameter('test', 'foo'); + $this->assertEquals( 'foo', $request->get_parameter('test'), 'Failed to set single-entry parameter'); + + $request->set_parameter('test', 'bar'); + $this->assertEquals( array('foo', 'bar'), $request->get_parameter('test'), 'Failed to set single-entry parameter'); + + $request->set_parameter('test', 'bar', false); + $this->assertEquals( 'bar', $request->get_parameter('test'), 'Failed to set single-entry parameter'); + } + + public function testUnsetParameter() { + $request = new OAuthRequest('', ''); + $this->assertEquals( NULL, $request->get_parameter('test')); + + $request->set_parameter('test', 'foo'); + $this->assertEquals( 'foo', $request->get_parameter('test')); + + $request->unset_parameter('test'); + $this->assertEquals( NULL, $request->get_parameter('test'), 'Failed to unset parameter'); + } + + public function testCreateRequestFromConsumerAndToken() { + $cons = new OAuthConsumer('key', 'kd94hf93k423kf44'); + $token = new OAuthToken('token', 'pfkkdhi9sl3r4s00'); + + $request = OAuthRequest::from_consumer_and_token($cons, $token, 'POST', 'http://example.com'); + $this->assertEquals('POST', $request->get_normalized_http_method()); + $this->assertEquals('http://example.com', $request->get_normalized_http_url()); + $this->assertEquals('1.0', $request->get_parameter('oauth_version')); + $this->assertEquals($cons->key, $request->get_parameter('oauth_consumer_key')); + $this->assertEquals($token->key, $request->get_parameter('oauth_token')); + $this->assertEquals(time(), $request->get_parameter('oauth_timestamp')); + $this->assertRegExp('/[0-9a-f]{32}/', $request->get_parameter('oauth_nonce')); + // We don't know what the nonce will be, except it'll be md5 and hence 32 hexa digits + + $request = OAuthRequest::from_consumer_and_token($cons, $token, 'POST', 'http://example.com', array('oauth_nonce'=>'foo')); + $this->assertEquals('foo', $request->get_parameter('oauth_nonce')); + + $request = OAuthRequest::from_consumer_and_token($cons, NULL, 'POST', 'http://example.com', array('oauth_nonce'=>'foo')); + $this->assertNull($request->get_parameter('oauth_token')); + + // Test that parameters given in the $http_url instead of in the $parameters-parameter + // will still be picked up + $request = OAuthRequest::from_consumer_and_token($cons, $token, 'POST', 'http://example.com/?foo=bar'); + $this->assertEquals('http://example.com/', $request->get_normalized_http_url()); + $this->assertEquals('bar', $request->get_parameter('foo')); + } + + public function testBuildRequestFromPost() { + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'foo=bar&baz=blargh'); + $this->assertEquals(array('foo'=>'bar','baz'=>'blargh'), OAuthRequest::from_request()->get_parameters(), 'Failed to parse POST parameters'); + } + + public function testBuildRequestFromGet() { + OAuthTestUtils::build_request('GET', 'http://testbed/test?foo=bar&baz=blargh'); + $this->assertEquals(array('foo'=>'bar','baz'=>'blargh'), OAuthRequest::from_request()->get_parameters(), 'Failed to parse GET parameters'); + } + + public function testBuildRequestFromHeader() { + $test_header = 'OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"'; + OAuthTestUtils::build_request('POST', 'http://testbed/test', '', $test_header); + $this->assertEquals(array('oauth_foo'=>'bar','oauth_baz'=>'bla,rgh'), OAuthRequest::from_request()->get_parameters(), 'Failed to split auth-header correctly'); + } + + public function testHasProperParameterPriority() { + $test_header = 'OAuth realm="",oauth_foo=header'; + OAuthTestUtils::build_request('POST', 'http://testbed/test?oauth_foo=get', 'oauth_foo=post', $test_header); + $this->assertEquals('header', OAuthRequest::from_request()->get_parameter('oauth_foo'), 'Loaded parameters in with the wrong priorities'); + + OAuthTestUtils::build_request('POST', 'http://testbed/test?oauth_foo=get', 'oauth_foo=post'); + $this->assertEquals('post', OAuthRequest::from_request()->get_parameter('oauth_foo'), 'Loaded parameters in with the wrong priorities'); + + OAuthTestUtils::build_request('POST', 'http://testbed/test?oauth_foo=get'); + $this->assertEquals('get', OAuthRequest::from_request()->get_parameter('oauth_foo'), 'Loaded parameters in with the wrong priorities'); + } + + public function testNormalizeHttpMethod() { + OAuthTestUtils::build_request('POST', 'http://testbed/test'); + $this->assertEquals('POST', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: POST'); + + OAuthTestUtils::build_request('post', 'http://testbed/test'); + $this->assertEquals('POST', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: post'); + + OAuthTestUtils::build_request('GET', 'http://testbed/test'); + $this->assertEquals('GET', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: GET'); + + OAuthTestUtils::build_request('PUT', 'http://testbed/test'); + $this->assertEquals('PUT', OAuthRequest::from_request()->get_normalized_http_method(), 'Failed to normalize HTTP method: PUT'); + } + + public function testNormalizeParameters() { + // This is mostly repeats of OAuthUtilTest::testParseParameters & OAuthUtilTest::TestBuildHttpQuery + + // Tests taken from + // http://wiki.oauth.net/TestCases ("Normalize Request Parameters") + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'name'); + $this->assertEquals( 'name=', OAuthRequest::from_request()->get_signable_parameters()); + + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=b'); + $this->assertEquals( 'a=b', OAuthRequest::from_request()->get_signable_parameters()); + + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=b&c=d'); + $this->assertEquals( 'a=b&c=d', OAuthRequest::from_request()->get_signable_parameters()); + + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=x%21y&a=x+y'); + $this->assertEquals( 'a=x%20y&a=x%21y', OAuthRequest::from_request()->get_signable_parameters()); + + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'x%21y=a&x=a'); + $this->assertEquals( 'x=a&x%21y=a', OAuthRequest::from_request()->get_signable_parameters()); + + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'a=1&c=hi there&f=25&f=50&f=a&z=p&z=t'); + $this->assertEquals( 'a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t', OAuthRequest::from_request()->get_signable_parameters()); + } + + public function testNormalizeHttpUrl() { + OAuthTestUtils::build_request('POST', 'http://example.com'); + $this->assertEquals('http://example.com', OAuthRequest::from_request()->get_normalized_http_url()); + + OAuthTestUtils::build_request('POST', 'https://example.com'); + $this->assertEquals('https://example.com', OAuthRequest::from_request()->get_normalized_http_url()); + + // Tests that http on !80 and https on !443 keeps the port + OAuthTestUtils::build_request('POST', 'http://example.com:8080'); + $this->assertEquals('http://example.com:8080', OAuthRequest::from_request()->get_normalized_http_url()); + + OAuthTestUtils::build_request('POST', 'https://example.com:80'); + $this->assertEquals('https://example.com:80', OAuthRequest::from_request()->get_normalized_http_url()); + + OAuthTestUtils::build_request('POST', 'http://example.com:443'); + $this->assertEquals('http://example.com:443', OAuthRequest::from_request()->get_normalized_http_url()); + + OAuthTestUtils::build_request('POST', 'http://Example.COM'); + $this->assertEquals('http://example.com', OAuthRequest::from_request()->get_normalized_http_url()); + + // Emulate silly behavior by some clients, where there Host header includes the port + OAuthTestUtils::build_request('POST', 'http://example.com'); + $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT']; + $this->assertEquals('http://example.com', OAuthRequest::from_request()->get_normalized_http_url()); + } + + public function testBuildPostData() { + OAuthTestUtils::build_request('POST', 'http://example.com'); + $this->assertEquals('', OAuthRequest::from_request()->to_postdata()); + + OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar'); + $this->assertEquals('foo=bar', OAuthRequest::from_request()->to_postdata()); + + OAuthTestUtils::build_request('GET', 'http://example.com?foo=bar'); + $this->assertEquals('foo=bar', OAuthRequest::from_request()->to_postdata()); + } + + public function testBuildUrl() { + OAuthTestUtils::build_request('POST', 'http://example.com'); + $this->assertEquals('http://example.com', OAuthRequest::from_request()->to_url()); + + OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar'); + $this->assertEquals('http://example.com?foo=bar', OAuthRequest::from_request()->to_url()); + + OAuthTestUtils::build_request('GET', 'http://example.com?foo=bar'); + $this->assertEquals('http://example.com?foo=bar', OAuthRequest::from_request()->to_url()); + } + + public function testConvertToString() { + OAuthTestUtils::build_request('POST', 'http://example.com'); + $this->assertEquals('http://example.com', (string) OAuthRequest::from_request()); + + OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar'); + $this->assertEquals('http://example.com?foo=bar', (string) OAuthRequest::from_request()); + + OAuthTestUtils::build_request('GET', 'http://example.com?foo=bar'); + $this->assertEquals('http://example.com?foo=bar', (string) OAuthRequest::from_request()); + } + + public function testBuildHeader() { + OAuthTestUtils::build_request('POST', 'http://example.com'); + $this->assertEquals('Authorization: OAuth', OAuthRequest::from_request()->to_header()); + $this->assertEquals('Authorization: OAuth realm="test"', OAuthRequest::from_request()->to_header('test')); + + OAuthTestUtils::build_request('POST', 'http://example.com', 'foo=bar'); + $this->assertEquals('Authorization: OAuth', OAuthRequest::from_request()->to_header()); + $this->assertEquals('Authorization: OAuth realm="test"', OAuthRequest::from_request()->to_header('test')); + + OAuthTestUtils::build_request('POST', 'http://example.com', 'oauth_test=foo'); + $this->assertEquals('Authorization: OAuth oauth_test="foo"', OAuthRequest::from_request()->to_header()); + $this->assertEquals('Authorization: OAuth realm="test",oauth_test="foo"', OAuthRequest::from_request()->to_header('test')); + + // Is headers supposted to be Urlencoded. More to the point: + // Should it be baz = bla,rgh or baz = bla%2Crgh ?? + // - morten.fangel + OAuthTestUtils::build_request('POST', 'http://example.com', '', 'OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"'); + $this->assertEquals('Authorization: OAuth oauth_foo="bar",oauth_baz="bla%2Crgh"', OAuthRequest::from_request()->to_header()); + $this->assertEquals('Authorization: OAuth realm="test",oauth_foo="bar",oauth_baz="bla%2Crgh"', OAuthRequest::from_request()->to_header('test')); + } + + public function testWontBuildHeaderWithArrayInput() { + $this->setExpectedException('OAuthException'); + OAuthTestUtils::build_request('POST', 'http://example.com', 'oauth_foo=bar&oauth_foo=baz'); + OAuthRequest::from_request()->to_header(); + } + + public function testBuildBaseString() { + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'n=v'); + $this->assertEquals('POST&http%3A%2F%2Ftestbed%2Ftest&n%3Dv', OAuthRequest::from_request()->get_signature_base_string()); + + OAuthTestUtils::build_request('POST', 'http://testbed/test', 'n=v&n=v2'); + $this->assertEquals('POST&http%3A%2F%2Ftestbed%2Ftest&n%3Dv%26n%3Dv2', OAuthRequest::from_request()->get_signature_base_string()); + + OAuthTestUtils::build_request('GET', 'http://example.com?n=v'); + $this->assertEquals('GET&http%3A%2F%2Fexample.com&n%3Dv', OAuthRequest::from_request()->get_signature_base_string()); + + $params = 'oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_timestamp=1191242090'; + $params .= '&oauth_nonce=hsu94j3884jdopsl&oauth_signature_method=PLAINTEXT&oauth_signature=ignored'; + OAuthTestUtils::build_request('POST', 'https://photos.example.net/request_token', $params); + $this->assertEquals('POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_' + .'consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884j' + .'dopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestam' + .'p%3D1191242090%26oauth_version%3D1.0', + OAuthRequest::from_request()->get_signature_base_string()); + + $params = 'file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03'; + $params .= '&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh'; + $params .= '&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1'; + OAuthTestUtils::build_request('GET', 'http://photos.example.net/photos?'.$params); + $this->assertEquals('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation' + .'.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%' + .'3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26o' + .'auth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jd' + .'k%26oauth_version%3D1.0%26size%3Doriginal', + OAuthRequest::from_request()->get_signature_base_string()); + } + + public function testBuildSignature() { + $params = 'file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03'; + $params .= '&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh'; + $params .= '&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1'; + OAuthTestUtils::build_request('GET', 'http://photos.example.net/photos?'.$params); + $r = OAuthRequest::from_request(); + + $cons = new OAuthConsumer('key', 'kd94hf93k423kf44'); + $token = new OAuthToken('token', 'pfkkdhi9sl3r4s00'); + + $hmac = new OAuthSignatureMethod_HMAC_SHA1(); + $plaintext = new OAuthSignatureMethod_PLAINTEXT(); + + $this->assertEquals('tR3+Ty81lMeYAr/Fid0kMTYa/WM=', $r->build_signature($hmac, $cons, $token)); + $this->assertEquals('kd94hf93k423kf44&pfkkdhi9sl3r4s00', $r->build_signature($plaintext, $cons, $token)); + } + + public function testSign() { + $params = 'file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03'; + $params .= '&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh'; + $params .= '&oauth_signature=__ignored__&oauth_signature_method=HMAC-SHA1'; + OAuthTestUtils::build_request('GET', 'http://photos.example.net/photos?'.$params); + $r = OAuthRequest::from_request(); + + $cons = new OAuthConsumer('key', 'kd94hf93k423kf44'); + $token = new OAuthToken('token', 'pfkkdhi9sl3r4s00'); + + $hmac = new OAuthSignatureMethod_HMAC_SHA1(); + $plaintext = new OAuthSignatureMethod_PLAINTEXT(); + + // We need to test both what the parameter is, and how the serialized request is.. + + $r->sign_request($hmac, $cons, $token); + $this->assertEquals('HMAC-SHA1', $r->get_parameter('oauth_signature_method')); + $this->assertEquals('tR3+Ty81lMeYAr/Fid0kMTYa/WM=', $r->get_parameter('oauth_signature')); + $expectedPostdata = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&' + . 'oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D&oauth_signature_method=HMAC-SHA1&' + . 'oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original'; + $this->assertEquals( $expectedPostdata, $r->to_postdata()); + + $r->sign_request($plaintext, $cons, $token); + $this->assertEquals('PLAINTEXT', $r->get_parameter('oauth_signature_method')); + $this->assertEquals('kd94hf93k423kf44&pfkkdhi9sl3r4s00', $r->get_parameter('oauth_signature')); + $expectedPostdata = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&' + . 'oauth_signature=kd94hf93k423kf44%26pfkkdhi9sl3r4s00&oauth_signature_method=PLAINTEXT&' + . 'oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original'; + $this->assertEquals( $expectedPostdata, $r->to_postdata()); + + } +} + +?> \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthServerTest.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthServerTest.php new file mode 100644 index 0000000000..d4d1192f8e --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthServerTest.php @@ -0,0 +1,225 @@ +consumer = new OAuthConsumer('key', 'secret'); + $this->request_token = new OAuthToken('requestkey', 'requestsecret'); + $this->access_token = new OAuthToken('accesskey', 'accesssecret'); + + $this->hmac_sha1 = new OAuthSignatureMethod_HMAC_SHA1(); + $this->plaintext = new OAuthSignatureMethod_PLAINTEXT(); + + $this->server = new OAuthServer( new Mock_OAuthDataStore() ); + $this->server->add_signature_method( $this->hmac_sha1 ); + $this->server->add_signature_method( $this->plaintext ); + } + + public function testAcceptValidRequest() { + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + list($consumer, $token) = $this->server->verify_request( $request ); + $this->assertEquals( $this->consumer, $consumer ); + $this->assertEquals( $this->access_token, $token ); + + $request->sign_request( $this->hmac_sha1, $this->consumer, $this->access_token ); + list($consumer, $token) = $this->server->verify_request( $request ); + $this->assertEquals( $this->consumer, $consumer ); + $this->assertEquals( $this->access_token, $token ); + } + + public function testAcceptRequestWithoutVersion() { + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->unset_parameter('oauth_version'); + $request->sign_request( $this->hmac_sha1, $this->consumer, $this->access_token ); + + $this->server->verify_request( $request ); + } + + public function testRejectRequestSignedWithRequestToken() { + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->request_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->request_token ); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request( $request ); + } + + public function testRejectRequestWithMissingParameters() { + // The list of required parameters is taken from + // Chapter 7 ("Accessing Protected Resources") + + $required_parameters = array( + 'oauth_consumer_key', + 'oauth_token', + 'oauth_signature_method', + 'oauth_signature', + 'oauth_timestamp', + 'oauth_nonce' + ); + + foreach( $required_parameters AS $required ) { + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + try { + $request->unset_parameter( $required ); + $this->server->verify_request($request); + $this->fail('Allowed a request without `' . $required . '`'); + } catch( OAuthException $e ) { /* expected */ } + } + } + + public function testRejectPastTimestamp() { + // We change the timestamp to be 10 hours ago, it should throw an exception + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->set_parameter( 'oauth_timestamp', $request->get_parameter('oauth_timestamp') - 10*60*60, false); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request($request); + } + + public function testRejectFutureTimestamp() { + // We change the timestamp to be 10 hours in the future, it should throw an exception + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->set_parameter( 'oauth_timestamp', $request->get_parameter('oauth_timestamp') + 10*60*60, false); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request($request); + } + + public function testRejectUsedNonce() { + // We give a known nonce and should see an exception + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + // The Mock datastore is set to say that the `nonce` nonce is known + $request->set_parameter( 'oauth_nonce', 'nonce', false); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request($request); + } + + public function testRejectInvalidSignature() { + // We change the signature post-signing to be something invalid + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + $request->set_parameter( 'oauth_signature', '__whatever__', false); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request($request); + } + + public function testRejectInvalidConsumer() { + // We use the consumer-key "unknown", which isn't known by the datastore. + + $unknown_consumer = new OAuthConsumer('unknown', '__unused__'); + + $request = OAuthRequest::from_consumer_and_token( $unknown_consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $unknown_consumer, $this->access_token ); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request( $request ); + } + + public function testRejectInvalidToken() { + // We use the access-token "unknown" which isn't known by the datastore + + $unknown_token = new OAuthToken('unknown', '__unused__'); + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $unknown_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $unknown_token ); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request( $request ); + } + + public function testRejectUnknownSignatureMethod() { + // We use a server that only supports HMAC-SHA1, but requests with PLAINTEXT signature + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + + $server = new OAuthServer( new Mock_OAuthDataStore() ); + $server->add_signature_method( $this->hmac_sha1 ); + + $this->setExpectedException('OAuthException'); + $server->verify_request( $request ); + } + + public function testRejectUnknownVersion() { + // We use the version "1.0a" which isn't "1.0", so reject the request + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + $request->set_parameter('oauth_version', '1.0a', false); + + $this->setExpectedException('OAuthException'); + $this->server->verify_request( $request ); + } + + public function testCreateRequestToken() { + // We request a new Request Token + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, NULL, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, NULL ); + + $token = $this->server->fetch_request_token($request); + $this->assertEquals($this->request_token, $token); + } + + public function testRejectSignedRequestTokenRequest() { + // We request a new Request Token, but the request is signed with a token which should fail + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->request_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->request_token ); + + $this->setExpectedException('OAuthException'); + $token = $this->server->fetch_request_token($request); + } + + public function testCreateAccessToken() { + // We request a new Access Token + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->request_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->request_token ); + + $token = $this->server->fetch_access_token($request); + $this->assertEquals($this->access_token, $token); + } + + public function testRejectUnsignedAccessTokenRequest() { + // We request a new Access Token, but we didn't sign the request with a Access Token + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, NULL, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, NULL ); + + $this->setExpectedException('OAuthException'); + $token = $this->server->fetch_access_token($request); + } + + public function testRejectAccessTokenSignedAccessTokenRequest() { + // We request a new Access Token, but the request is signed with an access token, so fail! + + $request = OAuthRequest::from_consumer_and_token( $this->consumer, $this->access_token, 'POST', 'http://example.com'); + $request->sign_request( $this->plaintext, $this->consumer, $this->access_token ); + + $this->setExpectedException('OAuthException'); + $token = $this->server->fetch_access_token($request); + } +} diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodHmacSha1Test.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodHmacSha1Test.php new file mode 100644 index 0000000000..3d5cc7524a --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodHmacSha1Test.php @@ -0,0 +1,60 @@ +method = new OAuthSignatureMethod_HMAC_SHA1(); + } + + public function testIdentifyAsHmacSha1() { + $this->assertEquals('HMAC-SHA1', $this->method->get_name()); + } + + public function testBuildSignature() { + // Tests taken from http://wiki.oauth.net/TestCases section 9.2 ("HMAC-SHA1") + $request = new Mock_OAuthBaseStringRequest('bs'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = NULL; + $this->assertEquals('egQqG5AJep5sJ7anhXju1unge2I=', $this->method->build_signature( $request, $consumer, $token) ); + + $request = new Mock_OAuthBaseStringRequest('bs'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = new OAuthToken('__unused__', 'ts'); + $this->assertEquals('VZVjXceV7JgPq/dOTnNmEfO0Fv8=', $this->method->build_signature( $request, $consumer, $token) ); + + $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26' + . 'oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26' + . 'oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal'); + $consumer = new OAuthConsumer('__unused__', 'kd94hf93k423kf44'); + $token = new OAuthToken('__unused__', 'pfkkdhi9sl3r4s00'); + $this->assertEquals('tR3+Ty81lMeYAr/Fid0kMTYa/WM=', $this->method->build_signature( $request, $consumer, $token) ); + } + + public function testVerifySignature() { + // Tests taken from http://wiki.oauth.net/TestCases section 9.2 ("HMAC-SHA1") + $request = new Mock_OAuthBaseStringRequest('bs'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = NULL; + $signature = 'egQqG5AJep5sJ7anhXju1unge2I='; + $this->assertTrue( $this->method->check_signature( $request, $consumer, $token, $signature) ); + + $request = new Mock_OAuthBaseStringRequest('bs'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = new OAuthToken('__unused__', 'ts'); + $signature = 'VZVjXceV7JgPq/dOTnNmEfO0Fv8='; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + + $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26' + . 'oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26' + . 'oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal'); + $consumer = new OAuthConsumer('__unused__', 'kd94hf93k423kf44'); + $token = new OAuthToken('__unused__', 'pfkkdhi9sl3r4s00'); + $signature = 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + + } +} \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodPlaintextTest.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodPlaintextTest.php new file mode 100644 index 0000000000..d096243313 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodPlaintextTest.php @@ -0,0 +1,79 @@ +method = new OAuthSignatureMethod_PLAINTEXT(); + } + + public function testIdentifyAsPlaintext() { + $this->assertEquals('PLAINTEXT', $this->method->get_name()); + } + + public function testBuildSignature() { + // Tests based on from http://wiki.oauth.net/TestCases section 9.2 ("HMAC-SHA1") + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = NULL; + $this->assertEquals('cs&', $this->method->build_signature( $request, $consumer, $token) ); + + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = new OAuthToken('__unused__', 'ts'); + $this->assertEquals('cs&ts', $this->method->build_signature( $request, $consumer, $token) ); + + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'kd94hf93k423kf44'); + $token = new OAuthToken('__unused__', 'pfkkdhi9sl3r4s00'); + $this->assertEquals('kd94hf93k423kf44&pfkkdhi9sl3r4s00', $this->method->build_signature( $request, $consumer, $token) ); + + // Tests taken from Chapter 9.4.1 ("Generating Signature") from the spec + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'djr9rjt0jd78jf88'); + $token = new OAuthToken('__unused__', 'jjd999tj88uiths3'); + $this->assertEquals('djr9rjt0jd78jf88&jjd999tj88uiths3', $this->method->build_signature( $request, $consumer, $token) ); + + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'djr9rjt0jd78jf88'); + $token = new OAuthToken('__unused__', 'jjd99$tj88uiths3'); + $this->assertEquals('djr9rjt0jd78jf88&jjd99%24tj88uiths3', $this->method->build_signature( $request, $consumer, $token) ); + } + + public function testVerifySignature() { + // Tests based on from http://wiki.oauth.net/TestCases section 9.2 ("HMAC-SHA1") + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = NULL; + $signature = 'cs&'; + $this->assertTrue( $this->method->check_signature( $request, $consumer, $token, $signature) ); + + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'cs'); + $token = new OAuthToken('__unused__', 'ts'); + $signature = 'cs&ts'; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'kd94hf93k423kf44'); + $token = new OAuthToken('__unused__', 'pfkkdhi9sl3r4s00'); + $signature = 'kd94hf93k423kf44&pfkkdhi9sl3r4s00'; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + + // Tests taken from Chapter 9.4.1 ("Generating Signature") from the spec + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'djr9rjt0jd78jf88'); + $token = new OAuthToken('__unused__', 'jjd999tj88uiths3'); + $signature = 'djr9rjt0jd78jf88&jjd999tj88uiths3'; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + + $request = new Mock_OAuthBaseStringRequest('__unused__'); + $consumer = new OAuthConsumer('__unused__', 'djr9rjt0jd78jf88'); + $token = new OAuthToken('__unused__', 'jjd99$tj88uiths3'); + $signature = 'djr9rjt0jd78jf88&jjd99%24tj88uiths3'; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + } +} \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodRsaSha1Test.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodRsaSha1Test.php new file mode 100644 index 0000000000..74e5037734 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodRsaSha1Test.php @@ -0,0 +1,43 @@ +method = new Mock_OAuthSignatureMethod_RSA_SHA1(); + } + + public function testIdentifyAsRsaSha1() { + $this->assertEquals('RSA-SHA1', $this->method->get_name()); + } + + public function testBuildSignature() { + if( ! function_exists('openssl_get_privatekey') ) { + $this->markTestSkipped('OpenSSL not available, can\'t test RSA-SHA1 functionality'); + } + + // Tests taken from http://wiki.oauth.net/TestCases section 9.3 ("RSA-SHA1") + $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal'); + $consumer = new OAuthConsumer('dpf43f3p2l4k3l03', '__unused__'); + $token = NULL; + $signature = 'jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE='; + $this->assertEquals($signature, $this->method->build_signature( $request, $consumer, $token) ); + } + + public function testVerifySignature() { + if( ! function_exists('openssl_get_privatekey') ) { + $this->markTestSkipped('OpenSSL not available, can\'t test RSA-SHA1 functionality'); + } + + // Tests taken from http://wiki.oauth.net/TestCases section 9.3 ("RSA-SHA1") + $request = new Mock_OAuthBaseStringRequest('GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal'); + $consumer = new OAuthConsumer('dpf43f3p2l4k3l03', '__unused__'); + $token = NULL; + $signature = 'jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE='; + $this->assertTrue($this->method->check_signature( $request, $consumer, $token, $signature) ); + } +} \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthTokenTest.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthTokenTest.php new file mode 100644 index 0000000000..20d8113af7 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthTokenTest.php @@ -0,0 +1,20 @@ +assertEquals('oauth_token=token&oauth_token_secret=secret', $token->to_string()); + + $token = new OAuthToken('token&', 'secret%'); + $this->assertEquals('oauth_token=token%26&oauth_token_secret=secret%25', $token->to_string()); + } + public function testConvertToString() { + $token = new OAuthToken('token', 'secret'); + $this->assertEquals('oauth_token=token&oauth_token_secret=secret', (string) $token); + + $token = new OAuthToken('token&', 'secret%'); + $this->assertEquals('oauth_token=token%26&oauth_token_secret=secret%25', (string) $token); + } +} \ No newline at end of file diff --git a/plugin/ims_lti/vendor/oauth1/tests/OAuthUtilTest.php b/plugin/ims_lti/vendor/oauth1/tests/OAuthUtilTest.php new file mode 100644 index 0000000000..072c9facd2 --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/OAuthUtilTest.php @@ -0,0 +1,151 @@ +assertEquals('abcABC123', OAuthUtil::urlencode_rfc3986('abcABC123')); + $this->assertEquals('-._~', OAuthUtil::urlencode_rfc3986('-._~')); + $this->assertEquals('%25', OAuthUtil::urlencode_rfc3986('%')); + $this->assertEquals('%2B', OAuthUtil::urlencode_rfc3986('+')); + $this->assertEquals('%0A', OAuthUtil::urlencode_rfc3986("\n")); + $this->assertEquals('%20', OAuthUtil::urlencode_rfc3986(' ')); + $this->assertEquals('%7F', OAuthUtil::urlencode_rfc3986("\x7F")); + //$this->assertEquals('%C2%80', OAuthUtil::urlencode_rfc3986("\x00\x80")); + //$this->assertEquals('%E3%80%81', OAuthUtil::urlencode_rfc3986("\x30\x01")); + + // Last two checks disabled because of lack of UTF-8 support, or lack + // of knowledge from me (morten.fangel) on how to use it properly.. + + // A few tests to ensure code-coverage + $this->assertEquals( '', OAuthUtil::urlencode_rfc3986(NULL)); + $this->assertEquals( '', OAuthUtil::urlencode_rfc3986(new stdClass())); + } + + public function testUrldecode() { + // Tests taken from + // http://wiki.oauth.net/TestCases ("Parameter Encoding") + $this->assertEquals('abcABC123', OAuthUtil::urldecode_rfc3986('abcABC123')); + $this->assertEquals('-._~', OAuthUtil::urldecode_rfc3986('-._~')); + $this->assertEquals('%', OAuthUtil::urldecode_rfc3986('%25')); + $this->assertEquals('+', OAuthUtil::urldecode_rfc3986('%2B')); + $this->assertEquals("\n", OAuthUtil::urldecode_rfc3986('%0A')); + $this->assertEquals(' ', OAuthUtil::urldecode_rfc3986('%20')); + $this->assertEquals("\x7F", OAuthUtil::urldecode_rfc3986('%7F')); + //$this->assertEquals("\x00\x80", OAuthUtil::urldecode_rfc3986('%C2%80')); + //$this->assertEquals("\x30\x01", OAuthUtil::urldecode_rfc3986('%E3%80%81')); + + // Last two checks disabled because of lack of UTF-8 support, or lack + // of knowledge from me (morten.fangel) on how to use it properly.. + } + + public function testParseParameter() { + // Tests taken from + // http://wiki.oauth.net/TestCases ("Normalize Request Parameters") + + $this->assertEquals( + array('name'=>''), + OAuthUtil::parse_parameters('name') + ); + $this->assertEquals( + array('a'=>'b'), + OAuthUtil::parse_parameters('a=b') + ); + $this->assertEquals( + array('a'=>'b','c'=>'d'), + OAuthUtil::parse_parameters('a=b&c=d') + ); + $this->assertEquals( + array('a'=>array('x!y','x y')), + OAuthUtil::parse_parameters('a=x!y&a=x+y') + ); + $this->assertEquals( + array('x!y'=>'a', 'x' =>'a'), + OAuthUtil::parse_parameters('x!y=a&x=a') + ); + } + + public function testBuildHttpQuery() { + // Tests taken from + // http://wiki.oauth.net/TestCases ("Normalize Request Parameters") + $this->assertEquals( + 'name=', + OAuthUtil::build_http_query(array('name'=>'')) + ); + $this->assertEquals( + 'a=b', + OAuthUtil::build_http_query(array('a'=>'b')) + ); + $this->assertEquals( + 'a=b&c=d', + OAuthUtil::build_http_query(array('a'=>'b','c'=>'d')) + ); + $this->assertEquals( + 'a=x%20y&a=x%21y', + OAuthUtil::build_http_query(array('a'=>array('x!y','x y'))) + ); + $this->assertEquals( + 'x=a&x%21y=a', + OAuthUtil::build_http_query(array('x!y'=>'a', 'x' =>'a')) + ); + + // Test taken from the Spec 9.1.1 + $this->assertEquals( + 'a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t', + OAuthUtil::build_http_query(array('a'=>'1', 'c' =>'hi there', 'f'=>array(25, 50, 'a'), 'z'=>array('p','t'))) + ); + + // From issue 164, by hidetaka + // Based on discussion at + // http://groups.google.com/group/oauth/browse_thread/thread/7c698004be0d536/dced7b6c82b917b2?lnk=gst&q=sort# + $this->assertEquals( + 'x=200&x=25&y=B&y=a', + OAuthUtil::build_http_query(array('x'=>array(25, 200), 'y'=>array('a', 'B'))) + ); + } + + public function testSplitHeader() { + $this->assertEquals( + array('oauth_foo'=>'bar','oauth_baz'=>'bla,rgh'), + OAuthUtil::split_header('OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"') + ); + $this->assertEquals( + array(), + OAuthUtil::split_header('OAuth realm="",foo=bar,baz="bla,rgh"') + ); + $this->assertEquals( + array('foo'=>'bar', 'baz'=>'bla,rgh'), + OAuthUtil::split_header('OAuth realm="",foo=bar,baz="bla,rgh"', false) + ); + $this->assertEquals( + array('oauth_foo' => 'hi there'), + OAuthUtil::split_header('OAuth realm="",oauth_foo=hi+there,foo=bar,baz="bla,rgh"') + ); + + } + + public function testGetHeaders() { + if (function_exists('apache_request_headers')) { + $this->markTestSkipped('We assume the apache module is well tested. Since this module is present, no need testing our suplement'); + } + + $_SERVER['HTTP_HOST'] = 'foo'; + $_SERVER['HTTP_X_WHATEVER'] = 'bar'; + $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar'), OAuthUtil::get_headers() ); + + // Test picking up the Content-Type of POST requests running as an Apache module but not having the ARH method + $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar', 'Content-Type'=>'application/x-www-form-urlencoded'), OAuthUtil::get_headers() ); + + // Test picking up the Content-Type of POST requests when using CGI + unset($_SERVER['CONTENT_TYPE']); + $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar'), OAuthUtil::get_headers() ); + $_ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $this->assertEquals( array('Host'=>'foo', 'X-Whatever'=>'bar', 'Content-Type'=>'application/x-www-form-urlencoded'), OAuthUtil::get_headers() ); + } +} diff --git a/plugin/ims_lti/vendor/oauth1/tests/common.php b/plugin/ims_lti/vendor/oauth1/tests/common.php new file mode 100644 index 0000000000..9d789c9cab --- /dev/null +++ b/plugin/ims_lti/vendor/oauth1/tests/common.php @@ -0,0 +1,60 @@ + Date: Fri, 16 Nov 2018 14:40:36 -0500 Subject: [PATCH 14/14] WIP LTI using new oauth lib - refs BT#13469 --- plugin/ims_lti/Entity/ImsLtiTool.php | 32 +++++++++++++++++- plugin/ims_lti/ImsLtiPlugin.php | 37 ++++++++++++++++++++ plugin/ims_lti/form.php | 50 +++++++++++++--------------- 3 files changed, 92 insertions(+), 27 deletions(-) diff --git a/plugin/ims_lti/Entity/ImsLtiTool.php b/plugin/ims_lti/Entity/ImsLtiTool.php index efc4f3c4de..62ae873d8d 100644 --- a/plugin/ims_lti/Entity/ImsLtiTool.php +++ b/plugin/ims_lti/Entity/ImsLtiTool.php @@ -285,13 +285,43 @@ class ImsLtiTool foreach ($strings as $string) { $pairs = explode('=', $string); + $key = self::parseCustomKey($pairs[0]); + $value = $pairs[1]; - $params['custom_'.$pairs[0]] = $pairs[1]; + $params['custom_'.$key] = $value; } return $params; } + /** + * Map the key from custom param. + * + * @param string $key + * + * @return string + */ + private static function parseCustomKey($key) + { + $newKey = ''; + $key = strtolower($key); + $split = str_split($key); + + foreach ($split as $char) { + if ( + ($char >= 'a' && $char <= 'z') || ($char >= '0' && $char <= '9') + ) { + $newKey .= $char; + + continue; + } + + $newKey .= '_'; + } + + return $newKey; + } + /** * Set activeDeepLinking. * diff --git a/plugin/ims_lti/ImsLtiPlugin.php b/plugin/ims_lti/ImsLtiPlugin.php index 3af28bda0c..727d3a4111 100644 --- a/plugin/ims_lti/ImsLtiPlugin.php +++ b/plugin/ims_lti/ImsLtiPlugin.php @@ -506,4 +506,41 @@ class ImsLtiPlugin extends Plugin return (string) $launchUrl; } + + /** + * @param array $params + */ + public function trimParams(array &$params) + { + foreach ($params as $key => $value) { + $newValue = preg_replace('/\s+/', ' ', $value); + + $params[$key] = trim($newValue); + } + } + + /** + * @param ImsLtiTool $tool + * @param array $params + * + * @return array + */ + public function removeUrlParamsFromLaunchParams(ImsLtiTool $tool, array &$params) + { + $urlQuery = parse_url($tool->getLaunchUrl(), PHP_URL_QUERY); + + if (empty($urlQuery)) { + return $params; + } + + $queryParams = []; + parse_str($urlQuery, $queryParams); + $queryKeys = array_keys($queryParams); + + foreach ($queryKeys as $key) { + if (isset($params[$key])) { + unset($params[$key]); + } + } + } } diff --git a/plugin/ims_lti/form.php b/plugin/ims_lti/form.php index 2d2ca63d87..3feade575e 100644 --- a/plugin/ims_lti/form.php +++ b/plugin/ims_lti/form.php @@ -109,26 +109,33 @@ $params['tool_consumer_instance_guid'] = $platformDomain; $params['tool_consumer_instance_name'] = api_get_setting('siteName'); $params['tool_consumer_instance_url'] = api_get_path(WEB_PATH); $params['tool_consumer_instance_contact_email'] = api_get_setting('emailAdministrator'); +$params['oauth_callback'] = 'about:blank'; $params += $tool->parseCustomParams(); +$imsLtiPlugin->trimParams($params); + if (!empty($tool->getConsumerKey()) && !empty($tool->getSharedSecret())) { - $oauth = new OAuthSimple( + $consumer = new OAuthConsumer( $tool->getConsumerKey(), - $tool->getSharedSecret() + $tool->getSharedSecret(), + null + ); + $hmacMethod = new OAuthSignatureMethod_HMAC_SHA1(); + + $request = OAuthRequest::from_consumer_and_token( + $consumer, + '', + 'POST', + $tool->getLaunchUrl(), + $params ); - $oauth->setAction('post'); - $oauth->setSignatureMethod('HMAC-SHA1'); - $oauth->setParameters($params); - $result = $oauth->sign(array( - 'path' => $tool->getLaunchUrl(), - 'parameters' => array( - 'oauth_callback' => 'about:blank' - ) - )); - - $params = $result['parameters']; + $request->sign_request($hmacMethod, $consumer, ''); + + $params = $request->get_parameters(); } + +$imsLtiPlugin->removeUrlParamsFromLaunchParams($tool, $params); ?> @@ -138,19 +145,10 @@ if (!empty($tool->getConsumerKey()) && !empty($tool->getSharedSecret())) { - $values) { - echo ''.PHP_EOL; - } - ?> - + $value) { ?> + + - - +