map = cleanupProperties(
+ convertToMap(rawProperties), null);
+ return convertToProperties(map);
+ }
+
+ /**
+ * Checks to see if the passed propertyName is equal to one of the Strings
+ * contained in {@link BasicLTIConstants#validPropertyNames}. String
+ * matching is case sensitive.
+ *
+ * @param propertyName
+ * @return true if propertyName is equal to one of the Strings contained in
+ * {@link BasicLTIConstants#validPropertyNames} or is a custom parameter oe
+ * extension parameter ; else return false.
+ */
+ public static boolean isSpecifiedPropertyName(final String propertyName) {
+ boolean found = false;
+ if (propertyName.startsWith(CUSTOM_PREFIX)) {
+ return true;
+ }
+ if (propertyName.startsWith(EXTENSION_PREFIX)) {
+ return true;
+ }
+ if (propertyName.startsWith(OAUTH_PREFIX)) {
+ return true;
+ }
+ for (String key : BasicLTIConstants.validPropertyNames) {
+ if (key.equals(propertyName)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * 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)}
+ *
+ * @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}
+ * @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) {
+ final Map signedMap = signProperties(
+ convertToMap(postProp), url, method, oauth_consumer_key,
+ oauth_consumer_secret, org_id, org_desc, org_url, null, null);
+ 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}
+ * @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) {
+ postProp = BasicLTIUtil.cleanupProperties(postProp);
+
+ 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");
+ }
+
+ // Allow caller to internationalize this for us...
+ if (postProp.get(BASICLTI_SUBMIT) == null) {
+ postProp.put(BASICLTI_SUBMIT, "Launch Endpoint with BasicLTI Data");
+ }
+ 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);
+ }
+
+ if (postProp.get("oauth_callback") == null) {
+ postProp.put("oauth_callback", "about:blank");
+ }
+
+ if (oauth_consumer_key == null || oauth_consumer_secret == null) {
+ dPrint("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);
+ // System.out.println("Base Message String\n"+OAuthSignatureMethod.getBaseString(oam)+"\n");
+
+ 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) {
+ M_log.warning("BasicLTIUtil.signProperties OAuth Exception "
+ + e.getMessage());
+ throw new Error(e);
+ } catch (java.io.IOException e) {
+ M_log.warning("BasicLTIUtil.signProperties IO Exception "
+ + e.getMessage());
+ throw new Error(e);
+ } catch (java.net.URISyntaxException e) {
+ M_log.warning("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) {
+ M_log.warning(e.getLocalizedMessage());
+ base_string = null;
+ return false;
+ }
+
+ try {
+ oav.validateMessage(oam, acc);
+ } catch (Exception e) {
+ M_log.warning("Provider failed to validate message");
+ M_log.warning(e.getLocalizedMessage());
+ if (base_string != null) {
+ M_log.warning(base_string);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Create the HTML to render a POST form and then automatically submit it.
+ * Make sure to call {@link #cleanupProperties(Properties)} before signing.
+ *
+ * @deprecated Moved to {@link #postLaunchHTML(Map, String, boolean)}
+ * @param cleanProperties Assumes you have called
+ * {@link #cleanupProperties(Properties)} beforehand.
+ * @param endpoint The LTI launch url.
+ * @param debug Useful for viewing the HTML before posting to end point.
+ * @return the HTML ready for IFRAME src = inclusion.
+ */
+ public static String postLaunchHTML(final Properties cleanProperties,
+ String endpoint, boolean debug) {
+ Map map = convertToMap(cleanProperties);
+ return postLaunchHTML(map, endpoint, debug);
+ }
+
+ /**
+ * Create the HTML to render a POST form and then automatically submit it.
+ * Make sure to call {@link #cleanupProperties(Properties)} before signing.
+ *
+ * @param cleanProperties Assumes you have called
+ * {@link #cleanupProperties(Properties)} beforehand.
+ * @param endpoint The LTI launch url.
+ * @param debug 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, boolean debug) {
+ 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;
+ }
+ StringBuilder text = new StringBuilder();
+ // paint form
+ 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...
+ text.append("\n");
+
+ // paint debug output
+ if (debug) {
+ text.append("\n");
+ text.append("BasicLTI Endpoint\n");
+ text.append(endpoint);
+ text.append("\n\n");
+ text.append("BasicLTI 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");
+ } else {
+ // paint auto submit script
+ text
+ .append(" \n");
+ }
+
+ String htmltext = text.toString();
+ return htmltext;
+ }
+
+ /**
+ * @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) {
+ M_log.warning("BasicLTIUtil exception parsing BasicLTI descriptor: "
+ + e.getMessage());
+ return false;
+ }
+ if (tm == null) {
+ M_log.warning("Unable to parse XML in parseDescriptor");
+ return false;
+ }
+
+ String launch_url = StringUtils.stripToNull(XMLMap.getString(tm, "/basic_lti_link/launch_url"));
+ String secure_launch_url = StringUtils.stripToNull(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", StringUtils.stripToNull(XMLMap.getString(tm, "/basic_lti_link/x-secure/launch_key")));
+ setProperty(launch_info, "secret", StringUtils.stripToNull(XMLMap.getString(tm, "/basic_lti_link/x-secure/launch_secret")));
+
+ List