/* IMS POX XML versions of this service */ public void doErrorXML( HttpServletRequest request, HttpServletResponse response, IMSPOXRequest pox, String s, String message, Exception e) throws java.io.IOException { if (e != null) { M_log.error(e.getLocalizedMessage(), e); } String msg = rb.getString(s) + ": " + message; M_log.info(msg); response.setContentType("application/xml"); PrintWriter out = response.getWriter(); String output = null; if (pox == null) { output = IMSPOXRequest.getFatalResponse(msg); } else { String body = null; String operation = pox.getOperation(); if (operation != null) { body = "<" + operation.replace("Request", "Response") + "/>"; } output = pox.getResponseFailure(msg, null, body); } out.println(output); M_log.debug(output); }
protected void processOutcomeXml( HttpServletRequest request, HttpServletResponse response, String lti_message_type, String sourcedid, IMSPOXRequest pox) throws java.io.IOException { // Things look good - time to process the grade boolean isRead = BasicLTIUtil.equals(lti_message_type, "readResultRequest"); boolean isDelete = BasicLTIUtil.equals(lti_message_type, "deleteResultRequest"); Map<String, String> bodyMap = pox.getBodyMap(); String result_resultscore_textstring = bodyMap.get("/resultRecord/result/resultScore/textString"); String result_resultdata_text = bodyMap.get("/resultRecord/result/resultData/text"); String sourced_id = bodyMap.get("/resultRecord/result/sourcedId"); // System.out.println("comment="+result_resultdata_text); // System.out.println("grade="+result_resultscore_textstring); if (BasicLTIUtil.isBlank(result_resultscore_textstring) && !isRead && !isDelete) { doErrorXML(request, response, pox, "outcomes.missing", "result_resultscore_textstring", null); return; } // Lets return an XML Response Map<String, Object> theMap = new TreeMap<String, Object>(); String theGrade = null; boolean success = false; String message = null; Object retval = null; boolean strict = ServerConfigurationService.getBoolean(SakaiBLTIUtil.LTI_STRICT, false); try { Double dGrade; if (isRead) { retval = SakaiBLTIUtil.getGrade(sourcedid, request, ltiService); String sGrade = ""; String comment = ""; if (retval instanceof Map) { Map grade = (Map) retval; comment = (String) grade.get("comment"); dGrade = (Double) grade.get("grade"); if (dGrade != null) { sGrade = dGrade.toString(); } } else { Object check = SakaiBLTIUtil.checkSourceDid(sourcedid, request, ltiService); if (check instanceof Boolean && ((Boolean) check)) { // Read fail with Good SourceDID is treated as empty } else { doErrorXML(request, response, pox, "outcomes.fail", (String) retval, null); return; } } theMap.put("/readResultResponse/result/sourcedId", sourced_id); theMap.put("/readResultResponse/result/resultScore/textString", sGrade); theMap.put("/readResultResponse/result/resultScore/language", "en"); if (!strict) { theMap.put("/readResultResponse/result/resultData/text", comment); } message = "Result read"; } else if (isDelete) { retval = SakaiBLTIUtil.deleteGrade(sourcedid, request, ltiService); if (retval instanceof String) { doErrorXML(request, response, pox, "outcomes.fail", (String) retval, null); return; } theMap.put("/deleteResultResponse", ""); message = "Result deleted"; } else { dGrade = new Double(result_resultscore_textstring); if (dGrade < 0.0 || dGrade > 1.0) { throw new Exception("Grade out of range"); } dGrade = new Double(result_resultscore_textstring); retval = SakaiBLTIUtil.setGrade(sourcedid, request, ltiService, dGrade, result_resultdata_text); if (retval instanceof String) { doErrorXML(request, response, pox, "outcomes.fail", (String) retval, null); return; } theMap.put("/replaceResultResponse", ""); message = "Result replaced"; } success = true; } catch (Exception e) { doErrorXML(request, response, pox, "outcome.grade.fail", e.getMessage(), e); } if (!success) return; String output = null; String theXml = ""; if (theMap.size() > 0) theXml = XMLMap.getXMLFragment(theMap, true); output = pox.getResponseSuccess(message, theXml); response.setContentType("application/xml"); PrintWriter out = response.getWriter(); out.println(output); M_log.debug(output); }
@SuppressWarnings("unchecked") protected void doPostXml(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String ipAddress = request.getRemoteAddr(); M_log.debug("LTI POX Service request from IP=" + ipAddress); String allowOutcomes = ServerConfigurationService.getString( SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED, SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED_DEFAULT); if (!"true".equals(allowOutcomes)) allowOutcomes = null; if (allowOutcomes == null) { M_log.warn("LTI Services are disabled IP=" + ipAddress); response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } IMSPOXRequest pox = new IMSPOXRequest(request); if (!pox.valid) { doErrorXML(request, response, pox, "pox.invalid", pox.errorMessage, null); return; } // check lti_message_type String lti_message_type = pox.getOperation(); String sourcedid = null; String message_type = null; if (M_log.isDebugEnabled()) M_log.debug("POST\n" + XMLMap.prettyPrint(pox.postBody)); Map<String, String> bodyMap = pox.getBodyMap(); if (("replaceResultRequest".equals(lti_message_type) || "readResultRequest".equals(lti_message_type) || "deleteResultRequest".equals(lti_message_type)) && allowOutcomes != null) { sourcedid = bodyMap.get("/resultRecord/sourcedGUID/sourcedId"); message_type = "basicoutcome"; } else { String output = pox.getResponseUnsupported("Not supported " + lti_message_type); response.setContentType("application/xml"); PrintWriter out = response.getWriter(); out.println(output); return; } // No point continuing without a sourcedid if (BasicLTIUtil.isBlank(sourcedid)) { doErrorXML(request, response, pox, "outcomes.missing", "sourcedid", null); return; } // Handle the outcomes here using the new SakaiBLTIUtil code if (allowOutcomes != null && "basicoutcome".equals(message_type)) { processOutcomeXml(request, response, lti_message_type, sourcedid, pox); return; } // Truncate this to the maximum length to insure no cruft at the end if (sourcedid.length() > 2048) sourcedid = sourcedid.substring(0, 2048); // Attempt to parse the sourcedid, any failure is fatal String placement_id = null; String signature = null; String user_id = null; try { int pos = sourcedid.indexOf(":::"); if (pos > 0) { signature = sourcedid.substring(0, pos); String dec2 = sourcedid.substring(pos + 3); pos = dec2.indexOf(":::"); user_id = dec2.substring(0, pos); placement_id = dec2.substring(pos + 3); } } catch (Exception e) { // Log some detail for ourselves M_log.warn( "Unable to decrypt result_sourcedid IP=" + ipAddress + " Error=" + e.getMessage(), e); signature = null; placement_id = null; user_id = null; } // Send a more generic message back to the caller if (placement_id == null || user_id == null) { doErrorXML( request, response, pox, "outcomes.sourcedid", "missing user_id or placement_id", null); return; } M_log.debug("signature=" + signature); M_log.debug("user_id=" + user_id); M_log.debug("placement_id=" + placement_id); Properties pitch = SakaiBLTIUtil.getPropertiesFromPlacement(placement_id, ltiService); if (pitch == null) { M_log.debug("Error retrieving result_sourcedid information"); doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null); return; } String siteId = pitch.getProperty(LTIService.LTI_SITE_ID); Site site = null; try { site = SiteService.getSite(siteId); } catch (Exception e) { M_log.debug("Error retrieving result_sourcedid site: " + e.getLocalizedMessage(), e); } // Send a more generic message back to the caller if (site == null) { doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null); return; } // Check the message signature using OAuth String oauth_consumer_key = pox.getOAuthConsumerKey(); String oauth_secret = pitch.getProperty(LTIService.LTI_SECRET); M_log.debug("oauth_secret: " + oauth_secret); oauth_secret = SakaiBLTIUtil.decryptSecret(oauth_secret); M_log.debug("oauth_secret (decrypted): " + oauth_secret); String URL = SakaiBLTIUtil.getOurServletPath(request); pox.validateRequest(oauth_consumer_key, oauth_secret, request, URL); if (!pox.valid) { if (pox.base_string != null) { M_log.warn(pox.base_string); } doErrorXML(request, response, pox, "outcome.no.validate", oauth_consumer_key, null); return; } // Check the signature of the sourcedid to make sure it was not altered String placement_secret = pitch.getProperty(LTIService.LTI_PLACEMENTSECRET); // Send a generic message back to the caller if (placement_secret == null) { M_log.debug("placement_secret is null"); doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null); return; } String pre_hash = placement_secret + ":::" + user_id + ":::" + placement_id; String received_signature = LegacyShaUtil.sha256Hash(pre_hash); M_log.debug("Received signature=" + signature + " received=" + received_signature); boolean matched = signature.equals(received_signature); String old_placement_secret = pitch.getProperty(LTIService.LTI_OLDPLACEMENTSECRET); if (old_placement_secret != null && !matched) { pre_hash = placement_secret + ":::" + user_id + ":::" + placement_id; received_signature = LegacyShaUtil.sha256Hash(pre_hash); M_log.debug("Received signature II=" + signature + " received=" + received_signature); matched = signature.equals(received_signature); } // Send a message back to the caller if (!matched) { doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null); return; } response.setContentType("application/xml"); PrintWriter writer = response.getWriter(); String desc = "Message received and validated operation=" + pox.getOperation(); String output = pox.getResponseUnsupported(desc); writer.println(output); }