From c3768f8208f96fb2275fcee796272f19807f3923 Mon Sep 17 00:00:00 2001 From: Andrew Gardener Date: Wed, 30 Sep 2020 13:51:03 -0700 Subject: [PATCH] Add LTI 1.3 --- .gitignore | 1 + Dockerfile | 4 +- Dockerfile-app-unittest | 4 +- app/config/core.php | 4 +- app/config/sql/delta_18.sql | 91 + app/config/sql/ipeer.sql | 5 +- app/config/sql/ipeer_samples_data.sql | 132 +- app/controllers/components/lti_requester.php | 103 - app/controllers/components/lti_verifier.php | 288 --- app/controllers/courses_controller.php | 11 +- app/controllers/lti_controller.php | 297 +-- .../ltitoolregistrations_controller.php | 130 + app/libs/caliper/entity.php | 3 - app/libs/lti.php | 24 + app/libs/lti/launch_data_parser.php | 258 ++ app/libs/lti/lti_cache.php | 47 + app/libs/lti/lti_cookie.php | 37 + app/libs/lti/lti_database.php | 53 + app/libs/lti/names_and_roles_service.php | 136 + .../names_and_roles_service_data_parser.php | 144 ++ app/libs/upgrade_scripts/upgrade_349.php | 58 + app/locale/default.pot | 8 + app/models/course.php | 5 + app/models/event.php | 15 +- app/models/lti_context.php | 152 ++ app/models/lti_nonce.php | 43 + app/models/lti_resource_link.php | 87 + app/models/lti_tool_registration.php | 158 ++ app/models/lti_user.php | 162 ++ app/models/user.php | 18 +- .../caliper/app_controller_hooks.test.php | 2 + app/tests/cases/caliper/course_hooks.test.php | 2 + .../caliper/event_mixeval_hooks.test.php | 2 + .../cases/caliper/event_rubric_hooks.test.php | 2 + .../event_simple_evaulation_hooks.test.php | 2 + .../cases/caliper/event_survey_hooks.test.php | 2 + .../cases/caliper/mixeval_hooks.test.php | 2 + app/tests/cases/caliper/rubric_hooks.test.php | 2 + .../caliper/simple_evaluation_hooks.test.php | 2 + app/tests/cases/caliper/survey_hooks.test.php | 2 + .../components/evaluation_component.test.php | 22 +- .../controllers/accesses_controller.test.php | 46 +- .../controllers/courses_controller.test.php | 5 +- .../departments_controller.test.php | 2 + .../controllers/evaltools_controller.test.php | 2 + .../evaluations_controller.test.php | 10 +- .../controllers/events_controller.test.php | 2 + .../controllers/faculties_controller.test.php | 2 + .../controllers/groups_controller.test.php | 2 + .../controllers/home_controller.test.php | 4 +- .../cases/controllers/lti_controller.test.php | 2183 +++++++++++++++++ .../controllers/mixevals_controller.test.php | 2 + .../oauth_clients_controller.test.php | 2 + .../oauth_tokens_controller.test.php | 2 + .../controllers/rubrics_controller.test.php | 32 +- .../controllers/searchs_controller.test.php | 2 + .../simpleevaluations_controller.test.php | 2 + .../surveygroups_controller.test.php | 2 + .../controllers/surveys_controller.test.php | 2 + .../sysparameters_controller.test.php | 2 + .../cases/controllers/v1_controller.test.php | 24 +- app/tests/cases/models/access.test.php | 26 +- app/tests/cases/models/course.test.php | 2 + .../cases/models/course_department.test.php | 13 +- app/tests/cases/models/department.test.php | 2 + app/tests/cases/models/email_merge.test.php | 2 + .../cases/models/email_schedule.test.php | 2 + .../cases/models/email_template.test.php | 2 + .../cases/models/evaluation_mixeval.test.php | 4 +- .../models/evaluation_mixeval_detail.test.php | 2 + .../cases/models/evaluation_rubric.test.php | 2 + .../models/evaluation_rubric_detail.test.php | 2 + .../cases/models/evaluation_simple.test.php | 2 + .../models/evaluation_submission.test.php | 2 + app/tests/cases/models/event.test.php | 2 + .../cases/models/event_template_type.test.php | 2 + app/tests/cases/models/faculty.test.php | 11 +- app/tests/cases/models/group.test.php | 2 + app/tests/cases/models/group_event.test.php | 2 + .../cases/models/groups_members.test.php | 2 + .../models/lti_tool_registration.test.php | 66 + app/tests/cases/models/mixeval.test.php | 4 +- .../cases/models/mixeval_question.test.php | 4 +- .../models/mixeval_question_desc.test.php | 6 +- app/tests/cases/models/oauth_client.test.php | 12 +- app/tests/cases/models/oauth_token.test.php | 12 +- app/tests/cases/models/penalty.test.php | 18 +- app/tests/cases/models/personalize.test.php | 2 + app/tests/cases/models/question.test.php | 4 +- app/tests/cases/models/response.test.php | 2 + app/tests/cases/models/roles_user.test.php | 11 +- app/tests/cases/models/rubric.test.php | 2 + app/tests/cases/models/rubric_lom.test.php | 2 + .../cases/models/rubrics_criteria.test.php | 2 + .../models/rubrics_criteria_comment.test.php | 2 + .../cases/models/simple_evaluation.test.php | 4 +- app/tests/cases/models/survey.test.php | 2 + app/tests/cases/models/survey_group.test.php | 2 + .../cases/models/survey_group_member.test.php | 2 + .../cases/models/survey_group_set.test.php | 4 +- app/tests/cases/models/survey_input.test.php | 2 + .../cases/models/survey_question.test.php | 4 +- app/tests/cases/models/sys_parameter.test.php | 8 +- app/tests/cases/models/user.test.php | 2 + app/tests/cases/models/user_course.test.php | 10 +- app/tests/cases/models/user_enrol.test.php | 2 + app/tests/fixtures/lti_context_fixture.php | 15 + app/tests/fixtures/lti_nonce_fixture.php | 15 + .../fixtures/lti_resource_link_fixture.php | 15 + .../lti_tool_registration_fixture.php | 16 + app/tests/fixtures/lti_user_fixture.php | 15 + app/views/courses/home.ctp | 16 +- app/views/elements/courses/submenu.ctp | 7 + app/views/lti/index.ctp | 10 - app/views/lti_tool_registrations/add.ctp | 70 + app/views/lti_tool_registrations/edit.ctp | 70 + app/views/lti_tool_registrations/index.ctp | 47 + app/views/pages/admin.ctp | 9 + build/sqlclean/superadmin.sql | 4 +- composer.json | 71 +- composer.lock | 425 +++- docker-compose.yml | 10 +- readme.md | 4 +- version.txt | 2 +- 124 files changed, 5066 insertions(+), 880 deletions(-) create mode 100644 app/config/sql/delta_18.sql delete mode 100644 app/controllers/components/lti_requester.php delete mode 100644 app/controllers/components/lti_verifier.php create mode 100644 app/controllers/ltitoolregistrations_controller.php create mode 100644 app/libs/lti.php create mode 100644 app/libs/lti/launch_data_parser.php create mode 100644 app/libs/lti/lti_cache.php create mode 100644 app/libs/lti/lti_cookie.php create mode 100644 app/libs/lti/lti_database.php create mode 100644 app/libs/lti/names_and_roles_service.php create mode 100644 app/libs/lti/names_and_roles_service_data_parser.php create mode 100644 app/libs/upgrade_scripts/upgrade_349.php create mode 100644 app/models/lti_context.php create mode 100644 app/models/lti_nonce.php create mode 100644 app/models/lti_resource_link.php create mode 100644 app/models/lti_tool_registration.php create mode 100644 app/models/lti_user.php create mode 100644 app/tests/cases/controllers/lti_controller.test.php create mode 100644 app/tests/cases/models/lti_tool_registration.test.php create mode 100644 app/tests/fixtures/lti_context_fixture.php create mode 100644 app/tests/fixtures/lti_nonce_fixture.php create mode 100644 app/tests/fixtures/lti_resource_link_fixture.php create mode 100644 app/tests/fixtures/lti_tool_registration_fixture.php create mode 100644 app/tests/fixtures/lti_user_fixture.php delete mode 100644 app/views/lti/index.ctp create mode 100644 app/views/lti_tool_registrations/add.ctp create mode 100644 app/views/lti_tool_registrations/edit.ctp create mode 100644 app/views/lti_tool_registrations/index.ctp diff --git a/.gitignore b/.gitignore index 08d69b1b5..a0dafff54 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ tags vendor/ .idea .data +.DS_Store diff --git a/Dockerfile b/Dockerfile index f15f14efe..af1468a3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge && rm -rf /var/lib/apt/lists/* \ && ln -s /usr/lib/x86_64-linux-gnu/libldap.so /usr/lib/libldap.so \ && ln -s /usr/lib/x86_64-linux-gnu/liblber.so /usr/lib/liblber.so \ - && docker-php-ext-install -j$(nproc) xml gd ldap mysqli \ + && docker-php-ext-install -j$(nproc) xml gd ldap mysqli intl \ && pecl install timezonedb xdebug\ && docker-php-ext-enable timezonedb xdebug\ && curl https://getcomposer.org/download/1.8.4/composer.phar -o /usr/local/bin/composer \ @@ -21,7 +21,7 @@ COPY . /var/www/html COPY docker/docker-entrypoint-php-fpm.sh / RUN cd /var/www/html \ - && composer install --no-ansi --no-dev --no-interaction --no-plugins --no-progress --no-suggest --optimize-autoloader \ + && composer install --no-ansi --no-dev --no-interaction --no-plugins --no-progress --no-scripts --no-suggest --optimize-autoloader \ && mkdir -p /var/www/html/app/tmp/cache/persistent /var/www/html/app/tmp/cache/models /var/www/html/app/tmp/logs \ && chown www-data:www-data -R /var/www/html/app/tmp/cache \ && chown www-data:www-data -R /var/www/html/app/tmp/logs diff --git a/Dockerfile-app-unittest b/Dockerfile-app-unittest index 588eea5c3..6204505bc 100644 --- a/Dockerfile-app-unittest +++ b/Dockerfile-app-unittest @@ -12,9 +12,11 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge && rm -rf /var/lib/apt/lists/* \ && ln -s /usr/lib/x86_64-linux-gnu/libldap.so /usr/lib/libldap.so \ && ln -s /usr/lib/x86_64-linux-gnu/liblber.so /usr/lib/liblber.so \ - && docker-php-ext-install -j$(nproc) xml gd ldap mysqli pdo_mysql\ + && docker-php-ext-install -j$(nproc) xml gd ldap mysqli pdo_mysql intl \ && pecl install timezonedb \ && docker-php-ext-enable timezonedb \ + && pecl install xdebug \ + && docker-php-ext-enable xdebug \ && curl https://getcomposer.org/download/1.8.4/composer.phar -o /usr/local/bin/composer \ && chmod +x /usr/local/bin/composer \ && pecl install -f oauth-2.0.2 \ diff --git a/app/config/core.php b/app/config/core.php index 31cdcea59..e868aa68b 100644 --- a/app/config/core.php +++ b/app/config/core.php @@ -324,7 +324,7 @@ /** * iPeer database version */ - Configure::write('DATABASE_VERSION', 17); + Configure::write('DATABASE_VERSION', 18); $CWL['LoginURL'] = 'https://www.auth.cwl.ubc.ca/auth/login'; @@ -347,7 +347,7 @@ $CWL['applicationID'] = ''; $CWL['applicationPassword'] = ''; - define('IPEER_VERSION', '3.4.8'); + define('IPEER_VERSION', '3.4.9'); /** diff --git a/app/config/sql/delta_18.sql b/app/config/sql/delta_18.sql new file mode 100644 index 000000000..64a32bf9f --- /dev/null +++ b/app/config/sql/delta_18.sql @@ -0,0 +1,91 @@ +ALTER TABLE `courses` MODIFY `canvas_id` varchar(255) NULL DEFAULT NULL; +ALTER TABLE `courses` ADD INDEX `canvas_id` (`canvas_id`); +ALTER TABLE `users` DROP `lti_id`; + +DROP TABLE IF EXISTS `lti_tool_registrations`; +CREATE TABLE `lti_tool_registrations` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `iss` varchar(255) NOT NULL, + `client_id` varchar(255) NOT NULL, + `auth_login_url` varchar(255) NOT NULL, + `auth_token_url` varchar(255) NOT NULL, + `key_set_url` varchar(255) NOT NULL, + `tool_private_key` text NOT NULL, + `tool_public_key` text NOT NULL, + `user_identifier_field` varchar(255) DEFAULT NULL, + `student_number_field` varchar(255) DEFAULT NULL, + `term_field` varchar(255) DEFAULT NULL, + `canvas_id_field` varchar(255) DEFAULT NULL, + `faculty_name_field` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `iss` (`iss`), + KEY `client_id` (`client_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +INSERT INTO `acos` (id, parent_id, model, foreign_key, alias, lft, rght) VALUES +(338,2,NULL,NULL,'Ltitoolregistrations',664,673), +(339,338,NULL,NULL,'index',665,666), +(340,338,NULL,NULL,'add',667,668), +(341,338,NULL,NULL,'edit',669,670), +(342,338,NULL,NULL,'delete',671,672), +(343,2,NULL,NULL,'Lti',673,674), +(344,343,NULL,NULL,'roster',675,676); + +INSERT INTO `aros_acos` (id, aro_id, aco_id, _create, _read, _update, _delete) VALUES +(NULL,1,338,'1','1','1','1'), +(NULL,2,344,'1','1','1','1'), +(NULL,3,344,'1','1','1','1'), +(NULL,4,344,'-1','-1','-1','-1'), +(NULL,5,344,'-1','-1','-1','-1'); + +DROP TABLE IF EXISTS `lti_nonces`; +CREATE TABLE `lti_nonces` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `nonce` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `nonce` (`nonce`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `lti_contexts`; +CREATE TABLE `lti_contexts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lti_tool_registration_id` int(11) NOT NULL, + `context_id` varchar(255) NOT NULL, + `course_id` int(11) DEFAULT NULL, + `nrps_context_memberships_url` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`lti_tool_registration_id`) REFERENCES `lti_tool_registrations` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE, + UNIQUE KEY `lti_tool_registration_id_context_id` (`lti_tool_registration_id`,`context_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `lti_resource_links`; +CREATE TABLE `lti_resource_links` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lti_context_id` int(11) NOT NULL, + `resource_link_id` varchar(255) NOT NULL, + `event_id` int(11) DEFAULT NULL, + `lineitems_url` varchar(255) DEFAULT NULL, + `lineitem_url` varchar(255) DEFAULT NULL, + `scope_lineitem` INT(1) DEFAULT '0', + `scope_lineitem_read_only` INT(1) DEFAULT '0', + `scope_result_readonly` INT(1) DEFAULT '0', + `scope_result_score` INT(1) DEFAULT '0', + PRIMARY KEY (`id`), + FOREIGN KEY (`lti_context_id`) REFERENCES `lti_contexts` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`event_id`) REFERENCES `events` (`id`) ON DELETE CASCADE, + UNIQUE KEY `lti_context_id_resource_link_id` (`lti_context_id`,`resource_link_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `lti_users`; +CREATE TABLE `lti_users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lti_tool_registration_id` int(11) NOT NULL, + `lti_user_id` varchar(255) NOT NULL, + `ipeer_user_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`lti_tool_registration_id`) REFERENCES `lti_tool_registrations` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`ipeer_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + UNIQUE KEY `lti_tool_registration_id_lti_user_id` (`lti_tool_registration_id`,`lti_user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/app/config/sql/ipeer.sql b/app/config/sql/ipeer.sql index 7734ad35e..cc11c5ea5 100644 --- a/app/config/sql/ipeer.sql +++ b/app/config/sql/ipeer.sql @@ -369,7 +369,10 @@ INSERT INTO acos (id, parent_id, model, foreign_key, alias, lft, rght) VALUES (331,300,NULL,NULL,'viewusername',660,661), (332,300,NULL,NULL,'submitstudenteval',662,663), (333,84,NULL,NULL,'export',193,194), -(334,84,NULL,NULL,'import',195,196); +(334,84,NULL,NULL,'import',195,196), +(335,16,NULL,NULL,'syncCanvasEnrollment',NULL,NULL), +(336,112,NULL,NULL,'syncCanvas',NULL,NULL), +(337,64,NULL,NULL,'exportCanvas',NULL,NULL); -- -------------------------------------------------------- diff --git a/app/config/sql/ipeer_samples_data.sql b/app/config/sql/ipeer_samples_data.sql index 1c6226e88..8e181106b 100644 --- a/app/config/sql/ipeer_samples_data.sql +++ b/app/config/sql/ipeer_samples_data.sql @@ -2220,8 +2220,6 @@ INSERT INTO user_tutors (id, user_id, course_id, creator_id, created, updater_id (3, 37, 2, 0, NOW(), NULL, NOW()), (4, 37, 3, 0, NOW(), NULL, NOW()); -SET foreign_key_checks = 1; - ------------------------ @@ -2268,7 +2266,7 @@ VALUES ( ); INSERT INTO `sys_parameters` ( - `parameter_code`, `parameter_value`, `parameter_type`, + `parameter_code`, `parameter_value`, `parameter_type`, `description`, `record_status`, `creator_id`, `created`, `updater_id`, `modified`) VALUES ( @@ -2277,7 +2275,7 @@ VALUES ( ); INSERT INTO `sys_parameters` ( - `parameter_code`, `parameter_value`, `parameter_type`, + `parameter_code`, `parameter_value`, `parameter_type`, `description`, `record_status`, `creator_id`, `created`, `updater_id`, `modified`) VALUES ( @@ -2311,6 +2309,7 @@ INSERT INTO `acos` ALTER TABLE `courses` ADD COLUMN `canvas_id` VARCHAR(25) NULL DEFAULT NULL; -- add table to store oauth access/refresh tokens and expiry timestamp of the access token +DROP TABLE IF EXISTS `user_oauths`; CREATE TABLE IF NOT EXISTS `user_oauths` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL DEFAULT '0', @@ -2375,6 +2374,7 @@ ALTER TABLE `events` ADD COLUMN `canvas_assignment_id` VARCHAR(25) NULL DEFAULT --- START: Added by DB upgrade to version 17 -- add table to store delayed jobs +DROP TABLE IF EXISTS `jobs`; CREATE TABLE IF NOT EXISTS `jobs` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `handler` TEXT NOT NULL, @@ -2391,3 +2391,127 @@ CREATE TABLE IF NOT EXISTS `jobs` ( -- store course term ALTER TABLE `courses` ADD COLUMN `term` VARCHAR(50) NULL DEFAULT NULL; --- END: Added by DB upgrade to version 17 + +--- START: Added by DB upgrade to version 18 +ALTER TABLE `courses` MODIFY `canvas_id` varchar(255) NULL DEFAULT NULL; +ALTER TABLE `courses` ADD INDEX `canvas_id` (`canvas_id`); +ALTER TABLE `users` DROP `lti_id`; + +DROP TABLE IF EXISTS `lti_tool_registrations`; +CREATE TABLE `lti_tool_registrations` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `iss` varchar(255) NOT NULL, + `client_id` varchar(255) NOT NULL, + `auth_login_url` varchar(255) NOT NULL, + `auth_token_url` varchar(255) NOT NULL, + `key_set_url` varchar(255) NOT NULL, + `tool_private_key` text NOT NULL, + `tool_public_key` text NOT NULL, + `user_identifier_field` varchar(255) DEFAULT NULL, + `student_number_field` varchar(255) DEFAULT NULL, + `term_field` varchar(255) DEFAULT NULL, + `canvas_id_field` varchar(255) DEFAULT NULL, + `faculty_name_field` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `iss` (`iss`), + KEY `client_id` (`client_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `lti_tool_registrations` +-- +INSERT INTO `lti_tool_registrations` (`id`, `iss`, `client_id`, `auth_login_url`, `auth_token_url`, `key_set_url`, `tool_private_key`, `tool_public_key`, `user_identifier_field`, `student_number_field`, `term_field`, `canvas_id_field`, `faculty_name_field`) VALUES +(1, 'https://docker-canvas.instructure.com', '10000000000013', 'http://mock_lti.com/api/lti/authorize_redirect', 'http://mock_lti.com/login/oauth2/token', 'http://mock_lti.com/api/lti/security/jwks', '-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEApBK2zJCDg9s8QDeci6E4QWlTSGav3qh5edjbXULo5Mv0KxN7\nqLyC05QfRrs/I+P5a6D18S/ecAGYPH5xQKuPvqOEotPrRhHCkJB5PtLDsF4ZZbr/\nwWLWG5OYhCkY/H2Wip9rsx1GjKG73EMMTqT2p14K+GW4dg8/HbxQLA4yGeNnGr4D\nd87A+n9wUvMZYAxoCiHiSFD7x0hVIg/q4VXoWHBGEnnqCMC9Gtd7g7HZtQzbYMm4\nm2uY5JHhs+MXS8YKf6Ftc58sJHK5fMtjs9vMVOCkAlrEiEEn+tEHOjSNlzMg+P03\nUU79Lt/MDjXv3mtEPVmPjpJevT4Kjf1HxCSUNQIDAQABAoIBAF5Jmt9IFSwLKz7E\nNqRPS+LbQk8TI/JS4yxQoQ+hSfFh+7ldguzfGFe6gZbGOGzJsCZX475tAelgITpy\nd2bwsLSfh7ODEWu8/RDS1bpyqJ6MFRBPPHbH8775POaGL6O6EG8tWlkec9KRh0H3\nDfWL+2sHMkq5Oh4ueNj/xRrsNYKGLsD0bJMS9eFswDCpvL3fscu/JrrQT+CltBTJ\nj8lTHmGRIF9UOg0Ef1kEgOxcR+AZ2djP3d+zkKZxMATLKWnA1HRFPb7XpJQfjA+k\nsismB4FuhvTSN6IRaci1U5qASHUnIjbTMfFsqJ3h17RivQmSEz1r28OE1HyD/tdE\nIIy3WikCgYEAz6r27/MfEWXgOAwloKJAQi0sqa/0VkWQcNPN3jSgO+AN0iqPLSuj\nAlrLqlLcRlyYAfe7t6B/8SAklJtdy0x9uBaJXrmHhby4jeBksyycQULUzTUhGdbW\nGDfR+XbvNoGUaH1q+vIpglz2jw3N1/M6B/i16wd6Q81UO5WYxQj1j7sCgYEAykJW\nIq4A4UQmtM6gdsXXranV80NOlcH0p521Ec6wpU0dxfI+qVVbT4FxqxfB9Pq5N4V0\nreEYN1ALbjLi3fvChbx8P+lg6k/Tuhn7oiH3kado8iUUR00KyRwWeaMbVwUzU9sQ\nUhB/XfR3J7l3inN/dAlfdSsYbnQJN2U88CKEVM8CgYBfM2UZAz+O3kE38HmfdkI3\nFDaRY9SDaEibML4Dy+RZDpHHczNH5eVIww7y+iF5MCGPZV5tA+sjQzUB22fYNyy7\nI7m97xetu6JviBsh+KV5VYXwvRZ7nf1wBMcBsgBf4G+Ep1pPyIw28x8k3ZMsGJjV\n5rKfGEJ4qryexCnQyhao2QKBgCel71qm/3cpM+k3pA8EY24gn9cq94m11q7Q5IDU\nIp6UymRWQ2BQYjDosA6Y/qV2TL6Mg73eJTAamdMFWKGpS42J0FV6+0uTUG7nzwMO\nY4iC57in+hysBpQ71FAN4DsjwtcKV12u7DjPxlfcLInQcEif2b2PMB/e0Tuxtcth\nCM3TAoGAM+z4u7mi5jxyW9teAYtx3Yb6RGeuly7XvlknV0Lwf2438P2HNZiOa4SE\nSXHZir6LWNv8HOdGapYxUlDfmeNneo4D9B8lBpVs/FsuQF1aOI6B299SlVLPmF+a\nl88qKzXKv7M1pcOv74GK1AIVDF8XJvt1PyaQX92M14q2Ga8Jdjk=\n-----END RSA PRIVATE KEY-----', '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApBK2zJCDg9s8QDeci6E4\nQWlTSGav3qh5edjbXULo5Mv0KxN7qLyC05QfRrs/I+P5a6D18S/ecAGYPH5xQKuP\nvqOEotPrRhHCkJB5PtLDsF4ZZbr/wWLWG5OYhCkY/H2Wip9rsx1GjKG73EMMTqT2\np14K+GW4dg8/HbxQLA4yGeNnGr4Dd87A+n9wUvMZYAxoCiHiSFD7x0hVIg/q4VXo\nWHBGEnnqCMC9Gtd7g7HZtQzbYMm4m2uY5JHhs+MXS8YKf6Ftc58sJHK5fMtjs9vM\nVOCkAlrEiEEn+tEHOjSNlzMg+P03UU79Lt/MDjXv3mtEPVmPjpJevT4Kjf1HxCSU\nNQIDAQAB\n-----END PUBLIC KEY-----', 'https://purl.imsglobal.org/spec/lti/claim/custom|username', 'https://purl.imsglobal.org/spec/lti/claim/custom|student_number', 'https://purl.imsglobal.org/spec/lti/claim/custom|term_name', 'https://purl.imsglobal.org/spec/lti/claim/custom|canvas_course_id', 'https://purl.imsglobal.org/spec/lti/claim/custom|account_name'); + +INSERT INTO `acos` (id, parent_id, model, foreign_key, alias, lft, rght) VALUES +(338,2,NULL,NULL,'Ltitoolregistrations',664,673), +(339,338,NULL,NULL,'index',665,666), +(340,338,NULL,NULL,'add',667,668), +(341,338,NULL,NULL,'edit',669,670), +(342,338,NULL,NULL,'delete',671,672), +(343,2,NULL,NULL,'Lti',673,674), +(344,343,NULL,NULL,'roster',675,676); + +INSERT INTO `aros_acos` (id, aro_id, aco_id, _create, _read, _update, _delete) VALUES +(NULL,1,338,'1','1','1','1'), +(NULL,2,344,'1','1','1','1'), +(NULL,3,344,'1','1','1','1'), +(NULL,4,344,'-1','-1','-1','-1'), +(NULL,5,344,'-1','-1','-1','-1'); + +DROP TABLE IF EXISTS `lti_nonces`; +CREATE TABLE `lti_nonces` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `nonce` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `nonce` (`nonce`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `lti_contexts`; +CREATE TABLE `lti_contexts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lti_tool_registration_id` int(11) NOT NULL, + `context_id` varchar(255) NOT NULL, + `course_id` int(11) DEFAULT NULL, + `nrps_context_memberships_url` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`lti_tool_registration_id`) REFERENCES `lti_tool_registrations` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE, + UNIQUE KEY `lti_tool_registration_id_context_id` (`lti_tool_registration_id`,`context_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `lti_contexts` +-- +INSERT INTO `lti_contexts` (`id`, `lti_tool_registration_id`, `context_id`, `course_id`, `nrps_context_memberships_url`) VALUES +(1, 1, 'mock_lti_context_id', 1, 'http://mock_lti.com/api/lti/courses/13/names_and_roles'); + + +DROP TABLE IF EXISTS `lti_resource_links`; +CREATE TABLE `lti_resource_links` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lti_context_id` int(11) NOT NULL, + `resource_link_id` varchar(255) NOT NULL, + `event_id` int(11) DEFAULT NULL, + `lineitems_url` varchar(255) DEFAULT NULL, + `lineitem_url` varchar(255) DEFAULT NULL, + `scope_lineitem` INT(1) DEFAULT '0', + `scope_lineitem_read_only` INT(1) DEFAULT '0', + `scope_result_readonly` INT(1) DEFAULT '0', + `scope_result_score` INT(1) DEFAULT '0', + PRIMARY KEY (`id`), + FOREIGN KEY (`lti_context_id`) REFERENCES `lti_contexts` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`event_id`) REFERENCES `events` (`id`) ON DELETE CASCADE, + UNIQUE KEY `lti_context_id_resource_link_id` (`lti_context_id`,`resource_link_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `lti_resource_links` +-- +INSERT INTO `lti_resource_links` (`id`, `lti_context_id`, `resource_link_id`, `event_id`, `lineitems_url`, `lineitem_url`, `scope_lineitem`, `scope_lineitem_read_only`, `scope_result_readonly`, `scope_result_score`) VALUES +(1, 1, 'mock_lti_resource_id', null, 'http://mock_lti.com/api/lti/courses/13/line_items', null, 1, 1, 0, 1), +(2, 1, 'mock_lti_context_id', null, 'http://mock_lti.com/api/lti/courses/13/line_items', null, 1, 1, 0, 1); + +DROP TABLE IF EXISTS `lti_users`; +CREATE TABLE `lti_users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lti_tool_registration_id` int(11) NOT NULL, + `lti_user_id` varchar(255) NOT NULL, + `ipeer_user_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`lti_tool_registration_id`) REFERENCES `lti_tool_registrations` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`ipeer_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + UNIQUE KEY `lti_tool_registration_id_lti_user_id` (`lti_tool_registration_id`,`lti_user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; +--- END: Added by DB upgrade to version 18 + + +-- +-- Dumping data for table `lti_users` +-- +INSERT INTO `lti_users` (`id`, `lti_tool_registration_id`, `lti_user_id`, `ipeer_user_id`) VALUES +(1, 1, 'mock_lti_user_id_instructor', 2); + + + +SET foreign_key_checks = 1; \ No newline at end of file diff --git a/app/controllers/components/lti_requester.php b/app/controllers/components/lti_requester.php deleted file mode 100644 index aac9b4f46..000000000 --- a/app/controllers/components/lti_requester.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @copyright 2012 All rights reserved. - * @license MIT {@link http://www.opensource.org/licenses/MIT} - */ -class LtiRequesterComponent extends CakeObject -{ - public $components = array('LtiVerifier'); - - /** - * requestRoster - * - * @param mixed $params - * - * @access public - * @return void - */ - public function requestRoster($params) - { - // Check that roster requests are supported by the server - $ltimid = $params['ext_ims_lis_memberships_id']; - if (empty($ltimid)) { - return "Missing 'ext_ims_lis_memberships_id'. Calling LTI consumer does not support roster requests."; - } - - $ltimurl = $params['ext_ims_lis_memberships_url']; - if (empty($ltimurl)) { - return "Missing 'ext_ims_lis_memberships_url'. Calling LTI consumer does not support roster requests."; - } - - // build an array of all the params we need to use - $oauth_key = $params['oauth_consumer_key']; // assuming this exists - $request = array( - 'oauth_version' => '1.0', - 'oauth_nonce' => rand() . "-" . rand(), - 'oauth_timestamp' => time(), - 'oauth_consumer_key' => $oauth_key, - 'oauth_signature_method' => 'HMAC-SHA1', - 'oauth_callback' => 'about:blank', - 'lti_version' => 'LTI-1p0', - 'lti_message_type' => 'basic-lis-readmembershipsforcontext', - 'id' => $ltimid, - ); - - // calculate the signature of all the params - $secret = $this->LtiVerifier->getSecret($oauth_key); - if (empty($secret)) { - return "Missing secret, key '$oauth_key' not found in keystore."; - } - $hmac = $this->LtiVerifier->getHMAC($request, $secret, $ltimurl); - - // add the signature in into the params - $request['oauth_signature'] = $hmac; - $request = http_build_query($request); - - // send the actual POST request - $params = array( - 'http' => array('method' => 'POST', 'content' => $request) - ); - $ctx = stream_context_create($params); - $fp = @fopen($ltimurl, 'rb', false, $ctx); - if (!$fp) { - return 'Unable to connect to ' . $ltimurl; - } - // read the response - $response = @stream_get_contents($fp); - if ($response === false) { - return 'Unable to read data from ' . $ltimurl; - } - - // parse the response xml - $xml = new SimpleXMLElement($response); - if (strcasecmp($xml->statusinfo->codemajor, 'Success') != 0) { - return 'Unable to retrieve roster: ' . $xml->statusinfo->description; - } - - // convert the response xml to an array - $ret = array(); - foreach ($xml->memberships->member as $member) { - $student = array(); - foreach ($member as $key => $val) { - // note $val is still a SimpleXML object, so need cast with "" - $student[$key] = "$val"; - } - $ret[] = $student; - } - return $ret; - } -} diff --git a/app/controllers/components/lti_verifier.php b/app/controllers/components/lti_verifier.php deleted file mode 100644 index de11818ff..000000000 --- a/app/controllers/components/lti_verifier.php +++ /dev/null @@ -1,288 +0,0 @@ - - * @copyright 2012 All rights reserved. - * @license MIT {@link http://www.opensource.org/licenses/MIT} - */ -class LtiVerifierComponent extends CakeObject -{ - public $components = array('Auth'); - - private $store = array('lti_secret' => 'secret'); - // time range in seconds within which the timestamp is considered valid - // Note that for security reasons, since we're not storing the nonces, - // the timestamp plays a double role as a nonce, the delay should be - // kept low, only a few minutes max. - private $timestamptimeout = 60; - - /** - * The chances of this function working would be significantly improved - * if the User table has a column named 'lti_id'. Also would help if the - * Auth component was used. - * - * @param array $params the LTI parameters - * @param array $user the user model used by this app - * - * @return - false if authentication successful, a string describing the - * error otherwise - * */ - - public function login($params, $user) - { - $userid = $params['user_id']; - $ret = $user->find( - 'first', - array('conditions' => array('User.lti_id' => $userid))); - if (empty($ret)) { - return 'User not found'; - } - $userid = $ret['User']['id']; - $ret = $this->Auth->login($userid); - if ($ret == 0) { - return 'Access denied'; - } - - return false; - } - - /** - * Checks for compliance to LTI specs - * - * @param array $params - the LTI parameters - * - * @return - false if params are LTI compliant, a string describing the - * error otherwise - * */ - - public function checkParams($params) - { - // Perform basic LTI validation by checking that all - // required fields are present - $lti_ver = $params['lti_version']; - if (strcmp($lti_ver, 'LTI-1p0') != 0) { - return 'Incompatible or missing lti_version.'; - } - $lti_type = $params['lti_message_type']; - if (strcmp($lti_type, 'basic-lti-launch-request') != 0) { - return 'Incompatible or missing basic lti request.'; - } - $lti_resource = $params['resource_link_id']; - if (empty($lti_resource)) { - return 'Missing resource link id.'; - } - - // Check that oauth parameters have been transmitted - $oauth_key = $params['oauth_consumer_key']; - if (empty($oauth_key)) { - return 'Missing oauth parameter: oauth_consumer_key.'; - } - $oauth_method = $params['oauth_signature_method']; - if (strcmp($oauth_method, "HMAC-SHA1") != 0) { - return 'Incompatible oauth signature method, only HMAC-SHA1 is supported.'; - } - $oauth_time = $params['oauth_timestamp']; - if (abs($oauth_time - time()) > $this->timestamptimeout) { - return 'Failed oauth timestamp verification.'; - } - $oauth_nonce = $params['oauth_nonce']; - if (empty($oauth_nonce)) { - return 'Missing oauth nonce.'; - } - $oauth_version = $params['oauth_version']; - if (strcmp($oauth_version, "1.0") != 0) { - return 'Incompatible OAuth version.'; - } - - // Check that messages haven't been tampered with using OAuth - $secret = $this->getSecret($oauth_key); - if (empty($secret)) { - return "Missing secret, key '$oauth_key' not found in keystore."; - } - $hmac = $this->getHMAC($params, $secret); - $oauth_signature = $params['oauth_signature']; - if (strcmp($hmac, $oauth_signature) != 0) { - return 'Message integrity could not be verified.'; - } - - return false; - } - - /** - * Reads the LTI parameters and return an array containing - * information about the course. - * - * @param array $params - the LTI request parameters - * - * @return false if the LTI request is missing course information, the - * array containing two keys 'course' and 'title' otherwise. - * */ - - public function getCourseInfo($params) - { - $ret = array(); - $ret['course'] = $params['context_label']; - $ret['title'] = $params['context_title']; - if ($ret['course'] && $ret['title']) { - return $ret; - } - return false; - } - - /** - * Given a key, check the keystore for the corresponding secret. - * - * Currently, the store is just a hard coded array. - * - * @param string $key - the key used to access the secret - * - * @return false if the secret was not found, a string containing the - * secret if found - * */ - - public function getSecret($key) - { - if (isset($this->store[$key])) { - return $this->store[$key]; - } - return false; - } - - /** - * Calculate the HMAC value according to OAuth specs. - * - * @param array $params - the array of POST parameters of the LTI request - * @param string $secret - the secret string - * @param string $url - the url which is receiving the LTI request, if left - * - * blank, defaults to the URL that the current script is executing on - * - * @return the resulting hash string - * */ - - public function getHMAC($params, $secret, $url = '') - { - $secret .= '&'; - $input = $this->getHashInput($params, $url); - return $this->hash($input, $secret); - } - - /** - * Given a set of data and a secret, generate a hash and - * base64 encode it. - * - * @param array $data - the data to be hashed - * @param string $secret - the secret used to salt the data - * - * @return base64 encodeded hash - * */ - - private function hash($data, $secret) - { - return base64_encode(hash_hmac("sha1", $data, $secret, true)); - } - - /** - * Convert the LTI params into the proper data format - * to pass into the hash function. - * - * @param array $params - the LTI parameters - * @param string $url - the URL of the page receiving the LTI request. Defaults to - * a blank string. If left blank, will use the URL of the current page - * that the php scrip is running on. - * - * @return A correctly formatted data string suitable for use in calculating - * the OAuth HMac. - * */ - - private function getHashInput($params, $url = '') - { - $ret = "POST&"; - if (empty($url)) { - $ret .= $this->rfc3986($this->getURL()) . '&'; - } else { - $ret .= $this->rfc3986($url) . '&'; - } - $ret .= $this->rfc3986($this->convertParams($params)); - return $ret; - } - - /** - * Convert each key-value pair into the correct RFC 3986 encoding - * required by OAuth. - * - * @param array $params - the LTI parameters - * - * @return A string containing all the key-value pairs of $param - * delimited by an ampersand and encoded into RFC 3986 - * */ - - private function convertParams($params) - { - $tmp = array(); - foreach ($params as $key => $val) { - $tmp[$this->rfc3986($key)] = $this->rfc3986($val); - } - $params = $tmp; - - // sort by byte order - uksort($params, 'strcmp'); - - $tmp = array(); - foreach ($params as $parameter => $value) { - if (strcmp($parameter, "oauth_signature") == 0) { - continue; - } - array_push($tmp, $parameter . '=' . $value); - } - return implode('&', $tmp); - } - - /** - * Convert a string into rfc3986 compliant encoding - * - * @param string $input - the string to be converted - * - * @return The rfc3986 compliant string - * */ - private function rfc3986($input) - { - // Note workaround for: - // - Prior to PHP 5.3.0, rawurlencode encoded tildes (~) per rfc1738. - return str_replace('%7E', '~', rawurlencode($input)); - } - - /** - * Get the URL of the current page that PHP is being executed on. - * - * @return The URL of the current page that PHP is being executed on. - * */ - private function getURL() - { - $pageURL = 'http'; - if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") { - $pageURL .= "s"; - } - $pageURL .= "://"; - if ($_SERVER["SERVER_PORT"] != "80") { - $pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] - . $_SERVER["REQUEST_URI"]; - } else { - $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]; - } - return $pageURL; - } -} diff --git a/app/controllers/courses_controller.php b/app/controllers/courses_controller.php index edd6f526d..677a1c104 100644 --- a/app/controllers/courses_controller.php +++ b/app/controllers/courses_controller.php @@ -12,7 +12,7 @@ class CoursesController extends AppController { public $name = 'Courses'; public $uses = array('GroupEvent', 'Course', 'Personalize', 'UserCourse', - 'UserEnrol', 'Group', 'Event', 'User', 'UserFaculty', 'Department', + 'UserEnrol', 'Group', 'Event', 'User', 'UserFaculty', 'Department', 'LtiContext', 'CourseDepartment', 'EvaluationSubmission', 'SurveyInput', 'UserTutor', 'SysParameter'); public $helpers = array('Html', 'Ajax', 'excel', 'Javascript', 'Time', 'Js' => array('Prototype'), 'FileUpload.FileUpload'); @@ -203,10 +203,19 @@ function home($id) $this->redirect('index'); return; } + $lti_contexts = $this->LtiContext->getByCourseId($id); + $ltiNrpsEnabled = false; + foreach ($lti_contexts as $lti_context) { + if (!empty($lti_context['LtiContext']['nrps_context_memberships_url'])) { + $ltiNrpsEnabled = true; + break; + } + } $this->set('data', $course); $this->set('title_for_layout', $course['Course']['full_name']); $this->set('canvasEnabled', in_array($this->SysParameter->get('system.canvas_enabled', 'false'), array('1', 'true', 'yes'))); + $this->set('ltiNrpsEnabled', $ltiNrpsEnabled); //Setup the courseId to session $this->Session->write('ipeerSession.courseId', $id); diff --git a/app/controllers/lti_controller.php b/app/controllers/lti_controller.php index f12ca4844..02550ff87 100644 --- a/app/controllers/lti_controller.php +++ b/app/controllers/lti_controller.php @@ -1,229 +1,176 @@ - * @copyright 2012 All rights reserved. + * @author Steven Marshall + * @copyright 2019 All rights reserved. * @license MIT {@link http://www.opensource.org/licenses/MIT} + * @link https://www.imsglobal.org/spec/security/v1p0/#fig_oidcflow */ class LtiController extends AppController { - public $name = "Lti"; - public $uses = array('User', 'Course', 'Role'); - public $components = array('LtiVerifier', 'LtiRequester'); + public $uses = array('LtiContext', 'LtiResourceLink', 'LtiUser', + 'User', 'Course'); + + public function __construct() + { + parent::__construct(); + } /** * beforeFilter * - * * @access public * @return void */ function beforeFilter() { - $this->Auth->allow('index'); + //ensure the user is logged out of iPeer when starting the login/launch actions + if (!empty($this->params['action']) && in_array($this->params['action'], ['login', 'launch'])) { + if ($this->Auth->isAuthorized()) { + $this->Auth->logout(); + } + } + $this->Auth->allow('login', 'launch'); + parent::beforeFilter(); } + /** + * OIDC login action called by platform. + */ + public function login() + { + try { + $login = LTI_OIDC_Login::new( + new LTIDatabase(), + new LTICache(), + new LTICookie() + ); + $url = Router::url('/lti/launch', true); + $redirect = $login->do_oidc_login_redirect($url); + // fix `die` call issue for testing by redirecting manually using framework + $this->redirect($redirect->get_redirect_url()); + } catch (OIDC_Exception $e) { + $this->Session->setFlash(sprintf("Error doing OIDC login: %s", $e->getMessage())); + $this->redirect('/home/index'); + } + } /** - * index - * - * - * @access public - * @return void + * Launch action called by platform. */ - public function index() + public function launch() { - // First verify that this is a legit LTI request - $ret = $this->LtiVerifier->checkParams($this->params['form']); - if ($ret) { - // param check failed - $this->set('invalidlti', $ret); - return; + // fixes potential warning when state/id_token param is missing + // (will still properly error out in validation) + if (empty($_POST['state'])) { + $_POST['state'] = null; + } + if (empty($_POST['id_token'])) { + $_POST['id_token'] = null; } - // Request the class roster - $roster = $this->LtiRequester->requestRoster($this->params['form']); - if (!is_array($roster)) { - $this->set('invalidlti', $roster); + $launch = LTI_Message_Launch::new( + new LTIDatabase(), + new LTICache(), + new LTICookie() + ); + try { + $launch->validate(); + } catch (LTI_Exception $e) { + $this->Session->setFlash($e->getMessage()); + $this->redirect('/logout'); return; } - // Get course information - $ret = $this->LtiVerifier->getCourseInfo($this->params['form']); - if (!$ret) { - // failed to get course info - $this->set('invalidlti', "Missing course info in LTI request"); + if (!$launch->is_resource_launch()) { + $this->Session->setFlash("Not an LTI Launch."); + $this->redirect('/logout'); return; } + $launch_data = $launch->get_launch_data(); + $launch_data_parser = new LaunchDataParser($launch_data); - // APP SPECIFIC CODE STARTS HERE - // Check whether the course already exists - $course = $this->Course->find( - 'first', - array('conditions' => array('Course.course' => $ret['course']),) - ); - if (empty($course)) { - // Non-existing course, create course - $this->data = array(); - $this->data['course'] = $ret['course']; - $this->data['title'] = $ret['title']; - $this->data['record_status'] = Course::STATUS_ACTIVE; - $this->data = $this->Course->save($this->data); - if (!$this->data) { - $this->set('invalidlti', "Unable to add course"); - return; - } + $this->log(json_encode($launch_data, 448), 'lti/launch'); - // Create users, if needed, and enrol them to the course - foreach ($roster as $person) { - $this->addUser($person, $this->Course->id); - } + // automatically create Faculty if needed + $faculty = $this->LtiContext->syncFaculty($launch_data_parser); + $faculty_id = $faculty['Faculty']['id']; - } else { - // Existing course, update course - // Get current roster in iPeer - $courseid = $course['Course']['id']; - $ipeerroster = $this->User->getEnrolledStudents($courseid); - // Compare with roster from LTI - // Remove users that are on both lists - foreach ($roster as $ltikey => $ltiuser) { - foreach ($ipeerroster as $ipeerkey => $ipeeruser) { - if ($ltiuser['user_id'] == $ipeeruser['User']['lti_id']) { - unset($roster[$ltikey]); - unset($ipeerroster[$ipeerkey]); - continue; - } - } - } - // Remaining users in ipeerroster needs to be dropped - foreach ($ipeerroster as $ipeeruser) { - $this->User->removeStudent( - $ipeeruser['User']['id'], $courseid); - } - // Remaining users in roster needs to be added - foreach ($roster as $person) { - if (!$this->isLTIInstructor($person)) { - $this->addUser($person, $courseid); - } - } - } - // END APP SPECIFIC CODE - - // Let's try logging in: - $ret = $this->LtiVerifier->login($this->params['form'], $this->User); - if ($ret) { - $this->set('invalidlti', $ret); - return; - } + // automatically create/update lti context + course + $lti_context = $this->LtiContext->syncLaunchContext($launch_data_parser); + $lti_context_id = $lti_context['LtiContext']['id']; + $course_id = $lti_context['Course']['id']; - // APP SPECIFIC CODE BELOW - $this->redirect('/home'); + // automatically create/update lti resource link + assignment (skipping assignment for now) + $lti_resource_link = $this->LtiResourceLink->syncLaunchResourceLink($launch_data_parser, $lti_context_id); - } + // automatically create/update lti user + user + $lti_user = $this->LtiUser->syncUser( + $launch_data_parser->lti_tool_registration['id'], + $launch_data_parser->getParam('sub'), + $launch_data_parser->getUserData() + ); + $user_id = $lti_user['User']['id']; + $role_id = $launch_data_parser->getCourseRole(); + // automatically update user enrollment + $this->LtiUser->syncUserEnrollment($course_id, $user_id, $faculty_id, $role_id); - /** - * addUser - * - * @param mixed $info info - * @param mixed $courseid course id - * - * @access private - * @return void - */ - private function addUser($info, $courseid) - { - $first = $info['person_name_given']; - $last = $info['person_name_family']; - $email = $info['person_contact_email_primary']; - $lti_id = $info['user_id']; - $instructor = $this->isLTIInstructor($info); - - // Prepare user data - $cdata = array(); - $cdata['User']['username'] = $first . $last; - $cdata['User']['first_name'] = $first; - $cdata['User']['last_name'] = $last; - $cdata['User']['email'] = $email; - $cdata['User']['send_email_notification'] = false; - $cdata['User']['lti_id'] = $lti_id; - // TODO USER_TYPE_STUDENT needs to change to const instead of public later - $cdata['Role']['RolesUser']['role_id'] = $this->User->USER_TYPE_STUDENT; - $cdata['User']['created'] = date('Y-m-d H:i:s'); - if ($instructor !== false) { - // this guy is a prof - $cdata['Role']['RolesUser']['role_id'] = $this->User->USER_TYPE_INSTRUCTOR; + // Automatic user login and log + $this->Auth->login($user_id); + if (method_exists($this, '_afterLogin')) { + $this->_afterLogin(false); } - // Check if user already exists - $user = $this->User->getByUsername($cdata['User']['username']); - if (!empty($user)) { - // pre-existing user, just need to add them to course - $this->addUserToCourse($user['User']['id'], $courseid, $instructor); - // user might not have an lti_id, so save one - $user['User']['lti_id'] = $lti_id; - $this->User->save($user); - return false; - } + $this->log($lti_user['User'], 'lti/user'); - // Need to create a new user - $this->User->create(); - if ($this->User->save($cdata)) { - // User enrolment - $this->addUserToCourse($this->User->id, $courseid, $instructor); - return false; + $this->Session->setFlash(__('LTI launch success', true), 'good'); + if (in_array($role_id, [$this->User->USER_TYPE_ADMIN, $this->User->USER_TYPE_INSTRUCTOR, $this->User->USER_TYPE_TA])) { + // Redirect to course page is instructor/admin + $this->redirect("/courses/home/$course_id"); } else { - return $cdata['User']['username']; + // else redirect to home page for students + $this->redirect('/home/index'); } } - /** - * addUserToCourse - * - * @param mixed $userid user id - * @param mixed $courseid course id - * @param mixed $instructor instructor + * Update roster by course ID from platform. * - * @access private - * @return void + * Called by tool, not platform. + * @param string $course_id */ - private function addUserToCourse($userid, $courseid, $instructor) + public function roster($course_id) { - // TODO use Role methods instead of this if possible - $student_role_id = $this->Role->field('id', array('name =' => 'student')); - $instructor_role_id = $this->Role->field( - 'id', array('name' => 'instructor')); - - if ($instructor === false) { - $this->User->registerRole($userid, $student_role_id); - $this->User->UserEnrol->insertCourses($userid, array($courseid)); - } else { - $this->User->registerRole($userid, $instructor_role_id); - $this->Course->addInstructor($courseid, $userid); + if (!User::hasPermission('controllers/Lti/roster')) { + return; } - } + $names_and_roles_service = new NamesAndRolesService($course_id); + try + { + $names_and_roles_service->sync_membership(); + $this->Session->setFlash(__('Imported Users from LMS', true), 'good'); + $this->redirect("/courses/home/$course_id"); - /** - * isLTIInstructor - * - * @param mixed $info - * - * @access private - * @return void - */ - private function isLTIInstructor($info) - { - $instructor = stripos($info['roles'], 'Instructor'); - // note that !== is required, we MUST compare type since - // stripos() may return 0 for a valid match - if ($instructor === false) { - return false; + } catch (LTI_Exception $e) { + $this->Session->setFlash($e->getMessage()); + $this->redirect("/courses/home/$course_id"); } - return true; } } diff --git a/app/controllers/ltitoolregistrations_controller.php b/app/controllers/ltitoolregistrations_controller.php new file mode 100644 index 000000000..4c01391f6 --- /dev/null +++ b/app/controllers/ltitoolregistrations_controller.php @@ -0,0 +1,130 @@ + + * @copyright 2019 All rights reserved. + * @license MIT {@link http://www.opensource.org/licenses/MIT} + * @link https://www.imsglobal.org/spec/security/v1p0/#fig_oidcflow + */ +class LtiToolRegistrationsController extends AppController +{ + public $name = 'LtiToolRegistrations'; + public $uses = array('LtiToolRegistration'); + + /** + * Index action + */ + public function index() + { + $this->set('title_for_layout', __('Lti 1.3 Tool Registrations',true)); + $this->set('headings', array('Issuers', 'Settings', 'Actions')); + + $ret = $this->LtiToolRegistration->find('all'); + + $registrations = array(); + foreach ($ret as $registration) { + $tmp = array(); + $tmp['id'] = $registration['LtiToolRegistration']['id']; + $tmp['iss'] = $registration['LtiToolRegistration']['iss']; + $tmp['client_id'] = $registration['LtiToolRegistration']['client_id']; + $registrations[] = $tmp; + } + $this->set('registrations', $registrations); + } + + /** + * Add action + */ + public function add() + { + $this->set('title_for_layout', __('Add Lti 1.3 Tool Registration',true)); + + try { + + // POST request + if (!empty($this->data)) { + // Save all + if ($this->LtiToolRegistration->save($this->data)) { + $this->Session->setFlash(__('Tool registration has been created', true), 'good'); + $this->redirect(array('action' => 'index')); + } + } + + } catch (Exception $e) { + + $this->Session->setFlash($e->getMessage()); + $this->redirect(array('action'=>'index')); + + } + } + + /** + * Edit action + * + * @param mixed $id + */ + public function edit($id = null) + { + $this->set('title_for_layout', __('Edit Lti 1.3 Tool Registration',true)); + + try { + + // POST request + if (!empty($this->data)) { + + // Delete associated deployments from dB + $id = $this->data['LtiToolRegistration']['id']; + $conditions = 'lti_tool_registration_id = ' . $id; + + // Save all + if ($this->LtiToolRegistration->save($this->data)) { + $this->Session->setFlash(__('Tool registration has been updated', true), 'good'); + $this->redirect(array('action' => 'index')); + } + + } else { + + if (empty($id)) { + $this->redirect(array('action' => 'index')); + } + + } + + $this->data = $this->LtiToolRegistration->findById($id); + + } catch (Exception $e) { + + $this->Session->setFlash($e->getMessage()); + $this->redirect(array('action'=>'index')); + + } + } + + /** + * Delete action + * + * @param mixed $id + */ + public function delete($id = null) + { + try { + + if ($this->LtiToolRegistration->delete($id)) { + $this->Session->setFlash(__('Tool registration has been deleted', true), 'good'); + $this->redirect(array('action'=>'index')); + } + $this->Session->setFlash(__('Tool registration was not deleted', true)); + $this->redirect(array('action' => 'index')); + + } catch (Exception $e) { + + $this->Session->setFlash($e->getMessage()); + $this->redirect(array('action'=>'index')); + + } + } +} diff --git a/app/libs/caliper/entity.php b/app/libs/caliper/entity.php index c75fc7d56..ef48a2af8 100644 --- a/app/libs/caliper/entity.php +++ b/app/libs/caliper/entity.php @@ -142,9 +142,6 @@ public static function membership($course, $user, $roles) { if (array_key_exists('canvas_id', $course) && $course['canvas_id']) { $extensions['canvas_course_id'] = $course['canvas_id']; } - if (array_key_exists('lti_id', $user) && $user['lti_id']) { - $extensions['lti_user_id'] = $user['lti_id']; - } if (count($extensions) > 0) { $entity->setExtensions($extensions); diff --git a/app/libs/lti.php b/app/libs/lti.php new file mode 100644 index 000000000..c12541673 --- /dev/null +++ b/app/libs/lti.php @@ -0,0 +1,24 @@ +LtiToolRegistration = \ClassRegistry::init('LtiToolRegistration'); + $this->User = \ClassRegistry::init('User'); + $this->launch_data = $launch_data; + + $iss = $launch_data['iss']; + $results = $this->LtiToolRegistration->findByIss($iss); + $this->lti_tool_registration = $results['LtiToolRegistration']; + return $this; + } + + public function getParam($param_name) { + if (!isset($this->launch_data)) { + return null; + } + if (!isset($this->launch_data[$param_name])) { + return null; + } + return $this->launch_data[$param_name]; + } + + public function getClaim($claim_name) { + if (!isset($this->launch_data)) { + return null; + } + if (!isset($this->launch_data["https://purl.imsglobal.org/spec/lti/claim/$claim_name"])) { + return null; + } + return $this->launch_data["https://purl.imsglobal.org/spec/lti/claim/$claim_name"]; + } + + public function getClaimParam($claim_name, $param_name) { + $claim = $this->getClaim($claim_name); + if (!isset($claim)) { + return null; + } + if (!isset($claim[$param_name])) { + return null; + } + return $claim[$param_name]; + } + + public function getNrpsClaim() { + if (!isset($this->launch_data)) { + return null; + } + if (!isset($this->launch_data["https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice"])) { + return null; + } + return $this->launch_data["https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice"]; + } + + public function getNrpsClaimParam($param_name) { + $claim = $this->getNrpsClaim(); + if (!isset($claim)) { + return null; + } + if (!isset($claim[$param_name])) { + return null; + } + return $claim[$param_name]; + } + + public function getAgsClaim() { + if (!isset($this->launch_data)) { + return null; + } + if (!isset($this->launch_data["https://purl.imsglobal.org/spec/lti-ags/claim/endpoint"])) { + return null; + } + return $this->launch_data["https://purl.imsglobal.org/spec/lti-ags/claim/endpoint"]; + } + + public function getAgsClaimParam($param_name) { + $claim = $this->getAgsClaim(); + if (!isset($claim)) { + return null; + } + if (!isset($claim[$param_name])) { + return null; + } + return $claim[$param_name]; + } + + public function hasAgsClaimScope($scope) { + $claim = $this->getAgsClaim(); + if (empty($claim)) { + return 0; + } + if (empty($claim['scope'])) { + return 0; + } + return in_array("https://purl.imsglobal.org/spec/lti-ags/scope/$scope", $claim['scope']) ? 1 : 0; + } + + public function getUserData() { + return array( + 'username' => $this->getUserIdentifierValue(), + 'first_name' => $this->getParam('given_name'), + 'last_name' => $this->getParam('family_name'), + 'student_no' => $this->getStudentNumberValue(), + 'email' => $this->getParam('email'), + ); + } + + + public function getCourseData() { + return array( + 'course' => $this->getClaimParam('context', 'label'), + 'title' => $this->getClaimParam('context', 'title'), + 'canvas_id' => $this->getCanvasIdValue(), + 'term' => $this->getTermValue(), + ); + } + + # Core context roles + # http://purl.imsglobal.org/vocab/lis/v2/membership#Administrator + # http://purl.imsglobal.org/vocab/lis/v2/membership#ContentDeveloper + # http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor + # http://purl.imsglobal.org/vocab/lis/v2/membership#Learner + # http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor + # Instructor Sub-role + # Grader, GuestInstructor, Instructor, Lecturer, PrimaryInstructor + # SecondaryInstructor, TeachingAssistant, TeachingAssistantGroup + # TeachingAssistantOffering, TeachingAssistantSection, TeachingAssistantTemplate + public function getCourseRole() { + $roles = $this->getClaim('roles'); + $is_admin = $is_instructor = $is_student = $is_ta = $is_content_developer = FALSE; + + foreach ($roles as $role) { + # supports long and short formats. ex: + # http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor + # Instructor + # http://purl.imsglobal.org/vocab/lis/v2/membership/Instructor#TeachingAssistant + # Instructor#TeachingAssistant + $short_role = preg_replace('/http\:\/\/purl\.imsglobal\.org\/vocab\/lis\/v2\/membership(\#|\/)/i', '', $role); + if ('Administrator' == $short_role) { + $is_admin = TRUE; + } elseif ('Instructor' == $short_role) { + $is_instructor = TRUE; + } elseif ('ContentDeveloper' == $short_role) { + $is_content_developer = TRUE; + } elseif ('Instructor#TeachingAssistant' == $short_role) { + $is_ta = TRUE; + } elseif ('Learner' == $short_role) { + $is_student = TRUE; + } + } + + if ($is_admin) { + return $this->User->USER_TYPE_INSTRUCTOR; + } elseif ($is_instructor && !$is_ta) { + return $this->User->USER_TYPE_INSTRUCTOR; + } elseif ($is_content_developer) { + return $this->User->USER_TYPE_INSTRUCTOR; + } elseif ($is_ta) { + return $this->User->USER_TYPE_TA; + } else { + return $this->User->USER_TYPE_STUDENT; + } + } + + private function getDynamicFieldValue($field) { + if (empty($field)) { + return null; + } + $data_ref = $this->launch_data; + $field_parts = explode("|", $field); + + foreach($field_parts as $field_part) { + if (!is_array($data_ref) || empty($data_ref[$field_part])) { + return null; + } + $data_ref = $data_ref[$field_part]; + } + if (is_array($data_ref) || empty($data_ref)) { + return null; + } + return $data_ref; + } + + public function getUserIdentifierValue() { + if (!empty($this->lti_tool_registration['user_identifier_field'])) { + $user_identifier_field = $this->lti_tool_registration['user_identifier_field']; + + $value = $this->getDynamicFieldValue($user_identifier_field); + if (!empty($value)) { + return $value; + } + } + // default user identifer value + return $this->getParam('sub'); + } + + public function getStudentNumberValue() { + if (!empty($this->lti_tool_registration['student_number_field'])) { + $student_number_field = $this->lti_tool_registration['student_number_field']; + + $value = $this->getDynamicFieldValue($student_number_field); + if (!empty($value)) { + return $value; + } + } + // default student number value + return null; + } + + public function getCanvasIdValue() { + if (!empty($this->lti_tool_registration['canvas_id_field'])) { + $canvas_id_field = $this->lti_tool_registration['canvas_id_field']; + + $value = $this->getDynamicFieldValue($canvas_id_field); + if (!empty($value)) { + return $value; + } + } + // default canvas_id value + return null; + } + + public function getTermValue() { + if (!empty($this->lti_tool_registration['term_field'])) { + $term_field = $this->lti_tool_registration['term_field']; + + $value = $this->getDynamicFieldValue($term_field); + if (!empty($value)) { + return $value; + } + } + // default term value + return null; + } + + public function getFacultyNameValue() { + if (!empty($this->lti_tool_registration['faculty_name_field'])) { + $faculty_name_field = $this->lti_tool_registration['faculty_name_field']; + + $value = $this->getDynamicFieldValue($faculty_name_field); + if (!empty($value)) { + return $value; + } + } + // default term value + return 'LTI Default Faculty'; + } +} diff --git a/app/libs/lti/lti_cache.php b/app/libs/lti/lti_cache.php new file mode 100644 index 000000000..bf83f8a69 --- /dev/null +++ b/app/libs/lti/lti_cache.php @@ -0,0 +1,47 @@ +LtiNonce = \ClassRegistry::init('LtiNonce'); + return $this; + } + + public function get_launch_data($key) { + // do not support launch data in 'cache' + return null; + } + + public function cache_launch_data($key, $jwt_body) { + // do not support launch data in 'cache' + return $this; + } + + public function cache_nonce($nonce) { + $data = array( + 'LtiNonce' => array( + 'nonce' => $nonce + ) + ); + $this->LtiNonce->save($data); + return $this; + } + + public function check_nonce($nonce) { + $lti_nonce = $this->LtiNonce->findByNonce($nonce); + if (empty($lti_nonce)) { + return false; + } + $this->LtiNonce->deleteAll(array( + 'LtiNonce.nonce' => $nonce, + )); + return true; + } +} \ No newline at end of file diff --git a/app/libs/lti/lti_cookie.php b/app/libs/lti/lti_cookie.php new file mode 100644 index 000000000..576541657 --- /dev/null +++ b/app/libs/lti/lti_cookie.php @@ -0,0 +1,37 @@ + time() + $exp + ]; + + // SameSite none and secure will be required for tools to work inside iframes + $same_site_options = [ + 'samesite' => 'None', + 'secure' => true + ]; + + if (PHP_VERSION_ID < 70300) { + $options += [ + 'path' => "/", + 'domain' => "", + 'secure' => false, + 'httponly' => false + ]; + extract(array_merge($cookie_options, $options)); // => $expires, $path, $domain, $secure, $httponly + @setcookie("LEGACY_" . $name, $value, $expires, $path, $domain, $secure, $httponly); + return $this; + } + + @setcookie($name, $value, array_merge($cookie_options, $same_site_options, $options)); + + // Set a second fallback cookie in the event that "SameSite" is not supported + @setcookie("LEGACY_" . $name, $value, array_merge($cookie_options, $options)); + return $this; + } +} \ No newline at end of file diff --git a/app/libs/lti/lti_database.php b/app/libs/lti/lti_database.php new file mode 100644 index 000000000..2a7df5cb3 --- /dev/null +++ b/app/libs/lti/lti_database.php @@ -0,0 +1,53 @@ +LtiToolRegistration = \ClassRegistry::init('LtiToolRegistration'); + return $this; + } + + /** + * Find registration by issuer. + * + * @see vendor/imsglobal/lti-1p3-tool/src/lti/LTI_Registration.php + * @param string $iss + * @return IMSGlobal\LTI\LTI_Registration + */ + public function find_registration_by_issuer($iss) { + $results = $this->LtiToolRegistration->findByIss($iss); + if (empty($results)) { + return false; + } + $lti_tool_registration = $results['LtiToolRegistration']; + return LTI_Registration::new() + ->set_auth_login_url($lti_tool_registration['auth_login_url']) + ->set_auth_token_url($lti_tool_registration['auth_token_url']) + ->set_client_id($lti_tool_registration['client_id']) + ->set_key_set_url($lti_tool_registration['key_set_url']) + ->set_issuer($lti_tool_registration['iss']) + ->set_tool_private_key($lti_tool_registration['tool_private_key']); + } + + /** + * Find deployment by deployment_id. + * + * @see vendor/imsglobal/lti-1p3-tool/src/lti/LTI_Deployment.php + * @param string $iss + * @param string $deployment_id + * @return IMSGlobal\LTI\LTI_Deployment + */ + public function find_deployment($iss, $deployment_id) { + // ignore deployment validation (just assume valid) + return LTI_Deployment::new()->set_deployment_id($deployment_id); + } +} diff --git a/app/libs/lti/names_and_roles_service.php b/app/libs/lti/names_and_roles_service.php new file mode 100644 index 000000000..34c72bfb6 --- /dev/null +++ b/app/libs/lti/names_and_roles_service.php @@ -0,0 +1,136 @@ +User = \ClassRegistry::init('User'); + $this->LtiContext = \ClassRegistry::init('LtiContext'); + $this->LtiUser = \ClassRegistry::init('LtiUser'); + $this->course_id = $course_id; + $this->service_connectors = array(); + $this->lti_contexts = array(); + + $lti_contexts = $this->LtiContext->getByCourseId($course_id); + foreach ($lti_contexts as $lti_context) { + if (!empty($lti_context['LtiContext']['nrps_context_memberships_url'])) { + $this->lti_contexts []= $lti_context; + + $lti_tool_registration = $lti_context['LtiToolRegistration']; + $registration = LTI_Registration::new() + ->set_auth_login_url($lti_tool_registration['auth_login_url']) + ->set_auth_token_url($lti_tool_registration['auth_token_url']) + ->set_client_id($lti_tool_registration['client_id']) + ->set_key_set_url($lti_tool_registration['key_set_url']) + ->set_issuer($lti_tool_registration['iss']) + ->set_tool_private_key($lti_tool_registration['tool_private_key']); + $this->service_connectors[$lti_tool_registration['id']] = new LTI_Service_Connector($registration); + } + } + return $this; + } + + public function sync_membership() { + $instructors = array(); + $tas = array(); + $students = array(); + foreach ($this->lti_contexts as $lti_context) { + $lti_tool_registration = $lti_context['LtiToolRegistration']; + $service_connector = $this->service_connectors[$lti_tool_registration['id']]; + $context_memberships_url = $lti_context['LtiContext']['nrps_context_memberships_url']; + + // get resource link to pull membership from + $lti_resource_links = $lti_context['LtiResourceLink']; + $resource_link_id = $lti_resource_links[0]['resource_link_id']; + + // Canvas HACK: if context_id == resource_link_id then resource link + // will have membership for entire class (use these first if available) + foreach ($lti_resource_links as $lti_resource_link) { + if ($lti_resource_link['resource_link_id'] == $lti_context['LtiContext']['context_id']) { + $resource_link_id = $lti_resource_link['resource_link_id']; + break; + } + } + + if (strpos($context_memberships_url, '?') !== false) { + $context_memberships_url .= "&rlid=$resource_link_id"; + } else { + $context_memberships_url .= "?rlid=$resource_link_id"; + } + + $service_data = array('context_memberships_url' => $context_memberships_url); + $nrps = new LTI_Names_Roles_Provisioning_Service($service_connector, $service_data); + + $all_members = $nrps->get_members(); + foreach($all_members as $member_data) { + $nrsp_data_parser = new NamesAndRolesServiceDataParser($lti_tool_registration, $member_data); + if ($nrsp_data_parser->isActive()) { + // automatically create/update lti user + user + $lti_user = $this->LtiUser->syncUser( + $nrsp_data_parser->lti_tool_registration['id'], + $nrsp_data_parser->getParam('user_id'), + $nrsp_data_parser->getUserData() + ); + $user_id = $lti_user['User']['id']; + $role_id = $nrsp_data_parser->getCourseRole(); + + // automatically update user enrollment + $this->LtiUser->syncUserEnrollment($this->course_id, $user_id, null, $role_id); + + if (in_array($role_id, [$this->User->USER_TYPE_ADMIN, $this->User->USER_TYPE_INSTRUCTOR])) { + $instructors[$user_id] = true; + } elseif ($role_id == $this->User->USER_TYPE_TA) { + $tas[$user_id] = true; + } elseif ($role_id == $this->User->USER_TYPE_STUDENT) { + $students[$user_id] = true; + } + } + } + } + + // remove old instructors who are no longer present + $current_instructor_ids = array_keys($instructors); + $existing_instructors = $this->User->getInstructorsByCourse($this->course_id); + foreach ($existing_instructors as $instructor) { + $user_id = $instructor['User']['id']; + if (!in_array($user_id, $current_instructor_ids)) { + $this->User->removeInstructor($user_id, $this->course_id); + } + } + + // remove old tas who are no longer present + $current_ta_ids = array_keys($tas); + $existing_tas = $this->User->getTutorsByCourse($this->course_id); + foreach ($existing_tas as $ta) { + $user_id = $ta['User']['id']; + if (!in_array($user_id, $current_ta_ids)) { + $this->User->removeTutor($user_id, $this->course_id); + } + } + + // remove old students who are no longer present + $current_student_ids = array_keys($students); + $existing_students = $this->User->getEnrolledStudents($this->course_id); + foreach ($existing_students as $student) { + $user_id = $student['User']['id']; + if (!in_array($user_id, $current_student_ids)) { + $this->User->removeStudent($user_id, $this->course_id); + } + } + } +} \ No newline at end of file diff --git a/app/libs/lti/names_and_roles_service_data_parser.php b/app/libs/lti/names_and_roles_service_data_parser.php new file mode 100644 index 000000000..55409000a --- /dev/null +++ b/app/libs/lti/names_and_roles_service_data_parser.php @@ -0,0 +1,144 @@ +User = \ClassRegistry::init('User'); + $this->member_data = $member_data; + $this->lti_tool_registration = $lti_tool_registration; + return $this; + } + + public function getParam($param_name) { + if (!isset($this->member_data)) { + return null; + } + if (!isset($this->member_data[$param_name])) { + return null; + } + return $this->member_data[$param_name]; + } + + public function isActive() { + return $this->getParam('status') == 'Active'; + } + + public function getUserData() { + return array( + 'username' => $this->getUserIdentifierValue(), + 'first_name' => $this->getParam('given_name'), + 'last_name' => $this->getParam('family_name'), + 'student_no' => $this->getStudentNumberValue(), + 'email' => $this->getParam('email'), + ); + } + + # Core context roles + # http://purl.imsglobal.org/vocab/lis/v2/membership#Administrator + # http://purl.imsglobal.org/vocab/lis/v2/membership#ContentDeveloper + # http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor + # http://purl.imsglobal.org/vocab/lis/v2/membership#Learner + # http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor + # Instructor Sub-role + # Grader, GuestInstructor, Instructor, Lecturer, PrimaryInstructor + # SecondaryInstructor, TeachingAssistant, TeachingAssistantGroup + # TeachingAssistantOffering, TeachingAssistantSection, TeachingAssistantTemplate + public function getCourseRole() { + $roles = $this->getParam('roles'); + $is_admin = $is_instructor = $is_student = $is_ta = $is_content_developer = FALSE; + + foreach ($roles as $role) { + # supports long and short formats. ex: + # http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor + # Instructor + # http://purl.imsglobal.org/vocab/lis/v2/membership/Instructor#TeachingAssistant + # Instructor#TeachingAssistant + $short_role = preg_replace('/http\:\/\/purl\.imsglobal\.org\/vocab\/lis\/v2\/membership(\#|\/)/i', '', $role); + if ('Administrator' == $short_role) { + $is_admin = TRUE; + } elseif ('Instructor' == $short_role) { + $is_instructor = TRUE; + } elseif ('ContentDeveloper' == $short_role) { + $is_content_developer = TRUE; + } elseif ('Instructor#TeachingAssistant' == $short_role) { + $is_ta = TRUE; + } elseif ('Learner' == $short_role) { + $is_student = TRUE; + } + } + + if ($is_admin) { + return $this->User->USER_TYPE_INSTRUCTOR; + } elseif ($is_instructor && !$is_ta) { + return $this->User->USER_TYPE_INSTRUCTOR; + } elseif ($is_content_developer) { + return $this->User->USER_TYPE_INSTRUCTOR; + } elseif ($is_ta) { + return $this->User->USER_TYPE_TA; + } else { + return $this->User->USER_TYPE_STUDENT; + } + } + + private function getDynamicFieldValue($field) { + if (empty($field)) { + return null; + } + $field_parts = explode("|", $field); + $data_ref = null; + + if (sizeof($field_parts) == 1 && !empty($this->member_data[$field_parts[0]])) { + return $this->member_data[$field_parts[0]]; + } + + foreach ($this->member_data['message'] as $message) { + if ($message['https://purl.imsglobal.org/spec/lti/claim/message_type'] != 'LtiResourceLinkRequest') { + continue; + } + $data_ref = $message; + foreach($field_parts as $field_part) { + if (!is_array($data_ref) || empty($data_ref[$field_part])) { + return null; + } + $data_ref = $data_ref[$field_part]; + } + break; + } + + if (is_array($data_ref) || empty($data_ref)) { + return null; + } + return $data_ref; + } + + public function getUserIdentifierValue() { + if (!empty($this->lti_tool_registration['user_identifier_field'])) { + $user_identifier_field = $this->lti_tool_registration['user_identifier_field']; + + $value = $this->getDynamicFieldValue($user_identifier_field); + if (!empty($value)) { + return $value; + } + } + // default user identifer value + return $this->getParam('user_id'); + } + + public function getStudentNumberValue() { + if (!empty($this->lti_tool_registration['student_number_field'])) { + $student_number_field = $this->lti_tool_registration['student_number_field']; + + $value = $this->getDynamicFieldValue($student_number_field); + if (!empty($value)) { + return $value; + } + } + // default student number value + return null; + } +} diff --git a/app/libs/upgrade_scripts/upgrade_349.php b/app/libs/upgrade_scripts/upgrade_349.php new file mode 100644 index 000000000..9bc5d8c9d --- /dev/null +++ b/app/libs/upgrade_scripts/upgrade_349.php @@ -0,0 +1,58 @@ +fromVersions = array(null, '3.4.8'); + $this->toVersion = '3.4.9'; + $this->dbVersion = 18; + } + + /** + * + * @access public + * @return bool + */ + public function isUpgradable() + { + return parent::isUpgradable(); + } + + /** + * up + * + * @access public + * @return boolean + */ + public function up() + { + $sysparameter = ClassRegistry::init('SysParameter'); + $dbv = $sysparameter->get('database.version'); + + $ret = $this->patchDb($dbv, $this->dbVersion); + if ($ret) { + $this->errors[] = sprintf(__('Database patching failed: %s', true), $ret); + return false; + } + $sysparameter->reload(); + + return true; + } +} \ No newline at end of file diff --git a/app/locale/default.pot b/app/locale/default.pot index 75d9a8532..a4b39618f 100644 --- a/app/locale/default.pot +++ b/app/locale/default.pot @@ -4776,6 +4776,14 @@ msgstr "" msgid "Compare" msgstr "" +#: +msgid "Updated roster from Canvas" +msgstr "" + +#: +msgid "LTI 1.3 launch success" +msgstr "" + #: /Users/compass/projects/ipeer/src//users/import.ctp:5 msgid "All fields mandatory, except email and password." msgstr "" diff --git a/app/models/course.php b/app/models/course.php index 33cb075c7..20d09567f 100644 --- a/app/models/course.php +++ b/app/models/course.php @@ -35,6 +35,11 @@ class Course extends AppModel 'exclusive' => false, 'finderSql' => '' ), + 'LtiContext' => array( + 'className' => 'LtiContext', + 'foreignKey' => 'course_id', + 'dependent' => true + ), 'Event' => array( 'className' => 'Event', 'conditions' => 'Event.record_status = "A"', diff --git a/app/models/event.php b/app/models/event.php index 1dce64483..46ba95110 100644 --- a/app/models/event.php +++ b/app/models/event.php @@ -178,6 +178,11 @@ class Event extends AppModel 'dependent' => true, 'foreignKey' => 'event_id' ), + 'LtiResourceLink' => array( + 'className' => 'LtiResourceLink', + 'foreignKey' => 'event_id', + 'dependent' => true + ), ); private $timezone = null; @@ -1099,9 +1104,13 @@ function importEventsByCsv($courseId,$events,$userId) $event_date_fields = array('due_date', 'release_date_begin', 'release_date_end', 'result_release_date_begin', 'result_release_date_end'); foreach ($event_date_fields as $eventDate) { if (is_string($eventData[$eventDate])) { - $timeStamp = strtotime($eventData[$eventDate]); - if($timeStamp) { - $eventData[$eventDate] = date("Y-m-d H:i:s", $timeStamp); + if(empty($eventData[$eventDate])) { + $eventData[$eventDate] = NULL; + } else { + $timeStamp = strtotime($eventData[$eventDate]); + if($timeStamp) { + $eventData[$eventDate] = date("Y-m-d H:i:s", $timeStamp); + } } } } diff --git a/app/models/lti_context.php b/app/models/lti_context.php new file mode 100644 index 000000000..2efa353fe --- /dev/null +++ b/app/models/lti_context.php @@ -0,0 +1,152 @@ + array( + 'className' => 'Course', + 'foreignKey' => 'course_id' + ), + 'LtiToolRegistration' => array( + 'className' => 'LtiToolRegistration', + 'foreignKey' => 'lti_tool_registration_id' + ) + ); + public $hasMany = array( + 'LtiResourceLink' => array( + 'className' => 'LtiResourceLink', + 'foreignKey' => 'lti_context_id', + 'dependent' => true + ), + ); + public $validate = array( + 'lti_tool_registration_id' => array( + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'context_id' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + ); + + public function syncFaculty($launch_data_parser) { + $this->Faculty = ClassRegistry::init('Faculty'); + $name = $launch_data_parser->getFacultyNameValue(); + + $faculty = $this->Faculty->find('first', array( + 'conditions' => array( + 'Faculty.name' => $name + ) + )); + if (empty($faculty)) { + $faculty = array( + 'Faculty' => array( + 'name' => $name, + ) + ); + $this->Faculty->save($faculty); + + // refresh data after save + $faculty = $this->Faculty->find('first', array( + 'conditions' => array( + 'Faculty.name' => $name + ) + )); + } + return $faculty; + } + + public function syncLaunchContext($launch_data_parser) + { + $this->Course = ClassRegistry::init('Course'); + + $lti_tool_registration_id = $launch_data_parser->lti_tool_registration['id']; + $context_id = $launch_data_parser->getClaimParam('context', 'id'); + + $lti_context = $this->getByRegistrationIdAndContextId($lti_tool_registration_id, $context_id); + if (empty($lti_context)) { + $lti_context = array( + 'LtiContext' => array( + 'lti_tool_registration_id' => $lti_tool_registration_id, + 'context_id' => $context_id, + ) + ); + } + $lti_context['LtiContext']['nrps_context_memberships_url'] = $launch_data_parser->getNrpsClaimParam('context_memberships_url'); + + $course_data = $launch_data_parser->getCourseData(); + //setup course link if needed + if (!isset($lti_context['LtiContext']['course_id'])) { + $existing_course = $this->Course->find('first', array( + 'conditions' => array('Course.course' => $course_data['course']), + 'contain' => false + )); + if (!empty($existing_course)) { + $lti_context['Course'] = $existing_course['Course']; + } else { + $lti_context['Course'] = $course_data; + } + } + //skip updating course + if (!empty($course_data['title'])) { + $lti_context['Course']['title'] = $course_data['title']; + } + if (!empty($course_data['canvas_id'])) { + $lti_context['Course']['canvas_id'] = $course_data['canvas_id']; + } + if (!empty($course_data['term'])) { + $lti_context['Course']['term'] = $course_data['term']; + } + $this->saveAll($lti_context); + + // refresh data after save + $lti_context = $this->getByRegistrationIdAndContextId($lti_tool_registration_id, $context_id); + return $lti_context; + } + + public function getByRegistrationIdAndContextId($lti_tool_registration_id, $context_id) + { + return $this->find('first', array( + 'conditions' => array( + 'LtiContext.lti_tool_registration_id' => $lti_tool_registration_id, + 'LtiContext.context_id' => $context_id, + ), + 'contain' => array( + 'Course' + ) + )); + } + + public function getByCourseId($course_id) + { + return $this->find('all', array( + 'conditions' => array( + 'LtiContext.course_id' => $course_id, + ), + 'contain' => array( + 'LtiResourceLink' + ) + )); + } +} diff --git a/app/models/lti_nonce.php b/app/models/lti_nonce.php new file mode 100644 index 000000000..fe14bda35 --- /dev/null +++ b/app/models/lti_nonce.php @@ -0,0 +1,43 @@ + array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + ); + + /** + * Find registration by `iss` in database. + * + * @param string $iss + * @return LtiNonce + */ + public function findByNonce($nonce) + { + return $this->find('first', array( + 'conditions' => array( + 'LtiNonce.nonce' => $nonce + ), + )); + } +} diff --git a/app/models/lti_resource_link.php b/app/models/lti_resource_link.php new file mode 100644 index 000000000..d7268c415 --- /dev/null +++ b/app/models/lti_resource_link.php @@ -0,0 +1,87 @@ + array( + 'className' => 'LtiContext', + 'foreignKey' => 'lti_context_id' + ), + 'Event' => array( + 'className' => 'Event', + 'foreignKey' => 'event_id' + ) + ); + public $validate = array( + 'lti_context_id' => array( + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'resource_link_id' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + ); + + public function syncLaunchResourceLink($launch_data_parser, $lti_context_id) + { + $resource_link_id = $launch_data_parser->getClaimParam('resource_link', 'id'); + $lti_resource_link = $this->getByLtiContextIdAndResourceLinkId($lti_context_id, $resource_link_id); + + if (empty($lti_resource_link)) { + $lti_resource_link = array( + 'LtiResourceLink' => array( + 'lti_context_id' => $lti_context_id, + 'resource_link_id' => $resource_link_id, + ) + ); + } + $lti_resource_link['LtiResourceLink']['lineitems_url'] = $launch_data_parser->getAgsClaimParam('lineitems'); + $lti_resource_link['LtiResourceLink']['lineitem_url'] = $launch_data_parser->getAgsClaimParam('lineitem'); + $lti_resource_link['LtiResourceLink']['scope_lineitem'] = $launch_data_parser->hasAgsClaimScope('lineitem'); + $lti_resource_link['LtiResourceLink']['scope_lineitem_read_only'] = $launch_data_parser->hasAgsClaimScope('lineitem.readonly'); + $lti_resource_link['LtiResourceLink']['scope_result_readonly'] = $launch_data_parser->hasAgsClaimScope('result.readonly'); + $lti_resource_link['LtiResourceLink']['scope_result_score'] = $launch_data_parser->hasAgsClaimScope('score'); + + // $event_data = $launch_data_parser->getEventData(); + + $this->save($lti_resource_link); + + // refresh data after save + $lti_resource_link = $this->getByLtiContextIdAndResourceLinkId($lti_context_id, $resource_link_id); + return $lti_resource_link; + } + + public function getByLtiContextIdAndResourceLinkId($lti_context_id, $resource_link_id) + { + return $this->find('first', array( + 'conditions' => array( + 'LtiResourceLink.lti_context_id' => $lti_context_id, + 'LtiResourceLink.resource_link_id' => $resource_link_id, + ), + 'contain' => array( + 'Event', + ) + )); + } +} diff --git a/app/models/lti_tool_registration.php b/app/models/lti_tool_registration.php new file mode 100644 index 000000000..de299b943 --- /dev/null +++ b/app/models/lti_tool_registration.php @@ -0,0 +1,158 @@ + + * @license MIT {@link http://www.opensource.org/licenses/MIT} + */ +class LtiToolRegistration extends AppModel +{ + public $name = 'LtiToolRegistration'; + public $validate = array( + 'iss' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'client_id' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'auth_login_url' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'auth_token_url' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'key_set_url' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'tool_private_key' => array( + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'tool_public_key' => array( + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'user_identifier_field' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => false, + 'allowEmpty' => true, + ), + ), + 'student_number_field' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => false, + 'allowEmpty' => true, + ), + ), + 'term_field' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => false, + 'allowEmpty' => true, + ), + ), + 'canvas_id_field' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => false, + 'allowEmpty' => true, + ), + ), + 'faculty_name_field' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => false, + 'allowEmpty' => true, + ), + ), + ); + + public $hasMany = array( + 'LtiContext' => array( + 'className' => 'LtiContext', + 'foreignKey' => 'lti_tool_registration_id', + 'dependent' => true + ), + ); + + /** + * Find registration by `iss` in database. + * + * @param string $iss + * @return LtiToolRegistration + */ + public function findByIss($iss) + { + return $this->find('first', array( + 'conditions' => array( + 'LtiToolRegistration.iss' => $iss + ), + )); + } +} diff --git a/app/models/lti_user.php b/app/models/lti_user.php new file mode 100644 index 000000000..6f932b939 --- /dev/null +++ b/app/models/lti_user.php @@ -0,0 +1,162 @@ + array( + 'className' => 'User', + 'foreignKey' => 'ipeer_user_id' + ) + ); + public $validate = array( + 'lti_tool_registration_id' => array( + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + 'lti_user_id' => array( + 'maxLength' => array( + 'rule' => array('maxLength', 255), + 'message' => 'Maximum 255 characters', + 'required' => true, + 'allowEmpty' => false, + ), + 'notEmpty' => array( + 'rule' => 'notEmpty', + 'message' => 'Cannot be blank', + 'required' => true, + ), + ), + ); + + public function syncUser($lti_tool_registration_id, $lti_user_id, $user_data) + { + $this->User = ClassRegistry::init('User'); + + $lti_user = $this->getByRegistrationIdAndLtiUserId($lti_tool_registration_id, $lti_user_id); + if (empty($lti_user)) { + $lti_user = array( + 'LtiUser' => array( + 'lti_tool_registration_id' => $lti_tool_registration_id, + 'lti_user_id' => $lti_user_id, + ) + ); + } + + //setup user link if needed + if (!isset($lti_user['LtiUser']['ipeer_user_id'])) { + $existing_user = $this->User->find('first', array( + 'conditions' => array('User.username' => $user_data['username']), + 'contain' => false + )); + if (!empty($existing_user)) { + $lti_user['User'] = $existing_user['User']; + } else { + $lti_user['User'] = $user_data; + } + } + //skip updating username + if (!empty($user_data['first_name'])) { + $lti_user['User']['first_name'] = $user_data['first_name']; + } + if (!empty($user_data['last_name'])) { + $lti_user['User']['last_name'] = $user_data['last_name']; + } + if (!empty($user_data['student_no'])) { + $lti_user['User']['student_no'] = $user_data['student_no']; + } + if (!empty($user_data['email'])) { + $lti_user['User']['email'] = $user_data['email']; + } + + $this->saveAll($lti_user); + + // refresh data after save + $lti_user = $this->getByRegistrationIdAndLtiUserId($lti_tool_registration_id, $lti_user_id); + return $lti_user; + } + + public function syncUserEnrollment($course_id, $user_id, $faculty_id, $role_id) + { + $this->User = ClassRegistry::init('User'); + $this->Role = ClassRegistry::init('Role'); + + # add new system roles as needed + $results = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user_id), + 'recursive' => 0 + )); + $system_role_ids = Set::extract('/Role/id', $results); + if (!in_array($role_id, $system_role_ids)) { + $this->User->registerRole($user_id, $role_id); + } + + # handle enrollments + $results = $this->User->find('first', array( + 'conditions' => array('User.id' => $user_id), + 'contain' => array( + 'Faculty' => array('conditions' => array('Faculty.id' => $faculty_id)), + 'Course' => array('conditions' => array('Course.id' => $course_id)), + 'Tutor' => array('conditions' => array('Tutor.id' => $course_id)), + 'Enrolment' => array('conditions' => array('Enrolment.id' => $course_id)), + ) + )); + + if (in_array($role_id, [$this->User->USER_TYPE_ADMIN, $this->User->USER_TYPE_INSTRUCTOR])) { + if (sizeof($results['Course']) == 0) { + $this->User->addInstructor($user_id, $course_id); + } + # ensure user is assigned to a Faculty if instructor or admin + if (sizeof($results['Faculty']) == 0 && !empty($faculty_id)) { + $this->User->habtmAdd('Faculty', $user_id, $faculty_id); + } + } else { + if (sizeof($results['Course']) > 0) { + $this->User->removeInstructor($user_id, $course_id); + } + } + + if ($role_id == $this->User->USER_TYPE_TA) { + if (sizeof($results['Tutor']) == 0) { + $this->User->addTutor($user_id, $course_id); + } + } else { + if (sizeof($results['Tutor']) > 0) { + $this->User->removeTutor($user_id, $course_id); + } + } + + if ($role_id == $this->User->USER_TYPE_STUDENT) { + if (sizeof($results['Enrolment']) == 0) { + $this->User->addStudent($user_id, $course_id); + } + } else { + if (sizeof($results['Enrolment']) > 0) { + $this->User->removeStudent($user_id, $course_id); + } + } + } + + public function getByRegistrationIdAndLtiUserId($lti_tool_registration_id, $lti_user_id) + { + return $this->find('first', array( + 'conditions' => array( + 'LtiUser.lti_tool_registration_id' => $lti_tool_registration_id, + 'LtiUser.lti_user_id' => $lti_user_id, + ), + 'contain' => array( + 'User' + ) + )); + } +} diff --git a/app/models/user.php b/app/models/user.php index b95c84344..e51c7027a 100644 --- a/app/models/user.php +++ b/app/models/user.php @@ -53,6 +53,11 @@ class User extends AppModel 'className' => 'SurveyGroupMember', 'foreignKey' => 'user_id', ), + 'LtiUser' => array( + 'className' => 'LtiUser', + 'foreignKey' => 'ipeer_user_id', + 'dependent' => true + ), ); public $hasAndBelongsToMany = array( @@ -1499,16 +1504,9 @@ protected function requiredWith($check, $with) protected function validEmail($check) { $email = $check['email']; - if (function_exists('filter_var')) { - // filter_var() requires php >= 5.2.0 - if (filter_var($email, FILTER_VALIDATE_EMAIL)) { - return true; - } - } else { - // really basic fallback validation - if (preg_match('/.+@.+/', $email)) { - return true; - } + // really basic fallback validation + if (preg_match('/.+@.+/', $email)) { + return true; } return false; } diff --git a/app/tests/cases/caliper/app_controller_hooks.test.php b/app/tests/cases/caliper/app_controller_hooks.test.php index ad137c3bd..586fa9780 100644 --- a/app/tests/cases/caliper/app_controller_hooks.test.php +++ b/app/tests/cases/caliper/app_controller_hooks.test.php @@ -23,6 +23,8 @@ class CaliperAppControllerHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/course_hooks.test.php b/app/tests/cases/caliper/course_hooks.test.php index a9aec1b4f..765d1ee4b 100644 --- a/app/tests/cases/caliper/course_hooks.test.php +++ b/app/tests/cases/caliper/course_hooks.test.php @@ -23,6 +23,8 @@ class CaliperCourseHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/event_mixeval_hooks.test.php b/app/tests/cases/caliper/event_mixeval_hooks.test.php index 5ea7904cd..78b30702b 100644 --- a/app/tests/cases/caliper/event_mixeval_hooks.test.php +++ b/app/tests/cases/caliper/event_mixeval_hooks.test.php @@ -23,6 +23,8 @@ class CaliperEventMixevalHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/event_rubric_hooks.test.php b/app/tests/cases/caliper/event_rubric_hooks.test.php index fb0f7ce3b..d0ce75ec1 100644 --- a/app/tests/cases/caliper/event_rubric_hooks.test.php +++ b/app/tests/cases/caliper/event_rubric_hooks.test.php @@ -23,6 +23,8 @@ class CaliperEventRubricHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/event_simple_evaulation_hooks.test.php b/app/tests/cases/caliper/event_simple_evaulation_hooks.test.php index c7da7e336..e5cf49425 100644 --- a/app/tests/cases/caliper/event_simple_evaulation_hooks.test.php +++ b/app/tests/cases/caliper/event_simple_evaulation_hooks.test.php @@ -23,6 +23,8 @@ class CaliperEventSimpleEvaluationHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/event_survey_hooks.test.php b/app/tests/cases/caliper/event_survey_hooks.test.php index 62a34b747..491e450bf 100644 --- a/app/tests/cases/caliper/event_survey_hooks.test.php +++ b/app/tests/cases/caliper/event_survey_hooks.test.php @@ -23,6 +23,8 @@ class CaliperEventSurveyHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/mixeval_hooks.test.php b/app/tests/cases/caliper/mixeval_hooks.test.php index 644764cdf..bee3317f8 100644 --- a/app/tests/cases/caliper/mixeval_hooks.test.php +++ b/app/tests/cases/caliper/mixeval_hooks.test.php @@ -24,6 +24,8 @@ class CaliperMixevalHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/rubric_hooks.test.php b/app/tests/cases/caliper/rubric_hooks.test.php index 2b3617c3b..b825026ab 100644 --- a/app/tests/cases/caliper/rubric_hooks.test.php +++ b/app/tests/cases/caliper/rubric_hooks.test.php @@ -24,6 +24,8 @@ class CaliperRubricHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/simple_evaluation_hooks.test.php b/app/tests/cases/caliper/simple_evaluation_hooks.test.php index 738709b3c..93b5028ad 100644 --- a/app/tests/cases/caliper/simple_evaluation_hooks.test.php +++ b/app/tests/cases/caliper/simple_evaluation_hooks.test.php @@ -23,6 +23,8 @@ class CaliperSimpleEvaluationHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/caliper/survey_hooks.test.php b/app/tests/cases/caliper/survey_hooks.test.php index 5798957e9..af7a175bc 100644 --- a/app/tests/cases/caliper/survey_hooks.test.php +++ b/app/tests/cases/caliper/survey_hooks.test.php @@ -23,6 +23,8 @@ class CaliperSurveyHooksTest extends CaliperAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/components/evaluation_component.test.php b/app/tests/cases/components/evaluation_component.test.php index f4bab43fb..ec0280c78 100644 --- a/app/tests/cases/components/evaluation_component.test.php +++ b/app/tests/cases/components/evaluation_component.test.php @@ -12,6 +12,8 @@ class EvaluationTestCase extends CakeTestCase { public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', 'app.penalty', 'app.user_grade_penalty', 'app.roles_user', 'app.event', 'app.event_template_type', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.group_event', 'app.evaluation_submission','app.evaluation_mixeval', 'app.survey_group_set', 'app.survey_group', 'app.groups_member', 'app.survey_group_member', 'app.question', 'app.survey_input', 'app.rubrics_lom', 'app.evaluation_rubric', 'app.evaluation_rubric_detail', @@ -181,7 +183,7 @@ function saveNGetEvalutionRubricDetail() { } - + /** * testGetStudentViewRubricResultDetailReview * @@ -361,7 +363,7 @@ function testFormatRubricEvaluationResultsMatrix() ), ); $this->assertEqual($expected, $result); - + // Tests null instance $result = Toolkit::formatRubricEvaluationResultsMatrix(null); $this->assertFalse($result); @@ -465,7 +467,7 @@ function testSaveNGetEvaluationMixevalDetail() // Get the grade with real values $grade = $this->EvaluationComponentTest->saveNGetEvaluationMixevalDetail ($evalMixevalId, $mixeval, $form, $auto_release); $this->assertEqual($grade, 0.8); - + // Test the method with null values $grade = $this->EvaluationComponentTest->saveNGetEvaluationMixevalDetail (null, null, null, null); $this->assertFalse($grade); @@ -528,7 +530,7 @@ function testGetStudentViewMixevalResultDetailReview() $this->assertWithinMargin($currentTime, $eval[5][0]['Event']['due_in'], 5); // Null due_in time since we are doing assertEqual $eval[5][0]['Event']['due_in'] = null; - + // Set up expected return value $expected = $this->setUpMixevalResultDetailReview(); $this->assertEqual($eval, $expected); @@ -539,10 +541,10 @@ function testGetStudentViewMixevalResultDetailReview() $eval = $this->EvaluationComponentTest->getStudentViewMixevalResultDetailReview(1, null); $this->assertFalse(!empty($eval)); } - + function testFormatMixevalEvaluationResultsMatrix() { - + } @@ -641,19 +643,19 @@ function testFormatSurveyEvaluationSummary() $survey = $this->EvaluationComponentTest->formatSurveyEvaluationSummary($surveyId, $eventId, $userIds); $expected = $this->setUpSurveyTestData(); $this->assertEqual($expected, $survey); - + // Testing null and 'random' values $survey = $this->EvaluationComponentTest->formatSurveyEvaluationSummary(999, $eventId, $userIds); $this->assertFalse($survey); // Set up survey results with no responses $expected = $this->setUpSurveyBlankData(); - + $survey = $this->EvaluationComponentTest->formatSurveyEvaluationSummary($surveyId, 999, $userIds); $this->assertEqual($expected, $survey); - + $survey = $this->EvaluationComponentTest->formatSurveyEvaluationSummary($surveyId, $eventId, array(84, 180, 230)); $this->assertEqual($expected, $survey); - + $survey = $this->EvaluationComponentTest->formatSurveyEvaluationSummary(null, null, null); $this->assertFalse($survey); } diff --git a/app/tests/cases/controllers/accesses_controller.test.php b/app/tests/cases/controllers/accesses_controller.test.php index 5bd70e779..d24283f23 100644 --- a/app/tests/cases/controllers/accesses_controller.test.php +++ b/app/tests/cases/controllers/accesses_controller.test.php @@ -6,7 +6,7 @@ Mock::generatePartial('AccessesController', 'MockAccessesController', array('isAuthorized', 'render', 'redirect', '_stop', 'header')); - + /** * AccessesControllerTest Accesses controller test case * @@ -16,10 +16,12 @@ class AccessesControllerTest extends ExtendedAuthTestCase { public $controller = null; - + public $fixtures = array( 'app.access', 'app.oauth_token', 'app.sys_parameter', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.course', 'app.group', 'app.group_event', 'app.evaluation_simple', 'app.survey_input', 'app.survey_group_member', 'app.survey_group_set', 'app.survey', 'app.question', 'app.response', 'app.survey_question', @@ -28,7 +30,7 @@ class AccessesControllerTest extends ExtendedAuthTestCase 'app.department', 'app.course_department', 'app.penalty', 'app.evaluation_rubric', 'app.evaluation_rubric_detail', 'app.evaluation_mixeval', 'app.evaluation_mixeval_detail' ); - + /** * startCase case startup * @@ -45,7 +47,7 @@ public function startCase() ) ); } - + /** * endCase case ending * @@ -55,7 +57,7 @@ public function startCase() public function endCase() { } - + /** * startTest prepare tests * @access public @@ -66,7 +68,7 @@ public function startTest($method) echo $method.TEST_LB; $this->controller = new MockAccessesController(); } - + /** * @access public * @return void @@ -79,12 +81,12 @@ public function endTest($method) unset($this->controller); ClassRegistry::flush(); } - + public function getController() { return $this->controller; } - + function testView() { //id should not be used since it can change @@ -96,43 +98,43 @@ function testView() $this->assertEqual($superadmin['permissions']['controllers/courses/add'], $allow); $this->assertEqual($superadmin['roleId'], 1); $this->assertEqual($superadmin['title_for_layout'], 'Permissions Editor > superadmin'); - + $admin = $this->testAction('/accesses/view/2', array('return' => 'vars')); unset($admin['permissions']['controllers/courses/add']["id"]); $this->assertEqual($admin['permissions']['controllers/courses/add'], $allow); $this->assertEqual($admin['roleId'], 2); $this->assertEqual($admin['title_for_layout'], 'Permissions Editor > admin'); - + $instructor = $this->testAction('/accesses/view/3', array('return' => 'vars')); unset($instructor['permissions']['controllers/courses/add']["id"]); $this->assertEqual($instructor['permissions']['controllers/courses/add'], $allow); $this->assertEqual($instructor['roleId'], 3); $this->assertEqual($instructor['title_for_layout'], 'Permissions Editor > instructor'); - + $tutor = $this->testAction('/accesses/view/4', array('return' => 'vars')); unset($tutor['permissions']['controllers/courses/add']["id"]); $this->assertEqual($tutor['permissions']['controllers/courses/add'], $deny); $this->assertEqual($tutor['roleId'], 4); $this->assertEqual($tutor['title_for_layout'], 'Permissions Editor > tutor'); - + $student = $this->testAction('/accesses/view/5', array('return' => 'vars')); unset($student['permissions']['controllers/courses/add']["id"]); $this->assertEqual($student['permissions']['controllers/courses/add'], $deny); $this->assertEqual($student['roleId'], 5); $this->assertEqual($student['title_for_layout'], 'Permissions Editor > student'); } - + function testEdit() { $this->Access = ClassRegistry::init('Access'); - + // no entry existed before the next test $result = $this->Access->find('first', array( 'conditions' => array('aro_id' => 2, 'aco_id' => 11) )); $this->assertEqual($result, array()); - - // testing allowing access to ALL actions that has no entry in the aros_acos table + + // testing allowing access to ALL actions that has no entry in the aros_acos table $this->testAction('/accesses/edit/allow/11/2'); $result = $this->Access->find('first', array( 'conditions' => array('aro_id' => 2, 'aco_id' => 11) @@ -141,7 +143,7 @@ function testEdit() $this->assertEqual($result['Access']['_read'], 1); $this->assertEqual($result['Access']['_update'], 1); $this->assertEqual($result['Access']['_delete'], 1); - + // testing denying access to ALL actions that has an entry in the aros_acos table $this->testAction('/accesses/edit/deny/11/2'); $result = $this->Access->find('first', array( @@ -151,15 +153,15 @@ function testEdit() $this->assertEqual($result['Access']['_read'], -1); $this->assertEqual($result['Access']['_update'], -1); $this->assertEqual($result['Access']['_delete'], -1); - + $this->Access->delete($result['Access']['id']); - + // no entry existed before the next test $result = $this->Access->find('first', array( 'conditions' => array('aro_id' => 2, 'aco_id' => 12) )); $this->assertEqual($result, array()); - + // testing allowing access to ONE action that has no entry in the aros_acos table $this->testAction('/accesses/edit/allow/12/2/create'); $result = $this->Access->find('first', array( @@ -169,7 +171,7 @@ function testEdit() $this->assertEqual($result['Access']['_read'], -1); $this->assertEqual($result['Access']['_update'], -1); $this->assertEqual($result['Access']['_delete'], -1); - + // testing denying access to ONE action that has an entry in the aros_acos table $this->testAction('/accesses/edit/deny/12/2/create'); $result = $this->Access->find('first', array( @@ -179,7 +181,7 @@ function testEdit() $this->assertEqual($result['Access']['_read'], -1); $this->assertEqual($result['Access']['_update'], -1); $this->assertEqual($result['Access']['_delete'], -1); - + $this->Access->delete($result['Access']['id']); } } diff --git a/app/tests/cases/controllers/courses_controller.test.php b/app/tests/cases/controllers/courses_controller.test.php index 0bbcfa3ec..69510766e 100644 --- a/app/tests/cases/controllers/courses_controller.test.php +++ b/app/tests/cases/controllers/courses_controller.test.php @@ -29,6 +29,8 @@ class CoursesControllerTest extends ExtendedAuthTestCase public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -137,7 +139,6 @@ class CoursesControllerTest extends ExtendedAuthTestCase 'created' => '2006-06-19 16:25:24', 'updater_id' => null, 'modified' => '2006-06-19 16:25:24', - 'lti_id' => null, 'full_name' => 'Instructor 1', 'student_no_with_full_name' => 'Instructor 1', 'creator' => 'Super Admin', @@ -171,7 +172,6 @@ class CoursesControllerTest extends ExtendedAuthTestCase 'created' => '2006-06-20 14:17:02', 'updater_id' => null, 'modified' => '2006-06-20 14:17:02', - 'lti_id' => null, 'full_name' => 'Instructor 2', 'student_no_with_full_name' => 'Instructor 2', 'creator' => 'Super Admin', @@ -206,7 +206,6 @@ class CoursesControllerTest extends ExtendedAuthTestCase 'created' => '2006-06-20 14:17:53', 'updater_id' => null, 'modified' => '2006-06-20 14:17:53', - 'lti_id' => null, 'full_name' => 'Instructor 3', 'student_no_with_full_name' => 'Instructor 3', 'creator' => 'Super Admin', diff --git a/app/tests/cases/controllers/departments_controller.test.php b/app/tests/cases/controllers/departments_controller.test.php index ecbdae173..557ab2f8e 100644 --- a/app/tests/cases/controllers/departments_controller.test.php +++ b/app/tests/cases/controllers/departments_controller.test.php @@ -19,6 +19,8 @@ function redirect($url, $status = null, $exit = true) { class DepartmentsControllerTestCase extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/evaltools_controller.test.php b/app/tests/cases/controllers/evaltools_controller.test.php index 87df43151..ec25b0251 100644 --- a/app/tests/cases/controllers/evaltools_controller.test.php +++ b/app/tests/cases/controllers/evaltools_controller.test.php @@ -21,6 +21,8 @@ class FakeController extends Controller { class EvaltoolsControllerTest extends CakeTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/evaluations_controller.test.php b/app/tests/cases/controllers/evaluations_controller.test.php index e7467109f..065cfbdad 100644 --- a/app/tests/cases/controllers/evaluations_controller.test.php +++ b/app/tests/cases/controllers/evaluations_controller.test.php @@ -22,6 +22,8 @@ class EvaluationControllerTest extends ExtendedAuthTestCase public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -163,13 +165,13 @@ function testMakeEvaluationSurveyForStudent() $result = $this->testAction('/evaluations/makeEvaluation/4', array('return' => 'vars')); $this->assertEqual($result['eventId'], 4); $this->assertEqual(count($result['questions']), 2); - $this->assertEqual($result['questions'][0]['Question']['prompt'], + $this->assertEqual($result['questions'][0]['Question']['prompt'], 'What was your GPA last term?'); - $this->assertEqual($result['questions'][1]['Question']['prompt'], + $this->assertEqual($result['questions'][1]['Question']['prompt'], 'Do you own a laptop?'); - $this->assertEqual(count($result['questions'][0]['ResponseOptions']), + $this->assertEqual(count($result['questions'][0]['ResponseOptions']), 4); - $this->assertEqual(count($result['questions'][1]['ResponseOptions']), + $this->assertEqual(count($result['questions'][1]['ResponseOptions']), 2); $message = $this->controller->Session->read('Message.flash'); $this->assertEqual($message['message'], ''); diff --git a/app/tests/cases/controllers/events_controller.test.php b/app/tests/cases/controllers/events_controller.test.php index 5a66851c5..9a86ae8bb 100644 --- a/app/tests/cases/controllers/events_controller.test.php +++ b/app/tests/cases/controllers/events_controller.test.php @@ -28,6 +28,8 @@ class EventsControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/faculties_controller.test.php b/app/tests/cases/controllers/faculties_controller.test.php index 39f8da492..d24c46fa4 100644 --- a/app/tests/cases/controllers/faculties_controller.test.php +++ b/app/tests/cases/controllers/faculties_controller.test.php @@ -14,6 +14,8 @@ class FacultiesControllerTestCase extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/groups_controller.test.php b/app/tests/cases/controllers/groups_controller.test.php index 743eff19c..6b4b47266 100644 --- a/app/tests/cases/controllers/groups_controller.test.php +++ b/app/tests/cases/controllers/groups_controller.test.php @@ -21,6 +21,8 @@ class GroupsControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/home_controller.test.php b/app/tests/cases/controllers/home_controller.test.php index b35f9294d..3d3743de2 100644 --- a/app/tests/cases/controllers/home_controller.test.php +++ b/app/tests/cases/controllers/home_controller.test.php @@ -22,6 +22,8 @@ class HomeControllerTest extends ExtendedAuthTestCase public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -80,7 +82,7 @@ function testIndex() $this->assertTrue( strtotime($result['course_list']['A'][0]['Course']['created']) > strtotime($result['course_list']['A'][1]['Course']['created'])); - + $this->assertEqual( $result['course_list']['A'][1]['Course']['course'], 'APSC 201'); $this->assertEqual( diff --git a/app/tests/cases/controllers/lti_controller.test.php b/app/tests/cases/controllers/lti_controller.test.php new file mode 100644 index 000000000..83c598e3b --- /dev/null +++ b/app/tests/cases/controllers/lti_controller.test.php @@ -0,0 +1,2183 @@ +; rel="current"', + '; rel="next"', + '; rel="first"', + '; rel="last"', + ); + $headers = array( + 'HTTP/1.1 200 OK', + 'Content-Type: application/vnd.ims.lti-nrps.v2.membershipcontainer+json; charset=utf-8', + 'Status: 200 OK', + 'Link: ' . implode(',', $links), + ); + $header = implode("\r\n", $headers)."\r\n"; + $CURL_HEADER_SIZE = strlen($header); + return $header . '{ + "id": "; rel="current"', + '; rel="previous"', + '; rel="first"', + '; rel="last"', + ); + $headers = array( + 'HTTP/1.1 200 OK', + 'Content-Type: application/vnd.ims.lti-nrps.v2.membershipcontainer+json; charset=utf-8', + 'Status: 200 OK', + 'Link: ' . implode(',', $links), + ); + $header = implode("\r\n", $headers)."\r\n"; + $CURL_HEADER_SIZE = strlen($header); + return $header . '{ + "id": "defaultLogin = array( + 'User' => array( + 'username' => 'root', + 'password' => md5('ipeeripeer') + ) + ); + } + + function endCase() { + } + + function startTest($method) { + echo $method.TEST_LB; + $this->controller = new MockLtiController(); + + $this->login_data = array( + "iss" => "https://docker-canvas.instructure.com", + "login_hint" => "135745", + "target_link_uri" => "https://localhost/lti/launch/", + "lti_message_hint" => "19481", + "lti_deployment_id" => "10000:10000000000013", + "client_id" => "10000000000013", + ); + $this->base_launch_data = array( + "https://purl.imsglobal.org/spec/lti/claim/message_type" => "LtiResourceLinkRequest", + "https://purl.imsglobal.org/spec/lti/claim/resource_link" => [ + "id" => "19481", + "title" => "Test Link", + "description" => "N/A" + ], + "https://purl.imsglobal.org/spec/lti/claim/context" => [ + "id" => "10045", + "label" => "Course 123", + "title" => "Test Course", + "type" => [ + "course_offering" + ] + ], + "https://purl.imsglobal.org/spec/lti/claim/tool_platform" => [ + "name" => "Mock Canvas", + "contact_email" => "", + "description" => "", + "url" => "", + "product_family_code" => "", + "version" => "1.0", + "guid" => 1176 + ], + "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" => [ + "scope" => [ + "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem", + "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly", + "https://purl.imsglobal.org/spec/lti-ags/scope/score" + ], + "lineitems" => "https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items" + ], + "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" => [ + "context_memberships_url" => "https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships", + "service_versions" => [ + "2.0" + ] + ], + "iss" => "https://docker-canvas.instructure.com", + "aud" => "10000000000013", + "iat" => time(), + "exp" => time() + (60 * 5), //expires in 5 minutes + "nonce" => "04aee25a09294ce3b94f532f90c755d21caf6c5e131e11eba5cd0242ac120005", + "https://purl.imsglobal.org/spec/lti/claim/version" => "1.3.0", + "locale" => "en-US", + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "canvas_course_id" => "1000", + "term_name" => "Default Term", + "account_name" => "Some Account" + ], + "https://purl.imsglobal.org/spec/lti/claim/deployment_id" => "10000:10000000000013", + "https://purl.imsglobal.org/spec/lti/claim/target_link_uri" => "https://localhost/lti/launch/" + ); + + + $this->base_instructor_data = array( + "given_name" => "Millie", + "family_name" => "Robel", + "middle_name" => "Cummerata", + "email" => "Millie.Robel@example.org", + "name" => "Millie Cummerata Robel", + "sub" => "instructor_lti_user_id", + "https://purl.imsglobal.org/spec/lti/claim/roles" => [ + "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor", + "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor" + ], + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "username" => "lti_instructor", + "student_number" => null + ], + ); + $this->instructor_launch_data = array_merge_recursive($this->base_launch_data, $this->base_instructor_data); + + + $this->base_ta_data = array( + "given_name" => "Stevie", + "family_name" => "PhD", + "middle_name" => "Kuhn", + "email" => "Stevie.PhD@example.org", + "name" => "Stevie Kuhn PhD", + "sub" => "ta_lti_user_id", + "https://purl.imsglobal.org/spec/lti/claim/roles" => [ + "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Learner", + "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor", + "http://purl.imsglobal.org/vocab/lis/v2/membership/Instructor#TeachingAssistant" + ], + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "username" => "lti_ta", + "student_number" => '1234567890' + ], + ); + $this->ta_launch_data = array_merge_recursive($this->base_launch_data, $this->base_ta_data); + + + $this->base_student_data = array( + "given_name" => "Maricela", + "family_name" => "Nikolaus", + "middle_name" => "Windler", + "email" => "Maricela.Nikolaus@example.org", + "name" => "Maricela Windler Nikolaus", + "sub" => "student_lti_user_id", + "https://purl.imsglobal.org/spec/lti/claim/roles" => [ + "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Learner", + "http://purl.imsglobal.org/vocab/lis/v2/membership#Learner" + ], + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "username" => "lti_student", + "student_number" => '0987654321' + ], + ); + $this->student_launch_data = array_merge_recursive($this->base_launch_data, $this->base_student_data); + + $this->platform_private_key = '-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAyDQ0JyGesS6gWeO3W0QqlzN0+6ODsbQ86wVvqAsloeSuKYWw +lvqjuCqrSJMMpRjaT7wDri8Xq2SGLMq9Yi1k9tXExIKqyIgZ8NROybHRhvEXL0rM +WR++03e7tOK/Kv+ok9crLiKeuaB+7NQWlgXqWpgMPKmk2Spa/qj4I1Xidq2MVxzV +ub9B/FtDyYulHxg+ifNoSkmIKVP+p7a91bYSvXnF1PJQ1QxrFpr0rn7VGQ4/Jvyk +YqRwGkJE/uXw5gAMP8JgXdlak+ntg2tljdvpp6kG9Dh3Vra9NmiehB8vPXBYtUo0 +QC/zE31BK4vLCUNEBQN5dGYDTwyjYYRkRMdWEK3S2vaoYi5FTZTpXCH0FgZr/8bZ +ygq7CaF9o11QHvUA3vjoAUpMf3KAyeDXIhmnpGbg4BcIHeFsgcua64NNXHPCsmXG +DgjG6Up3RT6Z0nDACTO03TXvcjkjA1WPGJpShzG/0eUN4T6u96pxKlPSQ76j083l +Q60LOTN9d0wKVw79Xg5OuUx0uW3nQRM157TXjZVad5QZ5Okzcb3kpFhwUqGh0Ufs +YVt3Io9+jGXN+JFhDYl+VqlTd42eOmno7Kcs9QIUPccqLQGq+tqDAZQWIGUWDdyk +RC98tJdOOKhVHXrpz7TS2dmEAq21X+NJ62vhzZdGTRbtYG8vS/15wplk6NsCAwEA +AQKCAgAHKoXdYAuH8w/Ooo3bHfRaUioIodXibqhUOe8WYu5svjyVBDyJwvHznc9g +uya+OBwEXkn8Gk0XbPM34jqSK5F2xmMTLPbY/l99/9N2zmnIvJN5tWHdeiXrTYNc +r6sWam1J0W9caOUpPCX0QIkzGIYcH7TRwmQXWTfwRUhl12VSSIGtUpJnN4HbUGxQ +6Ac34YxyL/zBKqL4O7ycmzgYpg+YqaWxACAa6CVjJevGh91agWiK8uByyXOxH5h2 ++UW8rdltD35B4w2mHWmg2nTESvqw7iYaEsquwhQWwPrqPBra8Pez5t06Q50d9aNM +tZBwcJscL398RknYXVmccXqMqF1GY69RKYiIdHfKLwnhSBdJ1w7BiswnllBSDVZL +STZA8dyKozSTnxsBq4PoO3cXGEYKdO+1GZy97h6ByTkaCFXZOdclBvPAyi/A4Ee/ +KSsdkUvKryyKfTgzIPXzLsDxhs0hpkI3v7PjKz9uzCvQpq9X3Cd88Gh8Z3MaW4YS +Q4OHTTEUthCR8205uuHm0DXYCTgWv4NrLRJexG/uy7h/enLKFmAfVCT1NUgKxeIS +3uq5yo/0McKekBN/JSYLooAomfKKFwFlc5QY9RSX/MmHxxK1X9BEXA3/c6RA37s8 +ymfCq8pKDy4/q3hyux1Tm+kiOikTm1lkPMM0y/qRTab+f3etZQKCAQEA3gxjcKzz +hpyVUkf+sKU1a6qU3IAxmJ2a5mZO+2IOK1ihZYfPwxnjP5DyIhyoG83Fo0GULRiB +RyaMn42hFYPM59ovKqSBPXU4rWGhWqXK80Yl+uS92z4/lfY05R7k8Sz2QcuYwf4d +xyLpjJKEQerrXyHH5MZj6fkJv4WZ2bko/C1H07ac7zcyPx+I/MN2mY1caDVz/2Pw +B+8m21HyVjY3HK10N5QyWdA+l36dmzfFmzagi1WkGFe/WVJ3BUhqqc5SXbKg9A8B +L+MXekU2S7jZ1J8esRxYLUzkeH2fk9OquVubdSVQgOnyHHOTvkkHAowXSdWMKGpC +eHYLAnlkfC55rQKCAQEA5tDCjFOljAptx6Jd5oYfaEEf9KjRajaQsHfAHG2e/UJC +S77BSVyc+ot4lGfN5AuXCDXkOVOt0useG2KxdEHVRycKSVwuRr1y17EjYZ9M+qB3 +kbqke3JkXWVt1lVcmBGjHpEdTKSvWqvTOtl8BMW6dnXCXlorj0B6NhAmwROrT+bj +KJNFYQ8O36o7k6NSvlWG55xc7DpLOPeMhym+1liIVwrps+9YI/IlzN+2OhIAjHJn +tokiBV0TT8tEvjoPbaq7IwccyCttdbAge713/L0wLBFsvkDOF3/Npf8DFYAcuFH2 +d2jWZ3LiJOKOsmQO0Adv3g48imRRXKERqr2QWrTNpwKCAQEAvXwo56BPeJHqwvp5 +F1kES0qYGcqziB8GbpLj15WHrenGYRQScdWHnVkdp4p40rE4dOajghAlUghNfGKq +EegVVc1U7rjPKRj9Msfbn7VXiV5VTtMgSRXHwTsHTHaevEi4JNGPHAy0cJkUYEcv +4eiMzvPO1yWNYb6JWQyzi558oSYq4zo0ldauZDuO9NQAQ2zkbHEg+dHYpYypxgMa +IAPH6AsE3+DxTr9sim8cI7bmRFvLiNueWr+WpKzAsJtpmlpc42RqAZtEUg8im86w +VNH74Xuf/1fGz3GMjl31bXr1d5P7B26+UiRR3YGrlHhRKRVPUkyPfHWhH5bsMkJR +Q7+NSQKCAQBy42SDDruvOh2sqeANd6M4dHoggMtEEAbzH5grTlE+BHYVV8zD5Gpq +t3N8gzLTmQVDW/fOpR03iDqDLRvhH0e20/Ll0xFhurjoLc7Lr8xUT/1UN0/Z9nWI +m40Ri4m8U8Ma2uZ3mN2Dx1UrzMdTZMxMXI80AbP+6Pwr3tw7bLvv2KAnOS7mgeVI +ZWakNT5haRbuQEFsgBOjNmzndlr8PDMZCGCNZMw9kDFKiewdeYp2XhfLnvSlMNAE +/sun2CSH1NyzMb4c0Kj6VIHGted8kPriZIX5KS6sObw2LPnvAMbK5FlG1JMsCN4R +uAeJOg65c4o2QGXYCNkKv02Y7CRnUemvAoIBADWUCHbCfTJf5duKsSnYBLu2RUFV +KVtTYOPbS2kqmVcE+dyCFBcBNKgnA28BJf6tbhSe1+M0+6aQQLj6gG//GTNVV093 +B1BVWdrmCX1hf0JmYGfxIkwkgf0e8IcKBKnLOniLtbnWh4O6Cw0PwjF9Wl/W0EL6 +r3uNVCiJIY40Exk7duBvJcuxFARVQHflNPfgI2rvAgF7Fb5GVF65DdydXOz7R4rD +g82scjMwgLlmPlM17DIbyJEx0bLkqGHue+HUdEEuGkjy/js0jZts3a2ihOXA+mwq +O+Izt6vChLopGp2MMeJALA12JdUtQa8hmF7GlSWHun5fOAVhcJiSkUNjqM0= +-----END RSA PRIVATE KEY-----'; + + $this->platform_public_key = '-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyDQ0JyGesS6gWeO3W0Qq +lzN0+6ODsbQ86wVvqAsloeSuKYWwlvqjuCqrSJMMpRjaT7wDri8Xq2SGLMq9Yi1k +9tXExIKqyIgZ8NROybHRhvEXL0rMWR++03e7tOK/Kv+ok9crLiKeuaB+7NQWlgXq +WpgMPKmk2Spa/qj4I1Xidq2MVxzVub9B/FtDyYulHxg+ifNoSkmIKVP+p7a91bYS +vXnF1PJQ1QxrFpr0rn7VGQ4/JvykYqRwGkJE/uXw5gAMP8JgXdlak+ntg2tljdvp +p6kG9Dh3Vra9NmiehB8vPXBYtUo0QC/zE31BK4vLCUNEBQN5dGYDTwyjYYRkRMdW +EK3S2vaoYi5FTZTpXCH0FgZr/8bZygq7CaF9o11QHvUA3vjoAUpMf3KAyeDXIhmn +pGbg4BcIHeFsgcua64NNXHPCsmXGDgjG6Up3RT6Z0nDACTO03TXvcjkjA1WPGJpS +hzG/0eUN4T6u96pxKlPSQ76j083lQ60LOTN9d0wKVw79Xg5OuUx0uW3nQRM157TX +jZVad5QZ5Okzcb3kpFhwUqGh0UfsYVt3Io9+jGXN+JFhDYl+VqlTd42eOmno7Kcs +9QIUPccqLQGq+tqDAZQWIGUWDdykRC98tJdOOKhVHXrpz7TS2dmEAq21X+NJ62vh +zZdGTRbtYG8vS/15wplk6NsCAwEAAQ== +-----END PUBLIC KEY-----'; + + $this->platform_kid = 'ymMKtYy2-TPvxW1EWO6DKxoDO_SW8QGyNTjBo2WG2EM'; + + $this->LtiToolRegistration = ClassRegistry::init('LtiToolRegistration'); + $this->LtiNonce = ClassRegistry::init('LtiNonce'); + $this->LtiContext = ClassRegistry::init('LtiContext'); + $this->LtiResourceLink = ClassRegistry::init('LtiResourceLink'); + $this->LtiUser = ClassRegistry::init('LtiUser'); + $this->Role = ClassRegistry::init('Role'); + $this->User = ClassRegistry::init('User'); + $this->Course = ClassRegistry::init('Course'); + $this->Faculty = ClassRegistry::init('Faculty'); + + $this->registration_count = $this->LtiToolRegistration->find('count'); + $this->lti_context_count = $this->LtiContext->find('count'); + $this->lti_resource_link_count = $this->LtiResourceLink->find('count'); + $this->lti_user_count = $this->LtiUser->find('count'); + $this->user_count = $this->User->find('count'); + $this->course_count = $this->Course->find('count'); + $this->faculty_count = $this->Faculty->find('count'); + } + + public function endTest($method) + { + // defer logout to end of the test as some of the test need check flash + // message. After logging out, message is destoryed. + if (isset($this->controller->Auth)) { + $this->controller->Auth->logout(); + } + unset($this->controller); + + unset($_COOKIE['lti1p3_mocked_state']); + unset($_POST['state']); + unset($_POST['id_token']); + + unset($_REQUEST['iss']); + unset($_REQUEST['login_hint']); + unset($_REQUEST['target_link_uri']); + unset($_REQUEST['lti_message_hint']); + unset($_REQUEST['lti_deployment_id']); + unset($_REQUEST['client_id']); + ClassRegistry::flush(); + } + + public function getController() + { + return $this->controller; + } + + function resetLaunchNonce() { + $this->LtiNonce->save(array( + 'LtiNonce' => array( + 'nonce' => $this->base_launch_data['nonce'], + ) + )); + } + + function gerenateJWK($payload) { + return JWT::encode($payload, $this->platform_private_key, 'RS256', $this->platform_kid); + } + + function gerenateInvalidSignatureJWK($payload) { + $invalid_private_key = str_replace('a', 'b', $this->platform_private_key); + return JWT::encode($payload, $invalid_private_key, 'RS256', $this->platform_kid); + } + + function gerenateInvalidKidJWK($payload) { + return JWT::encode($payload, $this->platform_private_key, 'RS256', $this->platform_kid."invalid"); + } + + function setupLoginData($data) { + unset($_REQUEST['iss']); + unset($_REQUEST['login_hint']); + unset($_REQUEST['target_link_uri']); + unset($_REQUEST['lti_message_hint']); + unset($_REQUEST['lti_deployment_id']); + unset($_REQUEST['client_id']); + if (!empty($data['iss'])) { + $_REQUEST['iss'] = $data['iss']; + } + if (!empty($data['login_hint'])) { + $_REQUEST['login_hint'] = $data['login_hint']; + } + if (!empty($data['target_link_uri'])) { + $_REQUEST['target_link_uri'] = $data['target_link_uri']; + } + if (!empty($data['lti_message_hint'])) { + $_REQUEST['lti_message_hint'] = $data['lti_message_hint']; + } + if (!empty($data['lti_deployment_id'])) { + $_REQUEST['lti_deployment_id'] = $data['lti_deployment_id']; + } + if (!empty($data['client_id'])) { + $_REQUEST['client_id'] = $data['client_id']; + } + } + + function setupLaunchData($data) { + unset($_POST['state']); + unset($_POST['id_token']); + if (!empty($data['state'])) { + $_POST['state'] = $data['state']; + } + if (!empty($data['id_token'])) { + $_POST['id_token'] = $data['id_token']; + } + } + + + function testLogin() { + // NOTE: its to hard to extract the redirect url and since there are random elements, just going to check * + $this->controller->expectOnce('redirect', array('*')); + //http://mock_lti.com/api/lti/authorize_redirect?scope=openid&response_type=id_token&response_mode=form_post&prompt=none&client_id=10000000000013&redirect_uri=%2Flti%2Flaunch&state=state-*&nonce=nonce-*&login_hint=135745<i_message_hint=19481 + + $this->setupLoginData($this->login_data); + $this->testAction( + '/lti/login', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertNull($message); + } + + function testLoginFailedMissingRequiredParams() { + $requiredParams = array( + 'iss' => 'Error doing OIDC login: Could not find issuer', + 'login_hint' => 'Error doing OIDC login: Could not find login hint', + ); + + $count = 0; + foreach($requiredParams as $requiredParam => $expectedMessage) { + $data_copy = $this->login_data; + unset($data_copy[$requiredParam]); + + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/home/index')); + $this->setupLoginData($data_copy); + $this->testAction( + '/lti/login', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], $expectedMessage); + } + } + + function testLoginSuccessMissingOptionalParams() { + $optionalParams = array( + 'lti_deployment_id', + 'client_id', + 'lti_message_hint', + 'target_link_uri' + ); + + $count = 0; + foreach($optionalParams as $optionalParam) { + $data_copy = $this->login_data; + unset($data_copy[$optionalParam]); + + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('*')); + $this->setupLoginData($data_copy); + $this->testAction( + '/lti/login', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertNull($message); + } + } + + function testInstructorLaunch() { + $_COOKIE['lti1p3_mocked_state'] = 'mocked_state'; + $data = array( + 'state' => 'mocked_state', + 'id_token' => $this->gerenateJWK($this->instructor_launch_data), + ); + + //-- TEST 1: Completely new launch + $this->resetLaunchNonce(); + $this->controller->expectOnce('redirect', array('/courses/home/5')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual(++$this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual(++$this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual(++$this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual(++$this->user_count, $this->User->find('count')); + $this->assertEqual(++$this->course_count, $this->Course->find('count')); + $this->assertEqual(++$this->faculty_count, $this->Faculty->find('count')); + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1000'); + $this->assertEqual($course['term'], 'Default Term'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_instructor'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'Millie'); + $this->assertEqual($user['last_name'], 'Robel'); + $this->assertNull($user['student_no']); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'Millie.Robel@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_INSTRUCTOR)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array()); + $this->assertEqual($ta_enrollment_course_ids, array()); + $this->assertEqual($instructor_enrollment_course_ids, array($course['id'])); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items'); + $this->assertNull($lti_resource_link['lineitem_url']); + $this->assertEqual($lti_resource_link['scope_lineitem'], 1); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 0); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 1); + $this->assertEqual($lti_resource_link['scope_result_score'], 1); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'instructor_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + + //-- TEST 2: Second launch with zero changes + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', 2); + $this->controller->expectArgumentsAt(1, 'redirect', array('/courses/home/5')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual($this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual($this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual($this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual($this->user_count, $this->User->find('count')); + $this->assertEqual($this->course_count, $this->Course->find('count')); + $this->assertEqual($this->faculty_count, $this->Faculty->find('count')); + + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1000'); + $this->assertEqual($course['term'], 'Default Term'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_instructor'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'Millie'); + $this->assertEqual($user['last_name'], 'Robel'); + $this->assertNull($user['student_no']); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'Millie.Robel@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_INSTRUCTOR)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array()); + $this->assertEqual($ta_enrollment_course_ids, array()); + $this->assertEqual($instructor_enrollment_course_ids, array($course['id'])); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items'); + $this->assertNull($lti_resource_link['lineitem_url']); + $this->assertEqual($lti_resource_link['scope_lineitem'], 1); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 0); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 1); + $this->assertEqual($lti_resource_link['scope_result_score'], 1); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'instructor_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + + + //-- TEST 3: Third launch with many changes (course and user) + $this->instructor_launch_data = array_replace($this->instructor_launch_data, array( + // user data + "given_name" => "MillieAAA", + "family_name" => "RobelBBB", + "middle_name" => "CummerataCCC", + "email" => "MillieAAA.RobelBBB@example.org", + "name" => "MillieAAA CummerataCCC RobelBBB", + "https://purl.imsglobal.org/spec/lti/claim/roles" => [ + "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Learner", + "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor", + "http://purl.imsglobal.org/vocab/lis/v2/membership/Instructor#TeachingAssistant" + ], + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "username" => "lti_instructor", + "student_number" => '123454321', + //course + "canvas_course_id" => "1010", + "term_name" => "Default Term 2", + "account_name" => "Some Other Account" + ], + // course + "https://purl.imsglobal.org/spec/lti/claim/context" => [ + "id" => "10045", + "label" => "Course 456", //doesn't change in ipeer if changes in LMS + "title" => "Test Course 2", + "type" => [ + "course_offering" + ] + ], + "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" => [ + "scope" => [ + "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly" + ], + "lineitems" => "https://some_other_url.com", + "lineitem" => "https://some_other_url2.com", + ], + "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" => [ + "context_memberships_url" => "https://some_other_url3.com", + "service_versions" => [ + "2.0" + ] + ], + )); + $data['id_token'] = $this->gerenateJWK($this->instructor_launch_data); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', 3); + $this->controller->expectArgumentsAt(2, 'redirect', array('/courses/home/5')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual($this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual($this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual($this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual($this->user_count, $this->User->find('count')); + $this->assertEqual($this->course_count, $this->Course->find('count')); + $this->assertEqual(++$this->faculty_count, $this->Faculty->find('count')); + + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Other Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course 2'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1010'); + $this->assertEqual($course['term'], 'Default Term 2'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_instructor'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'MillieAAA'); + $this->assertEqual($user['last_name'], 'RobelBBB'); + $this->assertEqual($user['student_no'], '123454321'); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'MillieAAA.RobelBBB@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_INSTRUCTOR, $this->User->USER_TYPE_TA)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array()); + $this->assertEqual($ta_enrollment_course_ids, array($course['id'])); + $this->assertEqual($instructor_enrollment_course_ids, array()); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://some_other_url3.com'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://some_other_url.com'); + $this->assertEqual($lti_resource_link['lineitem_url'], 'https://some_other_url2.com'); + $this->assertEqual($lti_resource_link['scope_lineitem'], 0); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 1); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 0); + $this->assertEqual($lti_resource_link['scope_result_score'], 0); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'instructor_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + } + + + function testTaLaunch() { + $_COOKIE['lti1p3_mocked_state'] = 'mocked_state'; + $data = array( + 'state' => 'mocked_state', + 'id_token' => $this->gerenateJWK($this->ta_launch_data), + ); + + //-- TEST 1: Completely new launch + $this->resetLaunchNonce(); + $this->controller->expectOnce('redirect', array('/courses/home/5')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual(++$this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual(++$this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual(++$this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual(++$this->user_count, $this->User->find('count')); + $this->assertEqual(++$this->course_count, $this->Course->find('count')); + $this->assertEqual(++$this->faculty_count, $this->Faculty->find('count')); + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1000'); + $this->assertEqual($course['term'], 'Default Term'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_ta'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'Stevie'); + $this->assertEqual($user['last_name'], 'PhD'); + $this->assertEqual($user['student_no'], '1234567890'); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'Stevie.PhD@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_TA)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array()); + $this->assertEqual($ta_enrollment_course_ids, array($course['id'])); + $this->assertEqual($instructor_enrollment_course_ids, array()); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items'); + $this->assertNull($lti_resource_link['lineitem_url']); + $this->assertEqual($lti_resource_link['scope_lineitem'], 1); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 0); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 1); + $this->assertEqual($lti_resource_link['scope_result_score'], 1); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'ta_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + + //-- TEST 2: Second launch with zero changes + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', 2); + $this->controller->expectArgumentsAt(1, 'redirect', array('/courses/home/5')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual($this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual($this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual($this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual($this->user_count, $this->User->find('count')); + $this->assertEqual($this->course_count, $this->Course->find('count')); + $this->assertEqual($this->faculty_count, $this->Faculty->find('count')); + + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1000'); + $this->assertEqual($course['term'], 'Default Term'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_ta'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'Stevie'); + $this->assertEqual($user['last_name'], 'PhD'); + $this->assertEqual($user['student_no'], '1234567890'); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'Stevie.PhD@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_TA)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array()); + $this->assertEqual($ta_enrollment_course_ids, array($course['id'])); + $this->assertEqual($instructor_enrollment_course_ids, array()); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items'); + $this->assertNull($lti_resource_link['lineitem_url']); + $this->assertEqual($lti_resource_link['scope_lineitem'], 1); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 0); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 1); + $this->assertEqual($lti_resource_link['scope_result_score'], 1); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'ta_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + + + //-- TEST 3: Third launch with many changes (course and user) + $this->ta_launch_data = array_replace($this->ta_launch_data, array( + // user data + "given_name" => "StevieAAA", + "family_name" => "PhDBBB", + "middle_name" => "KuhnCCC", + "email" => "StevieAAA.PhDBBB@example.org", + "name" => "StevieAAA KuhnCCC PhDBBB", + "https://purl.imsglobal.org/spec/lti/claim/roles" => [ + "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Learner", + "http://purl.imsglobal.org/vocab/lis/v2/membership#Learner" + ], + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "username" => "lti_ta", + "student_number" => '543212345', + //course + "canvas_course_id" => "1010", + "term_name" => "Default Term 2", + "account_name" => "Some Other Account" + ], + // course + "https://purl.imsglobal.org/spec/lti/claim/context" => [ + "id" => "10045", + "label" => "Course 456", //doesn't change in ipeer if changes in LMS + "title" => "Test Course 2", + "type" => [ + "course_offering" + ] + ], + "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" => [ + "scope" => [ + "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly" + ], + "lineitems" => "https://some_other_url.com", + "lineitem" => "https://some_other_url2.com", + ], + "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" => [ + "context_memberships_url" => "https://some_other_url3.com", + "service_versions" => [ + "2.0" + ] + ], + )); + $data['id_token'] = $this->gerenateJWK($this->ta_launch_data); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', 3); + $this->controller->expectArgumentsAt(2, 'redirect', array('/home/index')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual($this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual($this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual($this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual($this->user_count, $this->User->find('count')); + $this->assertEqual($this->course_count, $this->Course->find('count')); + $this->assertEqual(++$this->faculty_count, $this->Faculty->find('count')); + + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Other Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course 2'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1010'); + $this->assertEqual($course['term'], 'Default Term 2'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_ta'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'StevieAAA'); + $this->assertEqual($user['last_name'], 'PhDBBB'); + $this->assertEqual($user['student_no'], '543212345'); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'StevieAAA.PhDBBB@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_TA, $this->User->USER_TYPE_STUDENT)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array($course['id'])); + $this->assertEqual($ta_enrollment_course_ids, array()); + $this->assertEqual($instructor_enrollment_course_ids, array()); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://some_other_url3.com'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://some_other_url.com'); + $this->assertEqual($lti_resource_link['lineitem_url'], 'https://some_other_url2.com'); + $this->assertEqual($lti_resource_link['scope_lineitem'], 0); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 1); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 0); + $this->assertEqual($lti_resource_link['scope_result_score'], 0); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'ta_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + } + + function testStudentLaunch() { + $_COOKIE['lti1p3_mocked_state'] = 'mocked_state'; + $data = array( + 'state' => 'mocked_state', + 'id_token' => $this->gerenateJWK($this->student_launch_data), + ); + + //-- TEST 1: Completely new launch + $this->resetLaunchNonce(); + $this->controller->expectOnce('redirect', array('/home/index')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual(++$this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual(++$this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual(++$this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual(++$this->user_count, $this->User->find('count')); + $this->assertEqual(++$this->course_count, $this->Course->find('count')); + $this->assertEqual(++$this->faculty_count, $this->Faculty->find('count')); + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1000'); + $this->assertEqual($course['term'], 'Default Term'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_student'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'Maricela'); + $this->assertEqual($user['last_name'], 'Nikolaus'); + $this->assertEqual($user['student_no'], '0987654321'); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'Maricela.Nikolaus@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_STUDENT)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array($course['id'])); + $this->assertEqual($ta_enrollment_course_ids, array()); + $this->assertEqual($instructor_enrollment_course_ids, array()); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items'); + $this->assertNull($lti_resource_link['lineitem_url']); + $this->assertEqual($lti_resource_link['scope_lineitem'], 1); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 0); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 1); + $this->assertEqual($lti_resource_link['scope_result_score'], 1); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'student_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + + //-- TEST 2: Second launch with zero changes + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', 2); + $this->controller->expectArgumentsAt(1, 'redirect', array('/home/index')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual($this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual($this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual($this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual($this->user_count, $this->User->find('count')); + $this->assertEqual($this->course_count, $this->Course->find('count')); + $this->assertEqual($this->faculty_count, $this->Faculty->find('count')); + + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1000'); + $this->assertEqual($course['term'], 'Default Term'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_student'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'Maricela'); + $this->assertEqual($user['last_name'], 'Nikolaus'); + $this->assertEqual($user['student_no'], '0987654321'); + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'Maricela.Nikolaus@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_STUDENT)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array($course['id'])); + $this->assertEqual($ta_enrollment_course_ids, array()); + $this->assertEqual($instructor_enrollment_course_ids, array()); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/memberships'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://lti-ri.imsglobal.org/platforms/1176/contexts/10045/line_items'); + $this->assertNull($lti_resource_link['lineitem_url']); + $this->assertEqual($lti_resource_link['scope_lineitem'], 1); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 0); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 1); + $this->assertEqual($lti_resource_link['scope_result_score'], 1); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'student_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + + + //-- TEST 3: Third launch with many changes (course and user) + $this->student_launch_data = array_replace($this->student_launch_data, array( + // user data + "given_name" => "MaricelaAAA", + "family_name" => "NikolausBBB", + "middle_name" => "WindlerCCC", + "email" => "MaricelaAAA.NikolausBBB@example.org", + "name" => "MaricelaAAA WindlerCCC NikolausBBB", + "https://purl.imsglobal.org/spec/lti/claim/roles" => [ + "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor", + "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor" + ], + "https://purl.imsglobal.org/spec/lti/claim/custom" => [ + "username" => "lti_student", + "student_number" => null, + //course + "canvas_course_id" => "1010", + "term_name" => "Default Term 2", + "account_name" => "Some Other Account" + ], + // course + "https://purl.imsglobal.org/spec/lti/claim/context" => [ + "id" => "10045", + "label" => "Course 456", //doesn't change in ipeer if changes in LMS + "title" => "Test Course 2", + "type" => [ + "course_offering" + ] + ], + "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" => [ + "scope" => [ + "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly" + ], + "lineitems" => "https://some_other_url.com", + "lineitem" => "https://some_other_url2.com", + ], + "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" => [ + "context_memberships_url" => "https://some_other_url3.com", + "service_versions" => [ + "2.0" + ] + ], + )); + $data['id_token'] = $this->gerenateJWK($this->student_launch_data); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', 3); + $this->controller->expectArgumentsAt(2, 'redirect', array('/courses/home/5')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'LTI launch success'); + + $this->assertEqual($this->registration_count, $this->LtiToolRegistration->find('count')); + $this->assertEqual($this->lti_context_count, $this->LtiContext->find('count')); + $this->assertEqual($this->lti_resource_link_count, $this->LtiResourceLink->find('count')); + $this->assertEqual($this->lti_user_count, $this->LtiUser->find('count')); + $this->assertEqual($this->user_count, $this->User->find('count')); + $this->assertEqual($this->course_count, $this->Course->find('count')); + $this->assertEqual(++$this->faculty_count, $this->Faculty->find('count')); + + + // check faculty + $faculties = $this->Faculty->find('all'); + $faculty = end($faculties)['Faculty']; + $this->assertEqual($faculty['name'], 'Some Other Account'); + + // check course + $courses = $this->Course->find('all'); + $course = end($courses)['Course']; + $this->assertEqual($course['course'], 'Course 123'); + $this->assertEqual($course['title'], 'Test Course 2'); + $this->assertNull($course['homepage']); + $this->assertEqual($course['self_enroll'], 'off'); + $this->assertNull($course['password']); + $this->assertEqual($course['record_status'], 'A'); + $this->assertEqual($course['canvas_id'], '1010'); + $this->assertEqual($course['term'], 'Default Term 2'); + + // check user + $users = $this->User->find('all'); + $user = end($users)['User']; + $this->assertEqual($user['username'], 'lti_student'); + $this->assertNotNull($user['password']); + $this->assertEqual($user['first_name'], 'MaricelaAAA'); + $this->assertEqual($user['last_name'], 'NikolausBBB'); + $this->assertEqual($user['student_no'], '0987654321'); //values aren't changed if they are missing from launch + $this->assertEqual($user['title'], ''); + $this->assertEqual($user['email'], 'MaricelaAAA.NikolausBBB@example.org'); + $this->assertEqual($user['record_status'], 'A'); + + // check user role + $roles = $this->Role->find('all', array( + 'conditions' => array('User.id' => $user['id']), + 'recursive' => 0 + )); + $role_ids = Set::extract('/Role/id', $roles); + $this->assertEqual($role_ids, array($this->User->USER_TYPE_STUDENT, $this->User->USER_TYPE_INSTRUCTOR)); + + // check user enrollment + $student_enrollment_course_ids = $this->User->getEnrolledCourses($user['id']); + $ta_enrollment_course_ids = $this->User->getTutorCourses($user['id']); + $instructor_enrollment_course_ids = $this->User->getInstructorCourses($user['id']); + $this->assertEqual($student_enrollment_course_ids, array()); + $this->assertEqual($ta_enrollment_course_ids, array()); + $this->assertEqual($instructor_enrollment_course_ids, array($course['id'])); + + // check lti context + $lti_contexts = $this->LtiContext->find('all'); + $lti_context = end($lti_contexts)['LtiContext']; + $this->assertEqual($lti_context['lti_tool_registration_id'], 1); + $this->assertEqual($lti_context['context_id'], '10045'); + $this->assertEqual($lti_context['course_id'], $course['id']); + $this->assertEqual($lti_context['nrps_context_memberships_url'], 'https://some_other_url3.com'); + + // check lti resource link + $lti_resource_links = $this->LtiResourceLink->find('all'); + $lti_resource_link = end($lti_resource_links)['LtiResourceLink']; + $this->assertEqual($lti_resource_link['lti_context_id'], $lti_context['id']); + $this->assertEqual($lti_resource_link['resource_link_id'], '19481'); + $this->assertNull($lti_resource_link['event_id']); + $this->assertEqual($lti_resource_link['lineitems_url'], 'https://some_other_url.com'); + $this->assertEqual($lti_resource_link['lineitem_url'], 'https://some_other_url2.com'); + $this->assertEqual($lti_resource_link['scope_lineitem'], 0); + $this->assertEqual($lti_resource_link['scope_lineitem_read_only'], 1); + $this->assertEqual($lti_resource_link['scope_result_readonly'], 0); + $this->assertEqual($lti_resource_link['scope_result_score'], 0); + + // check lti user + $lti_users = $this->LtiUser->find('all'); + $lti_user = end($lti_users)['LtiUser']; + $this->assertEqual($lti_user['lti_tool_registration_id'], 1); + $this->assertEqual($lti_user['lti_user_id'], 'student_lti_user_id'); + $this->assertEqual($lti_user['ipeer_user_id'], $user['id']); + } + + + function testInvalidLaunch() { + $data = array( + 'state' => 'mocked_state', + 'id_token' => $this->gerenateJWK($this->student_launch_data), + ); + $count = 0; + + //-- TEST 1: Missing state Cookie + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + $this->setupLaunchData($data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'State not found'); + + + //-- TEST 2: miss matching states + $_COOKIE['lti1p3_mocked_state'] = 'incorrect_state'; + $invalid_data = $data; + unset($invalid_data['state']); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + $this->setupLaunchData($invalid_data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'State not found'); + + + // set valid cookie for future tests + $_COOKIE['lti1p3_mocked_state'] = 'mocked_state'; + + + //-- TEST 3: missing state post param + $invalid_data = $data; + unset($invalid_data['state']); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + $this->setupLaunchData($invalid_data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'State not found'); + + + //-- TEST 4: missing id_token post param + $invalid_data = $data; + unset($invalid_data['id_token']); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + $this->setupLaunchData($invalid_data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Missing id_token'); + + // NOTE Can't test at the moment since library error is thrown + // Fatal error: Uncaught Error: Class 'Firebase\JWT\SignatureInvalidException' not found in /var/www/html/vendor/fproject/php-jwt/src/JWT.php on line 113 + + // //-- TEST 5: invalid signature + // $invalid_data = $data; + // $invalid_data['id_token'] = $this->gerenateInvalidSignatureJWK($this->student_launch_data); + // $this->controller->expectCallCount('redirect', ++$count); + // $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + // $this->setupLaunchData($invalid_data); + // $this->testAction( + // '/lti/launch', + // array('fixturize' => true, 'method' => 'post') + // ); + // $message = $this->controller->Session->read('Message.flash'); + // $this->assertEqual($message['message'], 'LTI launch success'); + + //-- TEST 6: invalid Kid + $invalid_data = $data; + $invalid_data['id_token'] = $this->gerenateInvalidKidJWK($this->student_launch_data); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + $this->setupLaunchData($invalid_data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Unable to find public key'); + + + //-- TEST 6: invalid nonce + $invalid_student_data = $this->student_launch_data; + $invalid_student_data['nonce'] = 'some_invalid_nonce'; + $invalid_data = array( + 'state' => 'mocked_state', + 'id_token' => $this->gerenateJWK($invalid_student_data), + ); + $this->resetLaunchNonce(); + $this->controller->expectCallCount('redirect', ++$count); + $this->controller->expectArgumentsAt($count-1, 'redirect', array('/logout')); + $this->setupLaunchData($invalid_data); + $this->testAction( + '/lti/launch', + array('fixturize' => true, 'method' => 'post') + ); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Invalid Nonce'); + } + + + function testAdminRoster() { + $course = $this->Course->findById(1); + + $initial_instructor_ids = [2]; + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $initial_tutor_ids = [35,36]; + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $initial_student_ids = [5, 6, 7, 13, 15, 17, 19, 21, 26, 28, 31, 32, 33]; + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($initial_instructor_ids, $current_instructor_ids); + $this->assertEqual($initial_tutor_ids, $current_tutor_ids); + $this->assertEqual($initial_student_ids, $current_student_ids); + + $this->controller->expectOnce('redirect', array('/courses/home/1')); + $this->testAction('/lti/roster/1'); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Imported Users from LMS'); + + $users = $this->User->find('all'); + $new_users = array_slice($users, sizeof($users) - 3); + $new_instructor = $new_users[0]; + $new_tutor = $new_users[1]; + $new_student = $new_users[2]; + + $course = $this->Course->findById(1); + + # verify expected enrollments + $expected_instructor_ids = [2, $new_instructor['User']['id'], 5]; + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $expected_tutor_ids = [35, $new_tutor['User']['id'], 6]; + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $expected_student_ids = [7, $new_student['User']['id']]; + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($expected_instructor_ids, $current_instructor_ids); + $this->assertEqual($expected_tutor_ids, $current_tutor_ids); + $this->assertEqual($current_student_ids, $current_student_ids); + + # verify expected user data + + # instructors + $user = $this->User->findById(2); + $this->assertEqual($user['User']['username'], 'instructor1'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Instructor.changed'); + $this->assertEqual($user['User']['last_name'], '1.changed'); + $this->assertNull($user['User']['student_no']); + $this->assertEqual($user['User']['email'], 'instructor1.changed@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mock_lti_user_id_instructor'); + + $user = $new_instructor; + $this->assertEqual($user['User']['username'], 'new_instructor'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Instructor'); + $this->assertEqual($user['User']['last_name'], 'New'); + $this->assertNull($user['User']['student_no']); + $this->assertEqual($user['User']['email'], 'instructor.new@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_new_instructor'); + + $user = $this->User->findById(5); + $this->assertEqual($user['User']['username'], 'redshirt0001'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Ed'); + $this->assertEqual($user['User']['last_name'], 'Student (now instructor)'); + $this->assertEqual($user['User']['student_no'], '65498451'); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_redshirt0001'); + + # tutors + $user = $this->User->findById(35); + $this->assertEqual($user['User']['username'], 'tutor1'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Tutor'); + $this->assertEqual($user['User']['last_name'], '1'); + $this->assertEqual($user['User']['student_no'], ''); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_tutor1'); + + $user = $new_tutor; + $this->assertEqual($user['User']['username'], 'new_tutor'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Tutor'); + $this->assertEqual($user['User']['last_name'], 'New'); + $this->assertNull($user['User']['student_no']); + $this->assertEqual($user['User']['email'], 'tutor.new@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_new_tutor'); + + $user = $this->User->findById(6); + $this->assertEqual($user['User']['username'], 'redshirt0002'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Alex'); + $this->assertEqual($user['User']['last_name'], 'Student (now tutor)'); + $this->assertEqual($user['User']['student_no'], '65468188'); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_redshirt0002'); + + + # students + $user = $this->User->findById(7); + $this->assertEqual($user['User']['username'], 'redshirt0003'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Matt'); + $this->assertEqual($user['User']['last_name'], 'Student'); + $this->assertEqual($user['User']['student_no'], '98985481'); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_redshirt0003'); + + $user = $new_student; + $this->assertEqual($user['User']['username'], 'new_student'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'New'); + $this->assertEqual($user['User']['last_name'], 'Student'); + $this->assertEqual($user['User']['student_no'], '999999999999'); + $this->assertEqual($user['User']['email'], 'student.new@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_new_student'); + } + + + function testInstructorRoster() { + $this->login = array( + 'User' => array( + 'username' => 'instructor1', + 'password' => md5('ipeeripeer') + ) + ); + $course = $this->Course->findById(1); + + $initial_instructor_ids = [2]; + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $initial_tutor_ids = [35,36]; + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $initial_student_ids = [5, 6, 7, 13, 15, 17, 19, 21, 26, 28, 31, 32, 33]; + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($initial_instructor_ids, $current_instructor_ids); + $this->assertEqual($initial_tutor_ids, $current_tutor_ids); + $this->assertEqual($initial_student_ids, $current_student_ids); + + $this->controller->expectOnce('redirect', array('/courses/home/1')); + $this->testAction('/lti/roster/1'); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Imported Users from LMS'); + + $users = $this->User->find('all'); + $new_users = array_slice($users, sizeof($users) - 3); + $new_instructor = $new_users[0]; + $new_tutor = $new_users[1]; + $new_student = $new_users[2]; + + $course = $this->Course->findById(1); + + # verify expected enrollments + $expected_instructor_ids = [2, $new_instructor['User']['id'], 5]; + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $expected_tutor_ids = [35, $new_tutor['User']['id'], 6]; + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $expected_student_ids = [7, $new_student['User']['id']]; + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($expected_instructor_ids, $current_instructor_ids); + $this->assertEqual($expected_tutor_ids, $current_tutor_ids); + $this->assertEqual($current_student_ids, $current_student_ids); + + # verify expected user data + + # instructors + $user = $this->User->findById(2); + $this->assertEqual($user['User']['username'], 'instructor1'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Instructor.changed'); + $this->assertEqual($user['User']['last_name'], '1.changed'); + $this->assertNull($user['User']['student_no']); + $this->assertEqual($user['User']['email'], 'instructor1.changed@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mock_lti_user_id_instructor'); + + $user = $new_instructor; + $this->assertEqual($user['User']['username'], 'new_instructor'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Instructor'); + $this->assertEqual($user['User']['last_name'], 'New'); + $this->assertNull($user['User']['student_no']); + $this->assertEqual($user['User']['email'], 'instructor.new@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_new_instructor'); + + $user = $this->User->findById(5); + $this->assertEqual($user['User']['username'], 'redshirt0001'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Ed'); + $this->assertEqual($user['User']['last_name'], 'Student (now instructor)'); + $this->assertEqual($user['User']['student_no'], '65498451'); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_redshirt0001'); + + # tutors + $user = $this->User->findById(35); + $this->assertEqual($user['User']['username'], 'tutor1'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Tutor'); + $this->assertEqual($user['User']['last_name'], '1'); + $this->assertEqual($user['User']['student_no'], ''); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_tutor1'); + + $user = $new_tutor; + $this->assertEqual($user['User']['username'], 'new_tutor'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Tutor'); + $this->assertEqual($user['User']['last_name'], 'New'); + $this->assertNull($user['User']['student_no']); + $this->assertEqual($user['User']['email'], 'tutor.new@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_new_tutor'); + + $user = $this->User->findById(6); + $this->assertEqual($user['User']['username'], 'redshirt0002'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Alex'); + $this->assertEqual($user['User']['last_name'], 'Student (now tutor)'); + $this->assertEqual($user['User']['student_no'], '65468188'); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_redshirt0002'); + + + # students + $user = $this->User->findById(7); + $this->assertEqual($user['User']['username'], 'redshirt0003'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'Matt'); + $this->assertEqual($user['User']['last_name'], 'Student'); + $this->assertEqual($user['User']['student_no'], '98985481'); + $this->assertEqual($user['User']['email'], ''); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_redshirt0003'); + + $user = $new_student; + $this->assertEqual($user['User']['username'], 'new_student'); + $this->assertNotNull($user['User']['password']); + $this->assertEqual($user['User']['first_name'], 'New'); + $this->assertEqual($user['User']['last_name'], 'Student'); + $this->assertEqual($user['User']['student_no'], '999999999999'); + $this->assertEqual($user['User']['email'], 'student.new@email'); + + $this->assertEqual($user['LtiUser'][0]['lti_tool_registration_id'], 1); + $this->assertEqual($user['LtiUser'][0]['lti_user_id'], 'mocked_lti_user_id_new_student'); + } + + + function testTutorRoster() { + $this->login = array( + 'User' => array( + 'username' => 'tutor1', + 'password' => md5('ipeeripeer') + ) + ); + + $course = $this->Course->findById(1); + + $initial_instructor_ids = [2]; + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $initial_tutor_ids = [35,36]; + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $initial_student_ids = [5, 6, 7, 13, 15, 17, 19, 21, 26, 28, 31, 32, 33]; + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($initial_instructor_ids, $current_instructor_ids); + $this->assertEqual($initial_tutor_ids, $current_tutor_ids); + $this->assertEqual($initial_student_ids, $current_student_ids); + + $this->controller->expectOnce('redirect', array('/home')); + $this->testAction('/lti/roster/1'); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Error: You do not have permission to access the page.'); + + // roster should be unchanged + $course = $this->Course->findById(1); + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($initial_instructor_ids, $current_instructor_ids); + $this->assertEqual($initial_tutor_ids, $current_tutor_ids); + $this->assertEqual($initial_student_ids, $current_student_ids); + } + + + function testStudentRoster() { + $this->login = array( + 'User' => array( + 'username' => 'redshirt0001', + 'password' => md5('ipeeripeer') + ) + ); + + $course = $this->Course->findById(1); + + $initial_instructor_ids = [2]; + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $initial_tutor_ids = [35,36]; + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $initial_student_ids = [5, 6, 7, 13, 15, 17, 19, 21, 26, 28, 31, 32, 33]; + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($initial_instructor_ids, $current_instructor_ids); + $this->assertEqual($initial_tutor_ids, $current_tutor_ids); + $this->assertEqual($initial_student_ids, $current_student_ids); + + $this->controller->expectOnce('redirect', array('/home')); + $this->testAction('/lti/roster/1'); + $message = $this->controller->Session->read('Message.flash'); + $this->assertEqual($message['message'], 'Error: You do not have permission to access the page.'); + + // roster should be unchanged + $course = $this->Course->findById(1); + $current_instructor_ids = Set::extract('/Instructor/id', $course); + $current_tutor_ids = Set::extract('/Tutor/id', $course); + $current_student_ids = Set::extract('/Enrol/id', $course); + + $this->assertEqual($initial_instructor_ids, $current_instructor_ids); + $this->assertEqual($initial_tutor_ids, $current_tutor_ids); + $this->assertEqual($initial_student_ids, $current_student_ids); + } +} + +} diff --git a/app/tests/cases/controllers/mixevals_controller.test.php b/app/tests/cases/controllers/mixevals_controller.test.php index 2daf25800..2caeaa269 100644 --- a/app/tests/cases/controllers/mixevals_controller.test.php +++ b/app/tests/cases/controllers/mixevals_controller.test.php @@ -21,6 +21,8 @@ class MixevalsControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', 'app.roles_user', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', diff --git a/app/tests/cases/controllers/oauth_clients_controller.test.php b/app/tests/cases/controllers/oauth_clients_controller.test.php index a75d24d67..447ae9943 100644 --- a/app/tests/cases/controllers/oauth_clients_controller.test.php +++ b/app/tests/cases/controllers/oauth_clients_controller.test.php @@ -13,6 +13,8 @@ class OauthClientsControllerTestCase extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/oauth_tokens_controller.test.php b/app/tests/cases/controllers/oauth_tokens_controller.test.php index 9cfe611b7..2cca795cc 100644 --- a/app/tests/cases/controllers/oauth_tokens_controller.test.php +++ b/app/tests/cases/controllers/oauth_tokens_controller.test.php @@ -13,6 +13,8 @@ class OauthTokensControllerTestCase extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/rubrics_controller.test.php b/app/tests/cases/controllers/rubrics_controller.test.php index d0fa64dde..b5f6b68fd 100644 --- a/app/tests/cases/controllers/rubrics_controller.test.php +++ b/app/tests/cases/controllers/rubrics_controller.test.php @@ -21,6 +21,8 @@ class RubricsControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -76,37 +78,37 @@ function testIndex() { $result = $this->testAction('/rubrics/index', array('connection' => 'test_suite', 'return' => 'contents')); //$this->assertEqual($result['paramsForList']['data']['entries'][0]['Course']['course'], 'Math303'); } - + function testPostProcess() { - + } - + function testSetUpAjaxList() { - + } - + function testAjaxList() { - + } - + function testView() { - + } - + function testAdd() { $result = $this->testAction('/rubrics/add', array('connection' => 'test_suite', 'return' => 'vars')); } - + function testEdit() { - + } - + function testCopy() { - + } - + function testDelete() { - + } } diff --git a/app/tests/cases/controllers/searchs_controller.test.php b/app/tests/cases/controllers/searchs_controller.test.php index 088e86ff2..d3310ba35 100644 --- a/app/tests/cases/controllers/searchs_controller.test.php +++ b/app/tests/cases/controllers/searchs_controller.test.php @@ -12,6 +12,8 @@ class SearchsControllerTest extends CakeTestCase { var $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.rubrics_lom', 'app.group_event', 'app.evaluation_submission', 'app.rubrics_criteria_comment', 'app.survey_group_set', 'app.survey_group', 'app.rubrics_criteria', diff --git a/app/tests/cases/controllers/simpleevaluations_controller.test.php b/app/tests/cases/controllers/simpleevaluations_controller.test.php index 842f4612f..e753f132f 100644 --- a/app/tests/cases/controllers/simpleevaluations_controller.test.php +++ b/app/tests/cases/controllers/simpleevaluations_controller.test.php @@ -21,6 +21,8 @@ class SimpleevaluationsControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/surveygroups_controller.test.php b/app/tests/cases/controllers/surveygroups_controller.test.php index 8d3b0451f..2cf3242d0 100644 --- a/app/tests/cases/controllers/surveygroups_controller.test.php +++ b/app/tests/cases/controllers/surveygroups_controller.test.php @@ -21,6 +21,8 @@ class SurveygroupsControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/surveys_controller.test.php b/app/tests/cases/controllers/surveys_controller.test.php index 60c2b4073..4ed2f815b 100644 --- a/app/tests/cases/controllers/surveys_controller.test.php +++ b/app/tests/cases/controllers/surveys_controller.test.php @@ -21,6 +21,8 @@ class SurveyControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/sysparameters_controller.test.php b/app/tests/cases/controllers/sysparameters_controller.test.php index db47e2a6c..73e1c91ff 100644 --- a/app/tests/cases/controllers/sysparameters_controller.test.php +++ b/app/tests/cases/controllers/sysparameters_controller.test.php @@ -21,6 +21,8 @@ class SysparametersControllerTest extends ExtendedAuthTestCase { public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/controllers/v1_controller.test.php b/app/tests/cases/controllers/v1_controller.test.php index 5d549ecc5..bd48675d8 100644 --- a/app/tests/cases/controllers/v1_controller.test.php +++ b/app/tests/cases/controllers/v1_controller.test.php @@ -2,6 +2,8 @@ class V1ControllerTest extends CakeTestCase { public $fixtures = array( 'app.evaluation_mixeval', 'app.evaluation_rubric', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.evaluation_simple', 'app.course', 'app.role', 'app.user', 'app.group', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', @@ -1011,7 +1013,7 @@ function testUsersEvents() 'id' => '10', ), ); - + $expectedSubmittableEvents = array( array( 'title' => 'Term 1 Evaluation', @@ -1082,7 +1084,7 @@ function testUsersEvents() 'id' => '5', ) ); - + $expectedResultReleasedEvents = array( array( 'title' => 'simple evaluation 4', @@ -1113,7 +1115,7 @@ function testUsersEvents() 'id' => '9', ) ); - + $expectedFilteredEvents = array( array( 'title' => 'Term 1 Evaluation', @@ -1212,7 +1214,7 @@ function testUsersEvents() 'id' => '9', ) ); - + // get ALL events for redshirt0001 $url = $this->_getURL('/v1/users/redshirt0001/events/sub/0/results/0'); $actualEvents = $this->_oauthReq("$url"); @@ -1224,19 +1226,19 @@ function testUsersEvents() $courseUserEvents = $this->_oauthReq("$url"); $events = Set::sort(json_decode($courseUserEvents, true), '{n}.id', 'asc'); $this->assertEqual($expectedEvents, $events); - + // get events for redshirt0001 available for submissions $url = $this->_getURL('/v1/users/redshirt0001/events/sub/1/results/0'); $eventsSubmittable = $this->_oauthReq("$url"); $events = Set::sort(json_decode($eventsSubmittable, true), '{n}.id', 'asc'); $this->assertEqual($expectedSubmittableEvents, $events); - + // get events for redshirt0001 that have results released $url = $this->_getURL('/v1/users/redshirt0001/events/sub/0/results/1'); $eventsResult = $this->_oauthReq("$url"); $events = Set::sort(json_decode($eventsResult, true), '{n}.id', 'asc'); $this->assertEqual($expectedResultReleasedEvents, $events); - + // get events for redshirt0001 available for submissions OR have results released $url = $this->_getURL('/v1/users/redshirt0001/events/sub/1/results/1'); $filteredEvents = $this->_oauthReq("$url"); @@ -1248,25 +1250,25 @@ function testUsersEvents() $eventsSubmittable = $this->_oauthReq("$url"); $events = Set::sort(json_decode($eventsSubmittable, true), '{n}.id', 'asc'); $this->assertEqual($expectedSubmittableEvents, $events); - + // get events in course id 1 for redshirt0001 that have results released $url = $this->_getURL('/v1/courses/1/users/redshirt0001/events/sub/0/results/1'); $eventsResult = $this->_oauthReq("$url"); $events = Set::sort(json_decode($eventsResult, true), '{n}.id', 'asc'); $this->assertEqual($expectedResultReleasedEvents, $events); - + // get events in course id 1 for redshirt0001 available for submissions OR have results released $url = $this->_getURL('/v1/courses/1/users/redshirt0001/events/sub/1/results/1'); $filteredEvents = $this->_oauthReq("$url"); $events = Set::sort(json_decode($filteredEvents, true), '{n}.id', 'asc'); $this->assertEqual($expectedFilteredEvents, $events); - + // get ALL events for redshirt0001 - no filters $url = $this->_getURL('/v1/users/redshirt0001/events'); $events = $this->_oauthReq("$url"); $events = Set::sort(json_decode($events, true), '{n}.id', 'asc'); $this->assertEqual($expectedEvents, $events); - + // get ALL events in course id 1 for redshirt0001 - no filters $url = $this->_getURL('/v1/courses/1/users/redshirt0001/events'); $events = $this->_oauthReq("$url"); diff --git a/app/tests/cases/models/access.test.php b/app/tests/cases/models/access.test.php index 89bdc398e..fcd3d42bb 100644 --- a/app/tests/cases/models/access.test.php +++ b/app/tests/cases/models/access.test.php @@ -5,6 +5,8 @@ class AccessTestCase extends CakeTestCase { public $name = 'Access'; public $fixtures = array('app.access', 'app.oauth_token', 'app.sys_parameter', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.course', 'app.group', 'app.group_event', 'app.evaluation_simple', 'app.survey_input', 'app.survey_group_member', 'app.survey_group_set', 'app.survey', 'app.question', 'app.response', 'app.survey_question', @@ -12,35 +14,35 @@ class AccessTestCase extends CakeTestCase { 'app.user_course', 'app.user_tutor', 'app.user_enrol','app.groups_member', 'app.department', 'app.course_department', 'app.penalty'); public $Access = null; - + function startCase() { echo "Start Access model test.\n"; $this->Access = ClassRegistry::init('Access'); } - + function endCase() { } - + function startTest($method) { } - + function endTest($method) { } - + function testAccessInstance() { $this->assertTrue(is_a($this->Access, 'Access')); } - + function testLoadPermissions() { $this->Aco = ClassRegistry::init('Aco'); $this->Aro = ClassRegistry::init('Aro'); - + //id should not be used since it can change $allow = array('create' => 1, 'read' => 1, 'update' => 1, 'delete' => 1); $deny = array('create' => -1, 'read' => -1, 'update' => -1, 'delete' => -1); @@ -51,31 +53,31 @@ function testLoadPermissions() $superadmin = $this->Access->loadPermissions($acos, $group_aro); unset($superadmin["controllers/users/add"]["id"]); $this->assertEqual($superadmin['controllers/users/add'], $allow); - + // Testing for admin role $group_aro = $this->Aro->find('threaded', array('conditions'=>array('Aro.foreign_key'=>2, 'Aro.model'=>'Role'))); $admin = $this->Access->loadPermissions($acos, $group_aro); unset($admin["controllers/users/add"]["id"]); $this->assertEqual($admin['controllers/users/add'], $allow); - + // Testing for instructor role $group_aro = $this->Aro->find('threaded', array('conditions'=>array('Aro.foreign_key'=>3, 'Aro.model'=>'Role'))); $instructor = $this->Access->loadPermissions($acos, $group_aro); unset($instructor["controllers/users/add"]["id"]); $this->assertEqual($instructor['controllers/users/add'], $allow); - + // Testing for tutor role $group_aro = $this->Aro->find('threaded', array('conditions'=>array('Aro.foreign_key'=>4, 'Aro.model'=>'Role'))); $tutor = $this->Access->loadPermissions($acos, $group_aro); unset($tutor["controllers/users/add"]["id"]); $this->assertEqual($tutor['controllers/users/add'], $deny); - + // Testing for student role $group_aro = $this->Aro->find('threaded', array('conditions'=>array('Aro.foreign_key'=>5, 'Aro.model'=>'Role'))); $student = $this->Access->loadPermissions($acos, $group_aro); unset($student["controllers/users/add"]["id"]); $this->assertEqual($student['controllers/users/add'], $deny); - + // Testing for invalid role $group_aro = $this->Aro->find('threaded', array('conditions'=>array('Aro.foreign_key'=>999, 'Aro.model'=>'Role'))); $invalid = $this->Access->loadPermissions($acos, $group_aro); diff --git a/app/tests/cases/models/course.test.php b/app/tests/cases/models/course.test.php index 6a878a17e..e35d10aa0 100644 --- a/app/tests/cases/models/course.test.php +++ b/app/tests/cases/models/course.test.php @@ -4,6 +4,8 @@ class CourseTestCase extends CakeTestCase { public $name = 'Course'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/course_department.test.php b/app/tests/cases/models/course_department.test.php index c1c24dece..dc3997d38 100644 --- a/app/tests/cases/models/course_department.test.php +++ b/app/tests/cases/models/course_department.test.php @@ -3,7 +3,16 @@ App::import('Model', 'CourseDepartment'); class CourseDepartmentTestCase extends CakeTestCase { - var $fixtures = array('app.course_department', 'app.course', 'app.group', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', 'app.group_event', 'app.user_faculty', 'app.faculty', 'app.department', 'app.user_course', 'app.user_enrol', 'app.groups_member', 'app.role', 'app.roles_user', 'app.survey', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question'); + var $fixtures = array( + 'app.course_department', 'app.course', 'app.group', 'app.user', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', + 'app.evaluation_submission', 'app.event', 'app.event_template_type', + 'app.group_event', 'app.user_faculty', 'app.faculty', 'app.department', + 'app.user_course', 'app.user_enrol', 'app.groups_member', 'app.role', + 'app.roles_user', 'app.survey', 'app.survey_group_set', 'app.survey_group', + 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question' + ); function startTest($method) { echo "Start CourseDepartment model test.\n"; @@ -11,7 +20,7 @@ function startTest($method) { function endTest($method) { } - + function testinsertCourses() { //TODO } diff --git a/app/tests/cases/models/department.test.php b/app/tests/cases/models/department.test.php index 482361889..b94a2a2ac 100644 --- a/app/tests/cases/models/department.test.php +++ b/app/tests/cases/models/department.test.php @@ -6,6 +6,8 @@ class DepartmentTestCase extends CakeTestCase { public $name = 'Department'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/email_merge.test.php b/app/tests/cases/models/email_merge.test.php index 3a1254df7..6f7184086 100644 --- a/app/tests/cases/models/email_merge.test.php +++ b/app/tests/cases/models/email_merge.test.php @@ -5,6 +5,8 @@ class EmailMergeTestCase extends CakeTestCase { public $name = 'EmailMerge'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/email_schedule.test.php b/app/tests/cases/models/email_schedule.test.php index 53bee70b7..55e56ea31 100644 --- a/app/tests/cases/models/email_schedule.test.php +++ b/app/tests/cases/models/email_schedule.test.php @@ -5,6 +5,8 @@ class EmailScheduleTestCase extends CakeTestCase { public $name = 'EmailSchedule'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/email_template.test.php b/app/tests/cases/models/email_template.test.php index 4dd4da273..7cead6bfd 100644 --- a/app/tests/cases/models/email_template.test.php +++ b/app/tests/cases/models/email_template.test.php @@ -6,6 +6,8 @@ class EmailTemplateTestCase extends CakeTestCase public $name = 'EmailTemplate'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/evaluation_mixeval.test.php b/app/tests/cases/models/evaluation_mixeval.test.php index 7055b8101..db70fc8c0 100644 --- a/app/tests/cases/models/evaluation_mixeval.test.php +++ b/app/tests/cases/models/evaluation_mixeval.test.php @@ -5,6 +5,8 @@ class EvaluationMixevalTestCase extends CakeTestCase { public $name = 'EvaluationMixeval'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -168,7 +170,7 @@ function testSetAllEventGradeRelease() $this->assertEqual($searched[1]['EvaluationMixeval']['grade_release'], 0); $this->assertEqual($searched[2]['EvaluationMixeval']['grade_release'], 0); } - + function testGetResultsByEvaluateesAndEvaluators() { // TODO diff --git a/app/tests/cases/models/evaluation_mixeval_detail.test.php b/app/tests/cases/models/evaluation_mixeval_detail.test.php index 9775585df..9ee099f2c 100644 --- a/app/tests/cases/models/evaluation_mixeval_detail.test.php +++ b/app/tests/cases/models/evaluation_mixeval_detail.test.php @@ -4,6 +4,8 @@ class EvaluationMixevalDetailTestCase extends CakeTestCase { public $name = 'EvaluationMixevalDetail'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/evaluation_rubric.test.php b/app/tests/cases/models/evaluation_rubric.test.php index dff0e9cf2..21db2e03e 100644 --- a/app/tests/cases/models/evaluation_rubric.test.php +++ b/app/tests/cases/models/evaluation_rubric.test.php @@ -5,6 +5,8 @@ class EvaluationRubricTestCase extends CakeTestCase { public $name = 'EvaluationRubric'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/evaluation_rubric_detail.test.php b/app/tests/cases/models/evaluation_rubric_detail.test.php index 82ee30d91..ebd045591 100644 --- a/app/tests/cases/models/evaluation_rubric_detail.test.php +++ b/app/tests/cases/models/evaluation_rubric_detail.test.php @@ -6,6 +6,8 @@ class EvaluationRubricDetailTestCase extends CakeTestCase public $name = 'EvaluationRubricDetail'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/evaluation_simple.test.php b/app/tests/cases/models/evaluation_simple.test.php index fd1d6552a..35cb81c7a 100644 --- a/app/tests/cases/models/evaluation_simple.test.php +++ b/app/tests/cases/models/evaluation_simple.test.php @@ -6,6 +6,8 @@ class EvaluationSimpleTestCase extends CakeTestCase public $name = 'EvaluationSimple'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/evaluation_submission.test.php b/app/tests/cases/models/evaluation_submission.test.php index 849a056bc..e8c925c1c 100644 --- a/app/tests/cases/models/evaluation_submission.test.php +++ b/app/tests/cases/models/evaluation_submission.test.php @@ -5,6 +5,8 @@ class EvaluationSubmissionTestCase extends CakeTestCase { public $name = 'EvaluationSimple'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/event.test.php b/app/tests/cases/models/event.test.php index c6fe40eb8..cdfeef493 100644 --- a/app/tests/cases/models/event.test.php +++ b/app/tests/cases/models/event.test.php @@ -6,6 +6,8 @@ class EventTestCase extends CakeTestCase public $name = 'Event'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/event_template_type.test.php b/app/tests/cases/models/event_template_type.test.php index f2c198015..c8b7fde71 100644 --- a/app/tests/cases/models/event_template_type.test.php +++ b/app/tests/cases/models/event_template_type.test.php @@ -5,6 +5,8 @@ class EventTemplateTypeTestCase extends CakeTestCase { public $name = 'EventTemplateType'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/faculty.test.php b/app/tests/cases/models/faculty.test.php index 6f7f15026..cc37e3302 100644 --- a/app/tests/cases/models/faculty.test.php +++ b/app/tests/cases/models/faculty.test.php @@ -3,7 +3,16 @@ App::import('Model', 'Faculty'); class FacultyTestCase extends CakeTestCase { - var $fixtures = array('app.faculty', 'app.department', 'app.course_department', 'app.course', 'app.group', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', 'app.group_event', 'app.user_faculty', 'app.user_course', 'app.user_enrol', 'app.groups_member', 'app.role', 'app.roles_user', 'app.survey', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question'); + var $fixtures = array( + 'app.faculty', 'app.department', 'app.course_department', 'app.course', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', + 'app.group', 'app.user', 'app.evaluation_submission', 'app.event', + 'app.event_template_type', 'app.group_event', 'app.user_faculty', + 'app.user_course', 'app.user_enrol', 'app.groups_member', 'app.role', + 'app.roles_user', 'app.survey', 'app.survey_group_set', 'app.survey_group', + 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question' + ); function startTest($method) { echo "Start Faculty model test.\n"; diff --git a/app/tests/cases/models/group.test.php b/app/tests/cases/models/group.test.php index 302d75345..e211859a0 100644 --- a/app/tests/cases/models/group.test.php +++ b/app/tests/cases/models/group.test.php @@ -5,6 +5,8 @@ class GroupTestCase extends CakeTestCase { public $name = 'Group'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/group_event.test.php b/app/tests/cases/models/group_event.test.php index 10ecef4fa..7cee2c3fc 100644 --- a/app/tests/cases/models/group_event.test.php +++ b/app/tests/cases/models/group_event.test.php @@ -5,6 +5,8 @@ class GroupEventTestCase extends CakeTestCase { public $name = 'GroupEvent'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/groups_members.test.php b/app/tests/cases/models/groups_members.test.php index 005caafc4..d9b640276 100644 --- a/app/tests/cases/models/groups_members.test.php +++ b/app/tests/cases/models/groups_members.test.php @@ -6,6 +6,8 @@ class GroupsMembersTestCase extends CakeTestCase public $name = 'GroupsMembers'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/lti_tool_registration.test.php b/app/tests/cases/models/lti_tool_registration.test.php new file mode 100644 index 000000000..ee89a412d --- /dev/null +++ b/app/tests/cases/models/lti_tool_registration.test.php @@ -0,0 +1,66 @@ + + * @copyright 2019 All rights reserved. + * @license MIT {@link http://www.opensource.org/licenses/MIT} + */ +class LtiToolRegistrationTestCase extends CakeTestCase +{ + public $fixtures = array( + 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', + 'app.roles_user', 'app.event', 'app.event_template_type', + 'app.group_event', 'app.evaluation_submission', + 'app.survey', 'app.oauth_token', + 'app.survey_group_set', 'app.survey_group', + 'app.survey_group_member', 'app.question', + 'app.response', 'app.survey_question', 'app.user_course', + 'app.user_enrol', 'app.groups_member', 'app.mixeval', + 'app.mixeval_question', 'app.mixeval_question_desc', 'app.faculty', + 'app.user_faculty', 'app.department', 'app.course_department', + 'app.sys_parameter', 'app.user_tutor', 'app.penalty', + 'app.evaluation_simple', 'app.survey_input', + 'app.mixeval_question_type', 'app.evaluation_rubric', 'app.evaluation_rubric_detail', + 'app.evaluation_mixeval', 'app.evaluation_mixeval_detail' + ); + public $LtiToolRegistration; + + function startCase() + { + $this->LtiToolRegistration = ClassRegistry::init('LtiToolRegistration'); + } + + function test_findByIss() + { + + $ret = $this->LtiToolRegistration->findByIss('https://docker-canvas.instructure.com'); + $this->assertEqual($ret['LtiToolRegistration'], array( + 'id' => 1, + 'iss' => 'https://docker-canvas.instructure.com', + 'client_id' => '10000000000013', + 'auth_login_url' => 'http://mock_lti.com/api/lti/authorize_redirect', + 'auth_token_url' => 'http://mock_lti.com/login/oauth2/token', + 'key_set_url' => 'http://mock_lti.com/api/lti/security/jwks', + 'tool_private_key' => "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEApBK2zJCDg9s8QDeci6E4QWlTSGav3qh5edjbXULo5Mv0KxN7\nqLyC05QfRrs/I+P5a6D18S/ecAGYPH5xQKuPvqOEotPrRhHCkJB5PtLDsF4ZZbr/\nwWLWG5OYhCkY/H2Wip9rsx1GjKG73EMMTqT2p14K+GW4dg8/HbxQLA4yGeNnGr4D\nd87A+n9wUvMZYAxoCiHiSFD7x0hVIg/q4VXoWHBGEnnqCMC9Gtd7g7HZtQzbYMm4\nm2uY5JHhs+MXS8YKf6Ftc58sJHK5fMtjs9vMVOCkAlrEiEEn+tEHOjSNlzMg+P03\nUU79Lt/MDjXv3mtEPVmPjpJevT4Kjf1HxCSUNQIDAQABAoIBAF5Jmt9IFSwLKz7E\nNqRPS+LbQk8TI/JS4yxQoQ+hSfFh+7ldguzfGFe6gZbGOGzJsCZX475tAelgITpy\nd2bwsLSfh7ODEWu8/RDS1bpyqJ6MFRBPPHbH8775POaGL6O6EG8tWlkec9KRh0H3\nDfWL+2sHMkq5Oh4ueNj/xRrsNYKGLsD0bJMS9eFswDCpvL3fscu/JrrQT+CltBTJ\nj8lTHmGRIF9UOg0Ef1kEgOxcR+AZ2djP3d+zkKZxMATLKWnA1HRFPb7XpJQfjA+k\nsismB4FuhvTSN6IRaci1U5qASHUnIjbTMfFsqJ3h17RivQmSEz1r28OE1HyD/tdE\nIIy3WikCgYEAz6r27/MfEWXgOAwloKJAQi0sqa/0VkWQcNPN3jSgO+AN0iqPLSuj\nAlrLqlLcRlyYAfe7t6B/8SAklJtdy0x9uBaJXrmHhby4jeBksyycQULUzTUhGdbW\nGDfR+XbvNoGUaH1q+vIpglz2jw3N1/M6B/i16wd6Q81UO5WYxQj1j7sCgYEAykJW\nIq4A4UQmtM6gdsXXranV80NOlcH0p521Ec6wpU0dxfI+qVVbT4FxqxfB9Pq5N4V0\nreEYN1ALbjLi3fvChbx8P+lg6k/Tuhn7oiH3kado8iUUR00KyRwWeaMbVwUzU9sQ\nUhB/XfR3J7l3inN/dAlfdSsYbnQJN2U88CKEVM8CgYBfM2UZAz+O3kE38HmfdkI3\nFDaRY9SDaEibML4Dy+RZDpHHczNH5eVIww7y+iF5MCGPZV5tA+sjQzUB22fYNyy7\nI7m97xetu6JviBsh+KV5VYXwvRZ7nf1wBMcBsgBf4G+Ep1pPyIw28x8k3ZMsGJjV\n5rKfGEJ4qryexCnQyhao2QKBgCel71qm/3cpM+k3pA8EY24gn9cq94m11q7Q5IDU\nIp6UymRWQ2BQYjDosA6Y/qV2TL6Mg73eJTAamdMFWKGpS42J0FV6+0uTUG7nzwMO\nY4iC57in+hysBpQ71FAN4DsjwtcKV12u7DjPxlfcLInQcEif2b2PMB/e0Tuxtcth\nCM3TAoGAM+z4u7mi5jxyW9teAYtx3Yb6RGeuly7XvlknV0Lwf2438P2HNZiOa4SE\nSXHZir6LWNv8HOdGapYxUlDfmeNneo4D9B8lBpVs/FsuQF1aOI6B299SlVLPmF+a\nl88qKzXKv7M1pcOv74GK1AIVDF8XJvt1PyaQX92M14q2Ga8Jdjk=\n-----END RSA PRIVATE KEY-----", + 'tool_public_key' => "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApBK2zJCDg9s8QDeci6E4\nQWlTSGav3qh5edjbXULo5Mv0KxN7qLyC05QfRrs/I+P5a6D18S/ecAGYPH5xQKuP\nvqOEotPrRhHCkJB5PtLDsF4ZZbr/wWLWG5OYhCkY/H2Wip9rsx1GjKG73EMMTqT2\np14K+GW4dg8/HbxQLA4yGeNnGr4Dd87A+n9wUvMZYAxoCiHiSFD7x0hVIg/q4VXo\nWHBGEnnqCMC9Gtd7g7HZtQzbYMm4m2uY5JHhs+MXS8YKf6Ftc58sJHK5fMtjs9vM\nVOCkAlrEiEEn+tEHOjSNlzMg+P03UU79Lt/MDjXv3mtEPVmPjpJevT4Kjf1HxCSU\nNQIDAQAB\n-----END PUBLIC KEY-----", + 'user_identifier_field' => 'https://purl.imsglobal.org/spec/lti/claim/custom|username', + 'student_number_field' => 'https://purl.imsglobal.org/spec/lti/claim/custom|student_number', + 'term_field' => 'https://purl.imsglobal.org/spec/lti/claim/custom|term_name', + 'canvas_id_field' => 'https://purl.imsglobal.org/spec/lti/claim/custom|canvas_course_id', + 'faculty_name_field' => 'https://purl.imsglobal.org/spec/lti/claim/custom|account_name', + )); + + $ret = $this->LtiToolRegistration->findByIss(''); + $this->assertEqual($ret, array()); + } +} diff --git a/app/tests/cases/models/mixeval.test.php b/app/tests/cases/models/mixeval.test.php index 87298a8ad..b8d66919f 100644 --- a/app/tests/cases/models/mixeval.test.php +++ b/app/tests/cases/models/mixeval.test.php @@ -6,6 +6,8 @@ class MixevalTestCase extends CakeTestCase public $name = 'Mixeval'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey', 'app.oauth_token', @@ -16,7 +18,7 @@ class MixevalTestCase extends CakeTestCase 'app.mixeval_question', 'app.mixeval_question_desc', 'app.faculty', 'app.user_faculty', 'app.department', 'app.course_department', 'app.sys_parameter', 'app.user_tutor', 'app.penalty', - 'app.evaluation_simple', 'app.survey_input', + 'app.evaluation_simple', 'app.survey_input', 'app.mixeval_question_type', 'app.evaluation_rubric', 'app.evaluation_rubric_detail', 'app.evaluation_mixeval', 'app.evaluation_mixeval_detail' ); diff --git a/app/tests/cases/models/mixeval_question.test.php b/app/tests/cases/models/mixeval_question.test.php index ca915ee6e..145c46c33 100644 --- a/app/tests/cases/models/mixeval_question.test.php +++ b/app/tests/cases/models/mixeval_question.test.php @@ -6,12 +6,14 @@ class MixevalQuestionTestCase extends CakeTestCase public $name = 'MixevalQuestion'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.oauth_token', 'app.survey_group_set', 'app.survey_group', 'app.sys_parameter', 'app.survey_group_member', 'app.question', 'app.evaluation_simple', 'app.response', 'app.survey_question', 'app.user_course', - 'app.user_enrol', 'app.groups_member', 'app.mixeval', + 'app.user_enrol', 'app.groups_member', 'app.mixeval', 'app.mixeval_question', 'app.mixeval_question_desc', 'app.survey_input', 'app.survey', 'app.faculty', 'app.user_faculty', 'app.user_tutor', 'app.department', 'app.course_department', 'app.penalty', diff --git a/app/tests/cases/models/mixeval_question_desc.test.php b/app/tests/cases/models/mixeval_question_desc.test.php index 1ea03ed72..32d36c5ce 100644 --- a/app/tests/cases/models/mixeval_question_desc.test.php +++ b/app/tests/cases/models/mixeval_question_desc.test.php @@ -6,14 +6,16 @@ class MixevalQuestionDescTestCase extends CakeTestCase public $name = 'MixevalQuestionDesc'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.oauth_token', 'app.survey_group_set', 'app.survey_group', 'app.sys_parameter', 'app.survey_group_member', 'app.question', 'app.evaluation_simple', 'app.response', 'app.survey_question', 'app.user_course', - 'app.user_enrol', 'app.groups_member', 'app.mixeval', + 'app.user_enrol', 'app.groups_member', 'app.mixeval', 'app.mixeval_question', 'app.user_faculty', 'app.user_tutor', - 'app.mixeval_question_desc', 'app.survey_input', 'app.faculty', + 'app.mixeval_question_desc', 'app.survey_input', 'app.faculty', 'app.department', 'app.course_department', 'app.penalty', 'app.mixeval_question_type' ); diff --git a/app/tests/cases/models/oauth_client.test.php b/app/tests/cases/models/oauth_client.test.php index bfbd1a99b..84562f6a9 100644 --- a/app/tests/cases/models/oauth_client.test.php +++ b/app/tests/cases/models/oauth_client.test.php @@ -3,7 +3,17 @@ App::import('Model', 'OauthClient'); class OauthClientTestCase extends CakeTestCase { - var $fixtures = array('app.oauth_client', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', 'app.groups_member', 'app.survey', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', 'app.user_course', 'app.user_tutor', 'app.user_enrol', 'app.department', 'app.faculty', 'app.course_department', 'app.penalty', 'app.user_faculty', 'app.role', 'app.roles_user'); + var $fixtures = array( + 'app.oauth_client', 'app.user', 'app.evaluation_submission', 'app.event', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', + 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', + 'app.groups_member', 'app.survey', 'app.survey_group_set', 'app.survey_group', + 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', + 'app.user_course', 'app.user_tutor', 'app.user_enrol', 'app.department', + 'app.faculty', 'app.course_department', 'app.penalty', 'app.user_faculty', + 'app.role', 'app.roles_user' + ); function startTest($method) { $this->OauthClient =& ClassRegistry::init('OauthClient'); diff --git a/app/tests/cases/models/oauth_token.test.php b/app/tests/cases/models/oauth_token.test.php index 96cac1ce4..16949a314 100644 --- a/app/tests/cases/models/oauth_token.test.php +++ b/app/tests/cases/models/oauth_token.test.php @@ -3,7 +3,17 @@ App::import('Model', 'OauthToken'); class OauthTokenTestCase extends CakeTestCase { - var $fixtures = array('app.oauth_token', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', 'app.groups_member', 'app.survey', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', 'app.user_course', 'app.user_tutor', 'app.user_enrol', 'app.department', 'app.faculty', 'app.course_department', 'app.penalty', 'app.user_faculty', 'app.role', 'app.roles_user'); + var $fixtures = array( + 'app.oauth_token', 'app.user', 'app.evaluation_submission', 'app.event', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', + 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', + 'app.groups_member', 'app.survey', 'app.survey_group_set', 'app.survey_group', + 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', + 'app.user_course', 'app.user_tutor', 'app.user_enrol', 'app.department', + 'app.faculty', 'app.course_department', 'app.penalty', 'app.user_faculty', + 'app.role', 'app.roles_user' + ); function startTest($method) { $this->OauthToken =& ClassRegistry::init('OauthToken'); diff --git a/app/tests/cases/models/penalty.test.php b/app/tests/cases/models/penalty.test.php index fa60553bd..1c9cf23aa 100644 --- a/app/tests/cases/models/penalty.test.php +++ b/app/tests/cases/models/penalty.test.php @@ -5,6 +5,8 @@ class PenaltyTestCase extends CakeTestCase { public $name = 'Penalty'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -16,7 +18,7 @@ class PenaltyTestCase extends CakeTestCase 'app.evaluation_simple', 'app.survey_input', 'app.oauth_token', 'app.evaluation_rubric', 'app.evaluation_rubric_detail', 'app.evaluation_mixeval', 'app.evaluation_mixeval_detail' - + ); public $Penalty = null; @@ -111,7 +113,7 @@ function testGetPenaltyDays() $ret = $this->Penalty->getPenaltyDays(999); $this->assertEqual($ret, null); } - + function testGetPenaltyByPenaltiesAndDaysLate() { // valid event penalties set @@ -124,7 +126,7 @@ function testGetPenaltyByPenaltiesAndDaysLate() $this->assertEqual($penalties['1']['Penalty']['days_late'], 2); $this->assertEqual($penalties['2']['Penalty']['days_late'], 3); $this->assertEqual($penalties['3']['Penalty']['days_late'], 4); - + // valid event - right on time $ret = $this->Penalty->getPenaltyByPenaltiesAndDaysLate($penalties, 0); $this->assertEqual($ret, null); @@ -140,11 +142,11 @@ function testGetPenaltyByPenaltiesAndDaysLate() // valid event - final deduction $ret = $this->Penalty ->getPenaltyByPenaltiesAndDaysLate($penalties, 5); $this->assertEqual($ret['Penalty']['percent_penalty'], 60); - + // test inbetween penalties with several days between days late // 1 day 15% to 4 days 60% $penalties = array( $penalties['0'], $penalties['3'] ); - + // valid event between 1 amd 4 days $ret = $this->Penalty->getPenaltyByPenaltiesAndDaysLate($penalties, 1); $this->assertEqual($ret['Penalty']['percent_penalty'], 15); @@ -156,8 +158,8 @@ function testGetPenaltyByPenaltiesAndDaysLate() $this->assertEqual($ret['Penalty']['percent_penalty'], 60); $ret = $this->Penalty->getPenaltyByPenaltiesAndDaysLate($penalties, 4); $this->assertEqual($ret['Penalty']['percent_penalty'], 60); - - + + // valid empty event penalties set $penalties = $this->Penalty->getPenaltyByEventId(3); $this->assertEqual(empty($penalties), true); @@ -241,7 +243,7 @@ function testGetPenaltyByEventAndDaysLate() $ret = $this->Penalty->getPenaltyByEventAndDaysLate(999, 5); $this->assertEqual($ret, null); } - + function testGetPenaltyPercent() { //TODO diff --git a/app/tests/cases/models/personalize.test.php b/app/tests/cases/models/personalize.test.php index 8ea45f95f..85aba78e2 100644 --- a/app/tests/cases/models/personalize.test.php +++ b/app/tests/cases/models/personalize.test.php @@ -6,6 +6,8 @@ class PersonalizeTestCase extends CakeTestCase public $name = 'Personalize'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/question.test.php b/app/tests/cases/models/question.test.php index 93a6b0fab..1c2a098c1 100644 --- a/app/tests/cases/models/question.test.php +++ b/app/tests/cases/models/question.test.php @@ -6,6 +6,8 @@ class QuestionTestCase extends CakeTestCase public $name = 'Question'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -55,7 +57,7 @@ function testGetTypeById() $this->assertNull($faultyId); $this->assertNull($nullId); } - + function testCopyQuestions() { } diff --git a/app/tests/cases/models/response.test.php b/app/tests/cases/models/response.test.php index a53fc8732..1eec7b797 100644 --- a/app/tests/cases/models/response.test.php +++ b/app/tests/cases/models/response.test.php @@ -6,6 +6,8 @@ class ResponseTestCase extends CakeTestCase public $name = 'Response'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/roles_user.test.php b/app/tests/cases/models/roles_user.test.php index c98862758..a58da6d4c 100644 --- a/app/tests/cases/models/roles_user.test.php +++ b/app/tests/cases/models/roles_user.test.php @@ -3,7 +3,16 @@ App::import('Model', 'RolesUser'); class RolesUserTestCase extends CakeTestCase { - var $fixtures = array('app.roles_user', 'app.role', 'app.user', 'app.evaluation_submission', 'app.event', 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', 'app.groups_member', 'app.survey', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', 'app.user_course', 'app.user_tutor', 'app.user_enrol', 'app.department', 'app.faculty', 'app.course_department', 'app.user_faculty'); + var $fixtures = array( + 'app.roles_user', 'app.role', 'app.user', 'app.evaluation_submission', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', + 'app.event', 'app.event_template_type', 'app.course', 'app.group', + 'app.group_event', 'app.groups_member', 'app.survey', 'app.survey_group_set', + 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', + 'app.survey_question', 'app.user_course', 'app.user_tutor', 'app.user_enrol', + 'app.department', 'app.faculty', 'app.course_department', 'app.user_faculty' + ); function startTest($method) { echo "Start RolesUser model test.\n"; diff --git a/app/tests/cases/models/rubric.test.php b/app/tests/cases/models/rubric.test.php index fcc6bff9a..6b4cfd972 100644 --- a/app/tests/cases/models/rubric.test.php +++ b/app/tests/cases/models/rubric.test.php @@ -6,6 +6,8 @@ class RubricTestCase extends CakeTestCase public $name = 'Rubric'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey', diff --git a/app/tests/cases/models/rubric_lom.test.php b/app/tests/cases/models/rubric_lom.test.php index db2476957..849c9a6c1 100644 --- a/app/tests/cases/models/rubric_lom.test.php +++ b/app/tests/cases/models/rubric_lom.test.php @@ -7,6 +7,8 @@ class RubricsLomTestCase extends CakeTestCase public $name = 'RubricsLom'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', 'app.survey', diff --git a/app/tests/cases/models/rubrics_criteria.test.php b/app/tests/cases/models/rubrics_criteria.test.php index 5a1ff61f9..508c476d7 100644 --- a/app/tests/cases/models/rubrics_criteria.test.php +++ b/app/tests/cases/models/rubrics_criteria.test.php @@ -7,6 +7,8 @@ class RubricsCriteriaTestCase extends CakeTestCase public $name = 'RubricsCriteria'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', 'app.survey', diff --git a/app/tests/cases/models/rubrics_criteria_comment.test.php b/app/tests/cases/models/rubrics_criteria_comment.test.php index 52c4ab0f5..c25ecee7e 100644 --- a/app/tests/cases/models/rubrics_criteria_comment.test.php +++ b/app/tests/cases/models/rubrics_criteria_comment.test.php @@ -7,6 +7,8 @@ class RubricsCriteriaCommentTestCase extends CakeTestCase public $name = 'RubricsCriteriaComment'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', 'app.survey', diff --git a/app/tests/cases/models/simple_evaluation.test.php b/app/tests/cases/models/simple_evaluation.test.php index 66d74648c..b9e850c1a 100644 --- a/app/tests/cases/models/simple_evaluation.test.php +++ b/app/tests/cases/models/simple_evaluation.test.php @@ -6,12 +6,14 @@ class SimpleEvaluationTestCase extends CakeTestCase public $name = 'SimpleEvaluation'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', 'app.user_course', - 'app.user_enrol', 'app.groups_member', 'app.survey', + 'app.user_enrol', 'app.groups_member', 'app.survey', 'app.simple_evaluation', 'app.user_faculty' ); diff --git a/app/tests/cases/models/survey.test.php b/app/tests/cases/models/survey.test.php index 44412f5f9..7f6b2a969 100644 --- a/app/tests/cases/models/survey.test.php +++ b/app/tests/cases/models/survey.test.php @@ -6,6 +6,8 @@ class SurveyTestCase extends CakeTestCase public $name = 'Survey'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey', diff --git a/app/tests/cases/models/survey_group.test.php b/app/tests/cases/models/survey_group.test.php index 6e8dcdb6c..cf8c67654 100644 --- a/app/tests/cases/models/survey_group.test.php +++ b/app/tests/cases/models/survey_group.test.php @@ -5,6 +5,8 @@ class SurveyGroupTestCase extends CakeTestCase { public $name = 'SurveyGroup'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/survey_group_member.test.php b/app/tests/cases/models/survey_group_member.test.php index c38f42eb7..3d3f23c29 100644 --- a/app/tests/cases/models/survey_group_member.test.php +++ b/app/tests/cases/models/survey_group_member.test.php @@ -6,6 +6,8 @@ class SurveyGroupMemberTestCase extends CakeTestCase public $name = 'SurveyGroupMember'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/cases/models/survey_group_set.test.php b/app/tests/cases/models/survey_group_set.test.php index 9322643a9..5d9da9de1 100644 --- a/app/tests/cases/models/survey_group_set.test.php +++ b/app/tests/cases/models/survey_group_set.test.php @@ -6,6 +6,8 @@ class SurveyGroupSetTestCase extends CakeTestCase public $name = 'SurveyGroupSet'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -16,7 +18,7 @@ class SurveyGroupSetTestCase extends CakeTestCase 'app.faculty', 'app.user_faculty', 'app.department', 'app.course_department', 'app.sys_parameter', 'app.user_tutor', 'app.penalty', 'app.evaluation_simple', 'app.survey_input', - 'app.oauth_token', 'app.evaluation_mixeval', 'app.evaluation_rubric', + 'app.oauth_token', 'app.evaluation_mixeval', 'app.evaluation_rubric', 'app.evaluation_mixeval_detail', 'app.evaluation_rubric_detail' ); public $SurveyGroupSet = null; diff --git a/app/tests/cases/models/survey_input.test.php b/app/tests/cases/models/survey_input.test.php index c3271f541..38ac8e93b 100644 --- a/app/tests/cases/models/survey_input.test.php +++ b/app/tests/cases/models/survey_input.test.php @@ -5,6 +5,8 @@ class SurveyInputTestCase extends CakeTestCase { public $name = 'SurveyInput'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', 'app.penalty', diff --git a/app/tests/cases/models/survey_question.test.php b/app/tests/cases/models/survey_question.test.php index e352b249b..2bf3bf51b 100644 --- a/app/tests/cases/models/survey_question.test.php +++ b/app/tests/cases/models/survey_question.test.php @@ -6,6 +6,8 @@ class SurveyQuestionTestCase extends CakeTestCase public $name = 'SurveyQuestion'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -136,7 +138,7 @@ function testGetLastSurveyQuestionNum() $lastQuestionNum = $this->SurveyQuestion->getLastSurveyQuestionNum(1); $this->assertEqual($lastQuestionNum, 2); } - + function testAssignNumber() { //TODO diff --git a/app/tests/cases/models/sys_parameter.test.php b/app/tests/cases/models/sys_parameter.test.php index 63622a6d0..4e9ab16b8 100644 --- a/app/tests/cases/models/sys_parameter.test.php +++ b/app/tests/cases/models/sys_parameter.test.php @@ -5,6 +5,8 @@ class SysParameterTestCase extends CakeTestCase { public $name = 'EvaluationSimple'; public $fixtures = array('app.course', 'app.role', 'app.user', 'app.group', 'app.sys_parameter', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', @@ -60,19 +62,19 @@ function testGet() $result = $this->SysParameter->get('non.existing.key', 'default'); $this->assertEqual($result, 'default'); } - + function testNumberSysParam() { $result = $this->SysParameter->find('count'); $this->assertEqual($result, 31); - + $result = $this->SysParameter->find('list', array('fields' => array('SysParameter.parameter_code'))); $result_values = array_values($result); $expected = array( 'system.super_admin', 'system.admin_email', 'display.date_format', 'system.version', 'database.version', 'email.port', 'email.host', 'email.username', 'email.password', 'display.contact_info', 'display.login.header', 'display.login.footer', 'system.absolute_url', - 'google_analytics.tracking_id', 'google_analytics.domain', 'banner.custom_logo', 'system.timezone', + 'google_analytics.tracking_id', 'google_analytics.domain', 'banner.custom_logo', 'system.timezone', 'system.student_number', 'course.creation.instructions', 'system.canvas_enabled', 'system.canvas_baseurl', 'system.canvas_baseurl_ext', 'system.canvas_user_key', 'system.canvas_client_id', 'system.canvas_client_secret', 'system.canvas_force_login', diff --git a/app/tests/cases/models/user.test.php b/app/tests/cases/models/user.test.php index bd9966405..39f667a8d 100644 --- a/app/tests/cases/models/user.test.php +++ b/app/tests/cases/models/user.test.php @@ -5,6 +5,8 @@ class UserTestCase extends CakeTestCase { var $fixtures = array( 'app.user', 'app.evaluation_submission', 'app.event', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', 'app.groups_member', 'app.survey', 'app.survey_group_set', 'app.survey_group', 'app.survey_group_member', diff --git a/app/tests/cases/models/user_course.test.php b/app/tests/cases/models/user_course.test.php index 657b3f0f6..0b55fb7f7 100644 --- a/app/tests/cases/models/user_course.test.php +++ b/app/tests/cases/models/user_course.test.php @@ -6,12 +6,14 @@ class UserCourseTestCase extends CakeTestCase public $name = 'UserEnrol'; public $fixtures = array( 'app.user_course', 'app.user', 'app.evaluation_submission', 'app.event', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.event_template_type', 'app.course', 'app.group', 'app.group_event', - 'app.evaluation_simple', 'app.survey_input', 'app.faculty', - 'app.user_faculty', 'app.survey', 'app.survey_group_set', - 'app.survey_group', 'app.survey_group_member', 'app.question', + 'app.evaluation_simple', 'app.survey_input', 'app.faculty', + 'app.user_faculty', 'app.survey', 'app.survey_group_set', + 'app.survey_group', 'app.survey_group_member', 'app.question', 'app.response', 'app.survey_question', 'app.role', 'app.roles_user', - 'app.user_tutor', 'app.user_enrol', 'app.groups_member', + 'app.user_tutor', 'app.user_enrol', 'app.groups_member', 'app.department', 'app.course_department', 'app.penalty', 'app.oauth_token', 'app.evaluation_mixeval', 'app.evaluation_rubric', 'app.evaluation_mixeval_detail', 'app.evaluation_rubric_detail', 'app.sys_parameter' diff --git a/app/tests/cases/models/user_enrol.test.php b/app/tests/cases/models/user_enrol.test.php index 1a2c38b45..d2713d470 100644 --- a/app/tests/cases/models/user_enrol.test.php +++ b/app/tests/cases/models/user_enrol.test.php @@ -6,6 +6,8 @@ class UserEnrolTestCase extends CakeTestCase public $name = 'UserEnrol'; public $fixtures = array( 'app.course', 'app.role', 'app.user', 'app.group', + 'app.lti_user', 'app.lti_nonce', 'app.lti_tool_registration', + 'app.lti_resource_link', 'app.lti_context', 'app.roles_user', 'app.event', 'app.event_template_type', 'app.group_event', 'app.evaluation_submission', 'app.survey_group_set', 'app.survey_group', diff --git a/app/tests/fixtures/lti_context_fixture.php b/app/tests/fixtures/lti_context_fixture.php new file mode 100644 index 000000000..c3f17436b --- /dev/null +++ b/app/tests/fixtures/lti_context_fixture.php @@ -0,0 +1,15 @@ + 'LtiContext', 'records' => true); +} \ No newline at end of file diff --git a/app/tests/fixtures/lti_nonce_fixture.php b/app/tests/fixtures/lti_nonce_fixture.php new file mode 100644 index 000000000..e5719e3c2 --- /dev/null +++ b/app/tests/fixtures/lti_nonce_fixture.php @@ -0,0 +1,15 @@ + 'LtiNonce', 'records' => true); +} \ No newline at end of file diff --git a/app/tests/fixtures/lti_resource_link_fixture.php b/app/tests/fixtures/lti_resource_link_fixture.php new file mode 100644 index 000000000..c61b73fc2 --- /dev/null +++ b/app/tests/fixtures/lti_resource_link_fixture.php @@ -0,0 +1,15 @@ + 'LtiResourceLink', 'records' => true); +} \ No newline at end of file diff --git a/app/tests/fixtures/lti_tool_registration_fixture.php b/app/tests/fixtures/lti_tool_registration_fixture.php new file mode 100644 index 000000000..5bf95c15c --- /dev/null +++ b/app/tests/fixtures/lti_tool_registration_fixture.php @@ -0,0 +1,16 @@ + + * @license MIT {@link http://www.opensource.org/licenses/MIT} + */ +class LtiToolRegistrationFixture extends CakeTestFixture +{ + public $name = 'LtiToolRegistration'; + + public $import = array('model' => 'LtiToolRegistration', 'records' => true); +} \ No newline at end of file diff --git a/app/tests/fixtures/lti_user_fixture.php b/app/tests/fixtures/lti_user_fixture.php new file mode 100644 index 000000000..200abead7 --- /dev/null +++ b/app/tests/fixtures/lti_user_fixture.php @@ -0,0 +1,15 @@ + 'LtiUser', 'records' => true); +} \ No newline at end of file diff --git a/app/views/courses/home.ctp b/app/views/courses/home.ctp index 969982ec0..4a2f89991 100644 --- a/app/views/courses/home.ctp +++ b/app/views/courses/home.ctp @@ -50,7 +50,6 @@ $submenuTitle = __('Students', true); $params = array('submenu'=>$submenu, 'submenuTitle'=>$submenuTitle, 'course_id'=>$data['Course']['id']); echo $this->element('courses/submenu', $params); - $submenu = 'Group'; $submenuTitle = __('Groups', true); $params = array('controller'=>'courses', 'submenu'=>$submenu, 'submenuTitle'=>$submenuTitle, 'course_id'=>$data['Course']['id']); @@ -68,11 +67,18 @@ if (User::hasPermission('controllers/Surveys')) { echo $this->element('courses/submenu', $params); } +if ($ltiNrpsEnabled) { + $submenu = 'LTI'; + $submenuTitle = __('LTI', true); + $params = array('controller'=>'courses', 'submenu'=>$submenu, 'submenuTitle'=>$submenuTitle, 'course_id'=>$data['Course']['id']); + echo $this->element('courses/submenu', $params); +} + if ($canvasEnabled) { - $submenu = 'Canvas'; - $submenuTitle = __('Canvas', true); - $params = array('controller'=>'courses', 'submenu'=>$submenu, 'submenuTitle'=>$submenuTitle, 'course_id'=>$data['Course']['id']); - echo $this->element('courses/submenu', $params); + $submenu = 'Canvas'; + $submenuTitle = __('Canvas', true); + $params = array('controller'=>'courses', 'submenu'=>$submenu, 'submenuTitle'=>$submenuTitle, 'course_id'=>$data['Course']['id']); + echo $this->element('courses/submenu', $params); } ?> diff --git a/app/views/elements/courses/submenu.ctp b/app/views/elements/courses/submenu.ctp index a72e6d99f..d1b774325 100644 --- a/app/views/elements/courses/submenu.ctp +++ b/app/views/elements/courses/submenu.ctp @@ -90,6 +90,13 @@ switch($submenu) { // array('name' => 'Sync Canvas Groups', 'link' => "/groups/syncCanvas/$course_id") // ); } + case "LTI": + if ($ltiNrpsEnabled){ + array_push( + $items, + array('name' => 'Sync Users from LMS Course Roster', 'link' => "/lti/roster/$course_id") + ); + } break; } ?> diff --git a/app/views/lti/index.ctp b/app/views/lti/index.ctp deleted file mode 100644 index d7ab116e2..000000000 --- a/app/views/lti/index.ctp +++ /dev/null @@ -1,10 +0,0 @@ -LTI Request failure: $invalidlti

"; -} -else -{ - echo "

The LTI request completed successfully, but the app failed to redirect for some reason. Try clicking here to see if you're properly logged in.

"; -} -?> diff --git a/app/views/lti_tool_registrations/add.ctp b/app/views/lti_tool_registrations/add.ctp new file mode 100644 index 000000000..4fc60bd14 --- /dev/null +++ b/app/views/lti_tool_registrations/add.ctp @@ -0,0 +1,70 @@ +
+ +
+
    +
  • Html->link(__('Back to index', true), array('action' => 'index')); ?>
  • +
+
+ +

*

+ +Form->create('Ltitoolregistration'); + +echo $this->Form->input('LtiToolRegistration.iss', array( + 'type' => 'text', + 'label' => 'Issuer', + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.client_id', array( + 'type' => 'text', + 'label' => 'Client ID', + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.auth_login_url', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.auth_token_url', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.key_set_url', array( + 'size' => '60', + 'label' => 'Security JWKs Url', +)); +echo $this->Form->input('LtiToolRegistration.tool_private_key', array( + 'rows' => '5', + 'cols' => '60', + 'placeholder' => '-----BEGIN RSA PRIVATE KEY----- +... +-----END RSA PRIVATE KEY----- +', +)); +echo $this->Form->input('LtiToolRegistration.tool_public_key', array( + 'rows' => '5', + 'cols' => '60', + 'placeholder' => '-----BEGIN PUBLIC KEY----- +... +-----END PUBLIC KEY----- +', +)); +echo $this->Form->input('LtiToolRegistration.user_identifier_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.student_number_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.term_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.canvas_id_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.faculty_name_field', array( + 'size' => '60', +)); +?> + + Form->end(__('Create', true)); ?> + +
+ diff --git a/app/views/lti_tool_registrations/edit.ctp b/app/views/lti_tool_registrations/edit.ctp new file mode 100644 index 000000000..911d6516f --- /dev/null +++ b/app/views/lti_tool_registrations/edit.ctp @@ -0,0 +1,70 @@ +
+ +
+
    +
  • Html->link(__('Back to index', true), array('action' => 'index')); ?>
  • +
+
+ +

*

+ +Form->create('Ltitoolregistration'); + +echo $this->Form->input('LtiToolRegistration.id', array('type' => 'hidden')); +echo $this->Form->input('LtiToolRegistration.iss', array( + 'type' => 'text', + 'label' => 'Issuer', + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.client_id', array( + 'type' => 'text', + 'label' => 'Client ID', + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.auth_login_url', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.auth_token_url', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.key_set_url', array( + 'size' => '60', + 'label' => 'Security JWKs Url', +)); +echo $this->Form->input('LtiToolRegistration.tool_private_key', array( + 'rows' => '5', + 'cols' => '60', + 'placeholder' => '-----BEGIN RSA PRIVATE KEY----- +... +-----END RSA PRIVATE KEY----- +', +)); +echo $this->Form->input('LtiToolRegistration.tool_public_key', array( + 'rows' => '5', + 'cols' => '60', + 'placeholder' => '-----BEGIN PUBLIC KEY----- +... +-----END PUBLIC KEY----- +', +)); +echo $this->Form->input('LtiToolRegistration.user_identifier_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.student_number_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.term_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.canvas_id_field', array( + 'size' => '60', +)); +echo $this->Form->input('LtiToolRegistration.faculty_name_field', array( + 'size' => '60', +)); +?> + + Form->end(__('Update', true)); ?> + +
diff --git a/app/views/lti_tool_registrations/index.ctp b/app/views/lti_tool_registrations/index.ctp new file mode 100644 index 000000000..9d992e74f --- /dev/null +++ b/app/views/lti_tool_registrations/index.ctp @@ -0,0 +1,47 @@ +
+ +
+
    +
  • Html->link(__('Add Tool Registration', true), array('action'=>'add'), array('class'=>'add-button')); ?>
  • +
+
+ + + + + + + + + + + + + + + + + + + + +
IssuerClient IDActions
+
    +
  • Html->link(__('Edit', true), array('action'=>'edit', $registration['id'])); ?>
  • +
  • Html->link(__('Delete', true), array('action'=>'delete', $registration['id']), null, sprintf(__('Are you sure you want to delete %s?', true), $registration['iss'])); ?>
  • +
+
+ +
+ + diff --git a/app/views/pages/admin.ctp b/app/views/pages/admin.ctp index 83d3024d7..91eb53522 100644 --- a/app/views/pages/admin.ctp +++ b/app/views/pages/admin.ctp @@ -24,6 +24,15 @@ if (User::hasPermission('controllers/departments')) { echo ''; } +if (User::hasPermission('controllers/ltitoolregistrations')) { + echo '
  • '; + echo $this->Html->link( + __('LTI 1.3 Tool Registrations',true), + array('controller' => 'ltitoolregistrations') + ); + echo '
  • '; +} + if (User::hasPermission('functions/user/superadmin')) { echo "
  • "; echo $this->Html->link( diff --git a/build/sqlclean/superadmin.sql b/build/sqlclean/superadmin.sql index 51ee34879..d6f1e26e3 100644 --- a/build/sqlclean/superadmin.sql +++ b/build/sqlclean/superadmin.sql @@ -1,4 +1,4 @@ -INSERT INTO `users` (`id`, `username`, `password`, `first_name`, `last_name`, `student_no`, `title`, `email`, `last_login`, `last_logout`, `last_accessed`, `record_status`, `creator_id`, `created`, `updater_id`, `modified`, `lti_id`) VALUES -(1, 'root', 'cbbde255cc20cb7a566d4c7f12298729', 'Super', 'Admin', NULL, NULL, '', NULL, NULL, NULL, 'A', 0, '2012-07-17 11:01:37', NULL, NULL, NULL); +INSERT INTO `users` (`id`, `username`, `password`, `first_name`, `last_name`, `student_no`, `title`, `email`, `last_login`, `last_logout`, `last_accessed`, `record_status`, `creator_id`, `created`, `updater_id`, `modified`) VALUES +(1, 'root', 'cbbde255cc20cb7a566d4c7f12298729', 'Super', 'Admin', NULL, NULL, '', NULL, NULL, NULL, 'A', 0, '2012-07-17 11:01:37', NULL, NULL); INSERT INTO `roles_users` (`id`, `role_id`, `user_id`, `created`, `modified`) VALUES (37, 1, 1, '2012-07-17 11:14:51', '2012-07-17 11:14:51'); diff --git a/composer.json b/composer.json index 4b0dbf2d9..1bfab0e86 100644 --- a/composer.json +++ b/composer.json @@ -1,36 +1,41 @@ { - "repositories": [ - { - "type": "git", - "url": "https://github.com/IMSGlobal/caliper-php" + "repositories": [ + { + "type": "git", + "url": "https://github.com/IMSGlobal/caliper-php" + }, + { + "type": "vcs", + "url": "https://github.com/ubc/lti-1-3-php-library" + } + ], + "require": { + "compass/cake-guard": "1.0.7", + "nategood/httpful": "^0.2.20", + "imsglobal/caliper": "dev-develop#c7e34e230abc7bbb647f8f94245cb649f6191bcd as 1.2.0", + "imsglobal/lti-1p3-tool": "dev-master#f863ffd3da446b0efcd062e29ccc9d7a280aec98" + }, + "require-dev": { + "roave/security-advisories": "dev-master", + "phing/phing": "2.*", + "pdepend/pdepend": "2.*", + "phpmd/phpmd": "@stable", + "squizlabs/php_codesniffer": "2.*", + "sebastian/phpcpd": "*", + "jms/serializer": "1.7.*", + "phpdocumentor/phpdocumentor": "^2.9" + }, + "scripts": { + "post-install-cmd": [ + "[ -e app/config/database.php ] || cp app/config/database.php.default app/config/database.php", + "[ -L app/plugins/guard ] || [ -d app/plugins/guard ] || ln -s ../../vendor/compass/cake-guard app/plugins/guard", + "[ -e app/config/guard.php ] || cp app/plugins/guard/config/guard_default.php app/config/guard.php", + "mkdir -p app/tmp && chmod 777 app/tmp" + ] + }, + "config": { + "platform": { + "php": "7.2" + } } - ], - "require": { - "compass/cake-guard": "1.0.7", - "nategood/httpful": "^0.2.20", - "imsglobal/caliper": "dev-develop#c7e34e230abc7bbb647f8f94245cb649f6191bcd as 1.2.0" - }, - "require-dev": { - "roave/security-advisories": "dev-master", - "phing/phing": "2.*", - "pdepend/pdepend" : "2.*", - "phpmd/phpmd" : "@stable", - "squizlabs/php_codesniffer": "2.*", - "sebastian/phpcpd": "*", - "jms/serializer": "1.7.*", - "phpdocumentor/phpdocumentor": "^2.9" - }, - "scripts": { - "post-install-cmd": [ - "[ -e app/config/database.php ] || cp app/config/database.php.default app/config/database.php", - "[ -L app/plugins/guard ] || [ -d app/plugins/guard ] || ln -s ../../vendor/compass/cake-guard app/plugins/guard", - "[ -e app/config/guard.php ] || cp app/plugins/guard/config/guard_default.php app/config/guard.php", - "mkdir -p app/tmp && chmod 777 app/tmp" - ] - }, - "config": { - "platform": { - "php": "7.2" - } - } } diff --git a/composer.lock b/composer.lock index f35b9300c..db92b0bce 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4b335c58c09584e5b513ed9b738fef15", + "content-hash": "05fb266e75551e6c83f2e83b9febc226", "packages": [ { "name": "compass/cake-guard", @@ -37,6 +37,57 @@ "description": "Authentication Plugin for CakePHP 1.3", "time": "2020-07-17T21:49:24+00:00" }, + { + "name": "fproject/php-jwt", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/fproject/php-jwt.git", + "reference": "91047b202bbe7d966e8fce67ab16ebca8bdcb6b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fproject/php-jwt/zipball/91047b202bbe7d966e8fce67ab16ebca8bdcb6b7", + "reference": "91047b202bbe7d966e8fce67ab16ebca8bdcb6b7", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + }, + { + "name": "Bui Sy Nguyen", + "email": "nguyenbs@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Support several key types including JWK.", + "homepage": "https://github.com/fproject/php-jwt", + "time": "2018-04-02T10:36:30+00:00" + }, { "name": "imsglobal/caliper", "version": "dev-develop", @@ -76,6 +127,41 @@ ], "time": "2020-01-22T19:01:21+00:00" }, + { + "name": "imsglobal/lti-1p3-tool", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/ubc/lti-1-3-php-library.git", + "reference": "f863ffd3da446b0efcd062e29ccc9d7a280aec98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ubc/lti-1-3-php-library/zipball/f863ffd3da446b0efcd062e29ccc9d7a280aec98", + "reference": "f863ffd3da446b0efcd062e29ccc9d7a280aec98", + "shasum": "" + }, + "require": { + "fproject/php-jwt": "^4.0", + "phpseclib/phpseclib": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "IMSGlobal\\LTI\\": "src/lti" + } + }, + "authors": [ + { + "name": "Martin Lenord", + "email": "ims.m@rtin.dev" + } + ], + "support": { + "source": "https://github.com/ubc/lti-1-3-php-library/tree/master" + }, + "time": "2020-10-21T22:59:08+00:00" + }, { "name": "nategood/httpful", "version": "0.2.20", @@ -125,6 +211,111 @@ "restful" ], "time": "2015-10-26T16:11:30+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "2.0.29", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "497856a8d997f640b4a516062f84228a772a48a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/497856a8d997f640b4a516062f84228a772a48a8", + "reference": "497856a8d997f640b4a516062f84228a772a48a8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2020-09-08T04:24:43+00:00" } ], "packages-dev": [ @@ -248,16 +439,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.7", + "version": "1.2.8", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" + "reference": "8a7ecad675253e4654ea05505233285377405215" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215", + "reference": "8a7ecad675253e4654ea05505233285377405215", "shasum": "" }, "require": { @@ -305,25 +496,29 @@ "url": "https://packagist.com", "type": "custom" }, + { + "url": "https://github.com/composer", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2020-04-08T08:27:21+00:00" + "time": "2020-08-23T12:54:47+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.4.2", + "version": "1.4.4", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51" + "reference": "6e076a124f7ee146f2487554a94b6a19a74887ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", - "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6e076a124f7ee146f2487554a94b6a19a74887ba", + "reference": "6e076a124f7ee146f2487554a94b6a19a74887ba", "shasum": "" }, "require": { @@ -368,7 +563,7 @@ "type": "tidelift" } ], - "time": "2020-06-04T11:16:35+00:00" + "time": "2020-10-24T12:39:10+00:00" }, { "name": "container-interop/container-interop", @@ -404,16 +599,16 @@ }, { "name": "doctrine/annotations", - "version": "1.10.3", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "5db60a4969eba0e0c197a19c077780aadbc43c5d" + "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/5db60a4969eba0e0c197a19c077780aadbc43c5d", - "reference": "5db60a4969eba0e0c197a19c077780aadbc43c5d", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/ce77a7ba1770462cd705a91a151b6c3746f9c6ad", + "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad", "shasum": "" }, "require": { @@ -423,12 +618,14 @@ }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^7.5" + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^9.1.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -463,13 +660,13 @@ } ], "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], - "time": "2020-05-25T17:24:27+00:00" + "time": "2020-10-26T10:28:16+00:00" }, { "name": "doctrine/instantiator", @@ -525,20 +722,6 @@ "constructor", "instantiate" ], - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], "time": "2020-05-29T17:27:14+00:00" }, { @@ -601,20 +784,6 @@ "parser", "php" ], - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], "time": "2020-05-25T17:44:05+00:00" }, { @@ -834,16 +1003,16 @@ }, { "name": "monolog/monolog", - "version": "1.25.4", + "version": "1.25.5", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "3022efff205e2448b560c833c6fbbf91c3139168" + "reference": "1817faadd1846cd08be9a49e905dc68823bc38c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/3022efff205e2448b560c833c6fbbf91c3139168", - "reference": "3022efff205e2448b560c833c6fbbf91c3139168", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1817faadd1846cd08be9a49e905dc68823bc38c0", + "reference": "1817faadd1846cd08be9a49e905dc68823bc38c0", "shasum": "" }, "require": { @@ -917,7 +1086,7 @@ "type": "tidelift" } ], - "time": "2020-05-22T07:31:27+00:00" + "time": "2020-07-23T08:35:51+00:00" }, { "name": "nikic/php-parser", @@ -1083,6 +1252,7 @@ "self-update", "update" ], + "abandoned": true, "time": "2018-03-30T12:52:15+00:00" }, { @@ -1130,12 +1300,6 @@ "BSD-3-Clause" ], "description": "Official version of pdepend to be handled with Composer", - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", - "type": "tidelift" - } - ], "time": "2020-06-20T10:53:13+00:00" }, { @@ -1558,16 +1722,16 @@ }, { "name": "phpmd/phpmd", - "version": "2.8.2", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "714629ed782537f638fe23c4346637659b779a77" + "reference": "ce10831d4ddc2686c1348a98069771dd314534a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/714629ed782537f638fe23c4346637659b779a77", - "reference": "714629ed782537f638fe23c4346637659b779a77", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/ce10831d4ddc2686c1348a98069771dd314534a8", + "reference": "ce10831d4ddc2686c1348a98069771dd314534a8", "shasum": "" }, "require": { @@ -1578,6 +1742,8 @@ }, "require-dev": { "easy-doc/easy-doc": "0.0.0 || ^1.3.2", + "ext-json": "*", + "ext-simplexml": "*", "gregwar/rst": "^1.0", "mikey179/vfsstream": "^1.6.4", "phpunit/phpunit": "^4.8.36 || ^5.7.27", @@ -1624,28 +1790,34 @@ "phpmd", "pmd" ], - "time": "2020-02-16T20:15:50+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" + } + ], + "time": "2020-09-23T22:06:32+00:00" }, { "name": "phpoption/phpoption", - "version": "1.7.4", + "version": "1.7.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "b2ada2ad5d8a32b89088b8adc31ecd2e3a13baf3" + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/b2ada2ad5d8a32b89088b8adc31ecd2e3a13baf3", - "reference": "b2ada2ad5d8a32b89088b8adc31ecd2e3a13baf3", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", "shasum": "" }, "require": { "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.3", - "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -1689,7 +1861,7 @@ "type": "tidelift" } ], - "time": "2020-06-07T10:40:07+00:00" + "time": "2020-07-20T17:29:33+00:00" }, { "name": "phpunit/php-timer", @@ -1982,12 +2154,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "9f386dba391018e90a5f1e51abeffc6bf27583db" + "reference": "327370943772f9917bc2dc2aa4263db2d572a112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/9f386dba391018e90a5f1e51abeffc6bf27583db", - "reference": "9f386dba391018e90a5f1e51abeffc6bf27583db", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/327370943772f9917bc2dc2aa4263db2d572a112", + "reference": "327370943772f9917bc2dc2aa4263db2d572a112", "shasum": "" }, "conflict": { @@ -2003,6 +2175,7 @@ "bagisto/bagisto": "<0.1.5", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", + "baserproject/basercms": ">=4,<=4.3.6", "bolt/bolt": "<3.7.1", "brightlocal/phpwhois": "<=4.2.5", "buddypress/buddypress": "<5.1.2", @@ -2016,10 +2189,11 @@ "composer/composer": "<=1-alpha.11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/core": ">=2,<3.5.39", - "contao/core-bundle": ">=4,<4.4.46|>=4.5,<4.8.6", + "contao/core-bundle": ">=4,<4.4.52|>=4.5,<4.9.6|= 4.10.0", "contao/listing-bundle": ">=4,<4.4.8", "datadog/dd-trace": ">=0.30,<0.30.2", "david-garcia/phpwhois": "<=4.3.1", + "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", "doctrine/annotations": ">=1,<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", @@ -2031,8 +2205,8 @@ "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dolibarr/dolibarr": "<11.0.4", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.72|>=8,<8.8.8|>=8.9,<8.9.1|>=9,<9.0.1", - "drupal/drupal": ">=7,<7.72|>=8,<8.8.8|>=8.9,<8.9.1|>=9,<9.0.1", + "drupal/core": ">=7,<7.73|>=8,<8.8.10|>=8.9,<8.9.6|>=9,<9.0.6", + "drupal/drupal": ">=7,<7.73|>=8,<8.8.10|>=8.9,<8.9.6|>=9,<9.0.6", "endroid/qr-code-bundle": "<3.4.2", "enshrined/svg-sanitize": "<0.13.1", "erusev/parsedown": "<1.7.2", @@ -2041,11 +2215,12 @@ "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", "ezsystems/ezplatform": ">=1.7,<1.7.9.1|>=1.13,<1.13.5.1|>=2.5,<2.5.4", "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", - "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", "ezsystems/ezplatform-kernel": ">=1,<1.0.2.1", "ezsystems/ezplatform-user": ">=1,<1.0.1", "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.2|>=6,<6.7.9.1|>=6.8,<6.13.6.3|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<7.5.7.1", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.1|>=2011,<2017.12.7.2|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.4.2", + "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.2|>=2011,<2017.12.7.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.5.1", + "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1", "ezyang/htmlpurifier": "<4.1.1", "firebase/php-jwt": "<2", @@ -2054,6 +2229,7 @@ "friendsofsymfony/oauth2-php": "<1.3", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "fuel/core": "<1.8.1", "getgrav/grav": "<1.7-beta.8", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", @@ -2061,8 +2237,8 @@ "gregwar/rst": "<1.0.3", "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", - "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", - "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", + "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29|>=5.5,<=5.5.44|>=6,<6.18.34|>=7,<7.23.2", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "illuminate/view": ">=7,<7.1.2", "ivankristianto/phpwhois": "<=4.3", @@ -2070,24 +2246,35 @@ "joomla/session": "<1.3.1", "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", + "kitodo/presentation": "<3.1.2", "kreait/firebase-php": ">=3.2,<3.8.1", "la-haute-societe/tcpdf": "<6.2.22", - "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30|>=7,<7.1.2", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.34|>=7,<7.23.2", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "league/commonmark": "<0.18.3", "librenms/librenms": "<1.53", + "livewire/livewire": ">2.2.4,<2.2.6", "magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3", "magento/magento1ce": "<1.9.4.3", "magento/magento1ee": ">=1,<1.14.4.3", "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", + "marcwillmann/turn": "<0.3.3", + "mediawiki/core": ">=1.31,<1.31.4|>=1.32,<1.32.4|>=1.33,<1.33.1", + "mittwald/typo3_forum": "<1.2.1", "monolog/monolog": ">=1.8,<1.12", "namshi/jose": "<2.2", + "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", + "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nystudio107/craft-seomatic": "<3.3", "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", - "october/october": ">=1.0.319,<1.0.467", + "october/backend": ">=1.0.319,<1.0.467", + "october/cms": ">=1.0.319,<1.0.466", + "october/october": ">=1.0.319,<1.0.466", + "october/rain": ">=1.0.319,<1.0.468", "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", "openid/php-openid": "<2.3", + "openmage/magento-lts": "<19.4.6|>=20,<20.0.2", "oro/crm": ">=1.7,<1.7.4", "oro/platform": ">=1.7,<1.7.4", "padraic/humbug_get_contents": "<1.1.2", @@ -2095,6 +2282,7 @@ "paragonie/random_compat": "<2", "paypal/merchant-sdk-php": "<3.12", "pear/archive_tar": "<1.4.4", + "personnummer/personnummer": "<3.0.2", "phpfastcache/phpfastcache": ">=5,<5.0.13", "phpmailer/phpmailer": "<6.1.6", "phpmussel/phpmussel": ">=1,<1.6", @@ -2106,18 +2294,23 @@ "phpxmlrpc/extras": "<0.6.1", "pimcore/pimcore": "<6.3", "prestashop/autoupgrade": ">=4,<4.10.1", + "prestashop/contactform": ">1.0.1,<4.3", "prestashop/gamification": "<2.3.2", "prestashop/ps_facetedsearch": "<3.4.1", "privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2", "propel/propel": ">=2-alpha.1,<=2-alpha.7", "propel/propel1": ">=1,<=1.7.1", + "pterodactyl/panel": "<0.7.19|>=1-rc.0,<=1-rc.6", "pusher/pusher-php-server": "<2.2.1", "rainlab/debugbar-plugin": "<3.1", "robrichards/xmlseclibs": "<3.0.4", + "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", + "shopware/core": "<=6.3.1", + "shopware/platform": "<=6.3.1", "shopware/shopware": "<5.3.7", "silverstripe/admin": ">=1.0.3,<1.0.4|>=1.1,<1.1.1", "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", @@ -2144,11 +2337,12 @@ "ssddanbrown/bookstack": "<0.29.2", "stormpath/sdk": ">=0,<9.9.99", "studio-42/elfinder": "<2.1.49", + "sulu/sulu": "<1.6.34|>=2,<2.0.10|>=2.1,<2.1.1", "swiftmailer/swiftmailer": ">=4,<5.4.5", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", - "sylius/resource-bundle": "<1.3.13|>=1.4,<1.4.6|>=1.5,<1.5.1|>=1.6,<1.6.3", + "sylius/resource-bundle": "<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", "sylius/sylius": "<1.3.16|>=1.4,<1.4.12|>=1.5,<1.5.9|>=1.6,<1.6.5", "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", "symbiote/silverstripe-versionedfiles": "<=2.0.3", @@ -2158,7 +2352,7 @@ "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", - "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/mime": ">=4.3,<4.3.8", "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", @@ -2173,7 +2367,7 @@ "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", + "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5", "symfony/translation": ">=2,<2.0.17", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", @@ -2187,11 +2381,12 @@ "titon/framework": ">=0,<9.9.99", "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.38|>=2,<2.7", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.30|>=9,<9.5.17|>=10,<10.4.2", - "typo3/cms-core": ">=8,<8.7.30|>=9,<9.5.17|>=10,<10.4.2", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.30|>=9,<9.5.20|>=10,<10.4.6", + "typo3/cms-core": ">=8,<8.7.30|>=9,<9.5.20|>=10,<10.4.6", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", + "typo3fluid/fluid": ">=2,<2.0.5|>=2.1,<2.1.4|>=2.2,<2.2.1|>=2.3,<2.3.5|>=2.4,<2.4.1|>=2.5,<2.5.5|>=2.6,<2.6.1", "ua-parser/uap-php": "<3.8", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", @@ -2199,7 +2394,7 @@ "willdurand/js-translation-bundle": "<2.1.1", "yii2mod/yii2-cms": "<1.9.2", "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.15", + "yiisoft/yii2": "<2.0.38", "yiisoft/yii2-bootstrap": "<2.0.4", "yiisoft/yii2-dev": "<2.0.15", "yiisoft/yii2-elasticsearch": "<2.0.5", @@ -2261,7 +2456,7 @@ "type": "tidelift" } ], - "time": "2020-07-16T05:17:29+00:00" + "time": "2020-10-19T07:02:45+00:00" }, { "name": "sebastian/finder-facade", @@ -2304,6 +2499,7 @@ ], "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", "homepage": "https://github.com/sebastianbergmann/finder-facade", + "abandoned": true, "time": "2020-01-16T08:08:45+00:00" }, { @@ -2876,20 +3072,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.18.0", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-ctype": "For best performance" @@ -2897,7 +3093,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2948,24 +3144,24 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.18.0", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a" + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a", - "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-mbstring": "For best performance" @@ -2973,7 +3169,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3025,7 +3221,7 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/process", @@ -3305,16 +3501,16 @@ }, { "name": "twig/twig", - "version": "v1.43.0", + "version": "v1.43.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "597a03e85a60af6feee4f5127f3ef4279a1694c3" + "reference": "2311602f6a208715252febe682fa7c38e56a3373" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/597a03e85a60af6feee4f5127f3ef4279a1694c3", - "reference": "597a03e85a60af6feee4f5127f3ef4279a1694c3", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/2311602f6a208715252febe682fa7c38e56a3373", + "reference": "2311602f6a208715252febe682fa7c38e56a3373", "shasum": "" }, "require": { @@ -3366,18 +3562,6 @@ "templating" ], "funding": [ - { - "url": "https://certification.symfony.com/", - "type": "custom" - }, - { - "url": "https://live.symfony.com/", - "type": "custom" - }, - { - "url": "https://symfony.com/cloud/", - "type": "custom" - }, { "url": "https://github.com/fabpot", "type": "github" @@ -3387,7 +3571,7 @@ "type": "tidelift" } ], - "time": "2020-07-05T13:00:49+00:00" + "time": "2020-08-05T15:05:05+00:00" }, { "name": "webmozart/assert", @@ -4180,6 +4364,7 @@ "minimum-stability": "stable", "stability-flags": { "imsglobal/caliper": 20, + "imsglobal/lti-1p3-tool": 20, "roave/security-advisories": 20, "phpmd/phpmd": 0 }, diff --git a/docker-compose.yml b/docker-compose.yml index 0fcc614f7..b64389998 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: db: - image: mariadb:10.4 + image: mariadb:10.5 container_name: ipeer_db environment: - MYSQL_ROOT_PASSWORD=randompassword @@ -43,9 +43,7 @@ services: worker: &worker <<: *app container_name: ipeer_worker - command: cake/console/cake worker run - ports: - - "9002:9000" + command: bash -c "sleep 5 && cake/console/cake worker run" web: image: nginx:1.19-alpine container_name: ipeer_web @@ -59,15 +57,13 @@ services: # for running unit tests app-unittest: - image: ubcctlt/ipeer-app + image: ubcctlt/ipeer-app-unittest build: context: . dockerfile: Dockerfile-app-unittest container_name: ipeer_app_unittest volumes: - .:/var/www/html - ports: - - "9001:9000" environment: - IPEER_DB_HOST=db - IPEER_DB_USER=ipeer diff --git a/readme.md b/readme.md index 825ebd288..d82f7d34b 100644 --- a/readme.md +++ b/readme.md @@ -201,9 +201,11 @@ You may optionally override the user default IRI (from `$base_url/users/view/$us `CALIPER_ACTOR_UNIQUE_IDENTIFIER_PARAM`: Optionally set the actor's unique identifier using any column from the `user` table (ex: `username`, `id`, `email`). Will be inserted into the `CALIPER_ACTOR_BASE_URL` string. + iPeer 3.4.9 ----------- * Fix unsecure form submission when deployed load balancer with SSL offload (#663) +* Added LTI 1.3 iPeer 3.4.8 ----------- @@ -666,4 +668,4 @@ any suggestion or question? please let us know troubleshooting: -if you type http://yourserverpath/youripeerpath/ and you get http://yourserverpath/loginout/login, your mod_rewrite -is not set up properly. make sure the line in http.conf has 'AllowOverride All'. +is not set up properly. make sure the line in http.conf has 'AllowOverride All'. \ No newline at end of file diff --git a/version.txt b/version.txt index 7921bd0c8..7bcbb3808 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -3.4.8 +3.4.9