├── .gitignore ├── .travis.yml ├── LICENSE ├── NOTICE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── imsglobal │ │ ├── aspect │ │ ├── Lti.java │ │ ├── LtiKeySecretService.java │ │ └── LtiLaunchVerifier.java │ │ ├── json │ │ └── IMSJSONRequest.java │ │ ├── lti │ │ ├── BasicLTIConstants.java │ │ ├── BasicLTIUtil.java │ │ ├── XMLMap.java │ │ └── launch │ │ │ ├── LtiError.java │ │ │ ├── LtiLaunch.java │ │ │ ├── LtiOauthSigner.java │ │ │ ├── LtiOauthVerifier.java │ │ │ ├── LtiSigner.java │ │ │ ├── LtiSigningException.java │ │ │ ├── LtiUser.java │ │ │ ├── LtiVerificationException.java │ │ │ ├── LtiVerificationResult.java │ │ │ └── LtiVerifier.java │ │ ├── lti2 │ │ ├── LTI2Config.java │ │ ├── LTI2ConfigSample.java │ │ ├── LTI2Constants.java │ │ ├── LTI2SampleData.java │ │ ├── LTI2Servlet.java │ │ ├── LTI2Util.java │ │ └── objects │ │ │ ├── BaseJson.java │ │ │ ├── BaseJsonLd.java │ │ │ ├── consumer │ │ │ ├── Contact.java │ │ │ ├── Description.java │ │ │ ├── Name.java │ │ │ ├── NamedContext.java │ │ │ ├── ProductFamily.java │ │ │ ├── ProductInfo.java │ │ │ ├── ProductInstance.java │ │ │ ├── ProductName.java │ │ │ ├── ServiceOffered.java │ │ │ ├── ServiceOwner.java │ │ │ ├── ServiceOwnerName.java │ │ │ ├── ServiceProvider.java │ │ │ ├── ServiceProviderName.java │ │ │ ├── StandardServices.java │ │ │ ├── Support.java │ │ │ ├── TechnicalDescription.java │ │ │ ├── ToolConsumer.java │ │ │ └── Vendor.java │ │ │ └── provider │ │ │ ├── BaseUrlChoice.java │ │ │ ├── SecurityContract.java │ │ │ ├── ToolProfile.java │ │ │ └── ToolProxy.java │ │ └── pox │ │ └── IMSPOXRequest.java └── resources │ └── META-INF │ ├── LICENSE │ ├── NOTICE │ └── README └── test └── java └── org └── imsglobal └── lti ├── BasicLTIUtilTest.java ├── launch ├── BaseMockHttpServletRequest.java ├── LtiVerifierAndSignerTest.java ├── MockHttpGet.java └── MockHttpPost.java ├── lti2 ├── ProductInstanceTest.java ├── TestLtiConsumerProfile.java └── ToolConsumerTest.java └── pox └── IMSPOXRequestTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .idea/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk7 4 | - openjdk7 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | =============================================================================== 206 | 207 | The BasicLTI Utilities (basiclti-util) distribution includes a number of subcomponents 208 | with separate copyright notices and license terms. Your use of the 209 | code for the these subcomponents is subject to the terms and 210 | conditions of the following licenses. 211 | 212 | =============================================================================== 213 | OAuth: 214 | 215 | Copyright (c) 1998-2009 AOL LLC. 216 | Copyright 2007, 2008 Google, Inc. 217 | Copyright 2007, 2008 Netflix, Inc. 218 | 219 | Licensed under the Apache License, Version 2.0 (the "License"); 220 | you may not use this file except in compliance with the License. 221 | You may obtain a copy of the License at 222 | 223 | http://www.apache.org/licenses/LICENSE-2.0 224 | 225 | Unless required by applicable law or agreed to in writing, software 226 | distributed under the License is distributed on an "AS IS" BASIS, 227 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 228 | See the License for the specific language governing permissions and 229 | limitations under the License. 230 | 231 | =============================================================================== 232 | Base64: 233 | 234 | Copyright 1999-2008 The Apache Software Foundation. 235 | 236 | The ASF licenses this file to You under the Apache License, Version 2.0 237 | (the "License"); you may not use this file except in compliance with 238 | the License. You may obtain a copy of the License at 239 | 240 | http://www.apache.org/licenses/LICENSE-2.0 241 | 242 | Unless required by applicable law or agreed to in writing, software 243 | distributed under the License is distributed on an "AS IS" BASIS, 244 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 245 | See the License for the specific language governing permissions and 246 | limitations under the License. 247 | 248 | =============================================================================== 249 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | BasicLTI Utilities (basiclti-util) 2 | Copyright 2010 IMS Global Learning Consortium www.imsglobal.org 3 | 4 | ----------------------------------------------------------- 5 | 6 | This product includes software (OAuth) developed by 7 | AOL LLC., Google, Inc., and Netflix, Inc. (http://code.google.com/p/oauth/). 8 | 9 | This product includes software (Base64) developed at 10 | The Apache Software Foundation (http://www.apache.org/). 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #IMS Global - LTI™ Utilities 2 | 3 | [![Build Status](https://travis-ci.org/pfgray/basiclti-util-java.svg?branch=master)](https://travis-ci.org/pfgray/basiclti-util-java) 4 | 5 | What is it? 6 | ----------- 7 | 8 | LTI™ Utilities are a set of utility classes to aid in the development 9 | of LTI v1.0 consumers and providers. They deal with much of the heavy lifting 10 | and make the process more opaque to the developer. 11 | 12 | 13 | How to use: 14 | ----------- 15 | 16 | With Maven: 17 | Include in your project's `pom.xml`: 18 | 19 | ```xml 20 | 21 | org.imsglobal 22 | basiclti-util 23 | 1.1.2 24 | 25 | ``` 26 | 27 | This library provides support for: 28 | 29 | **Tool Providers**: 30 | 31 | 1. Verifying an LTI launch request 32 | 2. Sending LTI 1.1 Outcomes request (xml-based) 33 | 3. AspectJ launch verifiers for easy integration with Spring-web. 34 | 35 | **Tool Consumers**s: 36 | 37 | 1. Creating a valid LTI launch request 38 | 39 | Some exploratory support for LTIv2: 40 | 41 | 1. Parsing Tool Profiles 42 | 2. Validating Tool Capabilities & Services 43 | 44 | 45 | LTI Providers: 46 | ---- 47 | 48 | **Verifying an LTI launch request.** 49 | 50 | ```java 51 | HttpServletRequest request; // java servlet request 52 | LtiVerifier ltiVerifier = new LtiOauthVerifier(); 53 | String key = request.getParameter("oauth_consumer_key"); 54 | String secret = // retrieve corresponding secret for key from db 55 | LtiVerificationResult ltiResult = ltiVerifier.verify(request, secret); 56 | ``` 57 | 58 | **Sending LTI 1.1 Outcomes request (xml-based).** 59 | 60 | ```java 61 | //send Request directly 62 | IMSPOXRequest.sendReplaceResult(url, key, secret, sourcedid, score); 63 | 64 | //or build the request to send later: 65 | HttpPost request = IMSPOXRequest.buildReplaceResult(url, key, secret, sourcedid, score, true); 66 | ``` 67 | 68 | **AspectJ launch verifiers for easy integration with Spring-web.** 69 | 70 | Spring Controller (LTI Producer): 71 | ```java 72 | @Lti 73 | @RequestMapping(value = "/lti", method = RequestMethod.POST) 74 | public String ltiEntry(HttpServletRequest request, LtiVerificationResult result) { 75 | if(!result.getSuccess()){ 76 | return "error"; 77 | } else { 78 | return "success"; 79 | } 80 | } 81 | ``` 82 | 83 | KeyService Implementation: 84 | ```java 85 | public class MockKeyService implements LtiKeySecretService { 86 | public String getSecretForKey(String key) { 87 | return "secret"; 88 | } 89 | } 90 | ``` 91 | 92 | Spring Context xml: 93 | ```xml 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | ``` 102 | 103 | LTI Consumers: 104 | ---- 105 | 106 | **Building an LTI launch request.** 107 | 108 | ```java 109 | LtiSigner ltiSigner = new LtiOauthSigner(); 110 | Map signedParameters = signParameters(parameters, key, secret, url, "POST"); 111 | ``` 112 | 113 | Contributing: 114 | ------------- 115 | 116 | You are welcome to contribute code to this work, but before we accept your PR, must sign a Contributors Agreement. To request a Contributors Agreement, please contact info@imsglobal.org 117 | 118 | 119 | © 2014 IMS Global Learning Consortium, Inc. All Rights Reserved. 120 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | IMS BasicLTI Utilities 6 | BasicLTI Utilities are a set of utility classes to aid in the development of BasicLTI consumers and 7 | providers. They deal with much of the heavy lifting and make the process more opaque to the developer. 8 | 9 | https://github.com/IMSGlobal/basiclti-util-java 10 | org.imsglobal 11 | basiclti-util 12 | 1.2.1-SNAPSHOT 13 | 14 | IMS Global Learning Consortium 15 | www.imsglobal.org/ 16 | 17 | 2009 18 | jar 19 | 20 | 21 | The Apache Software License, Version 2.0 22 | http://www.apache.org/licenses/LICENSE-2.0.txt 23 | 24 | 25 | 26 | 27 | Charles Severance 28 | csev@umich.edu 29 | University of Michigan 30 | https://www.si.umich.edu/ 31 | 32 | 33 | Paul Gray 34 | pfbgray@gmail.com 35 | Learning Objects 36 | http://www.learningobjects.com/ 37 | 38 | 39 | Braden Anderson 40 | braden@instructure.com 41 | Instructure 42 | http://www.instructure.com/ 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-javadoc-plugin 51 | 2.10.1 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | scm:git:git@github.com:IMSGlobal/basiclti-util-java.git 60 | scm:git:git@github.com:IMSGlobal/basiclti-util-java.git 61 | git@github.com:IMSGlobal/basiclti-util-java.git 62 | HEAD 63 | 64 | 65 | 66 | 1.5.5 67 | UTF-8 68 | 69 | 70 | 71 | 72 | javax.servlet 73 | servlet-api 74 | 2.5 75 | provided 76 | 77 | 78 | org.apache.commons 79 | commons-lang3 80 | 3.1 81 | 82 | 83 | com.googlecode.json-simple 84 | json-simple 85 | 1.1 86 | 87 | 88 | org.codehaus.jackson 89 | jackson-mapper-asl 90 | 1.9.3 91 | 92 | 93 | org.codehaus.jackson 94 | jackson-core-asl 95 | 1.9.3 96 | jar 97 | 98 | 99 | net.oauth.core 100 | oauth-provider 101 | 20100527 102 | 103 | 104 | org.apache.httpcomponents 105 | httpclient 106 | 4.0.1 107 | 108 | 109 | oauth.signpost 110 | signpost-core 111 | 1.2.1.2 112 | 113 | 114 | oauth.signpost 115 | signpost-commonshttp4 116 | 1.2.1.2 117 | 118 | 119 | com.fasterxml.jackson.core 120 | jackson-core 121 | 2.4.1 122 | 123 | 124 | com.fasterxml.jackson.core 125 | jackson-annotations 126 | 2.4.1 127 | 128 | 129 | com.fasterxml.jackson.core 130 | jackson-databind 131 | 2.4.1 132 | 133 | 134 | junit 135 | junit 136 | 4.5 137 | jar 138 | test 139 | 140 | 141 | org.aspectj 142 | aspectjweaver 143 | 1.7.4 144 | jar 145 | 146 | 147 | org.powermock 148 | powermock-module-junit4 149 | ${powermock.version} 150 | test 151 | 152 | 153 | org.powermock 154 | powermock-api-mockito 155 | ${powermock.version} 156 | test 157 | 158 | 159 | commons-io 160 | commons-io 161 | 2.4 162 | jar 163 | 164 | 165 | 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-compiler-plugin 171 | 3.1 172 | 173 | 1.7 174 | 1.7 175 | 176 | 177 | 178 | org.apache.maven.plugins 179 | maven-source-plugin 180 | 2.2.1 181 | 182 | 183 | attach-sources 184 | 185 | jar-no-fork 186 | 187 | 188 | 189 | 190 | 191 | org.apache.maven.plugins 192 | maven-javadoc-plugin 193 | 2.9.1 194 | 195 | 196 | attach-javadocs 197 | 198 | jar 199 | 200 | 201 | 202 | 203 | 204 | org.apache.maven.plugins 205 | maven-gpg-plugin 206 | 207 | 208 | sign-artifacts 209 | verify 210 | 211 | sign 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | ossrh 222 | https://oss.sonatype.org/content/repositories/snapshots 223 | 224 | 225 | ossrh 226 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 227 | 228 | 229 | 230 | 231 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/aspect/Lti.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package org.imsglobal.aspect; 8 | 9 | import java.lang.annotation.Documented; 10 | import java.lang.annotation.ElementType; 11 | import java.lang.annotation.Retention; 12 | import java.lang.annotation.RetentionPolicy; 13 | import java.lang.annotation.Target; 14 | 15 | /** 16 | * 17 | * @author pgray 18 | */ 19 | @Documented 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Target(value = ElementType.METHOD) 22 | public @interface Lti { 23 | 24 | boolean rejectIfBad() default true; 25 | 26 | String keyService() default ""; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/aspect/LtiKeySecretService.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.aspect; 2 | 3 | /** 4 | * Created by paul on 5/28/14. 5 | */ 6 | public interface LtiKeySecretService { 7 | 8 | public String getSecretForKey(String key); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/aspect/LtiLaunchVerifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.imsglobal.aspect; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.annotation.Around; 11 | import org.aspectj.lang.annotation.Aspect; 12 | import org.imsglobal.lti.launch.LtiVerificationResult; 13 | import org.imsglobal.lti.launch.LtiVerifier; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * 20 | * @author pgray 21 | */ 22 | @Aspect 23 | public class LtiLaunchVerifier { 24 | 25 | public LtiKeySecretService keyService; 26 | 27 | public LtiVerifier ltiVerifier; 28 | 29 | public LtiLaunchVerifier(LtiKeySecretService keyService, LtiVerifier ltiVerifier) { 30 | this.keyService = keyService; 31 | this.ltiVerifier = ltiVerifier; 32 | } 33 | 34 | @Around("@annotation(launch)") 35 | public Object verifyLtiLaunch(ProceedingJoinPoint pjp, Lti launch) throws Throwable { 36 | HttpServletRequest request = null; 37 | for (Object arg : pjp.getArgs()) { 38 | if (HttpServletRequest.class.isInstance(arg)) { 39 | request = (HttpServletRequest) arg; 40 | } 41 | } 42 | if(request == null){ 43 | throw new IllegalStateException(getErrorMessageForArgumentClass("HttpServletRequest", pjp.getSignature().toLongString())); 44 | } 45 | 46 | String oauthSecret = keyService.getSecretForKey(request.getParameter("oauth_consumer_key")); 47 | LtiVerificationResult ltiResult = ltiVerifier.verify(request, oauthSecret);//BasicLTIUtil.validateMessage(request, request.getRequestURL().toString(), oauthSecret); 48 | 49 | Boolean ltiVerificationResultExists = false; 50 | //This array will hold the arguments to the join point, so we can pass them along to the advised function. 51 | List args = new ArrayList<>(pjp.getArgs().length); 52 | for (Object arg : pjp.getArgs()) { 53 | if (arg != null && arg.getClass().equals(LtiVerificationResult.class)) { 54 | args.add(ltiResult); 55 | ltiVerificationResultExists = true; 56 | } else { 57 | args.add(arg); 58 | } 59 | } 60 | if(!ltiVerificationResultExists){ 61 | throw new IllegalStateException(getErrorMessageForArgumentClass("LtiVerificationResult", pjp.getSignature().toLongString())); 62 | } 63 | 64 | return pjp.proceed(args.toArray()); 65 | } 66 | 67 | public String getErrorMessageForArgumentClass(String argumentClass, String signature){ 68 | return "The LtiLaunchVerifier instance cannot find the " + argumentClass + " argument on method: " + signature + ", are you sure it was declared?"; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/json/IMSJSONRequest.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.json; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintWriter; 5 | import java.io.StringWriter; 6 | import java.net.URLDecoder; 7 | import java.security.MessageDigest; 8 | import java.util.LinkedHashMap; 9 | import java.util.Map; 10 | import java.util.TreeMap; 11 | import java.util.logging.Logger; 12 | 13 | import javax.servlet.ServletInputStream; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | import net.oauth.OAuthAccessor; 18 | import net.oauth.OAuthConsumer; 19 | import net.oauth.OAuthMessage; 20 | import net.oauth.OAuthValidator; 21 | import net.oauth.SimpleOAuthValidator; 22 | import net.oauth.server.OAuthServlet; 23 | import net.oauth.signature.OAuthSignatureMethod; 24 | import org.apache.commons.codec.binary.Base64; 25 | 26 | import org.json.simple.JSONValue; 27 | 28 | public class IMSJSONRequest { 29 | 30 | private final static Logger Log = Logger.getLogger(IMSJSONRequest.class .getName()); 31 | 32 | public final static String STATUS = "status"; 33 | public final static String STATUS_CODE = "code"; 34 | public final static String STATUS_DESCRIPTION = "description"; 35 | 36 | public final static String CODE_MAJOR_SUCCESS = "success"; 37 | public final static String CODE_MAJOR_FAILURE = "failure"; 38 | public final static String CODE_MAJOR_UNSUPPORTED = "unsupported"; 39 | 40 | public String postBody = null; 41 | private String header = null; 42 | private String oauth_body_hash = null; 43 | private String oauth_consumer_key = null; 44 | 45 | public boolean valid = false; 46 | public String errorMessage = null; 47 | public String base_string = null; 48 | 49 | private static final String APPLICATION_JSON = "application/json"; 50 | 51 | public String getOAuthConsumerKey() 52 | { 53 | return oauth_consumer_key; 54 | } 55 | 56 | public String getPostBody() 57 | { 58 | return postBody; 59 | } 60 | 61 | // Normal Constructor 62 | public IMSJSONRequest(String oauth_consumer_key, String oauth_secret, HttpServletRequest request) 63 | { 64 | loadFromRequest(request); 65 | if ( ! valid ) return; 66 | validateRequest(oauth_consumer_key, oauth_secret, request); 67 | } 68 | 69 | // Constructor for delayed validation 70 | public IMSJSONRequest(HttpServletRequest request) 71 | { 72 | loadFromRequest(request); 73 | } 74 | 75 | // Constructor for testing... 76 | public IMSJSONRequest(String bodyString) 77 | { 78 | postBody = bodyString; 79 | } 80 | 81 | // Load but do not check the authentication 82 | @SuppressWarnings("deprecation") 83 | public void loadFromRequest(HttpServletRequest request) 84 | { 85 | header = request.getHeader("Authorization"); 86 | System.out.println("Header: "+header); 87 | oauth_body_hash = null; 88 | if ( header != null ) { 89 | if (header.startsWith("OAuth ")) header = header.substring(5); 90 | String [] parms = header.split(","); 91 | for ( String parm : parms ) { 92 | parm = parm.trim(); 93 | if ( parm.startsWith("oauth_body_hash=") ) { 94 | String [] pieces = parm.split("\""); 95 | if ( pieces.length == 2 ) oauth_body_hash = URLDecoder.decode(pieces[1]); 96 | } 97 | if ( parm.startsWith("oauth_consumer_key=") ) { 98 | String [] pieces = parm.split("\""); 99 | if ( pieces.length == 2 ) oauth_consumer_key = URLDecoder.decode(pieces[1]); 100 | } 101 | } 102 | } 103 | 104 | if ( oauth_body_hash == null ) { 105 | errorMessage = "Did not find oauth_body_hash"; 106 | Log.info(errorMessage+"\n"+header); 107 | return; 108 | } 109 | 110 | System.out.println("OBH="+oauth_body_hash); 111 | byte[] buf = new byte[1024]; 112 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 113 | int chars = 0; 114 | try { 115 | ServletInputStream is = request.getInputStream(); 116 | int readNum; 117 | do { 118 | readNum = is.read(buf); 119 | if (readNum>0) { 120 | bos.write(buf, 0, readNum); 121 | chars = chars + readNum; 122 | // We dont' want a DOS 123 | if ( chars > 10000000 ) { 124 | errorMessage = "Message body size exceeded"; 125 | return; 126 | } 127 | } 128 | } while (readNum>=0); 129 | } catch(Exception e) { 130 | errorMessage = "Could not read message body:"+e.getMessage(); 131 | return; 132 | } 133 | 134 | byte[] bytes = bos.toByteArray(); 135 | 136 | try { 137 | postBody = new String(bytes, "UTF-8"); 138 | MessageDigest md = MessageDigest.getInstance("SHA1"); 139 | md.update(bytes); 140 | byte[] output = Base64.encodeBase64(md.digest()); 141 | String hash = new String(output); 142 | System.out.println("HASH="+hash+" bytes="+bytes.length); 143 | if ( ! hash.equals(oauth_body_hash) ) { 144 | errorMessage = "Body hash does not match. bytes="+bytes.length; 145 | System.out.println(postBody); 146 | return; 147 | } 148 | } catch (Exception e) { 149 | errorMessage = "Could not compute body hash. bytes="+bytes.length; 150 | return; 151 | } 152 | valid = true; // So far we are valid 153 | } 154 | 155 | // Assumes data is all loaded 156 | public void validateRequest(String oauth_consumer_key, String oauth_secret, HttpServletRequest request) 157 | { 158 | validateRequest(oauth_consumer_key, oauth_secret, request, null) ; 159 | } 160 | 161 | public void validateRequest(String oauth_consumer_key, String oauth_secret, HttpServletRequest request, String URL) 162 | { 163 | valid = false; 164 | OAuthMessage oam = OAuthServlet.getMessage(request, URL); 165 | OAuthValidator oav = new SimpleOAuthValidator(); 166 | OAuthConsumer cons = new OAuthConsumer("about:blank#OAuth+CallBack+NotUsed", 167 | oauth_consumer_key, oauth_secret, null); 168 | 169 | OAuthAccessor acc = new OAuthAccessor(cons); 170 | 171 | try { 172 | base_string = OAuthSignatureMethod.getBaseString(oam); 173 | } catch (Exception e) { 174 | base_string = null; 175 | } 176 | 177 | try { 178 | oav.validateMessage(oam,acc); 179 | } catch(Exception e) { 180 | errorMessage = "Launch fails OAuth validation: "+e.getMessage(); 181 | return; 182 | } 183 | valid = true; 184 | } 185 | 186 | public boolean inArray(final String [] theArray, final String theString) 187 | { 188 | if ( theString == null ) return false; 189 | for ( String str : theArray ) { 190 | if ( theString.equals(str) ) return true; 191 | } 192 | return false; 193 | } 194 | 195 | public static Map getStatusUnsupported(String desc) 196 | { 197 | return getStatus(desc, CODE_MAJOR_UNSUPPORTED); 198 | } 199 | 200 | public static Map getStatusFailure(String desc) 201 | { 202 | return getStatus(desc, CODE_MAJOR_FAILURE); 203 | } 204 | 205 | public static Map getStatusSuccess(String desc) 206 | { 207 | return getStatus(desc, CODE_MAJOR_SUCCESS); 208 | } 209 | 210 | public static Map getStatus(String description, String major) 211 | { 212 | Map retval = new LinkedHashMap(); 213 | retval.put(STATUS_CODE,major); 214 | retval.put(STATUS_DESCRIPTION,description); 215 | return retval; 216 | } 217 | 218 | /* IMS JSON version of Errors - does the complet request - returns the JSON in case 219 | the code above us wants to log it. */ 220 | @SuppressWarnings("static-access") 221 | public static String doErrorJSON(HttpServletRequest request,HttpServletResponse response, 222 | IMSJSONRequest json, String message, Exception e) 223 | throws java.io.IOException 224 | { 225 | response.setContentType(APPLICATION_JSON); 226 | Map jsonResponse = new TreeMap(); 227 | 228 | Map status = null; 229 | if ( json == null ) { 230 | status = IMSJSONRequest.getStatusFailure(message); 231 | } else { 232 | status = json.getStatusFailure(message); 233 | if ( json.base_string != null ) { 234 | jsonResponse.put("base_string", json.base_string); 235 | } 236 | } 237 | jsonResponse.put(IMSJSONRequest.STATUS, status); 238 | if ( e != null ) { 239 | jsonResponse.put("exception", e.getLocalizedMessage()); 240 | try { 241 | StringWriter sw = new StringWriter(); 242 | PrintWriter pw = new PrintWriter(sw, true); 243 | e.printStackTrace(pw); 244 | pw.flush(); 245 | sw.flush(); 246 | jsonResponse.put("traceback", sw.toString() ); 247 | } catch ( Exception f ) { 248 | jsonResponse.put("traceback", f.getLocalizedMessage()); 249 | } 250 | } 251 | String jsonText = JSONValue.toJSONString(jsonResponse); 252 | PrintWriter out = response.getWriter(); 253 | out.println(jsonText); 254 | return jsonText; 255 | } 256 | 257 | /** Unit Tests */ 258 | static final String inputTestData = "\n" + 259 | "\n" + 260 | "\n" + 261 | "\n" + 262 | "V1.0\n" + 263 | "999999123\n" + 264 | "\n" + 265 | "\n" + 266 | "\n" + 267 | "\n" + 268 | "\n" + 269 | "\n" + 270 | "3124567\n" + 271 | "\n" + 272 | "\n" + 273 | "\n" + 274 | "en-us\n" + 275 | "A\n" + 276 | "\n" + 277 | "\n" + 278 | "\n" + 279 | "\n" + 280 | "\n" + 281 | ""; 282 | 283 | public static void runTest() { 284 | /* 285 | System.out.println("Runnig test."); 286 | IMSJSONRequest pox = new IMSJSONRequest(inputTestData); 287 | System.out.println("Version = "+pox.getHeaderVersion()); 288 | System.out.println("Operation = "+pox.getOperation()); 289 | Map bodyMap = pox.getBodyMap(); 290 | String guid = bodyMap.get("/resultRecord/sourcedGUID/sourcedId"); 291 | System.out.println("guid="+guid); 292 | String grade = bodyMap.get("/resultRecord/result/resultScore/textString"); 293 | System.out.println("grade="+grade); 294 | 295 | String desc = "Message received and validated operation="+pox.getOperation()+ 296 | " guid="+guid+" grade="+grade; 297 | 298 | String output = pox.getResponseUnsupported(desc); 299 | System.out.println("---- Unsupported ----"); 300 | System.out.println(output); 301 | 302 | Properties props = new Properties(); 303 | props.setProperty("fred","zap"); 304 | props.setProperty("sam",IMSPOXRequest.MINOR_IDALLOC); 305 | System.out.println("---- Generate Log Error ----"); 306 | output = pox.getResponseFailure(desc,props); 307 | System.out.println("---- Failure ----"); 308 | System.out.println(output); 309 | 310 | 311 | 312 | Map theMap = new TreeMap (); 313 | theMap.put("/readMembershipResponse/membershipRecord/sourcedId", "123course456"); 314 | 315 | List> lm = new ArrayList>(); 316 | Map mm = new TreeMap(); 317 | mm.put("/personSourcedId","123user456"); 318 | mm.put("/role/roleType","Learner"); 319 | lm.add(mm); 320 | 321 | mm = new TreeMap(); 322 | mm.put("/personSourcedId","789user123"); 323 | mm.put("/role/roleType","Instructor"); 324 | lm.add(mm); 325 | theMap.put("/readMembershipResponse/membershipRecord/membership/member", lm); 326 | 327 | String theXml = XMLMap.getXMLFragment(theMap, true); 328 | // System.out.println("th="+theXml); 329 | output = pox.getResponseSuccess(desc,theXml); 330 | System.out.println("---- Success String ----"); 331 | System.out.println(output); 332 | */ 333 | } 334 | 335 | /* 336 | 337 | roleType: 338 | Learner 339 | Instructor 340 | ContentDeveloper 341 | Member 342 | Manager 343 | Mentor 344 | Administrator 345 | TeachingAssistant 346 | 347 | fieldType: 348 | Boolean 349 | Integer 350 | Real 351 | String 352 | 353 | 355 | 356 | GUID.TYPE 357 | 358 | GUID.TYPE 359 | MEMBERSHIPIDTYPE.TYPE 360 | 361 | GUID.TYPE 362 | 363 | STRING 364 | STRING 365 | 366 | DATETIME 367 | DATETIME 368 | BOOLEAN 369 | 370 | LANGUAGESET.TYPE 371 | STRING 372 | 373 | 374 | STATUS.TYPE 375 | DATETIME 376 | GUID.TYPE 377 | 378 | 379 | STRING 380 | FIELDTYPE.TYPE 381 | STRING 382 | 383 | 384 | 385 | 386 | STRING 387 | FIELDTYPE.TYPE 388 | STRING 389 | 390 | 391 | 392 | 393 | INTEGER 394 | GUID.TYPE 395 | 396 | 397 | 398 | */ 399 | } 400 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/BasicLTIConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 IMS GLobal Learning Consortium 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | */ 16 | package org.imsglobal.lti; 17 | 18 | public class BasicLTIConstants { 19 | /** 20 | * context_id=8213060-006f-27b2066ac545 21 | *

22 | * This is an opaque identifier that uniquely identifies the context that 23 | * contains the link being launched. 24 | */ 25 | public static final String CONTEXT_ID = "context_id"; 26 | /** 27 | * context_label=SI182 28 | *

29 | * A label for the context - intended to fit in a column. 30 | */ 31 | public static final String CONTEXT_LABEL = "context_label"; 32 | /** 33 | * context_title=Design of Personal Environments 34 | *

35 | * A title of the context - it should be about the length of a line. 36 | */ 37 | public static final String CONTEXT_TITLE = "context_title"; 38 | 39 | /** 40 | * context_type=CourseSection 41 | *

42 | * This string is a comma-separated list of URN values that identify the type 43 | * of context. At a minimum, the list MUST include a URN value drawn from the 44 | * LIS vocabulary (see Appendix A). The assumed namespace of these URNs is the 45 | * LIS vocabulary so TCs can use the handles when the intent is to refer to an 46 | * LIS context type. If the TC wants to include a context type from another 47 | * namespace, a fully-qualified URN should be used. 48 | */ 49 | public static final String CONTEXT_TYPE = "context_type"; 50 | public static final String CONTEXT_TYPE_COURSE_OFFERING = "CourseOffering"; 51 | public static final String CONTEXT_TYPE_COURSE_SECTION = "CourseSection"; 52 | public static final String CONTEXT_TYPE_COURSE_TEMPLATE = "CourseTemplate"; 53 | public static final String CONTEXT_TYPE_GROUP = "Group"; 54 | 55 | /** 56 | * ext_param=value 57 | *

58 | * Systems can add their own values to the launch but should prefix 59 | * any extensions with "ext_". 60 | */ 61 | public static final String EXTENSION_PREFIX = "ext_"; 62 | /** 63 | * custom_keyname=value 64 | *

65 | * The creator of a Basic LTI link can add custom key/value parameters to a 66 | * launch which are to be included with the launch of the Basic LTI link. The 67 | * Common Cartridge section below describes how these parameters are 68 | * represented when storing custom parameters in a Common Cartridge. 69 | *

70 | * When there are custom name / value parameters in the launch, a POST 71 | * parameter is included for each custom parameter. The parameter names are 72 | * mapped to lower case and any character that is neither a number nor letter 73 | * in a parameter name is replaced with an "underscore". So if a custom entry 74 | * was as follows: 75 | *

76 | * Review:Chapter=1.2.56 77 | *

78 | * Would map to: custom_review_chapter=1.2.56 79 | *

80 | * Creators of Basic LTI links would be well served to limit their parameter 81 | * names to lower case and to use no punctuation other than underscores. If 82 | * these custom parameters are included in the Basic LTI link, the TC must 83 | * include them in the launch data or the TP may fail to function. 84 | */ 85 | public static final String CUSTOM_PREFIX = "custom_"; 86 | /** 87 | * Parameters with the OAuth prefix are also acceptible. 88 | */ 89 | public static final String OAUTH_PREFIX = "oauth_"; 90 | /** 91 | * launch_presentation_document_target=iframe 92 | *

93 | * The value should be either 'frame', 'iframe' or 'window'. This field 94 | * communicates the kind of browser window/frame where the TC has launched the 95 | * tool. 96 | */ 97 | public static final String LAUNCH_PRESENTATION_DOCUMENT_TARGET = "launch_presentation_document_target"; 98 | /** 99 | * launch_presentation_height=240 100 | *

101 | * The height of the window or frame where the content from the tool will be 102 | * displayed. 103 | */ 104 | public static final String LAUNCH_PRESENTATION_HEIGHT = "launch_presentation_height"; 105 | /** 106 | * launch_presentation_locale=en_US_variant 107 | *

108 | * Language, country and variant separated by underscores. Language is the 109 | * lower-case, two-letter code as defined by ISO-639 (list of codes available 110 | * at http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt). Country is the 111 | * upper-case, two-letter code as defined by ISO-3166 (list of codes available 112 | * at http://www.chemie.fu- berlin.de/diverse/doc/ISO_3166.html). Country and 113 | * variant codes are optional. 114 | */ 115 | public static final String LAUNCH_PRESENTATION_LOCALE = "launch_presentation_locale"; 116 | /** 117 | * launch_presentation_return_url=http://lmsng.school.edu/portal/123/page/988/ 118 | *

119 | * Fully qualified URL where the TP can redirect the user back to the TC interface. This 120 | * URL can be used once the TP is finished or if the TP cannot start or has some 121 | * technical difficulty. In the case of an error, the TP may add a parameter called 122 | * lti_errormsg that includes some detail as to the nature of the error. The 123 | * lti_errormsg value should make sense if displayed to the user. If the tool has 124 | * displayed a message to the end user and only wants to give the TC a message to log, 125 | * use the parameter lti_errorlog instead of lti_errormsg. If the tool is terminating 126 | * normally, and wants a message displayed to the user it can include a text message as 127 | * the lti_msg parameter to the return URL. If the tool is terminating normally and 128 | * wants to give the TC a message to log, use the parameter lti_log. This data should be 129 | * sent on the URL as a GET - so the TP should take care to keep the overall length of 130 | * the parameters small enough to fit within the limitations of a GET request. 131 | */ 132 | public static final String LAUNCH_PRESENTATION_RETURN_URL = "launch_presentation_return_url"; 133 | /** 134 | * launch_presentation_width=320 135 | *

136 | * The width of the window or frame where the content from the tool will be 137 | * displayed. 138 | */ 139 | public static final String LAUNCH_PRESENTATION_WIDTH = "launch_presentation_width"; 140 | /** 141 | * launch_presentation_css_url=http://www.toolconsumer.url/path/to/lti.css 142 | *

143 | * This points to a fully qualified URL for a CSS which can be used to style the tool. 144 | * There are no officially defined CSS classes for this file, but the Consumer can 145 | * apply styles to paragraphs, body, and the various HTML elements. It is up to the 146 | * tool as to whether this CSS is used or not, and in what order this is included relative 147 | * to the tool-specific CSS. 148 | */ 149 | public static final String LAUNCH_PRESENTATION_CSS_URL = "launch_presentation_css_url"; 150 | /** 151 | * lis_person_contact_email_primary=user@school.edu 152 | *

153 | * These fields contain information about the user account that is performing 154 | * this launch. The names of these data items are taken from LIS. The precise 155 | * meaning of the content in these fields is defined by LIS. 156 | */ 157 | public static final String LIS_PERSON_CONTACT_EMAIL_PRIMARY = "lis_person_contact_email_primary"; 158 | /** 159 | * lis_person_name_family=Public 160 | *

161 | * These fields contain information about the user account that is performing 162 | * this launch. The names of these data items are taken from LIS. The precise 163 | * meaning of the content in these fields is defined by LIS. 164 | */ 165 | public static final String LIS_PERSON_NAME_FAMILY = "lis_person_name_family"; 166 | /** 167 | * lis_person_name_full=Jane Q. Public 168 | *

169 | * These fields contain information about the user account that is performing 170 | * this launch. The names of these data items are taken from LIS. The precise 171 | * meaning of the content in these fields is defined by LIS. 172 | */ 173 | public static final String LIS_PERSON_NAME_FULL = "lis_person_name_full"; 174 | /** 175 | * lis_person_name_given=Jane 176 | *

177 | * These fields contain information about the user account that is performing 178 | * this launch. The names of these data items are taken from LIS. The precise 179 | * meaning of the content in these fields is defined by LIS. 180 | */ 181 | public static final String LIS_PERSON_NAME_GIVEN = "lis_person_name_given"; 182 | 183 | /** 184 | * lis_person_sourcedid=school.edu:user 185 | *

186 | * This field contains the LIS identifier for the user account that is 187 | * performing this launch. The example syntax of "school:user" 188 | * is not the required format – lis_person_sourcedid is simply a 189 | * unique identifier (i.e., a normalized string). This field 190 | * is optional and its content and meaning are defined by LIS. 191 | */ 192 | public static final String LIS_PERSON_SOURCEDID = "lis_person_sourcedid"; 193 | 194 | /** 195 | * lis_course_offering_sourcedid=school.edu:SI182-F08 196 | * lis_course_section_sourcedid=school.edu:SI182-001-F08 197 | *

198 | * These fields contain LIS course identifiers associated with the 199 | * context of this launch. These fields are optional and their 200 | * content and meaning are defined by LIS. 201 | */ 202 | public static final String LIS_COURSE_OFFERING_SOURCEDID = "lis_course_offering_sourcedid"; 203 | public static final String LIS_COURSE_SECTION_SOURCEDID = "lis_course_section_sourcedid"; 204 | 205 | /** 206 | * lis_outcome_service_url=http://lmsng.school.edu/service/ltiout/ 207 | *

208 | * This field should be no more than 1023 characters long and the TP 209 | * should assume that the URL might change from launch to launch and 210 | * allow for the fact that a TC might change their service URL from 211 | * launch to launch and only use the most recent value for this 212 | * parameter. The service URL may support various operations / services. 213 | * The TC will respond with a response of 'unimplemented' for services 214 | * it does not support. 215 | */ 216 | public static final String LIS_OUTCOME_SERVICE_URL = "lis_outcome_service_url"; 217 | 218 | /** 219 | * lis_result_sourcedid=83873872987329873264783687634 220 | *

221 | * This field contains an identifier that indicates the LIS Result 222 | * Identifier (if any) associated with this launch. This field is 223 | * optional and its content and meaning is defined by LIS. 224 | */ 225 | public static final String LIS_RESULT_SOURCEDID = "lis_result_sourcedid"; 226 | 227 | /** 228 | * lti_message_type=basic-lti-launch-request 229 | *

230 | * This indicates that this is a Basic LTI Launch Message. This allows a TP to 231 | * accept a number of different LTI message types at the same launch URL. This 232 | * parameter is required. 233 | */ 234 | public static final String LTI_MESSAGE_TYPE = "lti_message_type"; 235 | public static final String LTI_MESSAGE_TYPE_TOOLPROXYREGISTRATIONREQUEST = "ToolProxyRegistrationRequest"; 236 | public static final String LTI_MESSAGE_TYPE_TOOLPROXY_RE_REGISTRATIONREQUEST = "ToolProxyReregistrationRequest"; 237 | public static final String LTI_MESSAGE_TYPE_BASICLTILAUNCHREQUEST = "basic-lti-launch-request"; 238 | /** 239 | * lti_version=LTI-1p0 240 | *

241 | * This indicates which version of the specification is being used for this 242 | * particular message. This parameter is required. 243 | */ 244 | public static final String LTI_VERSION = "lti_version"; 245 | public static final String LTI_VERSION_1 = "LTI-1p0"; 246 | public static final String LTI_VERSION_2 = "LTI-2p0"; 247 | 248 | /** 249 | * tool_consumer_info_product_family_code=desire2learn 250 | *

251 | * In order to better assist tools in using extensions and also making their user 252 | * interface fit into the TC's user interface that they are being called from, 253 | * each TC is encouraged to include the this parameter. Possible example values 254 | * for this field might be: 255 | * 256 | * learn 257 | * desire2learn 258 | * sakai 259 | * eracer 260 | * olat 261 | * webct 262 | * This parameter is optional but recommended. 263 | */ 264 | public static final String TOOL_CONSUMER_INFO_PRODUCT_FAMILY_CODE = "tool_consumer_info_product_family_code"; 265 | 266 | /** 267 | * tool_consumer_info_version=9.2.4 268 | *

269 | * This field should have a major release number followed by a period. The format of the minor release is flexible. Possible vaues for this field might be: 270 | * 271 | * 9.1.7081 272 | * 2.8-01 273 | * 7.1 274 | * 8 275 | * The Tool Provider should be flexible when parsing this field. This parameter is optional but recommended. 276 | 277 | */ 278 | public static final String TOOL_CONSUMER_INFO_VERSION = "tool_consumer_info_version"; 279 | 280 | /** 281 | * resource_link_id=88391-e1919-bb3456 282 | *

283 | * This is an opaque unique identifier that the TC guarantees will be unique 284 | * within the TC for every placement of the link. If the tool / activity is 285 | * placed multiple times in the same context, each of those placements will be 286 | * distinct. This value will also change if the item is exported from one 287 | * system or context and imported into another system or context. This 288 | * parameter is required. 289 | */ 290 | public static final String RESOURCE_LINK_ID = "resource_link_id"; 291 | 292 | /** 293 | * resource_link_title=My Weekly Wiki 294 | *

295 | * A title for the resource. This is the clickable text that appears 296 | * in the link. This parameter is recommended. 297 | */ 298 | public static final String RESOURCE_LINK_TITLE = "resource_link_title"; 299 | 300 | /** 301 | * resource_link_description=… 302 | *

303 | * A plain text description of the link’s destination, suitable for 304 | * display alongside the link. Typically no more than several lines 305 | * long. This parameter is optional. 306 | */ 307 | public static final String RESOURCE_LINK_DESCRIPTION = "resource_link_description"; 308 | 309 | /** 310 | * roles=Instructor,Student 311 | *

312 | * A comma-separated list of URN values for roles. If this list is non-empty, 313 | * it should contain at least one role from the LIS System Role, LIS 314 | * Institution Role, or LIS Context Role vocabularies (See Appendix A). The 315 | * assumed namespace of these URNs is the LIS vocabulary of LIS Context Roles 316 | * so TCs can use the handles when the intent is to refer to an LIS context 317 | * role. If the TC wants to include a role from another namespace, a 318 | * fully-qualified URN should be used. Usage of roles from non-LIS 319 | * vocabularies is discouraged as it may limit interoperability. This 320 | * parameter is recommended. 321 | */ 322 | public static final String ROLES = "roles"; 323 | 324 | /** 325 | * tc_profile_url=http://... 326 | *

327 | * This URL specifies the address where the Tool Provider can retrieve 328 | * the Tool Consumer Profile. This URL must be retrievable by a GET 329 | * request by the Tool Provider. If the URL is protected from retrieval 330 | * in general, the Tool Consumer must append the necessary parameters to 331 | * allow the Tool Provider to retrieve the URL with nothing more than 332 | * a GET request. It is legal for this URL to contain a security token 333 | * that is changed for each ToolProxyRegistrationRequest so the Tool 334 | * Provider must retrieve the tc_profile_url on each request. 335 | */ 336 | public static final String TC_PROFILE_URL = "tc_profile_url"; 337 | 338 | /** 339 | * tool_consumer_instance_contact_email=System.Admin@school.edu 340 | *

341 | * An email contact for the TC instance. 342 | */ 343 | public static final String TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL = "tool_consumer_instance_contact_email"; 344 | /** 345 | * tool_consumer_instance_description=University of School (LMSng) 346 | *

347 | * This is a user visible field - it should be about the length of a line. 348 | */ 349 | public static final String TOOL_CONSUMER_INSTANCE_DESCRIPTION = "tool_consumer_instance_description"; 350 | // global settings 351 | /** 352 | * tool_consumer_instance_guid=lmsng.school.edu 353 | *

354 | * This is a key to be used when setting a TC-wide password. The TP uses this 355 | * as a key to look up the TC-wide secret when validating a message. A common 356 | * practice is to use the DNS of the organization or the DNS of the TC 357 | * instance. If the organization has multiple TC instances, then the best 358 | * practice is to prefix the domain name with a locally unique identifier for 359 | * the TC instance. This parameter is recommended. 360 | */ 361 | public static final String TOOL_CONSUMER_INSTANCE_GUID = "tool_consumer_instance_guid"; 362 | /** 363 | * tool_consumer_instance_name=SchoolU 364 | *

365 | * This is a user visible field - it should be about the length of a column. 366 | */ 367 | public static final String TOOL_CONSUMER_INSTANCE_NAME = "tool_consumer_instance_name"; 368 | /** 369 | * Missing from implementation guide. Needs documentation. Not required, but 370 | * "tasty". 371 | */ 372 | public static final String TOOL_CONSUMER_INSTANCE_URL = "tool_consumer_instance_url"; 373 | 374 | /** 375 | * user_id=0ae836b9-7fc9-4060-006f-27b2066ac545 376 | *

377 | * Uniquely identifies the user. This should not contain any identifying 378 | * information for the user. Best practice is that this field should be a 379 | * TC-generated long-term "primary key" to the user record - not the logical 380 | * key. This parameter is recommended. 381 | */ 382 | public static final String USER_ID = "user_id"; 383 | 384 | /** 385 | * user_image=http://my.sakai.org/direct/profile/0ae836b9-7fc9-4060-006f-27b2066ac545/image 386 | *

387 | * This attribute specifies the URI for an image of the user who launched this request. 388 | * This image is suitable for use as a "profile picture" or an avatar representing the user. 389 | * It is expected to be a relatively small graphic image file using a widely supported image 390 | * format (i.e. PNG, JPG, or GIF) with a square aspect ratio. This parameter is optional. 391 | */ 392 | public static final String USER_IMAGE = "user_image"; 393 | 394 | /** 395 | * ext_sakai_provider_eid=jsmith26 396 | *

397 | * If set, this will signal that the external application has provided an eid which 398 | * should be used preferentially. Many external applications will not have access to a user's uuid 399 | * in Sakai, so this allows integrations with those systems. 400 | * This parameter is optional and is unique to the Sakai Basic LTI provider. 401 | */ 402 | public static final String EXT_SAKAI_PROVIDER_EID = "ext_sakai_provider_eid"; 403 | 404 | /** 405 | * ext_sakai_provider_displayid=john.smith 406 | *

407 | * If set, this will indicate to an external application that the user is normally 408 | * known by this ID and when displaying the ID to the user this ID should be used instead of the 409 | * user_id and ext_sakai_provider_eid. 410 | * This parameter is optional and is unique to the Sakai Basic LTI provider. 411 | */ 412 | public static final String EXT_SAKAI_PROVIDER_DISPLAYID = "ext_sakai_provider_displayid"; 413 | 414 | /** 415 | * Utility array useful for validating property names when building launch 416 | * data. 417 | */ 418 | public static final String[] validPropertyNames = { CONTEXT_ID, 419 | CONTEXT_LABEL, CONTEXT_TITLE, CONTEXT_TYPE, 420 | LAUNCH_PRESENTATION_DOCUMENT_TARGET, LAUNCH_PRESENTATION_HEIGHT, 421 | LAUNCH_PRESENTATION_LOCALE, LAUNCH_PRESENTATION_RETURN_URL, 422 | LAUNCH_PRESENTATION_WIDTH, LIS_PERSON_CONTACT_EMAIL_PRIMARY, 423 | LAUNCH_PRESENTATION_CSS_URL, 424 | TOOL_CONSUMER_INFO_PRODUCT_FAMILY_CODE, 425 | TOOL_CONSUMER_INFO_VERSION, 426 | LIS_PERSON_NAME_FAMILY, LIS_PERSON_NAME_FULL, LIS_PERSON_NAME_GIVEN, 427 | LIS_PERSON_SOURCEDID, LIS_COURSE_OFFERING_SOURCEDID, 428 | LIS_COURSE_SECTION_SOURCEDID, 429 | LIS_OUTCOME_SERVICE_URL, LIS_RESULT_SOURCEDID, 430 | LTI_MESSAGE_TYPE, LTI_VERSION, RESOURCE_LINK_ID, 431 | RESOURCE_LINK_TITLE, RESOURCE_LINK_DESCRIPTION, ROLES, 432 | TC_PROFILE_URL, 433 | TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL, TOOL_CONSUMER_INSTANCE_DESCRIPTION, 434 | TOOL_CONSUMER_INSTANCE_GUID, TOOL_CONSUMER_INSTANCE_NAME, 435 | TOOL_CONSUMER_INSTANCE_URL, USER_ID, USER_IMAGE }; 436 | 437 | /** 438 | * The default site type to use if a site needs to be created. Can be overriden in sakai.properties or as part of the launch. 439 | * This contains a number of preconfigured roles, so that the IMS role vocabulary can be used. 440 | * See BLTI-151 441 | */ 442 | public static final String NEW_SITE_TYPE = "lti"; 443 | 444 | } 445 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiError.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | /** 4 | * Created by paul on 5/28/14. 5 | */ 6 | public class LtiError { 7 | 8 | private final String label; 9 | 10 | public static final LtiError INVALID_HASH = new LtiError("invalid_hash"); 11 | public static final LtiError TIMESTAMP_MISMATCH = new LtiError("timestamp_mismatch"); 12 | public static final LtiError BAD_REQUEST = new LtiError("bad_request"); 13 | 14 | private LtiError(String label){ 15 | this.label = label; 16 | } 17 | 18 | public String toString(){ 19 | return label; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiLaunch.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by paul on 5/28/14. 8 | */ 9 | public class LtiLaunch { 10 | 11 | private LtiUser user; 12 | 13 | private String version; 14 | private String messageType; 15 | private String resourceLinkId; 16 | 17 | private String contextId; 18 | private String launchPresentationReturnUrl; 19 | private String toolConsumerInstanceGuid; 20 | 21 | public LtiLaunch(HttpServletRequest request) { 22 | this.user = new LtiUser(request); 23 | this.version = request.getParameter("lti_version"); 24 | this.messageType = request.getParameter("lti_message_type"); 25 | this.resourceLinkId = request.getParameter("resource_link_id"); 26 | this.contextId = request.getParameter("context_id"); 27 | this.launchPresentationReturnUrl = request.getParameter("launch_presentation_return_url"); 28 | this.toolConsumerInstanceGuid = request.getParameter("tool_consumer_instance_guid"); 29 | } 30 | 31 | public LtiLaunch(Map parameters) { 32 | this.user = new LtiUser(parameters); 33 | this.version = parameters.get("lti_version"); 34 | this.messageType = parameters.get("lti_message_type"); 35 | this.resourceLinkId = parameters.get("resource_link_id"); 36 | this.contextId = parameters.get("context_id"); 37 | this.launchPresentationReturnUrl = parameters.get("launch_presentation_return_url"); 38 | this.toolConsumerInstanceGuid = parameters.get("tool_consumer_instance_guid"); 39 | } 40 | 41 | public LtiUser getUser() { 42 | return user; 43 | } 44 | 45 | public void setUser(LtiUser user) { 46 | this.user = user; 47 | } 48 | 49 | public String getVersion() { 50 | return version; 51 | } 52 | 53 | public void setVersion(String version) { 54 | this.version = version; 55 | } 56 | 57 | public String getMessageType() { 58 | return messageType; 59 | } 60 | 61 | public void setMessageType(String messageType) { 62 | this.messageType = messageType; 63 | } 64 | 65 | public String getResourceLinkId() { 66 | return resourceLinkId; 67 | } 68 | 69 | public void setResourceLinkId(String resourceLinkId) { 70 | this.resourceLinkId = resourceLinkId; 71 | } 72 | 73 | public String getContextId() { 74 | return contextId; 75 | } 76 | 77 | public void setContextId(String contextId) { 78 | this.contextId = contextId; 79 | } 80 | 81 | public String getLaunchPresentationReturnUrl() { 82 | return launchPresentationReturnUrl; 83 | } 84 | 85 | public void setLaunchPresentationReturnUrl(String launchPresentationReturnUrl) { 86 | this.launchPresentationReturnUrl = launchPresentationReturnUrl; 87 | } 88 | 89 | public String getToolConsumerInstanceGuid() { 90 | return toolConsumerInstanceGuid; 91 | } 92 | 93 | public void setToolConsumerInstanceGuid(String toolConsumerInstanceGuid) { 94 | this.toolConsumerInstanceGuid = toolConsumerInstanceGuid; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiOauthSigner.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import net.oauth.OAuthAccessor; 4 | import net.oauth.OAuthConsumer; 5 | import net.oauth.OAuthException; 6 | import net.oauth.OAuthMessage; 7 | import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer; 8 | import oauth.signpost.exception.OAuthCommunicationException; 9 | import oauth.signpost.exception.OAuthExpectationFailedException; 10 | import oauth.signpost.exception.OAuthMessageSignerException; 11 | import oauth.signpost.http.HttpParameters; 12 | import org.apache.commons.codec.binary.Base64; 13 | import org.apache.http.HttpEntity; 14 | import org.apache.http.HttpEntityEnclosingRequest; 15 | import org.apache.http.HttpRequest; 16 | import org.apache.commons.io.IOUtils; 17 | 18 | import java.io.IOException; 19 | import java.net.URISyntaxException; 20 | import java.net.URLEncoder; 21 | import java.security.MessageDigest; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * This class signs LTI requests according to the Oauth 1.0 spec 28 | * @author Paul Gray 29 | * @since 1.1 30 | */ 31 | public class LtiOauthSigner implements LtiSigner { 32 | 33 | private MessageDigest md; 34 | 35 | public LtiOauthSigner() { 36 | try{ 37 | md = MessageDigest.getInstance("SHA1"); 38 | } catch(NoSuchAlgorithmException e) { 39 | throw new RuntimeException("Could not construct new instance of LtiOauthSigner", e); 40 | } 41 | } 42 | 43 | public LtiOauthSigner(MessageDigest md) { 44 | this.md = md; 45 | } 46 | 47 | @Override 48 | public HttpRequest sign(HttpRequest request, String key, String secret) throws LtiSigningException { 49 | CommonsHttpOAuthConsumer signer = new CommonsHttpOAuthConsumer(key, secret); 50 | try { 51 | String body = getRequestBody(request); 52 | String bodyHash = new String(Base64.encodeBase64(md.digest(body.getBytes()))); 53 | 54 | HttpParameters params = new HttpParameters(); 55 | params.put("oauth_body_hash", URLEncoder.encode(bodyHash, "UTF-8")); 56 | signer.setAdditionalParameters(params); 57 | 58 | signer.sign(request); 59 | } catch (OAuthMessageSignerException|OAuthExpectationFailedException|OAuthCommunicationException|IOException e) { 60 | throw new LtiSigningException("Exception encountered while singing Lti request...", e); 61 | } 62 | return request; 63 | } 64 | 65 | @Override 66 | public Map signParameters(Map parameters, String key, String secret, String url, String method) throws LtiSigningException { 67 | OAuthMessage oam = new OAuthMessage(method, url, parameters.entrySet()); 68 | OAuthConsumer cons = new OAuthConsumer(null, key, secret, null); 69 | OAuthAccessor acc = new OAuthAccessor(cons); 70 | try { 71 | oam.addRequiredParameters(acc); 72 | 73 | Map signedParameters = new HashMap<>(); 74 | for(Map.Entry param : oam.getParameters()){ 75 | signedParameters.put(param.getKey(), param.getValue()); 76 | } 77 | return signedParameters; 78 | } catch (OAuthException |IOException |URISyntaxException e) { 79 | throw new LtiSigningException("Error signing LTI request.", e); 80 | } 81 | } 82 | 83 | private String getRequestBody(HttpRequest req) throws IOException { 84 | if(req instanceof HttpEntityEnclosingRequest){ 85 | HttpEntity body = ((HttpEntityEnclosingRequest) req).getEntity(); 86 | if(body == null) { 87 | return ""; 88 | } else { 89 | return IOUtils.toString(body.getContent()); 90 | } 91 | } else { 92 | // requests with no entity have an empty string as the body 93 | return ""; 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiOauthVerifier.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import net.oauth.*; 4 | import net.oauth.server.OAuthServlet; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.util.Arrays; 8 | import java.util.Map; 9 | import java.util.logging.Logger; 10 | 11 | /** 12 | * This class verifies LTI launches according to the Oauth 1.0 spec 13 | * @author Paul Gray 14 | * @since 1.1 15 | */ 16 | public class LtiOauthVerifier implements LtiVerifier { 17 | 18 | public static final String OAUTH_KEY_PARAMETER= "oauth_consumer_key"; 19 | 20 | private final static Logger logger = Logger.getLogger(LtiOauthVerifier.class.getName()); 21 | 22 | /** 23 | * This method verifies the signed HttpServletRequest 24 | * @param request the HttpServletRequest that will be verified 25 | * @param secret the secret to verify the properties with 26 | * @return the result of the verification, along with contextual 27 | * information 28 | * @throws LtiVerificationException 29 | */ 30 | @Override 31 | public LtiVerificationResult verify(HttpServletRequest request, String secret) throws LtiVerificationException { 32 | OAuthMessage oam = OAuthServlet.getMessage(request, OAuthServlet.getRequestURL(request)); 33 | String oauth_consumer_key = null; 34 | try { 35 | oauth_consumer_key = oam.getConsumerKey(); 36 | } catch (Exception e) { 37 | return new LtiVerificationResult(false, LtiError.BAD_REQUEST, "Unable to find consumer key in message"); 38 | } 39 | 40 | OAuthValidator oav = new SimpleOAuthValidator(); 41 | OAuthConsumer cons = new OAuthConsumer(null, oauth_consumer_key, secret, null); 42 | OAuthAccessor acc = new OAuthAccessor(cons); 43 | 44 | try { 45 | oav.validateMessage(oam, acc); 46 | } catch (Exception e) { 47 | return new LtiVerificationResult(false, LtiError.BAD_REQUEST, "Failed to validate: " + e.getLocalizedMessage()); 48 | } 49 | return new LtiVerificationResult(true, new LtiLaunch(request)); 50 | } 51 | 52 | /** 53 | * This method will verify a collection of parameters 54 | * @param parameters the parameters that will be verified. mapped by key & value 55 | * @param url the url this request was made at 56 | * @param method the method this url was requested with 57 | * @param secret the secret to verify the propertihes with 58 | * @return 59 | * @throws LtiVerificationException 60 | */ 61 | @Override 62 | public LtiVerificationResult verifyParameters(Map parameters, String url, String method, String secret) throws LtiVerificationException { 63 | OAuthMessage oam = new OAuthMessage(method, url, parameters.entrySet()); 64 | OAuthConsumer cons = new OAuthConsumer(null, parameters.get(OAUTH_KEY_PARAMETER), secret, null); 65 | OAuthValidator oav = new SimpleOAuthValidator(); 66 | OAuthAccessor acc = new OAuthAccessor(cons); 67 | 68 | try { 69 | oav.validateMessage(oam, acc); 70 | } catch (Exception e) { 71 | return new LtiVerificationResult(false, LtiError.BAD_REQUEST, "Failed to validate: " + e.getLocalizedMessage() + ", Parameters: " + Arrays.toString(parameters.entrySet().toArray())); 72 | } 73 | return new LtiVerificationResult(true, new LtiLaunch(parameters)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiSigner.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import org.apache.http.HttpRequest; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * This interface contains methods that sign HttpRequests 9 | * and generic request parameters according to the LTI 10 | * specification. 11 | * @author Paul Gray 12 | * @since 1.1 13 | */ 14 | public interface LtiSigner { 15 | 16 | /** 17 | * This method will return a signed HttpRequest object. 18 | * Once returned, adding new parameters or changing the 19 | * body will invalidate the signature. This method should 20 | * be used for server to server connection requests. 21 | * For example, posting an LTI Outcome back to the LTI 22 | * Consumer. 23 | * @param request the HttpRequest that will be signed 24 | * @param key the key that will be added to the request. 25 | * @param secret the secret to be used 26 | * @return a signed HttpRequest object 27 | * @throws LtiSigningException 28 | */ 29 | public HttpRequest sign(HttpRequest request, String key, String secret) throws LtiSigningException; 30 | 31 | /** 32 | * This method will return a list of signed parameters. 33 | * Once returned, adding new parameters or changing the 34 | * body will invalidate the signature. This method will 35 | * overwrite reserved parameters from the underlying 36 | * specification. For example, if you are using the Oauth 37 | * implementation, oauth_signature will be removed 38 | * & replaced with the generated signature from the properties. 39 | * @param parameters the parameters that will be signed. mapped by key & value 40 | * @param key the key that will be added to the request. 41 | * @param secret the secret to be sign the parameters with 42 | * @return a map of signed parameters (including the signature) 43 | * @throws LtiSigningException 44 | */ 45 | public Map signParameters(Map parameters, String key, String secret, String url, String method) throws LtiSigningException; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiSigningException.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | /** 4 | * This exception is thrown to indicate that there was an error when signing an LTI request. 5 | * 6 | * Created by pgray on 8/23/14. 7 | */ 8 | public class LtiSigningException extends Exception { 9 | public LtiSigningException(String message, Exception exception) { 10 | super(message, exception); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiUser.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * Created by paul on 5/28/14. 10 | */ 11 | public class LtiUser { 12 | 13 | private String id; 14 | private List roles; 15 | 16 | public LtiUser(HttpServletRequest request) { 17 | this.id = request.getParameter("user_id"); 18 | this.roles = new LinkedList<>(); 19 | if(request.getParameter("roles") != null) { 20 | for (String role : request.getParameter("roles").split(",")) { 21 | this.roles.add(role.trim()); 22 | } 23 | } 24 | } 25 | 26 | public LtiUser(Map parameters) { 27 | this.id = parameters.get("user_id"); 28 | this.roles = new LinkedList<>(); 29 | if(parameters.get("roles") != null) { 30 | for (String role : parameters.get("roles").split(",")) { 31 | this.roles.add(role.trim()); 32 | } 33 | } 34 | } 35 | 36 | public String getId() { 37 | return id; 38 | } 39 | 40 | public void setId(String id) { 41 | this.id = id; 42 | } 43 | 44 | public List getRoles() { 45 | return roles; 46 | } 47 | 48 | public void setRoles(List roles) { 49 | this.roles = roles; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiVerificationException.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | /** 4 | * Created by pgray on 8/28/14. 5 | */ 6 | public class LtiVerificationException extends Exception { 7 | public LtiVerificationException(String message, Exception exception) { 8 | super(message, exception); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiVerificationResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.imsglobal.lti.launch; 7 | 8 | import org.imsglobal.lti.launch.LtiError; 9 | import org.imsglobal.lti.launch.LtiLaunch; 10 | 11 | /** 12 | * 13 | * @author pgray 14 | */ 15 | public class LtiVerificationResult { 16 | 17 | private Boolean success; 18 | private LtiError error; 19 | private String message; 20 | private LtiLaunch ltiLaunchResult; 21 | 22 | public LtiVerificationResult() { 23 | } 24 | 25 | public LtiVerificationResult(Boolean success, LtiError error, String message) { 26 | this.success = success; 27 | this.error = error; 28 | this.message = message; 29 | } 30 | 31 | public LtiVerificationResult(Boolean success, LtiLaunch ltiLaunchResult) { 32 | this.ltiLaunchResult = ltiLaunchResult; 33 | this.success = success; 34 | } 35 | 36 | public LtiVerificationResult(Boolean success, LtiError error, String message, LtiLaunch ltiLaunchResult) { 37 | this.success = success; 38 | this.error = error; 39 | this.message = message; 40 | this.ltiLaunchResult = ltiLaunchResult; 41 | } 42 | 43 | public Boolean getSuccess() { 44 | return success; 45 | } 46 | 47 | public void setSuccess(Boolean success) { 48 | this.success = success; 49 | } 50 | 51 | public LtiError getError() { 52 | return error; 53 | } 54 | 55 | public void setError(LtiError error) { 56 | this.error = error; 57 | } 58 | 59 | public LtiLaunch getLtiLaunchResult() { 60 | return ltiLaunchResult; 61 | } 62 | 63 | public void setLtiLaunchResult(LtiLaunch ltiLaunchResult) { 64 | this.ltiLaunchResult = ltiLaunchResult; 65 | } 66 | 67 | public String getMessage() { 68 | return message; 69 | } 70 | 71 | public void setMessage(String message) { 72 | this.message = message; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti/launch/LtiVerifier.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | 4 | import javax.servlet.http.HttpServletRequest; 5 | import java.util.Map; 6 | 7 | /** 8 | * This interface contains methods that verify HttpRequests 9 | * and generic request parameters according to the LTI 10 | * specification. 11 | * @author Paul Gray 12 | * @since 1.1 13 | */ 14 | public interface LtiVerifier { 15 | 16 | /** 17 | * This method will verify the HttpServletRequest. 18 | * 19 | * @param request the HttpServletRequest that will be verified 20 | * @param secret the secret to verify the properties with 21 | * @return an LtiVerificationResult which will 22 | * contain information about the request (whether or 23 | * not it is valid, and if it is valid, contextual 24 | * information about the request). 25 | * @throws LtiVerificationException 26 | */ 27 | public LtiVerificationResult verify(HttpServletRequest request, String secret) throws LtiVerificationException; 28 | 29 | /** 30 | * This method will verify a list of properties (mapped 31 | * by key & value). 32 | * @param parameters the parameters that will be verified. mapped by key & value 33 | * @param url the url this request was made at 34 | * @param method the method this url was requested with 35 | * @param secret the secret to verify the propertihes with 36 | * @return an LtiVerificationResult which will 37 | * contain information about the request (whether or 38 | * not it is valid, and if it is valid, contextual 39 | * information about the request). 40 | * @throws LtiVerificationException 41 | */ 42 | public LtiVerificationResult verifyParameters(Map parameters, String url, String method, String secret) throws LtiVerificationException; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/LTI2Config.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2; 3 | 4 | // Capture the information needed to build a tool profile 5 | public interface LTI2Config { 6 | // A globally unique identifier for the service. The domain name is typical. 7 | // The scope for this is tenant/customer 8 | public String getGuid(); 9 | public String getSupport_email(); 10 | 11 | // In a multi-tenant environment this data describes the tenant / customer. 12 | public String getService_owner_id(); 13 | public String getService_owner_owner_name(); 14 | public String getService_owner_description(); 15 | public String getService_owner_support_email(); 16 | 17 | // This represents the service provider that hosts a product. 18 | // If this is self hosted, it is reasonable that these values 19 | // are the same as the "owner" values above. 20 | public String getService_provider_id(); 21 | public String getService_provider_provider_name(); 22 | public String getService_provider_description(); 23 | public String getService_provider_support_email(); 24 | 25 | // This section is general information about the software product 26 | public String getProduct_family_product_code(); 27 | public String getProduct_family_vendor_code(); 28 | public String getProduct_family_vendor_name(); 29 | public String getProduct_family_vendor_description(); 30 | public String getProduct_family_vendor_website(); 31 | public String getProduct_family_vendor_contact(); 32 | 33 | // This is about one particular version of a product 34 | public String getProduct_info_product_name(); 35 | public String getProduct_info_product_version(); 36 | public String getProduct_info_product_description(); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/LTI2ConfigSample.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti2; 2 | 3 | public class LTI2ConfigSample implements LTI2Config { 4 | 5 | // A globally unique identifier for the service. The domain name is typical. 6 | // The scope for this is tenant/customer 7 | public String getGuid() { 8 | return "edunext.school.edu"; 9 | } 10 | public String getSupport_email() { 11 | return "support@edunext.school.edu"; 12 | } 13 | 14 | // In a multi-tenant environment this data describes the tenant / customer. 15 | public String getService_owner_id() { 16 | return "https://edunext.school.edu"; 17 | } 18 | public String getService_owner_owner_name() { 19 | return "ETS at SchoolEdu"; 20 | } 21 | public String getService_owner_description() { 22 | return "The Ed. Tech Services Division of SchoolEdu"; 23 | } 24 | public String getService_owner_support_email() { 25 | return "edunext@school.edu"; 26 | } 27 | 28 | // This represents the service provider that hosts a product. 29 | // If this is self hosted, it is reasonable that these values 30 | // are the same as the "owner" values above. 31 | public String getService_provider_id() { 32 | return "https://hosting.example.com"; 33 | } 34 | public String getService_provider_provider_name() { 35 | return "Example Hosting Services"; 36 | } 37 | public String getService_provider_description() { 38 | return "We are the best example of a hosting services for EduNext."; 39 | } 40 | public String getService_provider_support_email() { 41 | return "sales@hosting.example.com"; 42 | } 43 | 44 | // This section is about the software product 45 | public String getProduct_family_product_code() { 46 | return "edunext"; 47 | } 48 | public String getProduct_family_vendor_code() { 49 | return "edunext"; 50 | } 51 | public String getProduct_family_vendor_name() { 52 | return "Edu Next Project"; 53 | } 54 | public String getProduct_family_vendor_description() { 55 | return "EduNext is whats next in education."; 56 | } 57 | public String getProduct_family_vendor_website() { 58 | return "http://www.edunext.example.com"; 59 | } 60 | public String getProduct_family_vendor_contact() { 61 | return "sales@edunext.sample.com"; 62 | } 63 | 64 | // This is about one particular version of a product. 65 | public String getProduct_info_product_name() { 66 | return "Classes"; 67 | } 68 | public String getProduct_info_product_version() { 69 | return "2.0"; 70 | } 71 | public String getProduct_info_product_description() { 72 | return "Classes 2.0"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/LTI2Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 IMS GLobal Learning Consortium 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | * implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | * 16 | */ 17 | package org.imsglobal.lti2; 18 | 19 | public class LTI2Constants { 20 | /** 21 | * LTI2_VERSION_STRING=LTI-2p0 22 | *

23 | * This indicates an LTI 2.0 launch. 24 | */ 25 | public static final String LTI2_VERSION_STRING = "LTI-2p0"; 26 | 27 | /** 28 | * REG_KEY="9875" 29 | *

30 | * This is the registration key for the callback. 31 | */ 32 | public static final String REG_KEY = "reg_key"; 33 | 34 | /** 35 | * REG_PASSWORD="9875" 36 | *

37 | * This is the registration password for the callback. 38 | */ 39 | public static final String REG_PASSWORD = "reg_password"; 40 | 41 | /** 42 | * TC_PROFILE_URL 43 | *

44 | * This is the profile URL. 45 | */ 46 | public static final String TC_PROFILE_URL = "tc_profile_url"; 47 | 48 | public static final String JSONLD_ID = "@id"; 49 | public static final String CONTEXT = "@context"; 50 | public static final String TYPE = "@type"; 51 | public static final String VALUE = "@value"; 52 | public static final String GRAPH = "@graph"; 53 | 54 | public static final String CUSTOM_URL = "custom_url"; 55 | public static final String TOOL_PROXY_GUID = "tool_proxy_guid"; 56 | public static final String SHARED_SECRET = "shared_secret"; 57 | public static final String CUSTOM = "custom"; 58 | public static final String SECURITY_CONTRACT = "security_contract"; 59 | public static final String SERVICE = "service"; 60 | public static final String TOOL_SERVICE = "tool_service"; 61 | public static final String GRADE = "grade"; 62 | public static final String GRADE_TYPE_DECIMAL = "decimal"; 63 | public static final String COMMENT = "comment"; 64 | public static final String RESULTSCORE = "resultScore"; 65 | 66 | /** 67 | * Utility array useful for validating property names when building launch 68 | * data. 69 | */ 70 | public static final String[] validPropertyNames = { 71 | REG_KEY, REG_PASSWORD, TC_PROFILE_URL }; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/LTI2SampleData.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti2; 2 | 3 | import java.util.Properties; 4 | import org.imsglobal.lti.BasicLTIConstants; 5 | 6 | public class LTI2SampleData { 7 | 8 | public static Properties getSubstitution() { 9 | Properties lti2subst = new Properties(); 10 | lti2subst.setProperty("CourseOffering.id","context_id_999"); 11 | lti2subst.setProperty("CourseOffering.label","SI364"); 12 | lti2subst.setProperty("CourseOffering.title","Building Interactive Applications"); 13 | lti2subst.setProperty("Membership.role","Instructor"); 14 | lti2subst.setProperty("ResourceLink.id","res_link_999"); 15 | lti2subst.setProperty("ResourceLink.title","My weekly blog"); 16 | lti2subst.setProperty("User.id","user_id_007"); 17 | lti2subst.setProperty("User.username","bond"); 18 | lti2subst.setProperty("Person.name.given","James"); 19 | lti2subst.setProperty("Person.name.family","Bond"); 20 | lti2subst.setProperty("Person.name.full","James Bond"); 21 | lti2subst.setProperty("Person.email.primary","bond@example.com"); 22 | return lti2subst; 23 | } 24 | 25 | public static Properties getLaunch() { 26 | Properties launch = new Properties(); 27 | launch.setProperty(BasicLTIConstants.CONTEXT_ID,"context_id_999"); 28 | launch.setProperty(BasicLTIConstants.CONTEXT_LABEL,"SI364"); 29 | launch.setProperty(BasicLTIConstants.CONTEXT_TITLE,"Building Interactive Applications"); 30 | launch.setProperty(BasicLTIConstants.ROLES,"Instructor"); 31 | launch.setProperty(BasicLTIConstants.RESOURCE_LINK_ID,"res_link_999"); 32 | launch.setProperty(BasicLTIConstants.RESOURCE_LINK_TITLE,"My weekly blog"); 33 | launch.setProperty(BasicLTIConstants.USER_ID,"user_id_007"); 34 | launch.setProperty(BasicLTIConstants.LIS_PERSON_NAME_GIVEN,"James"); 35 | launch.setProperty(BasicLTIConstants.LIS_PERSON_NAME_FAMILY,"Bond"); 36 | launch.setProperty(BasicLTIConstants.LIS_PERSON_NAME_FULL,"James Bond"); 37 | launch.setProperty(BasicLTIConstants.LIS_PERSON_CONTACT_EMAIL_PRIMARY,"bond@example.com"); 38 | return launch; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/LTI2Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $URL: https://source.sakaiproject.org/svn/basiclti/trunk/basiclti-util/src/java/org/imsglobal/lti2/LTI2Util.java $ 3 | * $Id: LTI2Util.java 134448 2014-02-12 18:32:12Z csev@umich.edu $ 4 | * 5 | * Copyright (c) 2013 IMS GLobal Learning Consortium 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | * implied. See the License for the specific language governing 17 | * permissions and limitations under the License. 18 | */ 19 | 20 | package org.imsglobal.lti2; 21 | 22 | import java.net.URL; 23 | import java.util.ArrayList; 24 | import java.util.Enumeration; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | import java.util.Properties; 28 | import java.util.logging.Logger; 29 | 30 | import javax.servlet.http.HttpServletRequest; 31 | 32 | import org.imsglobal.lti.BasicLTIUtil; 33 | import org.imsglobal.lti2.objects.consumer.ServiceOffered; 34 | import org.imsglobal.lti2.objects.consumer.StandardServices; 35 | import org.imsglobal.lti2.objects.consumer.ToolConsumer; 36 | import org.json.simple.JSONArray; 37 | import org.json.simple.JSONObject; 38 | import org.json.simple.JSONValue; 39 | 40 | public class LTI2Util { 41 | 42 | // We use the built-in Java logger because this code needs to be very generic 43 | private static Logger M_log = Logger.getLogger(LTI2Util.class.toString()); 44 | 45 | public static final String SCOPE_LtiLink = "LtiLink"; 46 | public static final String SCOPE_ToolProxyBinding = "ToolProxyBinding"; 47 | public static final String SCOPE_ToolProxy = "ToolProxy"; 48 | 49 | private static final String EMPTY_JSON_OBJECT = "{\n}\n"; 50 | 51 | // Validate the incoming tool_services against a tool consumer 52 | public static String validateServices(ToolConsumer consumer, JSONObject providerProfile) 53 | { 54 | // Mostly to catch casting errors from bad JSON 55 | try { 56 | JSONObject security_contract = (JSONObject) providerProfile.get(LTI2Constants.SECURITY_CONTRACT); 57 | if ( security_contract == null ) { 58 | return "JSON missing security_contract"; 59 | } 60 | JSONArray tool_services = (JSONArray) security_contract.get(LTI2Constants.TOOL_SERVICE); 61 | 62 | List services_offered = consumer.getService_offered(); 63 | 64 | if ( tool_services != null ) for (Object o : tool_services) { 65 | JSONObject tool_service = (JSONObject) o; 66 | String json_service = (String) tool_service.get(LTI2Constants.SERVICE); 67 | 68 | boolean found = false; 69 | for (ServiceOffered service : services_offered ) { 70 | String service_endpoint = service.getEndpoint(); 71 | if ( service_endpoint.equals(json_service) ) { 72 | found = true; 73 | break; 74 | } 75 | } 76 | if ( ! found ) return "Service not allowed: "+json_service; 77 | } 78 | return null; 79 | } 80 | catch (Exception e) { 81 | return "Exception:"+ e.getLocalizedMessage(); 82 | } 83 | } 84 | 85 | // Validate incoming capabilities requested against out ToolConsumer 86 | public static String validateCapabilities(ToolConsumer consumer, JSONObject providerProfile) 87 | { 88 | List theTools = new ArrayList (); 89 | Properties info = new Properties(); 90 | 91 | // Mostly to catch casting errors from bad JSON 92 | try { 93 | String retval = parseToolProfile(theTools, info, providerProfile); 94 | if ( retval != null ) return retval; 95 | 96 | if ( theTools.size() < 1 ) return "No tools found in profile"; 97 | 98 | // Check all the capabilities requested by all the tools comparing against consumer 99 | List capabilities = consumer.getCapability_offered(); 100 | for ( Properties theTool : theTools ) { 101 | String ec = (String) theTool.get("enabled_capability"); 102 | JSONArray enabled_capability = (JSONArray) JSONValue.parse(ec); 103 | if ( enabled_capability != null ) for (Object o : enabled_capability) { 104 | ec = (String) o; 105 | if ( capabilities.contains(ec) ) continue; 106 | return "Capability not permitted="+ec; 107 | } 108 | } 109 | return null; 110 | } 111 | catch (Exception e ) { 112 | return "Exception:"+ e.getLocalizedMessage(); 113 | } 114 | } 115 | 116 | public static void allowEmail(List capabilities) { 117 | capabilities.add("Person.email.primary"); 118 | } 119 | 120 | public static void allowName(List capabilities) { 121 | capabilities.add("User.username"); 122 | capabilities.add("Person.name.fullname"); 123 | capabilities.add("Person.name.given"); 124 | capabilities.add("Person.name.family"); 125 | capabilities.add("Person.name.full"); 126 | } 127 | 128 | public static void allowResult(List capabilities) { 129 | capabilities.add("Result.sourcedId"); 130 | capabilities.add("Result.autocreate"); 131 | capabilities.add("Result.url"); 132 | } 133 | 134 | public static void allowSettings(List capabilities) { 135 | capabilities.add("LtiLink.custom.url"); 136 | capabilities.add("ToolProxy.custom.url"); 137 | capabilities.add("ToolProxyBinding.custom.url"); 138 | } 139 | 140 | // If this code looks like a hack - it is because the spec is a hack. 141 | // There are five possible scenarios for GET and two possible scenarios 142 | // for PUT. I begged to simplify the business logic but was overrulled. 143 | // So we write obtuse code. 144 | @SuppressWarnings({ "unchecked", "unused" }) 145 | public static Object getSettings(HttpServletRequest request, String scope, 146 | JSONObject link_settings, JSONObject binding_settings, JSONObject proxy_settings, 147 | String link_url, String binding_url, String proxy_url) 148 | { 149 | // Check to see if we are doing the bubble 150 | String bubbleStr = request.getParameter("bubble"); 151 | String acceptHdr = request.getHeader("Accept"); 152 | String contentHdr = request.getContentType(); 153 | 154 | if ( bubbleStr != null && bubbleStr.equals("all") && 155 | acceptHdr.indexOf(StandardServices.TOOLSETTINGS_FORMAT) < 0 ) { 156 | return "Simple format does not allow bubble=all"; 157 | } 158 | 159 | if ( SCOPE_LtiLink.equals(scope) || SCOPE_ToolProxyBinding.equals(scope) 160 | || SCOPE_ToolProxy.equals(scope) ) { 161 | // All good 162 | } else { 163 | return "Bad Setttings Scope="+scope; 164 | } 165 | 166 | boolean bubble = bubbleStr != null && "GET".equals(request.getMethod()); 167 | boolean distinct = bubbleStr != null && "distinct".equals(bubbleStr); 168 | boolean bubbleAll = bubbleStr != null && "all".equals(bubbleStr); 169 | 170 | // Check our output format 171 | boolean acceptComplex = acceptHdr == null || acceptHdr.indexOf(StandardServices.TOOLSETTINGS_FORMAT) >= 0; 172 | 173 | if ( distinct && link_settings != null && scope.equals(SCOPE_LtiLink) ) { 174 | Iterator i = link_settings.keySet().iterator(); 175 | while ( i.hasNext() ) { 176 | String key = (String) i.next(); 177 | if ( binding_settings != null ) binding_settings.remove(key); 178 | if ( proxy_settings != null ) proxy_settings.remove(key); 179 | } 180 | } 181 | 182 | if ( distinct && binding_settings != null && scope.equals(SCOPE_ToolProxyBinding) ) { 183 | Iterator i = binding_settings.keySet().iterator(); 184 | while ( i.hasNext() ) { 185 | String key = (String) i.next(); 186 | if ( proxy_settings != null ) proxy_settings.remove(key); 187 | } 188 | } 189 | 190 | // Lets get this party started... 191 | JSONObject jsonResponse = null; 192 | if ( (distinct || bubbleAll) && acceptComplex ) { 193 | jsonResponse = new JSONObject(); 194 | jsonResponse.put(LTI2Constants.CONTEXT,StandardServices.TOOLSETTINGS_CONTEXT); 195 | JSONArray graph = new JSONArray(); 196 | boolean started = false; 197 | if ( link_settings != null && SCOPE_LtiLink.equals(scope) ) { 198 | JSONObject cjson = new JSONObject(); 199 | cjson.put(LTI2Constants.JSONLD_ID,link_url); 200 | cjson.put(LTI2Constants.TYPE,SCOPE_LtiLink); 201 | cjson.put(LTI2Constants.CUSTOM,link_settings); 202 | graph.add(cjson); 203 | started = true; 204 | } 205 | if ( binding_settings != null && ( started || SCOPE_ToolProxyBinding.equals(scope) ) ) { 206 | JSONObject cjson = new JSONObject(); 207 | cjson.put(LTI2Constants.JSONLD_ID,binding_url); 208 | cjson.put(LTI2Constants.TYPE,SCOPE_ToolProxyBinding); 209 | cjson.put(LTI2Constants.CUSTOM,binding_settings); 210 | graph.add(cjson); 211 | started = true; 212 | } 213 | if ( proxy_settings != null && ( started || SCOPE_ToolProxy.equals(scope) ) ) { 214 | JSONObject cjson = new JSONObject(); 215 | cjson.put(LTI2Constants.JSONLD_ID,proxy_url); 216 | cjson.put(LTI2Constants.TYPE,SCOPE_ToolProxy); 217 | cjson.put(LTI2Constants.CUSTOM,proxy_settings); 218 | graph.add(cjson); 219 | } 220 | jsonResponse.put(LTI2Constants.GRAPH,graph); 221 | 222 | } else if ( distinct ) { // Simple format output 223 | jsonResponse = proxy_settings; 224 | if ( SCOPE_LtiLink.equals(scope) ) { 225 | jsonResponse.putAll(binding_settings); 226 | jsonResponse.putAll(link_settings); 227 | } else if ( SCOPE_ToolProxyBinding.equals(scope) ) { 228 | jsonResponse.putAll(binding_settings); 229 | } 230 | } else { // bubble not specified 231 | jsonResponse = new JSONObject(); 232 | jsonResponse.put(LTI2Constants.CONTEXT,StandardServices.TOOLSETTINGS_CONTEXT); 233 | JSONObject theSettings = null; 234 | String endpoint = null; 235 | if ( SCOPE_LtiLink.equals(scope) ) { 236 | endpoint = link_url; 237 | theSettings = link_settings; 238 | } else if ( SCOPE_ToolProxyBinding.equals(scope) ) { 239 | endpoint = binding_url; 240 | theSettings = binding_settings; 241 | } 242 | if ( SCOPE_ToolProxy.equals(scope) ) { 243 | endpoint = proxy_url; 244 | theSettings = proxy_settings; 245 | } 246 | if ( acceptComplex ) { 247 | JSONArray graph = new JSONArray(); 248 | JSONObject cjson = new JSONObject(); 249 | cjson.put(LTI2Constants.JSONLD_ID,endpoint); 250 | cjson.put(LTI2Constants.TYPE,scope); 251 | cjson.put(LTI2Constants.CUSTOM,theSettings); 252 | graph.add(cjson); 253 | jsonResponse.put(LTI2Constants.GRAPH,graph); 254 | } else { 255 | jsonResponse = theSettings; 256 | } 257 | } 258 | return jsonResponse; 259 | } 260 | 261 | // Parse a provider profile with lots of error checking... 262 | public static String parseToolProfile(List theTools, Properties info, JSONObject jsonObject) 263 | { 264 | try { 265 | return parseToolProfileInternal(theTools, info, jsonObject); 266 | } catch (Exception e) { 267 | M_log.warning("Internal error parsing tool proxy\n"+jsonObject.toString()); 268 | e.printStackTrace(); 269 | return "Internal error parsing tool proxy:"+e.getLocalizedMessage(); 270 | } 271 | } 272 | 273 | // Parse a provider profile with lots of error checking... 274 | @SuppressWarnings("unused") 275 | private static String parseToolProfileInternal(List theTools, Properties info, JSONObject jsonObject) 276 | { 277 | Object o = null; 278 | JSONObject tool_profile = (JSONObject) jsonObject.get("tool_profile"); 279 | if ( tool_profile == null ) { 280 | return "JSON missing tool_profile"; 281 | } 282 | JSONObject product_instance = (JSONObject) tool_profile.get("product_instance"); 283 | if ( product_instance == null ) { 284 | return "JSON missing product_instance"; 285 | } 286 | 287 | String instance_guid = (String) product_instance.get("guid"); 288 | if ( instance_guid == null ) { 289 | return "JSON missing product_info / guid"; 290 | } 291 | info.put("instance_guid",instance_guid); 292 | 293 | JSONObject product_info = (JSONObject) product_instance.get("product_info"); 294 | if ( product_info == null ) { 295 | return "JSON missing product_info"; 296 | } 297 | 298 | // Look for required fields 299 | JSONObject product_name = product_info == null ? null : (JSONObject) product_info.get("product_name"); 300 | String productTitle = product_name == null ? null : (String) product_name.get("default_value"); 301 | JSONObject description = product_info == null ? null : (JSONObject) product_info.get("description"); 302 | String productDescription = description == null ? null : (String) description.get("default_value"); 303 | 304 | JSONObject product_family = product_info == null ? null : (JSONObject) product_info.get("product_family"); 305 | String productCode = product_family == null ? null : (String) product_family.get("code"); 306 | JSONObject product_vendor = product_family == null ? null : (JSONObject) product_family.get("vendor"); 307 | description = product_vendor == null ? null : (JSONObject) product_vendor.get("description"); 308 | String vendorDescription = description == null ? null : (String) description.get("default_value"); 309 | String vendorCode = product_vendor == null ? null : (String) product_vendor.get("code"); 310 | 311 | if ( productTitle == null || productDescription == null ) { 312 | return "JSON missing product_name or description "; 313 | } 314 | if ( productCode == null || vendorCode == null || vendorDescription == null ) { 315 | return "JSON missing product code, vendor code or description"; 316 | } 317 | 318 | info.put("product_name", productTitle); 319 | info.put("description", productDescription); // Backwards compatibility 320 | info.put("product_description", productDescription); 321 | info.put("product_code", productCode); 322 | info.put("vendor_code", vendorCode); 323 | info.put("vendor_description", vendorDescription); 324 | 325 | o = tool_profile.get("base_url_choice"); 326 | if ( ! (o instanceof JSONArray)|| o == null ) { 327 | return "JSON missing base_url_choices"; 328 | } 329 | JSONArray base_url_choices = (JSONArray) o; 330 | 331 | String secure_base_url = null; 332 | String default_base_url = null; 333 | for ( Object i : base_url_choices ) { 334 | JSONObject url_choice = (JSONObject) i; 335 | secure_base_url = (String) url_choice.get("secure_base_url"); 336 | default_base_url = (String) url_choice.get("default_base_url"); 337 | } 338 | 339 | String launch_url = secure_base_url; 340 | if ( launch_url == null ) launch_url = default_base_url; 341 | if ( launch_url == null ) { 342 | return "Unable to determine launch URL"; 343 | } 344 | 345 | o = (JSONArray) tool_profile.get("resource_handler"); 346 | if ( ! (o instanceof JSONArray)|| o == null ) { 347 | return "JSON missing resource_handlers"; 348 | } 349 | JSONArray resource_handlers = (JSONArray) o; 350 | 351 | // Loop through resource handlers, read, and check for errors 352 | for(Object i : resource_handlers ) { 353 | JSONObject resource_handler = (JSONObject) i; 354 | JSONObject resource_type_json = (JSONObject) resource_handler.get("resource_type"); 355 | String resource_type_code = (String) resource_type_json.get("code"); 356 | if ( resource_type_code == null ) { 357 | return "JSON missing resource_type code"; 358 | } 359 | o = (JSONArray) resource_handler.get("message"); 360 | if ( ! (o instanceof JSONArray)|| o == null ) { 361 | return "JSON missing resource_handler / message"; 362 | } 363 | JSONArray messages = (JSONArray) o; 364 | 365 | JSONObject titleObject = (JSONObject) resource_handler.get("name"); 366 | String title = titleObject == null ? null : (String) titleObject.get("default_value"); 367 | if ( title == null || titleObject == null ) { 368 | return "JSON missing resource_handler / name / default_value"; 369 | } 370 | 371 | JSONObject buttonObject = (JSONObject) resource_handler.get("short_name"); 372 | String button = buttonObject == null ? null : (String) buttonObject.get("default_value"); 373 | 374 | JSONObject descObject = (JSONObject) resource_handler.get("description"); 375 | String resourceDescription = descObject == null ? null : (String) descObject.get("default_value"); 376 | 377 | String path = null; 378 | JSONArray parameter = null; 379 | JSONArray enabled_capability = null; 380 | for ( Object m : messages ) { 381 | JSONObject message = (JSONObject) m; 382 | String message_type = (String) message.get("message_type"); 383 | if ( ! "basic-lti-launch-request".equals(message_type) ) continue; 384 | if ( path != null ) { 385 | return "A resource_handler cannot have more than one basic-lti-launch-request message RT="+resource_type_code; 386 | } 387 | path = (String) message.get("path"); 388 | if ( path == null ) { 389 | return "A basic-lti-launch-request message must have a path RT="+resource_type_code; 390 | } 391 | o = (JSONArray) message.get("parameter"); 392 | if ( ! (o instanceof JSONArray)) { 393 | return "Must be an array: parameter RT="+resource_type_code; 394 | } 395 | parameter = (JSONArray) o; 396 | 397 | o = (JSONArray) message.get("enabled_capability"); 398 | if ( ! (o instanceof JSONArray)) { 399 | return "Must be an array: enabled_capability RT="+resource_type_code; 400 | } 401 | enabled_capability = (JSONArray) o; 402 | } 403 | 404 | // Ignore everything except launch handlers 405 | if ( path == null ) continue; 406 | 407 | // Check the URI 408 | String thisLaunch = launch_url; 409 | if ( ! thisLaunch.endsWith("/") && ! path.startsWith("/") ) thisLaunch = thisLaunch + "/"; 410 | thisLaunch = thisLaunch + path; 411 | try { 412 | URL url = new URL(thisLaunch); 413 | } catch ( Exception e ) { 414 | return "Bad launch URL="+thisLaunch; 415 | } 416 | 417 | // Passed all the tests... Lets keep it... 418 | Properties theTool = new Properties(); 419 | 420 | theTool.put("resource_type", resource_type_code); // Backwards compatibility 421 | theTool.put("resource_type_code", resource_type_code); 422 | if ( title == null ) title = productTitle; 423 | if ( title != null ) theTool.put("title", title); 424 | if ( button != null ) theTool.put("button", button); 425 | if ( resourceDescription == null ) resourceDescription = productDescription; 426 | if ( resourceDescription != null ) theTool.put("description", resourceDescription); 427 | if ( parameter != null ) theTool.put("parameter", parameter.toString()); 428 | if ( enabled_capability != null ) theTool.put("enabled_capability", enabled_capability.toString()); 429 | theTool.put("launch", thisLaunch); 430 | theTools.add(theTool); 431 | } 432 | return null; // All good 433 | } 434 | 435 | public static JSONObject parseSettings(String settings) 436 | { 437 | if ( settings == null || settings.length() < 1 ) { 438 | settings = EMPTY_JSON_OBJECT; 439 | } 440 | return (JSONObject) JSONValue.parse(settings); 441 | } 442 | 443 | /* Two possible formats: 444 | 445 | key=val;key2=val2; 446 | 447 | key=val 448 | key2=val2 449 | */ 450 | public static boolean mergeLTI1Custom(Properties custom, String customstr) 451 | { 452 | if ( customstr == null || customstr.length() < 1 ) return true; 453 | 454 | String [] params = customstr.split("[\n;]"); 455 | for (int i = 0 ; i < params.length; i++ ) { 456 | String param = params[i]; 457 | if ( param == null ) continue; 458 | if ( param.length() < 1 ) continue; 459 | 460 | int pos = param.indexOf("="); 461 | if ( pos < 1 ) continue; 462 | if ( pos+1 > param.length() ) continue; 463 | String key = mapKeyName(param.substring(0,pos)); 464 | if ( key == null ) continue; 465 | 466 | if ( custom.containsKey(key) ) continue; 467 | 468 | String value = param.substring(pos+1); 469 | if ( value == null ) continue; 470 | value = value.trim(); 471 | if ( value.length() < 1 ) continue; 472 | setProperty(custom, key, value); 473 | } 474 | return true; 475 | } 476 | 477 | /* 478 | "custom" : 479 | { 480 | "isbn" : "978-0321558145", 481 | "style" : "jazzy" 482 | } 483 | */ 484 | public static boolean mergeLTI2Custom(Properties custom, String customstr) 485 | { 486 | if ( customstr == null || customstr.length() < 1 ) return true; 487 | JSONObject json = null; 488 | try { 489 | json = (JSONObject) JSONValue.parse(customstr.trim()); 490 | } catch(Exception e) { 491 | M_log.warning("mergeLTI2Custom could not parse\n"+customstr); 492 | M_log.warning(e.getLocalizedMessage()); 493 | return false; 494 | } 495 | 496 | // This could happen if the old settings service was used 497 | // on an LTI 2.x placement to put in settings that are not 498 | // JSON - we just ignore it. 499 | if ( json == null ) return false; 500 | Iterator keys = json.keySet().iterator(); 501 | while( keys.hasNext() ){ 502 | String key = (String)keys.next(); 503 | if ( custom.containsKey(key) ) continue; 504 | Object value = json.get(key); 505 | if ( value instanceof String ){ 506 | setProperty(custom, key, (String) value); 507 | } 508 | } 509 | return true; 510 | } 511 | 512 | /* 513 | "parameter" : 514 | [ 515 | { "name" : "result_url", 516 | "variable" : "Result.url" 517 | }, 518 | { "name" : "discipline", 519 | "fixed" : "chemistry" 520 | } 521 | ] 522 | */ 523 | public static boolean mergeLTI2Parameters(Properties custom, String customstr) { 524 | if ( customstr == null || customstr.length() < 1 ) return true; 525 | JSONArray json = null; 526 | try { 527 | json = (JSONArray) JSONValue.parse(customstr.trim()); 528 | } catch(Exception e) { 529 | M_log.warning("mergeLTI2Parameters could not parse\n"+customstr); 530 | M_log.warning(e.getLocalizedMessage()); 531 | return false; 532 | } 533 | Iterator parameters = json.iterator(); 534 | while( parameters.hasNext() ){ 535 | Object o = parameters.next(); 536 | JSONObject parameter = null; 537 | try { 538 | parameter = (JSONObject) o; 539 | } catch(Exception e) { 540 | M_log.warning("mergeLTI2Parameters did not find list of objects\n"+customstr); 541 | M_log.warning(e.getLocalizedMessage()); 542 | return false; 543 | } 544 | 545 | String name = (String) parameter.get("name"); 546 | 547 | if ( name == null ) continue; 548 | if ( custom.containsKey(name) ) continue; 549 | String fixed = (String) parameter.get("fixed"); 550 | String variable = (String) parameter.get("variable"); 551 | if ( variable != null ) { 552 | setProperty(custom, name, variable); 553 | continue; 554 | } 555 | if ( fixed != null ) { 556 | setProperty(custom, name, fixed); 557 | } 558 | } 559 | return true; 560 | } 561 | 562 | public static void substituteCustom(Properties custom, Properties lti2subst) 563 | { 564 | if ( custom == null || lti2subst == null ) return; 565 | Enumeration e = custom.propertyNames(); 566 | while (e.hasMoreElements()) { 567 | String key = (String) e.nextElement(); 568 | String value = custom.getProperty(key); 569 | if ( value == null || value.length() < 1 ) continue; 570 | String newValue = lti2subst.getProperty(value); 571 | if ( newValue == null || newValue.length() < 1 ) continue; 572 | setProperty(custom, key, (String) newValue); 573 | } 574 | } 575 | 576 | // Place the custom values into the launch 577 | public static void addCustomToLaunch(Properties ltiProps, Properties custom) 578 | { 579 | Enumeration e = custom.propertyNames(); 580 | while (e.hasMoreElements()) { 581 | String keyStr = (String) e.nextElement(); 582 | String value = custom.getProperty(keyStr); 583 | setProperty(ltiProps,"custom_"+keyStr,value); 584 | } 585 | } 586 | 587 | @SuppressWarnings("deprecation") 588 | public static void setProperty(Properties props, String key, String value) { 589 | BasicLTIUtil.setProperty(props, key, value); 590 | } 591 | 592 | public static String mapKeyName(String keyname) { 593 | return BasicLTIUtil.mapKeyName(keyname); 594 | } 595 | 596 | } 597 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/BaseJson.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.imsglobal.lti2.objects; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import org.codehaus.jackson.annotate.JsonAnyGetter; 22 | import org.codehaus.jackson.annotate.JsonAnySetter; 23 | 24 | /** 25 | * 26 | * @author pgray 27 | */ 28 | public class BaseJson { 29 | 30 | private Map additionalProperties = new HashMap(); 31 | 32 | @com.fasterxml.jackson.annotation.JsonAnyGetter 33 | @JsonAnyGetter 34 | public Map getAdditionalProperties() { 35 | return this.additionalProperties; 36 | } 37 | 38 | @com.fasterxml.jackson.annotation.JsonAnySetter 39 | @JsonAnySetter 40 | public void addAdditionalProperty(String name, Object value) { 41 | this.additionalProperties.put(name, value); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/BaseJsonLd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.imsglobal.lti2.objects; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | 20 | /** 21 | * 22 | * @author paul 23 | */ 24 | public class BaseJsonLd extends BaseJson { 25 | 26 | @JsonProperty("@id") 27 | @org.codehaus.jackson.annotate.JsonProperty("@id") 28 | protected String id; 29 | @JsonProperty("@context") 30 | @org.codehaus.jackson.annotate.JsonProperty("@context") 31 | protected String context; 32 | @JsonProperty("@type") 33 | @org.codehaus.jackson.annotate.JsonProperty("@type") 34 | protected String type; 35 | 36 | public String getId() { 37 | return id; 38 | } 39 | 40 | public void setId(String id) { 41 | this.id = id; 42 | } 43 | 44 | public String getContext() { 45 | return context; 46 | } 47 | 48 | public void setContext(String context) { 49 | this.context = context; 50 | } 51 | 52 | public String getType() { 53 | return type; 54 | } 55 | 56 | public void setType(String type) { 57 | this.type = type; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/Contact.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "email" 17 | }) 18 | public class Contact { 19 | 20 | @JsonProperty("email") 21 | private String email; 22 | private Map additionalProperties = new HashMap(); 23 | 24 | public Contact(String email) { 25 | this.email = email; 26 | } 27 | 28 | public Contact() { 29 | } 30 | 31 | @JsonProperty("email") 32 | public String getEmail() { 33 | return email; 34 | } 35 | 36 | @JsonProperty("email") 37 | public void setEmail(String email) { 38 | this.email = email; 39 | } 40 | 41 | @JsonAnyGetter 42 | public Map getAdditionalProperties() { 43 | return this.additionalProperties; 44 | } 45 | 46 | @JsonAnySetter 47 | public void setAdditionalProperties(String name, Object value) { 48 | this.additionalProperties.put(name, value); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/Description.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "default_value", 17 | "key" 18 | }) 19 | public class Description { 20 | 21 | @JsonProperty("default_value") 22 | private String default_value; 23 | @JsonProperty("key") 24 | private String key; 25 | private Map additionalProperties = new HashMap(); 26 | 27 | public Description(String description) { 28 | this.default_value = description; 29 | this.key = "product.vendor.description"; 30 | } 31 | 32 | public Description() { 33 | } 34 | 35 | @JsonProperty("default_value") 36 | public String getDefault_value() { 37 | return default_value; 38 | } 39 | 40 | @JsonProperty("default_value") 41 | public void setDefault_value(String default_value) { 42 | this.default_value = default_value; 43 | } 44 | 45 | @JsonProperty("key") 46 | public String getKey() { 47 | return key; 48 | } 49 | 50 | @JsonProperty("key") 51 | public void setKey(String key) { 52 | this.key = key; 53 | } 54 | 55 | @JsonAnyGetter 56 | public Map getAdditionalProperties() { 57 | return this.additionalProperties; 58 | } 59 | 60 | @JsonAnySetter 61 | public void setAdditionalProperties(String name, Object value) { 62 | this.additionalProperties.put(name, value); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/Name.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "default_value", 17 | "key" 18 | }) 19 | public class Name { 20 | 21 | @JsonProperty("default_value") 22 | private String default_value; 23 | @JsonProperty("key") 24 | private String key; 25 | private Map additionalProperties = new HashMap(); 26 | 27 | public Name(String name) { 28 | this.default_value = name; 29 | this.key = "product.vendor.name"; 30 | } 31 | 32 | public Name() { 33 | } 34 | 35 | @JsonProperty("default_value") 36 | public String getDefault_value() { 37 | return default_value; 38 | } 39 | 40 | @JsonProperty("default_value") 41 | public void setDefault_value(String default_value) { 42 | this.default_value = default_value; 43 | } 44 | 45 | @JsonProperty("key") 46 | public String getKey() { 47 | return key; 48 | } 49 | 50 | @JsonProperty("key") 51 | public void setKey(String key) { 52 | this.key = key; 53 | } 54 | 55 | @JsonAnyGetter 56 | public Map getAdditionalProperties() { 57 | return this.additionalProperties; 58 | } 59 | 60 | @JsonAnySetter 61 | public void setAdditionalProperties(String name, Object value) { 62 | this.additionalProperties.put(name, value); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/NamedContext.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.map.annotate.JsonSerialize; 10 | 11 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 12 | @Generated("com.googlecode.jsonschema2pojo") 13 | 14 | public class NamedContext { 15 | 16 | private Map additionalProperties = new HashMap(); 17 | 18 | @JsonAnyGetter 19 | public Map getAdditionalProperties() { 20 | return this.additionalProperties; 21 | } 22 | 23 | @JsonAnySetter 24 | public void setAdditionalProperties(String name, Object value) { 25 | this.additionalProperties.put(name, value); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ProductFamily.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import org.imsglobal.lti2.LTI2Config; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import javax.annotation.Generated; 9 | import org.codehaus.jackson.annotate.JsonAnyGetter; 10 | import org.codehaus.jackson.annotate.JsonAnySetter; 11 | import org.codehaus.jackson.annotate.JsonProperty; 12 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 13 | import org.codehaus.jackson.map.annotate.JsonSerialize; 14 | 15 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 16 | @Generated("com.googlecode.jsonschema2pojo") 17 | @JsonPropertyOrder({ 18 | "code", 19 | "vendor" 20 | }) 21 | public class ProductFamily { 22 | 23 | @JsonProperty("code") 24 | private String code; 25 | @JsonProperty("vendor") 26 | private Vendor vendor; 27 | private Map additionalProperties = new HashMap(); 28 | 29 | public ProductFamily(LTI2Config cnf) { 30 | this.code = cnf.getProduct_family_product_code(); 31 | this.vendor = new Vendor(cnf); 32 | } 33 | 34 | public ProductFamily() { 35 | } 36 | 37 | @JsonProperty("code") 38 | public String getCode() { 39 | return code; 40 | } 41 | 42 | @JsonProperty("code") 43 | public void setCode(String code) { 44 | this.code = code; 45 | } 46 | 47 | @JsonProperty("vendor") 48 | public Vendor getVendor() { 49 | return vendor; 50 | } 51 | 52 | @JsonProperty("vendor") 53 | public void setVendor(Vendor vendor) { 54 | this.vendor = vendor; 55 | } 56 | 57 | @JsonAnyGetter 58 | public Map getAdditionalProperties() { 59 | return this.additionalProperties; 60 | } 61 | 62 | @JsonAnySetter 63 | public void setAdditionalProperties(String name, Object value) { 64 | this.additionalProperties.put(name, value); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ProductInfo.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import org.imsglobal.lti2.LTI2Config; 5 | 6 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 7 | import org.imsglobal.lti2.objects.BaseJson; 8 | 9 | @JsonPropertyOrder({ 10 | "product_name", 11 | "product_version", 12 | "description", 13 | "technical_description", 14 | "product_family" 15 | }) 16 | public class ProductInfo extends BaseJson { 17 | 18 | private ProductName product_name; 19 | private String product_version; 20 | private Description description; 21 | private TechnicalDescription technical_description; 22 | private ProductFamily product_family; 23 | 24 | public ProductInfo(LTI2Config cnf) { 25 | this.product_name = new ProductName(cnf.getProduct_info_product_name()); 26 | this.product_version = cnf.getProduct_info_product_version(); 27 | this.description = new Description(cnf.getProduct_info_product_description()); 28 | this.product_family = new ProductFamily(cnf); 29 | } 30 | 31 | public ProductInfo(){ 32 | 33 | } 34 | 35 | public ProductName getProduct_name() { 36 | return product_name; 37 | } 38 | 39 | public void setProduct_name(ProductName product_name) { 40 | this.product_name = product_name; 41 | } 42 | 43 | public String getProduct_version() { 44 | return product_version; 45 | } 46 | 47 | public void setProduct_version(String product_version) { 48 | this.product_version = product_version; 49 | } 50 | 51 | public Description getDescription() { 52 | return description; 53 | } 54 | 55 | public void setDescription(Description description) { 56 | this.description = description; 57 | } 58 | 59 | public TechnicalDescription getTechnical_description() { 60 | return technical_description; 61 | } 62 | 63 | public void setTechnical_description(TechnicalDescription technical_description) { 64 | this.technical_description = technical_description; 65 | } 66 | 67 | public ProductFamily getProduct_family() { 68 | return product_family; 69 | } 70 | 71 | public void setProduct_family(ProductFamily product_family) { 72 | this.product_family = product_family; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ProductInstance.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import org.imsglobal.lti2.LTI2Config; 5 | 6 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 7 | import org.imsglobal.lti2.objects.BaseJson; 8 | 9 | @JsonPropertyOrder({ 10 | "guid", 11 | "product_info", 12 | "support" 13 | }) 14 | public class ProductInstance extends BaseJson { 15 | 16 | private String guid; 17 | private ProductInfo product_info; 18 | private ServiceOwner service_owner; 19 | private ServiceProvider service_provider; 20 | private Support support; 21 | 22 | public ProductInstance(LTI2Config cnf) { 23 | this.guid = cnf.getGuid(); 24 | this.product_info = new ProductInfo(cnf); 25 | this.service_owner = new ServiceOwner(cnf); 26 | this.service_provider = new ServiceProvider(cnf); 27 | this.support = new Support(cnf.getSupport_email()); 28 | } 29 | 30 | public ProductInstance() { 31 | } 32 | 33 | public String getGuid() { 34 | return guid; 35 | } 36 | 37 | public void setGuid(String guid) { 38 | this.guid = guid; 39 | } 40 | 41 | public ProductInfo getProduct_info() { 42 | return product_info; 43 | } 44 | 45 | public void setProduct_info(ProductInfo product_info) { 46 | this.product_info = product_info; 47 | } 48 | 49 | public ServiceOwner getService_owner() { 50 | return service_owner; 51 | } 52 | 53 | public void setService_owner(ServiceOwner service_owner) { 54 | this.service_owner = service_owner; 55 | } 56 | 57 | public ServiceProvider getService_provider() { 58 | return service_provider; 59 | } 60 | 61 | public void setService_provider(ServiceProvider service_provider) { 62 | this.service_provider = service_provider; 63 | } 64 | 65 | public Support getSupport() { 66 | return support; 67 | } 68 | 69 | public void setSupport(Support support) { 70 | this.support = support; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ProductName.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "default_value", 17 | "key" 18 | }) 19 | public class ProductName { 20 | 21 | @JsonProperty("default_value") 22 | private String default_value; 23 | @JsonProperty("key") 24 | private String key; 25 | private Map additionalProperties = new HashMap(); 26 | 27 | public ProductName(String name) { 28 | this.default_value = name; 29 | this.key = "product.name"; 30 | } 31 | 32 | public ProductName() { 33 | } 34 | 35 | 36 | @JsonProperty("default_value") 37 | public String getDefault_value() { 38 | return default_value; 39 | } 40 | 41 | @JsonProperty("default_value") 42 | public void setDefault_value(String default_value) { 43 | this.default_value = default_value; 44 | } 45 | 46 | @JsonProperty("key") 47 | public String getKey() { 48 | return key; 49 | } 50 | 51 | @JsonProperty("key") 52 | public void setKey(String key) { 53 | this.key = key; 54 | } 55 | 56 | @JsonAnyGetter 57 | public Map getAdditionalProperties() { 58 | return this.additionalProperties; 59 | } 60 | 61 | @JsonAnySetter 62 | public void setAdditionalProperties(String name, Object value) { 63 | this.additionalProperties.put(name, value); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ServiceOffered.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Collections; 9 | import javax.annotation.Generated; 10 | import org.codehaus.jackson.annotate.JsonAnyGetter; 11 | import org.codehaus.jackson.annotate.JsonAnySetter; 12 | import org.codehaus.jackson.annotate.JsonProperty; 13 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 14 | import org.codehaus.jackson.map.annotate.JsonSerialize; 15 | 16 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 17 | @Generated("com.googlecode.jsonschema2pojo") 18 | @JsonPropertyOrder({ 19 | "@type", 20 | "@id", 21 | "endpoint", 22 | "format", 23 | "action" 24 | }) 25 | public class ServiceOffered { 26 | 27 | @JsonProperty("@type") 28 | private String _type; 29 | @JsonProperty("@id") 30 | private String _id; 31 | @JsonProperty("endpoint") 32 | private String endpoint; 33 | @JsonProperty("format") 34 | private List format = new ArrayList(); 35 | @JsonProperty("action") 36 | private List action = new ArrayList(); 37 | private Map additionalProperties = new HashMap(); 38 | 39 | public ServiceOffered() { } 40 | 41 | public ServiceOffered(String endpoint, String id, String type, String format, String action) { 42 | this.endpoint = endpoint; 43 | this._id = id; 44 | this._type = type; 45 | this.format.add(format); 46 | this.action.add(action); 47 | } 48 | 49 | public ServiceOffered(String endpoint, String id, String type, String[] format, String[] action) { 50 | this.endpoint = endpoint; 51 | this._id = id; 52 | this._type = type; 53 | Collections.addAll(this.format, format); 54 | Collections.addAll(this.action, action); 55 | } 56 | 57 | @JsonProperty("@type") 58 | public String get_type() { 59 | return _type; 60 | } 61 | 62 | @JsonProperty("@type") 63 | public void set_type(String _type) { 64 | this._type = _type; 65 | } 66 | 67 | @JsonProperty("@id") 68 | public String get_id() { 69 | return _id; 70 | } 71 | 72 | @JsonProperty("@id") 73 | public void set_id(String _id) { 74 | this._id = _id; 75 | } 76 | 77 | @JsonProperty("endpoint") 78 | public String getEndpoint() { 79 | return endpoint; 80 | } 81 | 82 | @JsonProperty("endpoint") 83 | public void setEndpoint(String endpoint) { 84 | this.endpoint = endpoint; 85 | } 86 | 87 | @JsonProperty("format") 88 | public List getFormat() { 89 | return format; 90 | } 91 | 92 | @JsonProperty("format") 93 | public void setFormat(List format) { 94 | this.format = format; 95 | } 96 | 97 | @JsonProperty("action") 98 | public List getAction() { 99 | return action; 100 | } 101 | 102 | @JsonProperty("action") 103 | public void setAction(List action) { 104 | this.action = action; 105 | } 106 | 107 | @JsonAnyGetter 108 | public Map getAdditionalProperties() { 109 | return this.additionalProperties; 110 | } 111 | 112 | @JsonAnySetter 113 | public void setAdditionalProperties(String name, Object value) { 114 | this.additionalProperties.put(name, value); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ServiceOwner.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti2.objects.consumer; 2 | 3 | import org.imsglobal.lti2.LTI2Config; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import javax.annotation.Generated; 8 | import org.codehaus.jackson.annotate.JsonAnyGetter; 9 | import org.codehaus.jackson.annotate.JsonAnySetter; 10 | import org.codehaus.jackson.annotate.JsonProperty; 11 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 12 | import org.codehaus.jackson.map.annotate.JsonSerialize; 13 | 14 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 15 | @Generated("com.googlecode.jsonschema2pojo") 16 | @JsonPropertyOrder({ 17 | "@id", 18 | "service_owner_name", 19 | "description", 20 | "timestamp", 21 | "support" 22 | }) 23 | public class ServiceOwner { 24 | 25 | @JsonProperty("@id") 26 | private String _id; 27 | @JsonProperty("service_owner_name") 28 | private ServiceOwnerName service_owner_name; 29 | @JsonProperty("description") 30 | private Description description; 31 | @JsonProperty("timestamp") 32 | private String timestamp; 33 | @JsonProperty("support") 34 | private Support support; 35 | private Map additionalProperties = new HashMap(); 36 | 37 | public ServiceOwner(LTI2Config cnf) { 38 | this._id = cnf.getService_owner_id(); 39 | this.service_owner_name = new ServiceOwnerName(cnf.getService_owner_owner_name()); 40 | this.description = new Description(cnf.getService_owner_description()); 41 | this.support = new Support(cnf.getService_owner_support_email()); 42 | } 43 | 44 | public ServiceOwner() { 45 | } 46 | 47 | @JsonProperty("@id") 48 | public String get_id() { 49 | return _id; 50 | } 51 | 52 | @JsonProperty("@id") 53 | public void set_id(String _id) { 54 | this._id = _id; 55 | } 56 | 57 | @JsonProperty("service_owner_name") 58 | public ServiceOwnerName getService_owner_name() { 59 | return service_owner_name; 60 | } 61 | 62 | @JsonProperty("service_owner_name") 63 | public void setService_name(ServiceOwnerName service_owner_name) { 64 | this.service_owner_name = service_owner_name; 65 | } 66 | 67 | @JsonProperty("description") 68 | public Description getDescription() { 69 | return description; 70 | } 71 | 72 | @JsonProperty("description") 73 | public void setDescription(Description description) { 74 | this.description = description; 75 | } 76 | 77 | @JsonProperty("timestamp") 78 | public String getTimestamp() { 79 | return timestamp; 80 | } 81 | 82 | @JsonProperty("timestamp") 83 | public void setTimestamp(String timestamp) { 84 | this.timestamp = timestamp; 85 | } 86 | 87 | @JsonProperty("support") 88 | public Support getSupport() { 89 | return support; 90 | } 91 | 92 | @JsonProperty("support") 93 | public void setSupport(Support support) { 94 | this.support = support; 95 | } 96 | 97 | @JsonAnyGetter 98 | public Map getAdditionalProperties() { 99 | return this.additionalProperties; 100 | } 101 | 102 | @JsonAnySetter 103 | public void setAdditionalProperties(String name, Object value) { 104 | this.additionalProperties.put(name, value); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ServiceOwnerName.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "default_value", 17 | "key" 18 | }) 19 | public class ServiceOwnerName { 20 | 21 | @JsonProperty("default_value") 22 | private String default_value; 23 | @JsonProperty("key") 24 | private String key; 25 | private Map additionalProperties = new HashMap(); 26 | 27 | public ServiceOwnerName(String name) { 28 | this.default_value = name; 29 | this.key = "product.name"; 30 | } 31 | 32 | public ServiceOwnerName() { 33 | } 34 | 35 | @JsonProperty("default_value") 36 | public String getDefault_value() { 37 | return default_value; 38 | } 39 | 40 | @JsonProperty("default_value") 41 | public void setDefault_value(String default_value) { 42 | this.default_value = default_value; 43 | } 44 | 45 | @JsonProperty("key") 46 | public String getKey() { 47 | return key; 48 | } 49 | 50 | @JsonProperty("key") 51 | public void setKey(String key) { 52 | this.key = key; 53 | } 54 | 55 | @JsonAnyGetter 56 | public Map getAdditionalProperties() { 57 | return this.additionalProperties; 58 | } 59 | 60 | @JsonAnySetter 61 | public void setAdditionalProperties(String name, Object value) { 62 | this.additionalProperties.put(name, value); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ServiceProvider.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import org.imsglobal.lti2.LTI2Config; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import javax.annotation.Generated; 9 | import org.codehaus.jackson.annotate.JsonAnyGetter; 10 | import org.codehaus.jackson.annotate.JsonAnySetter; 11 | import org.codehaus.jackson.annotate.JsonProperty; 12 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 13 | import org.codehaus.jackson.map.annotate.JsonSerialize; 14 | 15 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 16 | @Generated("com.googlecode.jsonschema2pojo") 17 | @JsonPropertyOrder({ 18 | "@id", 19 | "service_provider_name", 20 | "description", 21 | "timestamp", 22 | "support" 23 | }) 24 | public class ServiceProvider { 25 | 26 | @JsonProperty("@id") 27 | private String _id; 28 | @JsonProperty("service_provider_name") 29 | private ServiceProviderName service_provider_name; 30 | @JsonProperty("description") 31 | private Description description; 32 | @JsonProperty("timestamp") 33 | private String timestamp; 34 | @JsonProperty("support") 35 | private Support support; 36 | private Map additionalProperties = new HashMap(); 37 | 38 | public ServiceProvider(LTI2Config cnf) { 39 | this._id = cnf.getService_provider_id(); 40 | this.service_provider_name = new ServiceProviderName(cnf.getService_provider_provider_name()); 41 | this.description = new Description(cnf.getService_provider_description()); 42 | this.support = new Support(cnf.getService_provider_support_email()); 43 | } 44 | 45 | public ServiceProvider() { 46 | } 47 | 48 | @JsonProperty("@id") 49 | public String get_id() { 50 | return _id; 51 | } 52 | 53 | @JsonProperty("@id") 54 | public void set_id(String _id) { 55 | this._id = _id; 56 | } 57 | 58 | @JsonProperty("service_provider_name") 59 | public ServiceProviderName getService_provider_name() { 60 | return service_provider_name; 61 | } 62 | 63 | @JsonProperty("service_provider_name") 64 | public void setService_name(ServiceProviderName service_provider_name) { 65 | this.service_provider_name = service_provider_name; 66 | } 67 | 68 | @JsonProperty("description") 69 | public Description getDescription() { 70 | return description; 71 | } 72 | 73 | @JsonProperty("description") 74 | public void setDescription(Description description) { 75 | this.description = description; 76 | } 77 | 78 | @JsonProperty("timestamp") 79 | public String getTimestamp() { 80 | return timestamp; 81 | } 82 | 83 | @JsonProperty("timestamp") 84 | public void setTimestamp(String timestamp) { 85 | this.timestamp = timestamp; 86 | } 87 | 88 | @JsonProperty("support") 89 | public Support getSupport() { 90 | return support; 91 | } 92 | 93 | @JsonProperty("support") 94 | public void setSupport(Support support) { 95 | this.support = support; 96 | } 97 | 98 | @JsonAnyGetter 99 | public Map getAdditionalProperties() { 100 | return this.additionalProperties; 101 | } 102 | 103 | @JsonAnySetter 104 | public void setAdditionalProperties(String name, Object value) { 105 | this.additionalProperties.put(name, value); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/ServiceProviderName.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "default_value", 17 | "key" 18 | }) 19 | public class ServiceProviderName { 20 | 21 | @JsonProperty("default_value") 22 | private String default_value; 23 | @JsonProperty("key") 24 | private String key; 25 | private Map additionalProperties = new HashMap(); 26 | 27 | public ServiceProviderName(String name) { 28 | this.default_value = name; 29 | this.key = "product.name"; 30 | } 31 | 32 | public ServiceProviderName() { 33 | } 34 | 35 | @JsonProperty("default_value") 36 | public String getDefault_value() { 37 | return default_value; 38 | } 39 | 40 | @JsonProperty("default_value") 41 | public void setDefault_value(String default_value) { 42 | this.default_value = default_value; 43 | } 44 | 45 | @JsonProperty("key") 46 | public String getKey() { 47 | return key; 48 | } 49 | 50 | @JsonProperty("key") 51 | public void setKey(String key) { 52 | this.key = key; 53 | } 54 | 55 | @JsonAnyGetter 56 | public Map getAdditionalProperties() { 57 | return this.additionalProperties; 58 | } 59 | 60 | @JsonAnySetter 61 | public void setAdditionalProperties(String name, Object value) { 62 | this.additionalProperties.put(name, value); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/StandardServices.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.Arrays; 5 | 6 | public class StandardServices { 7 | 8 | public static final String TOOLPROXY_FORMAT = "application/vnd.ims.lti.v2.toolproxy+json"; 9 | public static final String TOOLSETTINGS_CONTEXT = "http://purl.imsglobal.org/ctx/lti/v2/ToolSettings"; 10 | public static final String TOOLSETTINGS_FORMAT = "application/vnd.ims.lti.v2.toolsettings+json"; 11 | public static final String TOOLSETTINGS_SIMPLE_FORMAT = "application/vnd.ims.lti.v2.toolsettings.simple+json"; 12 | 13 | public static final String TOOLPROXY_ID_CONTEXT = "http://purl.imsglobal.org/ctx/lti/v2/ToolProxyId"; 14 | public static final String TOOLPROXY_ID_FORMAT = "application/vnd.ims.lti.v2.toolproxy.id+json"; 15 | public static final String TOOLPROXY_ID_TYPE = "ToolProxy"; 16 | 17 | public static final String RESULT_FORMAT = "application/vnd.ims.lis.v2.result+json"; 18 | public static final String RESULT_CONTEXT = "http://purl.imsglobal.org/ctx/lis/v2/Result"; 19 | public static final String RESULT_TYPE = "Result"; 20 | 21 | public static ServiceOffered LTI2Registration(String endpoint) { 22 | ServiceOffered ret = new ServiceOffered(); 23 | ret.setEndpoint(endpoint); 24 | ret.set_id("tcp:ToolProxy.collection"); // TODO: Is this right see 5.6 in the docs 25 | ret.set_type("RestService"); 26 | ret.setFormat(Arrays.asList(TOOLPROXY_FORMAT)); 27 | ret.setAction(Arrays.asList("POST")); 28 | return ret; 29 | } 30 | 31 | // "endpoint" : "http://lms.example.com/resources/ToolProxy/{tool_proxy_guid}", 32 | public static ServiceOffered LTI2ProxyItem(String endpoint) { 33 | ServiceOffered ret = new ServiceOffered(); 34 | ret.setEndpoint(endpoint); 35 | ret.set_id("tcp:ToolProxy.item"); 36 | ret.set_type("RestService"); 37 | ret.setFormat(Arrays.asList(TOOLPROXY_FORMAT)); 38 | ret.setAction(Arrays.asList("GET", "PUT")); 39 | return ret; 40 | } 41 | 42 | // "endpoint" : "http://lms.example.com/resources/Result/{sourcedId}", 43 | public static ServiceOffered LTI2ResultItem(String endpoint) { 44 | ServiceOffered ret = new ServiceOffered(); 45 | ret.setEndpoint(endpoint); 46 | ret.set_id("tcp:Result.item"); // TODO: Is this right see 5.6 in the docs 47 | ret.set_type("RestService"); 48 | ret.setFormat(Arrays.asList(RESULT_FORMAT)); 49 | ret.setAction(Arrays.asList("GET", "PUT")); 50 | return ret; 51 | } 52 | 53 | // "endpoint" : "http://lms.example.com/resources/ToolProxy/{tool_proxy_guid}/custom", 54 | public static ServiceOffered LTI2ToolProxySettings(String endpoint) { 55 | ServiceOffered ret = new ServiceOffered(); 56 | ret.setEndpoint(endpoint); 57 | ret.set_id("tcp:ToolProxySettings"); 58 | ret.set_type("RestService"); 59 | ret.setFormat(Arrays.asList(TOOLSETTINGS_FORMAT, TOOLSETTINGS_SIMPLE_FORMAT)); 60 | ret.setAction(Arrays.asList("GET", "PUT")); 61 | return ret; 62 | } 63 | 64 | // "endpoint" : "http://lms.example.com/resources/links/{link_id}/custom", 65 | public static ServiceOffered LTI2LtiLinkSettings(String endpoint) { 66 | ServiceOffered ret = new ServiceOffered(); 67 | ret.setEndpoint(endpoint); 68 | ret.set_id("tcp:LtiLinkSettings"); 69 | ret.set_type("RestService"); 70 | ret.setFormat(Arrays.asList(TOOLSETTINGS_FORMAT, TOOLSETTINGS_SIMPLE_FORMAT)); 71 | ret.setAction(Arrays.asList("GET", "PUT")); 72 | return ret; 73 | } 74 | 75 | public static ServiceOffered LTI1Outcomes(String endpoint) { 76 | ServiceOffered ret = new ServiceOffered(); 77 | ret.setEndpoint(endpoint); 78 | ret.set_id("tcp:LTI_1_1_ResultService"); 79 | ret.set_type("RestService"); 80 | ret.setFormat(Arrays.asList("application/vnd.ims.lti.v1.outcome+xml")); 81 | ret.setAction(Arrays.asList("POST")); 82 | return ret; 83 | } 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/Support.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "email" 17 | }) 18 | public class Support { 19 | 20 | @JsonProperty("email") 21 | private String email; 22 | private Map additionalProperties = new HashMap(); 23 | 24 | public Support(String email) { 25 | this.email = email; 26 | } 27 | 28 | public Support() { 29 | } 30 | 31 | @JsonProperty("email") 32 | public String getEmail() { 33 | return email; 34 | } 35 | 36 | @JsonProperty("email") 37 | public void setEmail(String email) { 38 | this.email = email; 39 | } 40 | 41 | @JsonAnyGetter 42 | public Map getAdditionalProperties() { 43 | return this.additionalProperties; 44 | } 45 | 46 | @JsonAnySetter 47 | public void setAdditionalProperties(String name, Object value) { 48 | this.additionalProperties.put(name, value); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/TechnicalDescription.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.Generated; 7 | import org.codehaus.jackson.annotate.JsonAnyGetter; 8 | import org.codehaus.jackson.annotate.JsonAnySetter; 9 | import org.codehaus.jackson.annotate.JsonProperty; 10 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 11 | import org.codehaus.jackson.map.annotate.JsonSerialize; 12 | 13 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 14 | @Generated("com.googlecode.jsonschema2pojo") 15 | @JsonPropertyOrder({ 16 | "default_value", 17 | "key" 18 | }) 19 | public class TechnicalDescription { 20 | 21 | @JsonProperty("default_value") 22 | private String default_value; 23 | @JsonProperty("key") 24 | private String key; 25 | private Map additionalProperties = new HashMap(); 26 | 27 | public TechnicalDescription() { 28 | } 29 | 30 | @JsonProperty("default_value") 31 | public String getDefault_value() { 32 | return default_value; 33 | } 34 | 35 | @JsonProperty("default_value") 36 | public void setDefault_value(String default_value) { 37 | this.default_value = default_value; 38 | } 39 | 40 | @JsonProperty("key") 41 | public String getKey() { 42 | return key; 43 | } 44 | 45 | @JsonProperty("key") 46 | public void setKey(String key) { 47 | this.key = key; 48 | } 49 | 50 | @JsonAnyGetter 51 | public Map getAdditionalProperties() { 52 | return this.additionalProperties; 53 | } 54 | 55 | @JsonAnySetter 56 | public void setAdditionalProperties(String name, Object value) { 57 | this.additionalProperties.put(name, value); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/consumer/Vendor.java: -------------------------------------------------------------------------------- 1 | 2 | package org.imsglobal.lti2.objects.consumer; 3 | 4 | import org.imsglobal.lti2.LTI2Config; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import javax.annotation.Generated; 9 | import org.codehaus.jackson.annotate.JsonAnyGetter; 10 | import org.codehaus.jackson.annotate.JsonAnySetter; 11 | import org.codehaus.jackson.annotate.JsonProperty; 12 | import org.codehaus.jackson.annotate.JsonPropertyOrder; 13 | import org.codehaus.jackson.map.annotate.JsonSerialize; 14 | 15 | @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 16 | @Generated("com.googlecode.jsonschema2pojo") 17 | @JsonPropertyOrder({ 18 | "code", 19 | "vendor_name", 20 | "description", 21 | "website", 22 | "timestamp", 23 | "contact" 24 | }) 25 | public class Vendor { 26 | 27 | @JsonProperty("code") 28 | private String code; 29 | @JsonProperty("vendor_name") 30 | private Name vendor_name; 31 | @JsonProperty("description") 32 | private Description description; 33 | @JsonProperty("website") 34 | private String website; 35 | @JsonProperty("timestamp") 36 | private String timestamp; 37 | @JsonProperty("contact") 38 | private Contact contact; 39 | private Map additionalProperties = new HashMap(); 40 | 41 | public Vendor(LTI2Config cnf) { 42 | this.code = cnf.getProduct_family_vendor_code(); 43 | this.vendor_name = new Name(cnf.getProduct_family_vendor_name()); 44 | this.description = new Description(cnf.getProduct_family_vendor_description()); 45 | this.website = cnf.getProduct_family_vendor_website(); 46 | this.contact = new Contact(cnf.getProduct_family_vendor_contact()); 47 | } 48 | 49 | public Vendor() { 50 | } 51 | 52 | @JsonProperty("code") 53 | public String getCode() { 54 | return code; 55 | } 56 | 57 | @JsonProperty("code") 58 | public void setCode(String code) { 59 | this.code = code; 60 | } 61 | 62 | @JsonProperty("vendor_name") 63 | public Name getVendor_name() { 64 | return vendor_name; 65 | } 66 | 67 | @JsonProperty("name") 68 | public void setVendor_name(Name vendor_name) { 69 | this.vendor_name = vendor_name; 70 | } 71 | 72 | @JsonProperty("description") 73 | public Description getDescription() { 74 | return description; 75 | } 76 | 77 | @JsonProperty("description") 78 | public void setDescription(Description description) { 79 | this.description = description; 80 | } 81 | 82 | @JsonProperty("website") 83 | public String getWebsite() { 84 | return website; 85 | } 86 | 87 | @JsonProperty("website") 88 | public void setWebsite(String website) { 89 | this.website = website; 90 | } 91 | 92 | @JsonProperty("timestamp") 93 | public String getTimestamp() { 94 | return timestamp; 95 | } 96 | 97 | @JsonProperty("timestamp") 98 | public void setTimestamp(String timestamp) { 99 | this.timestamp = timestamp; 100 | } 101 | 102 | @JsonProperty("contact") 103 | public Contact getContact() { 104 | return contact; 105 | } 106 | 107 | @JsonProperty("contact") 108 | public void setContact(Contact contact) { 109 | this.contact = contact; 110 | } 111 | 112 | @JsonAnyGetter 113 | public Map getAdditionalProperties() { 114 | return this.additionalProperties; 115 | } 116 | 117 | @JsonAnySetter 118 | public void setAdditionalProperties(String name, Object value) { 119 | this.additionalProperties.put(name, value); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/provider/BaseUrlChoice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.imsglobal.lti2.objects.provider; 17 | 18 | /** 19 | * 20 | * @author paul 21 | */ 22 | public class BaseUrlChoice { 23 | 24 | private String default_base_url; 25 | private String secure_base_url; 26 | private Object selector; 27 | 28 | public BaseUrlChoice() { 29 | } 30 | 31 | public BaseUrlChoice(String default_base_url, String secure_base_url, Object selector) { 32 | this.default_base_url = default_base_url; 33 | this.secure_base_url = secure_base_url; 34 | this.selector = selector; 35 | } 36 | 37 | public String getDefault_base_url() { 38 | return default_base_url; 39 | } 40 | 41 | public void setDefault_base_url(String default_base_url) { 42 | this.default_base_url = default_base_url; 43 | } 44 | 45 | public String getSecure_base_url() { 46 | return secure_base_url; 47 | } 48 | 49 | public void setSecure_base_url(String secure_base_url) { 50 | this.secure_base_url = secure_base_url; 51 | } 52 | 53 | public Object getSelector() { 54 | return selector; 55 | } 56 | 57 | public void setSelector(Object selector) { 58 | this.selector = selector; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/provider/SecurityContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.imsglobal.lti2.objects.provider; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * 22 | * @author paul 23 | */ 24 | public class SecurityContract { 25 | 26 | private String shared_secret; 27 | private List tool_service; 28 | private List end_user_service; 29 | 30 | public SecurityContract() { 31 | } 32 | 33 | public SecurityContract(String shared_secret, List tool_service, List end_user_service) { 34 | this.shared_secret = shared_secret; 35 | this.tool_service = tool_service; 36 | this.end_user_service = end_user_service; 37 | } 38 | 39 | public String getShared_secret() { 40 | return shared_secret; 41 | } 42 | 43 | public void setShared_secret(String shared_secret) { 44 | this.shared_secret = shared_secret; 45 | } 46 | 47 | public List getTool_service() { 48 | return tool_service; 49 | } 50 | 51 | public void setTool_service(List tool_service) { 52 | this.tool_service = tool_service; 53 | } 54 | 55 | public List getEnd_user_service() { 56 | return end_user_service; 57 | } 58 | 59 | public void setEnd_user_service(List end_user_service) { 60 | this.end_user_service = end_user_service; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/provider/ToolProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.imsglobal.lti2.objects.provider; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | import com.fasterxml.jackson.databind.JsonNode; 23 | import org.imsglobal.lti2.objects.consumer.ProductInstance; 24 | 25 | /** 26 | * 27 | * @author paul 28 | */ 29 | public class ToolProfile { 30 | 31 | @JsonProperty("@id") 32 | @org.codehaus.jackson.annotate.JsonProperty("@id") 33 | private String id; 34 | private String lti_version; 35 | private JsonNode base_url_choice; 36 | private ProductInstance product_instance; 37 | private JsonNode resource_handler; 38 | private JsonNode message; 39 | 40 | public ToolProfile() { 41 | } 42 | 43 | public ToolProfile(String id, String lti_version, JsonNode base_url_choice, ProductInstance product_instance, JsonNode resource_handler, JsonNode message) { 44 | this.id = id; 45 | this.lti_version = lti_version; 46 | this.base_url_choice = base_url_choice; 47 | this.product_instance = product_instance; 48 | this.resource_handler = resource_handler; 49 | this.message = message; 50 | } 51 | 52 | public String getId() { 53 | return id; 54 | } 55 | 56 | public void setId(String id) { 57 | this.id = id; 58 | } 59 | 60 | public String getLti_version() { 61 | return lti_version; 62 | } 63 | 64 | public void setLti_version(String lti_version) { 65 | this.lti_version = lti_version; 66 | } 67 | 68 | public ProductInstance getProduct_instance() { 69 | return product_instance; 70 | } 71 | 72 | public void setProduct_instance(ProductInstance product_instance) { 73 | this.product_instance = product_instance; 74 | } 75 | 76 | public JsonNode getBase_url_choice() { 77 | return base_url_choice; 78 | } 79 | 80 | public void setBase_url_choice(JsonNode base_url_choice) { 81 | this.base_url_choice = base_url_choice; 82 | } 83 | 84 | public JsonNode getResource_handler() { 85 | return resource_handler; 86 | } 87 | 88 | public void setResource_handler(JsonNode resource_handler) { 89 | this.resource_handler = resource_handler; 90 | } 91 | 92 | public JsonNode getMessage() { 93 | return message; 94 | } 95 | 96 | public void setMessage(JsonNode message) { 97 | this.message = message; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/imsglobal/lti2/objects/provider/ToolProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.imsglobal.lti2.objects.provider; 18 | 19 | import org.codehaus.jackson.JsonNode; 20 | import org.imsglobal.lti2.objects.BaseJsonLd; 21 | 22 | /** 23 | * 24 | * @author pgray 25 | */ 26 | public class ToolProxy extends BaseJsonLd { 27 | 28 | private String tool_proxy_guid; 29 | private String custom_url; 30 | private String lti_version; 31 | private String tool_consumer_profile; 32 | private ToolProfile tool_profile; 33 | private Object custom; 34 | private SecurityContract security_contract; 35 | 36 | public static final String CONTENT_TYPE = "application/vnd.ims.lti.v2.toolproxy+json"; 37 | public static final String CONTEXT_URL = "http://purl.imsglobal.org/ctx/lti/v2/ToolProxyId"; 38 | public static final String TYPE = "http://purl.imsglobal.org/ctx/lti/v2/ToolProxyId"; 39 | 40 | public ToolProxy(){ 41 | } 42 | 43 | public ToolProxy(String id, String type, String tool_proxy_guid, String custom_url) { 44 | this.id = id; 45 | this.type = type; 46 | this.tool_proxy_guid = tool_proxy_guid; 47 | this.custom_url = custom_url; 48 | this.context = CONTEXT_URL; 49 | } 50 | 51 | public String getTool_proxy_guid() { 52 | return tool_proxy_guid; 53 | } 54 | 55 | public void setTool_proxy_guid(String tool_proxy_guid) { 56 | this.tool_proxy_guid = tool_proxy_guid; 57 | } 58 | 59 | public String getCustom_url() { 60 | return custom_url; 61 | } 62 | 63 | public void setCustom_url(String custom_url) { 64 | this.custom_url = custom_url; 65 | } 66 | 67 | public String getLti_version() { 68 | return lti_version; 69 | } 70 | 71 | public void setLti_version(String lti_version) { 72 | this.lti_version = lti_version; 73 | } 74 | 75 | public String getTool_consumer_profile() { 76 | return tool_consumer_profile; 77 | } 78 | 79 | public void setTool_consumer_profile(String tool_consumer_profile) { 80 | this.tool_consumer_profile = tool_consumer_profile; 81 | } 82 | 83 | public ToolProfile getTool_profile() { 84 | return tool_profile; 85 | } 86 | 87 | public void setTool_profile(ToolProfile tool_profile) { 88 | this.tool_profile = tool_profile; 89 | } 90 | 91 | public Object getCustom() { 92 | return custom; 93 | } 94 | 95 | public void setCustom(Object custom) { 96 | this.custom = custom; 97 | } 98 | 99 | public SecurityContract getSecurity_contract() { 100 | return security_contract; 101 | } 102 | 103 | public void setSecurity_contract(SecurityContract security_contract) { 104 | this.security_contract = security_contract; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | =============================================================================== 206 | 207 | The BasicLTI Utilities (basiclti-util) distribution includes a number of subcomponents 208 | with separate copyright notices and license terms. Your use of the 209 | code for the these subcomponents is subject to the terms and 210 | conditions of the following licenses. 211 | 212 | =============================================================================== 213 | OAuth: 214 | 215 | Copyright (c) 1998-2009 AOL LLC. 216 | Copyright 2007, 2008 Google, Inc. 217 | Copyright 2007, 2008 Netflix, Inc. 218 | 219 | Licensed under the Apache License, Version 2.0 (the "License"); 220 | you may not use this file except in compliance with the License. 221 | You may obtain a copy of the License at 222 | 223 | http://www.apache.org/licenses/LICENSE-2.0 224 | 225 | Unless required by applicable law or agreed to in writing, software 226 | distributed under the License is distributed on an "AS IS" BASIS, 227 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 228 | See the License for the specific language governing permissions and 229 | limitations under the License. 230 | 231 | =============================================================================== 232 | Base64: 233 | 234 | Copyright 1999-2008 The Apache Software Foundation. 235 | 236 | The ASF licenses this file to You under the Apache License, Version 2.0 237 | (the "License"); you may not use this file except in compliance with 238 | the License. You may obtain a copy of the License at 239 | 240 | http://www.apache.org/licenses/LICENSE-2.0 241 | 242 | Unless required by applicable law or agreed to in writing, software 243 | distributed under the License is distributed on an "AS IS" BASIS, 244 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 245 | See the License for the specific language governing permissions and 246 | limitations under the License. 247 | 248 | =============================================================================== 249 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/NOTICE: -------------------------------------------------------------------------------- 1 | BasicLTI Utilities (basiclti-util) 2 | Copyright 2010 IMS Global Learning Consortium www.imsglobal.org 3 | 4 | ----------------------------------------------------------- 5 | 6 | This product includes software (OAuth) developed by 7 | AOL LLC., Google, Inc., and Netflix, Inc. (http://code.google.com/p/oauth/). 8 | 9 | This product includes software (Base64) developed at 10 | The Apache Software Foundation (http://www.apache.org/). 11 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/README: -------------------------------------------------------------------------------- 1 | IMS Global Learning Consortium BasicLTI Utilities 2 | 3 | What is it? 4 | ----------- 5 | 6 | BasicLTI Utilities are a set of utility classes to aid in the development 7 | of BasicLTI consumers and providers. They deal with much of the heavy lifting 8 | and make the process more opaque to the developer. 9 | 10 | Licensing 11 | --------- 12 | 13 | Please see the file called LICENSE source directories. 14 | 15 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/BasicLTIUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti; 2 | 3 | import java.io.IOException; 4 | import java.net.URISyntaxException; 5 | import javax.servlet.http.HttpServletRequest; 6 | import net.oauth.OAuthAccessor; 7 | import net.oauth.OAuthException; 8 | import net.oauth.OAuthMessage; 9 | import net.oauth.SimpleOAuthValidator; 10 | import net.oauth.server.OAuthServlet; 11 | import net.oauth.signature.OAuthSignatureMethod; 12 | import org.imsglobal.lti.launch.LtiError; 13 | import org.imsglobal.lti.launch.LtiVerificationResult; 14 | import org.junit.Assert; 15 | import static org.junit.Assert.*; 16 | 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | import org.junit.runner.RunWith; 20 | import org.mockito.Matchers; 21 | import org.mockito.Mockito; 22 | import org.powermock.api.mockito.PowerMockito; 23 | import org.powermock.core.classloader.annotations.PrepareForTest; 24 | import org.powermock.modules.junit4.PowerMockRunner; 25 | 26 | @RunWith(PowerMockRunner.class) 27 | @PrepareForTest({OAuthServlet.class, OAuthSignatureMethod.class, SimpleOAuthValidator.class, BasicLTIUtil.class}) 28 | public class BasicLTIUtilTest { 29 | 30 | @Before 31 | public void setUp() throws Exception { 32 | } 33 | 34 | @Test 35 | public void testGetRealPath() { 36 | String fixed = BasicLTIUtil.getRealPath("http://localhost/path/blah/", "https://right.com"); 37 | assertEquals("https://right.com/path/blah/", fixed); 38 | fixed = BasicLTIUtil.getRealPath("https://localhost/path/blah/", "https://right.com"); 39 | assertEquals("https://right.com/path/blah/", fixed); 40 | fixed = BasicLTIUtil.getRealPath("https://localhost/path/blah/", "http://right.com"); 41 | assertEquals("http://right.com/path/blah/", fixed); 42 | 43 | // Test folks sending in URL with extra stuff... 44 | fixed = BasicLTIUtil.getRealPath("https://localhost/path/blah/", "https://right.com/path/blah"); 45 | assertEquals("https://right.com/path/blah/", fixed); 46 | } 47 | 48 | @Test 49 | public void testValidateMessageFailsWhenNoConsumerKey() throws IOException, Exception{ 50 | 51 | HttpServletRequest requestMock = Mockito.mock(HttpServletRequest.class); 52 | String url = "https://example.com/lti-launch"; 53 | 54 | PowerMockito.mockStatic(OAuthServlet.class); 55 | OAuthMessage messageMock = Mockito.mock(OAuthMessage.class); 56 | 57 | PowerMockito.when(OAuthServlet.getMessage(requestMock, url)).thenReturn(messageMock); 58 | 59 | Mockito.when(messageMock.getConsumerKey()).thenThrow(new IOException("io exception")); 60 | 61 | LtiVerificationResult result = BasicLTIUtil.validateMessage(requestMock, url, "secret"); 62 | 63 | Assert.assertEquals(LtiError.BAD_REQUEST, result.getError()); 64 | Assert.assertEquals(Boolean.FALSE, result.getSuccess()); 65 | 66 | } 67 | 68 | 69 | @Test 70 | public void testValidateMessageFailWhenUriIsMalformed() throws Exception { 71 | 72 | HttpServletRequest requestMock = Mockito.mock(HttpServletRequest.class); 73 | String url = "https://example.com/lti-launch"; 74 | 75 | PowerMockito.mockStatic(OAuthSignatureMethod.class); 76 | PowerMockito.when(OAuthSignatureMethod.getBaseString(Matchers.any(OAuthMessage.class))).thenThrow(new URISyntaxException("","",0)); 77 | 78 | LtiVerificationResult result = BasicLTIUtil.validateMessage(requestMock, url, "secret"); 79 | 80 | Assert.assertEquals(LtiError.BAD_REQUEST, result.getError()); 81 | Assert.assertEquals(Boolean.FALSE, result.getSuccess()); 82 | 83 | } 84 | 85 | @Test 86 | public void testValidateMessageFailOnIOException() throws Exception { 87 | 88 | HttpServletRequest requestMock = Mockito.mock(HttpServletRequest.class); 89 | String url = "https://example.com/lti-launch"; 90 | 91 | PowerMockito.mockStatic(OAuthSignatureMethod.class); 92 | PowerMockito.when(OAuthSignatureMethod.getBaseString(Matchers.any(OAuthMessage.class))).thenThrow(new IOException("")); 93 | 94 | LtiVerificationResult result = BasicLTIUtil.validateMessage(requestMock, url, "secret"); 95 | 96 | Assert.assertEquals(LtiError.BAD_REQUEST, result.getError()); 97 | Assert.assertEquals(Boolean.FALSE, result.getSuccess()); 98 | 99 | } 100 | 101 | @Test 102 | public void testValidateMessageFailOnValidateMessageIOException() throws Exception { 103 | 104 | SimpleOAuthValidator sov = Mockito.mock(SimpleOAuthValidator.class); 105 | PowerMockito.whenNew(SimpleOAuthValidator.class).withNoArguments().thenReturn(sov); 106 | Mockito.doThrow(new IOException("failed")).when(sov).validateMessage(Matchers.any(OAuthMessage.class), Matchers.any(OAuthAccessor.class)); 107 | PowerMockito.mockStatic(OAuthSignatureMethod.class); 108 | PowerMockito.when(OAuthSignatureMethod.getBaseString(Matchers.any(OAuthMessage.class))).thenReturn(""); 109 | 110 | LtiVerificationResult result = BasicLTIUtil.validateMessage(Mockito.mock(HttpServletRequest.class), "https://example.com/lti-launch", "secret"); 111 | 112 | Assert.assertEquals(LtiError.BAD_REQUEST, result.getError()); 113 | Assert.assertEquals(Boolean.FALSE, result.getSuccess()); 114 | Assert.assertEquals(null, result.getLtiLaunchResult()); 115 | } 116 | 117 | @Test 118 | public void testValidateMessageFailOnValidateMessageOAuthException() throws Exception { 119 | 120 | SimpleOAuthValidator sov = Mockito.mock(SimpleOAuthValidator.class); 121 | PowerMockito.whenNew(SimpleOAuthValidator.class).withNoArguments().thenReturn(sov); 122 | Mockito.doThrow(new OAuthException("failed")).when(sov).validateMessage(Matchers.any(OAuthMessage.class), Matchers.any(OAuthAccessor.class)); 123 | PowerMockito.mockStatic(OAuthSignatureMethod.class); 124 | PowerMockito.when(OAuthSignatureMethod.getBaseString(Matchers.any(OAuthMessage.class))).thenReturn(""); 125 | 126 | LtiVerificationResult result = BasicLTIUtil.validateMessage(Mockito.mock(HttpServletRequest.class), "https://example.com/lti-launch", "secret"); 127 | 128 | Assert.assertEquals(LtiError.BAD_REQUEST, result.getError()); 129 | Assert.assertEquals(Boolean.FALSE, result.getSuccess()); 130 | Assert.assertEquals(null, result.getLtiLaunchResult()); 131 | } 132 | 133 | @Test 134 | public void testValidateMessageFailOnValidateMessageURISyntaxException() throws Exception { 135 | 136 | SimpleOAuthValidator sov = Mockito.mock(SimpleOAuthValidator.class); 137 | PowerMockito.whenNew(SimpleOAuthValidator.class).withNoArguments().thenReturn(sov); 138 | Mockito.doThrow(new URISyntaxException("failed", "failed")).when(sov).validateMessage(Matchers.any(OAuthMessage.class), Matchers.any(OAuthAccessor.class)); 139 | PowerMockito.mockStatic(OAuthSignatureMethod.class); 140 | PowerMockito.when(OAuthSignatureMethod.getBaseString(Matchers.any(OAuthMessage.class))).thenReturn(""); 141 | 142 | LtiVerificationResult result = BasicLTIUtil.validateMessage(Mockito.mock(HttpServletRequest.class), "https://example.com/lti-launch", "secret"); 143 | 144 | Assert.assertEquals(LtiError.BAD_REQUEST, result.getError()); 145 | Assert.assertEquals(Boolean.FALSE, result.getSuccess()); 146 | Assert.assertEquals(null, result.getLtiLaunchResult()); 147 | } 148 | 149 | @Test 150 | public void testValidateMessagePass() throws Exception { 151 | 152 | SimpleOAuthValidator sov = Mockito.mock(SimpleOAuthValidator.class); 153 | PowerMockito.whenNew(SimpleOAuthValidator.class).withNoArguments().thenReturn(sov); 154 | Mockito.doNothing().when(sov).validateMessage(Matchers.any(OAuthMessage.class), Matchers.any(OAuthAccessor.class)); 155 | PowerMockito.mockStatic(OAuthSignatureMethod.class); 156 | PowerMockito.when(OAuthSignatureMethod.getBaseString(Matchers.any(OAuthMessage.class))).thenReturn(""); 157 | 158 | HttpServletRequest req = Mockito.mock(HttpServletRequest.class); 159 | Mockito.when(req.getParameter("user_id")).thenReturn("pgray"); 160 | Mockito.when(req.getParameter("roles")).thenReturn("instructor, teacher,administrator"); 161 | Mockito.when(req.getParameter("lti_version")).thenReturn("lpv1"); 162 | Mockito.when(req.getParameter("lti_message_type")).thenReturn("lti"); 163 | Mockito.when(req.getParameter("resource_link_id")).thenReturn("12345"); 164 | Mockito.when(req.getParameter("context_id")).thenReturn("9876"); 165 | Mockito.when(req.getParameter("launch_presentation_return_url")).thenReturn("http://example.com/return"); 166 | Mockito.when(req.getParameter("tool_consumer_instance_guid")).thenReturn("instance_id"); 167 | 168 | LtiVerificationResult result = BasicLTIUtil.validateMessage(req, "https://example.com/lti-launch", "secret1"); 169 | 170 | Assert.assertEquals(null, result.getError()); 171 | Assert.assertEquals(Boolean.TRUE, result.getSuccess()); 172 | Assert.assertNotNull(result.getLtiLaunchResult()); 173 | 174 | Assert.assertEquals("pgray", result.getLtiLaunchResult().getUser().getId()); 175 | Assert.assertEquals(3, result.getLtiLaunchResult().getUser().getRoles().size()); 176 | Assert.assertTrue(result.getLtiLaunchResult().getUser().getRoles().contains("instructor")); 177 | Assert.assertTrue(result.getLtiLaunchResult().getUser().getRoles().contains("teacher")); 178 | Assert.assertTrue(result.getLtiLaunchResult().getUser().getRoles().contains("administrator")); 179 | 180 | Assert.assertEquals("lpv1", result.getLtiLaunchResult().getVersion()); 181 | Assert.assertEquals("lti", result.getLtiLaunchResult().getMessageType()); 182 | Assert.assertEquals("12345", result.getLtiLaunchResult().getResourceLinkId()); 183 | Assert.assertEquals("9876", result.getLtiLaunchResult().getContextId()); 184 | Assert.assertEquals("http://example.com/return", result.getLtiLaunchResult().getLaunchPresentationReturnUrl()); 185 | Assert.assertEquals("instance_id", result.getLtiLaunchResult().getToolConsumerInstanceGuid()); 186 | 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/launch/BaseMockHttpServletRequest.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | 4 | import org.apache.http.Header; 5 | import org.apache.http.HttpRequest; 6 | 7 | import javax.servlet.RequestDispatcher; 8 | import javax.servlet.ServletInputStream; 9 | import javax.servlet.http.Cookie; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpSession; 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.UnsupportedEncodingException; 15 | import java.net.URI; 16 | import java.security.Principal; 17 | import java.util.*; 18 | 19 | /** 20 | * @author Paul Gray 21 | */ 22 | public abstract class BaseMockHttpServletRequest implements HttpServletRequest { 23 | 24 | HttpRequest req; 25 | 26 | public BaseMockHttpServletRequest(HttpRequest req) { 27 | this.req = req; 28 | } 29 | 30 | @Override 31 | public String getAuthType() { 32 | throw new UnsupportedOperationException("unsupported"); 33 | } 34 | 35 | @Override 36 | public Cookie[] getCookies() { 37 | throw new UnsupportedOperationException("unsupported"); 38 | } 39 | 40 | @Override 41 | public long getDateHeader(String name) { 42 | throw new UnsupportedOperationException("unsupported"); 43 | } 44 | 45 | @Override 46 | public String getHeader(String name) { 47 | throw new UnsupportedOperationException("unsupported"); 48 | } 49 | 50 | @Override 51 | public Enumeration getHeaders(String name) { 52 | List headerStrs = new ArrayList<>(); 53 | for(Header hdr: Arrays.asList(req.getHeaders(name))){ 54 | headerStrs.add(hdr.getValue()); 55 | } 56 | return Collections.enumeration(headerStrs); 57 | } 58 | 59 | @Override 60 | public Enumeration getHeaderNames() { 61 | List headerNames = new LinkedList<>(); 62 | for(Header hdr: req.getAllHeaders()) { 63 | headerNames.add(hdr.getName()); 64 | } 65 | return Collections.enumeration(headerNames); 66 | } 67 | 68 | @Override 69 | public int getIntHeader(String name) { 70 | throw new UnsupportedOperationException("unsupported"); 71 | } 72 | 73 | @Override 74 | public String getMethod() { 75 | throw new UnsupportedOperationException("unsupported"); 76 | } 77 | 78 | @Override 79 | public String getPathInfo() { 80 | throw new UnsupportedOperationException("unsupported"); 81 | } 82 | 83 | @Override 84 | public String getPathTranslated() { 85 | throw new UnsupportedOperationException("unsupported"); 86 | } 87 | 88 | @Override 89 | public String getContextPath() { 90 | throw new UnsupportedOperationException("unsupported"); 91 | } 92 | 93 | @Override 94 | public String getQueryString() { 95 | try { 96 | String q = new URI(req.getRequestLine().getUri()).getQuery(); 97 | return q; 98 | } catch(Exception e) { 99 | throw new RuntimeException(e); 100 | } 101 | } 102 | 103 | @Override 104 | public String getRemoteUser() { 105 | throw new UnsupportedOperationException("unsupported"); 106 | } 107 | 108 | @Override 109 | public boolean isUserInRole(String role) { 110 | throw new UnsupportedOperationException("unsupported"); 111 | } 112 | 113 | @Override 114 | public Principal getUserPrincipal() { 115 | throw new UnsupportedOperationException("unsupported"); 116 | } 117 | 118 | @Override 119 | public String getRequestedSessionId() { 120 | throw new UnsupportedOperationException("unsupported"); 121 | } 122 | 123 | @Override 124 | public String getRequestURI() { 125 | throw new UnsupportedOperationException("unsupported"); 126 | } 127 | 128 | @Override 129 | public StringBuffer getRequestURL() { 130 | try { 131 | String url = req.getRequestLine().getUri().replaceFirst("\\?(.*)", ""); 132 | return new StringBuffer(url); 133 | } catch (Exception e) { 134 | throw new RuntimeException(e); 135 | } 136 | } 137 | 138 | @Override 139 | public String getServletPath() { 140 | throw new UnsupportedOperationException("unsupported"); 141 | } 142 | 143 | @Override 144 | public HttpSession getSession(boolean create) { 145 | throw new UnsupportedOperationException("unsupported"); 146 | } 147 | 148 | @Override 149 | public HttpSession getSession() { 150 | throw new UnsupportedOperationException("unsupported"); 151 | } 152 | 153 | @Override 154 | public boolean isRequestedSessionIdValid() { 155 | throw new UnsupportedOperationException("unsupported"); 156 | } 157 | 158 | @Override 159 | public boolean isRequestedSessionIdFromCookie() { 160 | throw new UnsupportedOperationException("unsupported"); 161 | } 162 | 163 | @Override 164 | public boolean isRequestedSessionIdFromURL() { 165 | throw new UnsupportedOperationException("unsupported"); 166 | } 167 | 168 | @Override 169 | public boolean isRequestedSessionIdFromUrl() { 170 | throw new UnsupportedOperationException("unsupported"); 171 | } 172 | 173 | @Override 174 | public Object getAttribute(String name) { 175 | throw new UnsupportedOperationException("unsupported"); 176 | } 177 | 178 | @Override 179 | public Enumeration getAttributeNames() { 180 | throw new UnsupportedOperationException("unsupported"); 181 | } 182 | 183 | @Override 184 | public String getCharacterEncoding() { 185 | throw new UnsupportedOperationException("unsupported"); 186 | } 187 | 188 | @Override 189 | public void setCharacterEncoding(String env) throws UnsupportedEncodingException { 190 | 191 | } 192 | 193 | @Override 194 | public int getContentLength() { 195 | throw new UnsupportedOperationException("unsupported"); 196 | } 197 | 198 | @Override 199 | public String getContentType() { 200 | throw new UnsupportedOperationException("unsupported"); 201 | } 202 | 203 | @Override 204 | public ServletInputStream getInputStream() throws IOException { 205 | throw new UnsupportedOperationException("unsupported"); 206 | } 207 | 208 | @Override 209 | public String getParameter(String name) { 210 | return (String) this.getParameterMap().get(name); 211 | } 212 | 213 | @Override 214 | public Enumeration getParameterNames() { 215 | throw new UnsupportedOperationException("unsupported"); 216 | } 217 | 218 | @Override 219 | public String[] getParameterValues(String name) { 220 | throw new UnsupportedOperationException("unsupported"); 221 | } 222 | 223 | @Override 224 | public Map getParameterMap() { 225 | throw new UnsupportedOperationException("unsupported"); 226 | } 227 | 228 | @Override 229 | public String getProtocol() { 230 | throw new UnsupportedOperationException("unsupported"); 231 | } 232 | 233 | @Override 234 | public String getScheme() { 235 | throw new UnsupportedOperationException("unsupported"); 236 | } 237 | 238 | @Override 239 | public String getServerName() { 240 | throw new UnsupportedOperationException("unsupported"); 241 | } 242 | 243 | @Override 244 | public int getServerPort() { 245 | throw new UnsupportedOperationException("unsupported"); 246 | } 247 | 248 | @Override 249 | public BufferedReader getReader() throws IOException { 250 | throw new UnsupportedOperationException("unsupported"); 251 | } 252 | 253 | @Override 254 | public String getRemoteAddr() { 255 | throw new UnsupportedOperationException("unsupported"); 256 | } 257 | 258 | @Override 259 | public String getRemoteHost() { 260 | throw new UnsupportedOperationException("unsupported"); 261 | } 262 | 263 | @Override 264 | public void setAttribute(String name, Object o) { 265 | 266 | } 267 | 268 | @Override 269 | public void removeAttribute(String name) { 270 | 271 | } 272 | 273 | @Override 274 | public Locale getLocale() { 275 | throw new UnsupportedOperationException("unsupported"); 276 | } 277 | 278 | @Override 279 | public Enumeration getLocales() { 280 | throw new UnsupportedOperationException("unsupported"); 281 | } 282 | 283 | @Override 284 | public boolean isSecure() { 285 | throw new UnsupportedOperationException("unsupported"); 286 | } 287 | 288 | @Override 289 | public RequestDispatcher getRequestDispatcher(String path) { 290 | throw new UnsupportedOperationException("unsupported"); 291 | } 292 | 293 | @Override 294 | public String getRealPath(String path) { 295 | throw new UnsupportedOperationException("unsupported"); 296 | } 297 | 298 | @Override 299 | public int getRemotePort() { 300 | throw new UnsupportedOperationException("unsupported"); 301 | } 302 | 303 | @Override 304 | public String getLocalName() { 305 | throw new UnsupportedOperationException("unsupported"); 306 | } 307 | 308 | @Override 309 | public String getLocalAddr() { 310 | throw new UnsupportedOperationException("unsupported"); 311 | } 312 | 313 | @Override 314 | public int getLocalPort() { 315 | throw new UnsupportedOperationException("unsupported"); 316 | } 317 | 318 | protected static Map getQueryMap(String query) 319 | { 320 | String[] params = query.split("&"); 321 | Map map = new HashMap(); 322 | for (String param : params) 323 | { 324 | String name = param.split("=")[0]; 325 | String value = param.split("=")[1]; 326 | map.put(name, value); 327 | } 328 | return map; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/launch/LtiVerifierAndSignerTest.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import org.apache.http.HttpRequest; 4 | import org.apache.http.client.methods.HttpGet; 5 | import org.apache.http.client.methods.HttpPost; 6 | import org.apache.http.client.methods.HttpPut; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.*; 10 | import java.net.URI; 11 | 12 | 13 | /** 14 | * @author Paul Gray 15 | */ 16 | public class LtiVerifierAndSignerTest { 17 | 18 | LtiVerifier verifier = new LtiOauthVerifier(); 19 | LtiSigner signer = new LtiOauthSigner(); 20 | 21 | @Test 22 | public void verifierShouldVerifyCorrectlySignedLtiLaunches() throws Exception { 23 | 24 | String key = "key"; 25 | String secret = "secret"; 26 | HttpPost ltiLaunch = new HttpPost(new URI("http://example.com/test")); 27 | 28 | signer.sign(ltiLaunch, key, secret); 29 | LtiVerificationResult result = verifier.verify(new MockHttpPost(ltiLaunch), secret); 30 | 31 | assertTrue(result.getSuccess()); 32 | } 33 | 34 | @Test 35 | public void verifierShouldRejectIncorrectlySignedLtiLaunches() throws Exception { 36 | 37 | String key = "key"; 38 | String secret = "secret"; 39 | HttpPost ltiLaunch = new HttpPost(new URI("http://example.com/test")); 40 | 41 | signer.sign(ltiLaunch, key, secret); 42 | LtiVerificationResult result = verifier.verify(new MockHttpPost(ltiLaunch), "wrongSecret"); 43 | 44 | assertFalse(result.getSuccess()); 45 | } 46 | 47 | @Test 48 | public void verifierShouldVerifyCorrectlySignedLtiGetServiceRequests() throws Exception { 49 | 50 | String key = "key"; 51 | String secret = "secret"; 52 | HttpGet ltiServiceGetRequest = new HttpGet(new URI("http://example.com/test")); 53 | 54 | signer.sign(ltiServiceGetRequest, key, secret); 55 | LtiVerificationResult result = verifier.verify(new MockHttpGet(ltiServiceGetRequest), secret); 56 | 57 | assertTrue(result.getSuccess()); 58 | } 59 | 60 | @Test 61 | public void verifierShouldRejectIncorrectlySignedLtiGetServiceRequests() throws Exception { 62 | 63 | String key = "key"; 64 | String secret = "secret"; 65 | HttpGet ltiServiceGetRequest = new HttpGet(new URI("http://example.com/test")); 66 | 67 | signer.sign(ltiServiceGetRequest, key, secret); 68 | LtiVerificationResult result = verifier.verify(new MockHttpGet(ltiServiceGetRequest), "anotherWrongSecret"); 69 | 70 | assertFalse(result.getSuccess()); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/launch/MockHttpGet.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import org.apache.http.client.methods.HttpGet; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author Paul Gray 10 | */ 11 | public class MockHttpGet extends BaseMockHttpServletRequest { 12 | 13 | private HttpGet get; 14 | 15 | public MockHttpGet(HttpGet req) throws Exception { 16 | super(req); 17 | this.get = req; 18 | } 19 | 20 | @Override 21 | public String getMethod() { 22 | return "GET"; 23 | } 24 | 25 | @Override 26 | public Map getParameterMap() { 27 | String q = this.getQueryString(); 28 | if(q == null) { 29 | return new HashMap(); 30 | } else { 31 | return this.getQueryMap(this.getQueryString()); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/launch/MockHttpPost.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.launch; 2 | 3 | import org.apache.http.client.methods.HttpPost; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @author Paul Gray 9 | */ 10 | public class MockHttpPost extends BaseMockHttpServletRequest { 11 | 12 | private HttpPost post; 13 | 14 | public MockHttpPost(HttpPost req) throws Exception { 15 | super(req); 16 | this.post = req; 17 | } 18 | 19 | @Override 20 | public String getMethod() { 21 | return "POST"; 22 | } 23 | 24 | @Override 25 | public Map getParameterMap() { 26 | //todo: merge this with multipart body 27 | String q = this.getQueryString(); 28 | if(q == null) { 29 | return new HashMap(); 30 | } else { 31 | return this.getQueryMap(this.getQueryString()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/lti2/ProductInstanceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.imsglobal.lti.lti2; 18 | 19 | import com.fasterxml.jackson.core.JsonProcessingException; 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import com.fasterxml.jackson.databind.SerializationFeature; 22 | import java.io.IOException; 23 | import junit.framework.Assert; 24 | import org.imsglobal.lti2.objects.consumer.ProductInstance; 25 | import org.junit.Test; 26 | 27 | /** 28 | * 29 | * @author pgray 30 | */ 31 | public class ProductInstanceTest { 32 | 33 | public String getJson(Object o, Boolean prettyPrint) throws JsonProcessingException { 34 | ObjectMapper mapper = new ObjectMapper(); 35 | if(prettyPrint){ 36 | mapper.enable(SerializationFeature.INDENT_OUTPUT); 37 | } 38 | return mapper.writeValueAsString(o); 39 | } 40 | 41 | public T toObject(String json, Class claz) throws IOException { 42 | ObjectMapper mapper = new ObjectMapper(); 43 | return mapper.readValue(json, claz); 44 | } 45 | 46 | @Test 47 | public void serializeProfile() throws JsonProcessingException{ 48 | ProductInstance ppi = new ProductInstance(new TestLtiConsumerProfile()); 49 | ppi.addAdditionalProperty("test", "yolo"); 50 | ppi.addAdditionalProperty("another_test", "swag"); 51 | System.out.println(getJson(ppi, true)); 52 | } 53 | 54 | @Test 55 | public void deserializeProfile() throws JsonProcessingException, IOException{ 56 | ProductInstance ppi = new ProductInstance(new TestLtiConsumerProfile()); 57 | ppi.addAdditionalProperty("test", "yolo"); 58 | ppi.addAdditionalProperty("another_test", "swag"); 59 | 60 | String json = getJson(ppi, false); 61 | 62 | ProductInstance pi = toObject(json, ProductInstance.class); 63 | 64 | Assert.assertEquals("1", pi.getGuid()); 65 | Assert.assertEquals("yolo", pi.getAdditionalProperties().get("test")); 66 | Assert.assertEquals("swag", pi.getAdditionalProperties().get("another_test")); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/lti2/TestLtiConsumerProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.imsglobal.lti.lti2; 17 | 18 | import org.imsglobal.lti2.LTI2Config; 19 | import org.imsglobal.lti2.objects.consumer.ToolConsumer; 20 | 21 | /** 22 | * 23 | * @author pgray 24 | */ 25 | public class TestLtiConsumerProfile implements LTI2Config { 26 | 27 | public static String GUID = "1"; 28 | public static String SUPPORT_EMAIL = "test@example.com"; 29 | 30 | @Override 31 | public String getGuid() { 32 | return GUID; 33 | } 34 | 35 | @Override 36 | public String getSupport_email() { 37 | return SUPPORT_EMAIL; 38 | } 39 | 40 | @Override 41 | public String getService_owner_id() { 42 | return null; 43 | } 44 | 45 | @Override 46 | public String getService_owner_owner_name() { 47 | return null; 48 | } 49 | 50 | @Override 51 | public String getService_owner_description() { 52 | return null; 53 | } 54 | 55 | @Override 56 | public String getService_owner_support_email() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public String getService_provider_id() { 62 | return null; 63 | } 64 | 65 | @Override 66 | public String getService_provider_provider_name() { 67 | return null; 68 | } 69 | 70 | @Override 71 | public String getService_provider_description() { 72 | return null; 73 | } 74 | 75 | @Override 76 | public String getService_provider_support_email() { 77 | return null; 78 | } 79 | 80 | @Override 81 | public String getProduct_family_product_code() { 82 | return null; 83 | } 84 | 85 | @Override 86 | public String getProduct_family_vendor_code() { 87 | return null; 88 | } 89 | 90 | @Override 91 | public String getProduct_family_vendor_name() { 92 | return null; 93 | } 94 | 95 | @Override 96 | public String getProduct_family_vendor_description() { 97 | return null; 98 | } 99 | 100 | @Override 101 | public String getProduct_family_vendor_website() { 102 | return null; 103 | } 104 | 105 | @Override 106 | public String getProduct_family_vendor_contact() { 107 | return null; 108 | } 109 | 110 | @Override 111 | public String getProduct_info_product_name() { 112 | return null; 113 | } 114 | 115 | @Override 116 | public String getProduct_info_product_version() { 117 | return "1.0.0"; 118 | } 119 | 120 | @Override 121 | public String getProduct_info_product_description() { 122 | return null; 123 | } 124 | 125 | public String[] getCapabilities() { 126 | String[] caps = { 127 | ToolConsumer.LtiCapability.BASICLTI_LAUNCH, 128 | ToolConsumer.LtiCapability.USER_ID 129 | }; 130 | return caps; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/lti2/ToolConsumerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 IMS Global Learning Consortium. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.imsglobal.lti.lti2; 18 | 19 | import com.fasterxml.jackson.databind.DeserializationFeature; 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import com.fasterxml.jackson.databind.SerializationFeature; 22 | import org.imsglobal.lti2.objects.consumer.ToolConsumer; 23 | import org.junit.Test; 24 | 25 | import java.io.IOException; 26 | 27 | /** 28 | * 29 | * @author pgray 30 | */ 31 | public class ToolConsumerTest { 32 | 33 | 34 | @Test 35 | public void TestProfile() throws IOException { 36 | TestLtiConsumerProfile config = new TestLtiConsumerProfile(); 37 | ToolConsumer consumer = new ToolConsumer("guid", "LTI-2p0", "tcp?", config); 38 | //consumer.addCapabilites(config.getCapabilities()); 39 | ObjectMapper mapper = new ObjectMapper(); 40 | mapper.enable(SerializationFeature.INDENT_OUTPUT); 41 | mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); 42 | String json = mapper.writeValueAsString(consumer); 43 | System.out.println(json); 44 | 45 | ToolConsumer parsed = mapper.readValue(json, ToolConsumer.class); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/imsglobal/lti/pox/IMSPOXRequestTest.java: -------------------------------------------------------------------------------- 1 | package org.imsglobal.lti.pox; 2 | 3 | import org.apache.http.client.methods.HttpPost; 4 | import org.imsglobal.pox.IMSPOXRequest; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.Reader; 11 | import java.util.Date; 12 | import java.util.Map; 13 | 14 | /** 15 | * Created by pgray on 7/5/14. 16 | */ 17 | public class IMSPOXRequestTest { 18 | 19 | @Test 20 | public void test() { 21 | 22 | String messageId = String.valueOf(new Date().getTime()); 23 | String inputTestData = String.format(IMSPOXRequest.ReplaceResultMessageTemplate, messageId, "3124567", "A", ""); 24 | IMSPOXRequest pox = new IMSPOXRequest(inputTestData); 25 | Assert.assertEquals("V1.0", pox.getHeaderVersion()); 26 | Assert.assertEquals("replaceResultRequest", pox.getOperation()); 27 | Assert.assertEquals("should match messageId", messageId, pox.getHeaderMessageIdentifier()); 28 | 29 | Map bodyMap = pox.getBodyMap(); 30 | String guid = bodyMap.get("/resultRecord/sourcedGUID/sourcedId"); 31 | Assert.assertEquals("3124567", guid); 32 | String grade = bodyMap.get("/resultRecord/result/resultScore/textString"); 33 | Assert.assertEquals("A", grade); 34 | String resultScoreLang = bodyMap.get("/resultRecord/result/resultScore/language"); 35 | Assert.assertEquals("should have resultScore lang set to en", "en", resultScoreLang); 36 | } 37 | 38 | @Test 39 | public void testBuildReplaceResult() throws Exception { 40 | HttpPost post = IMSPOXRequest.buildReplaceResult("http://example.com", "key", "secret", "sourcedid", "0.95", "A", false); 41 | String header = post.getHeaders("Authorization")[0].getValue(); 42 | InputStream input = post.getEntity().getContent(); 43 | Reader reader = new InputStreamReader(input); 44 | String body = IMSPOXRequest.readPostBody(reader); 45 | 46 | IMSPOXRequest pox = new IMSPOXRequest(body); 47 | pox.setAuthHeader(header); 48 | pox.validatePostBody(); 49 | Assert.assertNull(pox.errorMessage); 50 | } 51 | } 52 | --------------------------------------------------------------------------------