org.tsugi.basiclti.BasicLTIUtil Maven / Gradle / Ivy
/*
*
* $URL$
* $Id$
*
* Copyright (c) 2008-2016 Charles R. Severance
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.tsugi.basiclti;
import static org.tsugi.basiclti.BasicLTIConstants.LTI_MESSAGE_TYPE_BASICLTILAUNCHREQUEST;
import static org.tsugi.basiclti.BasicLTIConstants.LTI_MESSAGE_TYPE_CONTENTITEMSELECTIONREQUEST;
import static org.tsugi.basiclti.BasicLTIConstants.LTI_VERSION;
import static org.tsugi.basiclti.BasicLTIConstants.LTI_VERSION_1;
import static org.tsugi.basiclti.BasicLTIConstants.CUSTOM_PREFIX;
import static org.tsugi.basiclti.BasicLTIConstants.LTI_MESSAGE_TYPE;
import static org.tsugi.basiclti.BasicLTIConstants.TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL;
import static org.tsugi.basiclti.BasicLTIConstants.TOOL_CONSUMER_INSTANCE_DESCRIPTION;
import static org.tsugi.basiclti.BasicLTIConstants.TOOL_CONSUMER_INSTANCE_GUID;
import static org.tsugi.basiclti.BasicLTIConstants.TOOL_CONSUMER_INSTANCE_NAME;
import static org.tsugi.basiclti.BasicLTIConstants.TOOL_CONSUMER_INSTANCE_URL;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.TimeZone;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.TreeMap;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.text.SimpleDateFormat;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthMessage;
import net.oauth.OAuthValidator;
import net.oauth.SimpleOAuthValidator;
import net.oauth.server.OAuthServlet;
import net.oauth.signature.OAuthSignatureMethod;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import lombok.extern.slf4j.Slf4j;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
/* Leave out until we have JTidy 0.8 in the repository
import org.w3c.tidy.Tidy;
import java.io.ByteArrayOutputStream;
*/
/**
* Some Utility code for IMS LTI
* http://www.anyexample.com/programming/java
* /java_simple_class_to_compute_sha_1_hash.xml
*
* Sample Descriptor
*
*
* <?xml version="1.0" encoding="UTF-8"?>
* <basic_lti_link xmlns="http://www.imsglobal.org/xsd/imsbasiclti_v1p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
* <title>generated by tp+user</title>
* <description>generated by tp+user</description>
* <custom>
* <parameter key="keyname">value</parameter>
* </custom>
* <extensions platform="www.lms.com">
* <parameter key="keyname">value</parameter>
* </extensions>
* <launch_url>url to the basiclti launch URL</launch_url>
* <secure_launch_url>url to the basiclti launch URL</secure_launch_url>
* <icon>url to an icon for this tool (optional)</icon>
* <secure_icon>url to an icon for this tool (optional)</secure_icon>
* <cartridge_icon identifierref="BLTI001_Icon"/>
* <vendor>
* <code>vendor.com</code>
* <name>Vendor Name</name>
* <description>
* This is a Grade Book that supports many column types.
* </description>
* <contact>
* <email>[email protected]</email>
* </contact>
* <url>http://www.vendor.com/product</url>
* </vendor>
* </basic_lti_link>
*
*/
@Slf4j
public class BasicLTIUtil {
// How to make ISO8601 Dates
// https://stackoverflow.com/questions/2891361/how-to-set-time-zone-of-a-java-util-date
// https://stackoverflow.com/questions/2201925/converting-iso-8601-compliant-string-to-java-util-date
public static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ssz";
public static final String EXTRA_BUTTON_HTML = "button_html";
public static final String EXTRA_ERROR_TIMEOUT = "error_timeout";
public static final String EXTRA_HTTP_POPUP = "http_popup";
public static final String EXTRA_HTTP_POPUP_FALSE = "false";
public static final String EXTRA_JAVASCRIPT = "extra_javascript";
public static final String EXTRA_FORM_ID = "extra_form_id";
/** To turn on really verbose debugging */
private static boolean verbosePrint = false;
private static final Pattern CUSTOM_REGEX = Pattern.compile("[^A-Za-z0-9]");
private static final String UNDERSCORE = "_";
private static final String EMPTY_JSON_OBJECT = "{\n}\n";
// Returns true if this is a LTI message with minimum values to meet the protocol
public static boolean isRequest(HttpServletRequest request) {
String message_type = request.getParameter(LTI_MESSAGE_TYPE);
if ( message_type == null ) return false;
if ( message_type.equals(LTI_MESSAGE_TYPE_BASICLTILAUNCHREQUEST) ||
message_type.equals(LTI_MESSAGE_TYPE_CONTENTITEMSELECTIONREQUEST) ) {
// Seems plausible
} else {
return false;
}
String version = request.getParameter(LTI_VERSION);
if ( version == null ) return true;
if ( !version.equals(LTI_VERSION_1) ) return false;
return true;
}
// expected_oauth_key can be null - if it is non-null it must match the key in the request
public static Object validateMessage(HttpServletRequest request, String URL,
String oauth_secret, String expected_oauth_key)
{
OAuthMessage oam = OAuthServlet.getMessage(request, URL);
String oauth_consumer_key = null;
try {
oauth_consumer_key = oam.getConsumerKey();
} catch (Exception e) {
return "Unable to find consumer key in message";
}
if ( expected_oauth_key != null && ! expected_oauth_key.equals(oauth_consumer_key) ) {
log.warn("BasicLTIUtil.validateMessage Incorrect consumer key={} expected key={}", oauth_consumer_key, expected_oauth_key);
return "Incorrect consumer key "+oauth_consumer_key;
}
OAuthValidator oav = new SimpleOAuthValidator();
OAuthConsumer cons = new OAuthConsumer("about:blank#OAuth+CallBack+NotUsed", oauth_consumer_key,oauth_secret, null);
OAuthAccessor acc = new OAuthAccessor(cons);
String base_string = null;
try {
base_string = OAuthSignatureMethod.getBaseString(oam);
} catch (Exception e) {
return "Unable to find base string";
}
try {
oav.validateMessage(oam, acc);
} catch (Exception e) {
if (base_string != null) {
return "Failed to validate: "+e.getLocalizedMessage()+"\nBase String\n"+base_string;
}
return "Failed to validate: "+e.getLocalizedMessage();
}
return Boolean.TRUE;
}
public static String validateDescriptor(String descriptor) {
if (descriptor == null)
return null;
if (descriptor.indexOf(" tm = XMLMap.getFullMap(descriptor.trim());
if (tm == null)
return null;
// We demand at least an endpoint
String ltiSecureLaunch = XMLMap.getString(tm,
"/basic_lti_link/secure_launch_url");
// We demand at least an endpoint
if (ltiSecureLaunch != null && ltiSecureLaunch.trim().length() > 0)
return ltiSecureLaunch;
String ltiLaunch = XMLMap.getString(tm, "/basic_lti_link/launch_url");
if (ltiLaunch != null && ltiLaunch.trim().length() > 0)
return ltiLaunch;
return null;
}
/**
* A simple utility method which implements the specified semantics of custom
* properties.
*
* i.e. The parameter names are mapped to lower case and any character that is
* neither a number nor letter in a parameter name is replaced with an
* "underscore".
*
* e.g. Review:Chapter=1.2.56 would map to custom_review_chapter=1.2.56.
*
* @param propertyName
* @return
*/
public static String adaptToCustomPropertyName(final String propertyName) {
if (propertyName == null || "".equals(propertyName)) {
throw new IllegalArgumentException("propertyName cannot be null");
}
String customName = propertyName.toLowerCase();
customName = CUSTOM_REGEX.matcher(customName).replaceAll(UNDERSCORE);
if (!customName.startsWith(CUSTOM_PREFIX)) {
customName = CUSTOM_PREFIX + customName;
}
return customName;
}
/**
* Add the necessary fields and sign.
*
* @deprecated See:
* {@link BasicLTIUtil#signProperties(Map, String, String, String, String, String, String, String, String, String, Map)}
*
* @param postProp
* @param url
* @param method
* @param oauth_consumer_key
* @param oauth_consumer_secret
* @param extra
* @return
*/
public static Properties signProperties(Properties postProp, String url,
String method, String oauth_consumer_key, String oauth_consumer_secret,
Map extra) {
final Map signedMap = signProperties(
convertToMap(postProp), url, method, oauth_consumer_key,
oauth_consumer_secret, null, null, null, null, null, extra);
return convertToProperties(signedMap);
}
/**
* Add the necessary fields and sign.
*
* @deprecated See:
* {@link BasicLTIUtil#signProperties(Map, String, String, String, String, String, String, String, String, String, Map)}
*
* @param postProp
* @param url
* @param method
* @param oauth_consumer_key
* @param oauth_consumer_secret
* @param org_id
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_GUID}
* @param org_desc
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_DESCRIPTION}
* @param org_url
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_URL}
* @param extra
* @return
*/
public static Properties signProperties(Properties postProp, String url,
String method, String oauth_consumer_key, String oauth_consumer_secret,
String org_id, String org_desc, String org_url, Map extra) {
final Map signedMap = signProperties(
convertToMap(postProp), url, method, oauth_consumer_key,
oauth_consumer_secret, org_id, org_desc, org_url, null, null, extra);
return convertToProperties(signedMap);
}
/**
* Add the necessary fields and sign.
*
* @param postProp
* @param url
* @param method
* @param oauth_consumer_key
* @param oauth_consumer_secret
* @param tool_consumer_instance_guid
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_GUID}
* @param tool_consumer_instance_description
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_DESCRIPTION}
* @param tool_consumer_instance_url
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_URL}
* @param tool_consumer_instance_name
* See: {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_NAME}
* @param tool_consumer_instance_contact_email
* See:
* {@link BasicLTIConstants#TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL}
* @param extra
* @return
*/
public static Map signProperties(
Map postProp, String url, String method,
String oauth_consumer_key, String oauth_consumer_secret,
String tool_consumer_instance_guid,
String tool_consumer_instance_description,
String tool_consumer_instance_url, String tool_consumer_instance_name,
String tool_consumer_instance_contact_email,
Map extra) {
if (tool_consumer_instance_guid != null)
postProp.put(TOOL_CONSUMER_INSTANCE_GUID, tool_consumer_instance_guid);
if (tool_consumer_instance_description != null)
postProp.put(TOOL_CONSUMER_INSTANCE_DESCRIPTION, tool_consumer_instance_description);
if (tool_consumer_instance_url != null)
postProp.put(TOOL_CONSUMER_INSTANCE_URL, tool_consumer_instance_url);
if (tool_consumer_instance_name != null)
postProp.put(TOOL_CONSUMER_INSTANCE_NAME, tool_consumer_instance_name);
if (tool_consumer_instance_contact_email != null)
postProp.put(TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL, tool_consumer_instance_contact_email);
return signProperties(postProp, url, method, oauth_consumer_key, oauth_consumer_secret, extra);
}
/**
* Add the necessary fields and sign.
*
* @param postProp
* @param url
* @param method
* @param oauth_consumer_key
* @param oauth_consumer_secret
* @param extra
* @return
*/
public static Map signProperties(
Map postProp, String url, String method,
String oauth_consumer_key, String oauth_consumer_secret,
Map extra) {
if ( postProp.get(LTI_VERSION) == null ) postProp.put(LTI_VERSION, "LTI-1p0");
if ( postProp.get(LTI_MESSAGE_TYPE) == null ) postProp.put(LTI_MESSAGE_TYPE, "basic-lti-launch-request");
if (postProp.get("oauth_callback") == null)
postProp.put("oauth_callback", "about:blank");
if (oauth_consumer_key == null || oauth_consumer_secret == null) {
log.debug("No signature generated in signProperties");
return postProp;
}
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet());
OAuthConsumer cons = new OAuthConsumer("about:blank", oauth_consumer_key,
oauth_consumer_secret, null);
OAuthAccessor acc = new OAuthAccessor(cons);
try {
oam.addRequiredParameters(acc);
String base_string = OAuthSignatureMethod.getBaseString(oam);
log.debug("Base Message String\n{}\n", base_string);
if ( extra != null ) {
extra.put("BaseString", base_string);
}
List> params = oam.getParameters();
Map nextProp = new HashMap();
// Convert to Map
for (final Map.Entry entry : params) {
nextProp.put(entry.getKey(), entry.getValue());
}
return nextProp;
} catch (net.oauth.OAuthException e) {
log.warn("BasicLTIUtil.signProperties OAuth Exception {}", e.getMessage());
throw new Error(e);
} catch (java.io.IOException e) {
log.warn("BasicLTIUtil.signProperties IO Exception {}", e.getMessage());
throw new Error(e);
} catch (java.net.URISyntaxException e) {
log.warn("BasicLTIUtil.signProperties URI Syntax Exception {}", e.getMessage());
throw new Error(e);
}
}
/**
* Check if the properties are properly signed
*
* @deprecated See:
* {@link BasicLTIUtil#checkProperties(Map, String, String, String, String)}
*
* @param postProp
* @param url
* @param method
* @param oauth_consumer_key
* @param oauth_consumer_secret
* @return
*/
public static boolean checkProperties(Properties postProp, String url,
String method, String oauth_consumer_key, String oauth_consumer_secret)
{
return checkProperties( convertToMap(postProp), url, method,
oauth_consumer_key, oauth_consumer_secret);
}
/**
* Check if the fields are properly signed
*
* @param postProp
* @param url
* @param method
* @param oauth_consumer_key
* @param oauth_consumer_secret
* @return
*/
public static boolean checkProperties(
Map postProp, String url, String method,
String oauth_consumer_key, String oauth_consumer_secret) {
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet());
OAuthConsumer cons = new OAuthConsumer("about:blank", oauth_consumer_key,
oauth_consumer_secret, null);
OAuthValidator oav = new SimpleOAuthValidator();
OAuthAccessor acc = new OAuthAccessor(cons);
String base_string = null;
try {
base_string = OAuthSignatureMethod.getBaseString(oam);
} catch (Exception e) {
log.warn(e.getLocalizedMessage());
base_string = null;
return false;
}
try {
oav.validateMessage(oam, acc);
} catch (Exception e) {
log.warn("Provider failed to validate message");
log.warn(e.getLocalizedMessage());
if (base_string != null) {
log.warn(base_string);
}
return false;
}
return true;
}
/**
* Create the HTML to render a POST form and then automatically submit it.
*
* @deprecated Moved to {@link #postLaunchHTML(Map, String, String, boolean, Map)}
* @param cleanProperties
* @param endpoint
* The LTI launch url.
* @param launchtext
* The LTI launch text. Used if javascript is turned off.
* @param debug
* Useful for viewing the HTML before posting to end point.
* @param extra
* @return the HTML ready for IFRAME src = inclusion.
*/
public static String postLaunchHTML(final Properties cleanProperties,
String endpoint, String launchtext, boolean debug, Map extra) {
Map map = convertToMap(cleanProperties);
return postLaunchHTML(map, endpoint, launchtext, debug, extra);
}
/**
* Create the HTML to render a POST form and then automatically submit it.
*
* @deprecated Moved to {@link #postLaunchHTML(Map, String, String, boolean, boolean, Map)}
* @param cleanProperties
* @param endpoint
* The LTI launch url.
* @param launchtext
* The LTI launch text. Used if javascript is turned off.
* @param autosubmit
* Whether or not we want the form autosubmitted
* @param debug
* Useful for viewing the HTML before posting to end point.
* @param extra
* @return the HTML ready for IFRAME src = inclusion.
*/
public static String postLaunchHTML(final Properties cleanProperties,
String endpoint, String launchtext, boolean autosubmit, boolean debug, Map extra) {
Map map = convertToMap(cleanProperties);
return postLaunchHTML(map, endpoint, launchtext, autosubmit, debug, extra);
}
/**
* Create the HTML to render a POST form and then automatically submit it.
*
* @param cleanProperties
* @param endpoint
* The LTI launch url.
* @param launchtext
* The LTI launch text. Used if javascript is turned off.
* @param debug
* Useful for viewing the HTML before posting to end point.
* @param extra
* Useful for viewing the HTML before posting to end point.
* @return the HTML ready for IFRAME src = inclusion.
*/
public static String postLaunchHTML(
final Map cleanProperties, String endpoint,
String launchtext, boolean debug, Map extra) {
// Assume autosubmit is true for backwards compatibility
boolean autosubmit = true;
return postLaunchHTML(cleanProperties, endpoint, launchtext, autosubmit, debug, extra);
}
/**
* Create the HTML to render a POST form and then automatically submit it.
*
* @param cleanProperties
* @param endpoint
* The LTI launch url.
* @param launchtext
* The LTI launch text. Used if javascript is turned off.
* @param autosubmit
* Whether or not we want the form autosubmitted
* @param extra
* Useful for viewing the HTML before posting to end point.
* @return the HTML ready for IFRAME src = inclusion.
*/
public static String postLaunchHTML(
final Map cleanProperties, String endpoint,
String launchtext, boolean autosubmit, boolean debug,
Map extra) {
if (cleanProperties == null || cleanProperties.isEmpty()) {
throw new IllegalArgumentException(
"cleanProperties == null || cleanProperties.isEmpty()");
}
if (endpoint == null) {
throw new IllegalArgumentException("endpoint == null");
}
Map newMap = null;
if (debug) {
// sort the properties for readability
newMap = new TreeMap(cleanProperties);
} else {
newMap = cleanProperties;
}
if ( extra == null ) extra = new TreeMap();
StringBuilder text = new StringBuilder();
// paint form
String submit_uuid = UUID.randomUUID().toString().replace("-","_");
String submit_form_id = extra.get(EXTRA_FORM_ID);
if ( submit_form_id == null ) submit_form_id = "ltiLaunchForm_"+submit_uuid;
text.append("\n");
text.append("\n");
text.append("\n");
// Paint the auto-pop up if we are transitioning from https: to http:
// and are not already the top frame...
String error_timeout = null;
String http_popup = null;
if ( extra != null ) {
error_timeout = extra.get(EXTRA_ERROR_TIMEOUT);
http_popup = extra.get(EXTRA_HTTP_POPUP);
}
if ( extra == null ) error_timeout = "Unable to send launch to remote URL";
text.append("\n");
// paint debug output
if (debug) {
text.append("\n");
text.append("LTI Endpoint\n");
text.append(endpoint);
text.append("\n\n");
text.append("LTI Parameters:\n");
for (Entry entry : newMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (value == null)
continue;
text.append(htmlspecialchars(key));
text.append("=");
text.append(htmlspecialchars(value));
text.append("\n");
}
text.append("
\n");
if ( extra != null ) {
String base_string = extra.get("BaseString");
if ( base_string != null ) {
text.append("","__>"));
text.append("\n-->\n");
}
}
} else if ( autosubmit ) {
// paint auto submit script
text.append(" \n");
}
String extraJavaScript = extra.get(EXTRA_JAVASCRIPT);
if ( extraJavaScript != null ) {
text.append(" \n");
}
String htmltext = text.toString();
return htmltext;
}
/**
* getOAuthURL - Form a GET request signed by OAuth
* @param method
* @param url
* @param oauth_consumer_key
* @param oauth_secret
*/
public static String getOAuthURL(String method, String url,
String oauth_consumer_key, String oauth_secret)
{
return getOAuthURL(method, url, oauth_consumer_key, oauth_secret, null);
}
/**
* getOAuthURL - Form a GET request signed by OAuth
* @param method
* @param url
* @param oauth_consumer_key
* @param oauth_secret
* @param signature
*/
public static String getOAuthURL(String method, String url,
String oauth_consumer_key, String oauth_secret, String signature)
{
if ( url == null ) return null;
OAuthMessage om = new OAuthMessage(method, url, null);
om.addParameter(OAuth.OAUTH_CONSUMER_KEY, oauth_consumer_key);
if ( signature == null ) signature = OAuth.HMAC_SHA1;
om.addParameter(OAuth.OAUTH_SIGNATURE_METHOD, signature);
om.addParameter(OAuth.OAUTH_VERSION, "1.0");
om.addParameter(OAuth.OAUTH_TIMESTAMP, new Long((new Date().getTime()) / 1000).toString());
om.addParameter(OAuth.OAUTH_NONCE, UUID.randomUUID().toString());
OAuthConsumer oc = new OAuthConsumer(null, oauth_consumer_key, oauth_secret, null);
try {
OAuthSignatureMethod osm = OAuthSignatureMethod.newMethod(signature, new OAuthAccessor(oc));
osm.sign(om);
url = OAuth.addParameters(url, om.getParameters());
return url;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* getOAuthURL - Form a GET request signed by OAuth
* @param method
* @param url
* @param oauth_consumer_key
* @param oauth_secret
* HttpURLConnection connection = sendOAuthURL('GET', url, oauth_consumer_key, oauth_secret)
* int responseCode = connection.getResponseCode();
* String data = readHttpResponse(connection)
*/
public static HttpURLConnection sendOAuthURL(String method, String url, String oauth_consumer_key, String oauth_secret)
{
String oauthURL = getOAuthURL(method, url, oauth_consumer_key, oauth_secret);
try {
URL urlConn = new URL(oauthURL);
HttpURLConnection connection = (HttpURLConnection) urlConn.openConnection();
connection.setRequestMethod(method);
// Since Java won't send Content-length unless we really send
// content - send some data character so we don't
// send a broken PUT
if ( ! "GET".equals(method) ) {
connection.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream());
out.write("42");
out.close();
}
connection.connect();
int responseCode = connection.getResponseCode();
return connection;
} catch(Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* getResponseCode - Read the HTTP Response
* @param connection
*/
public static int getResponseCode(HttpURLConnection connection)
{
try {
return connection.getResponseCode();
} catch(Exception e) {
return HttpURLConnection.HTTP_INTERNAL_ERROR;
}
}
/**
* readHttpResponse - Read the HTTP Response
* @param connection
*/
public static String readHttpResponse(HttpURLConnection connection)
{
try {
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
} catch(Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* @deprecated See: {@link #parseDescriptor(Map, Map, String)}
* @param launch_info
* Variable is mutated by this method.
* @param postProp
* Variable is mutated by this method.
* @param descriptor
* @return
*/
public static boolean parseDescriptor(Properties launch_info,
Properties postProp, String descriptor) {
// this is an ugly copy/paste of the non-@deprecated method
// could not convert data types as they variables get mutated (ugh)
Map tm = null;
try {
tm = XMLMap.getFullMap(descriptor.trim());
} catch (Exception e) {
log.warn("BasicLTIUtil exception parsing BasicLTI descriptor: {}", e.getMessage());
return false;
}
if (tm == null) {
log.warn("Unable to parse XML in parseDescriptor");
return false;
}
String launch_url = toNull(XMLMap.getString(tm,
"/basic_lti_link/launch_url"));
String secure_launch_url = toNull(XMLMap.getString(tm,
"/basic_lti_link/secure_launch_url"));
if (launch_url == null && secure_launch_url == null)
return false;
setProperty(launch_info, "launch_url", launch_url);
setProperty(launch_info, "secure_launch_url", secure_launch_url);
// Extensions for hand-authored placements - The export process should scrub
// these
setProperty(launch_info, "key", toNull(XMLMap.getString(tm,
"/basic_lti_link/x-secure/launch_key")));
setProperty(launch_info, "secret", toNull(XMLMap.getString(tm,
"/basic_lti_link/x-secure/launch_secret")));
List