コード例 #1
0
  public String getBodyHash() throws OAuthException {
    byte[] output;
    try {
      MessageDigest md = MessageDigest.getInstance("SHA1");
      md.update(bodyAsByteArray);
      output = OAuthSignatureMethod.base64Encode(md.digest()).getBytes();
    } catch (Exception e) {
      throw new OAuthException("Could not compute body hash: " + e.getMessage());
    }

    return new String(output);
  }
コード例 #2
0
  @SuppressWarnings("unchecked")
  protected void doPostForm(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    String ipAddress = request.getRemoteAddr();

    M_log.debug("Basic LTI Service request from IP=" + ipAddress);

    String allowOutcomes =
        ServerConfigurationService.getString(
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED,
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED_DEFAULT);
    if (!"true".equals(allowOutcomes)) allowOutcomes = null;

    String allowSettings =
        ServerConfigurationService.getString(
            SakaiBLTIUtil.BASICLTI_SETTINGS_ENABLED,
            SakaiBLTIUtil.BASICLTI_SETTINGS_ENABLED_DEFAULT);
    if (!"true".equals(allowSettings)) allowSettings = null;

    String allowRoster =
        ServerConfigurationService.getString(
            SakaiBLTIUtil.BASICLTI_ROSTER_ENABLED, SakaiBLTIUtil.BASICLTI_ROSTER_ENABLED_DEFAULT);
    if (!"true".equals(allowRoster)) allowRoster = null;

    if (allowOutcomes == null && allowSettings == null && allowRoster == null) {
      M_log.warn("LTI Services are disabled IP=" + ipAddress);
      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
      return;
    }

    // Lets return an XML Response
    Map<String, Object> theMap = new TreeMap<String, Object>();

    Map<String, String[]> params = (Map<String, String[]>) request.getParameterMap();
    for (Map.Entry<String, String[]> param : params.entrySet()) {
      M_log.debug(param.getKey() + ":" + param.getValue()[0]);
    }

    // check lti_message_type
    String lti_message_type = request.getParameter(BasicLTIConstants.LTI_MESSAGE_TYPE);
    theMap.put("/message_response/lti_message_type", lti_message_type);
    String sourcedid = null;
    String message_type = null;
    if (BasicLTIUtil.equals(lti_message_type, "basic-lis-replaceresult")
        || BasicLTIUtil.equals(lti_message_type, "basic-lis-createresult")
        || BasicLTIUtil.equals(lti_message_type, "basic-lis-updateresult")
        || BasicLTIUtil.equals(lti_message_type, "basic-lis-deleteresult")
        || BasicLTIUtil.equals(lti_message_type, "basic-lis-readresult")) {
      sourcedid = request.getParameter("sourcedid");
      if (allowOutcomes != null) message_type = "basicoutcome";
    } else if (BasicLTIUtil.equals(lti_message_type, "basic-lti-loadsetting")
        || BasicLTIUtil.equals(lti_message_type, "basic-lti-savesetting")
        || BasicLTIUtil.equals(lti_message_type, "basic-lti-deletesetting")) {
      sourcedid = request.getParameter("id");
      if (allowSettings != null) message_type = "toolsetting";
    } else if (BasicLTIUtil.equals(lti_message_type, "basic-lis-readmembershipsforcontext")) {
      sourcedid = request.getParameter("id");
      if (allowRoster != null) message_type = "roster";
    } else {
      doError(
          request,
          response,
          theMap,
          "outcomes.invalid",
          "lti_message_type=" + lti_message_type,
          null);
      return;
    }

    // If we have not gotten one of our allowed message types, stop now
    if (message_type == null) {
      doError(
          request,
          response,
          theMap,
          "outcomes.invalid",
          "lti_message_type=" + lti_message_type,
          null);
      return;
    }

    // Perform the Outcomee first because we use the SakaiBLTIUtil code for this
    if ("basicoutcome".equals(message_type)) {
      processOutcome(request, response, lti_message_type, sourcedid, theMap);
      return;
    }

    // No point continuing without a sourcedid
    if (BasicLTIUtil.isBlank(sourcedid)) {
      doError(request, response, theMap, "outcomes.missing", "sourcedid", null);
      return;
    }

    String lti_version = request.getParameter(BasicLTIConstants.LTI_VERSION);
    if (!BasicLTIUtil.equals(lti_version, "LTI-1p0")) {
      doError(request, response, theMap, "outcomes.invalid", "lti_version=" + lti_version, null);
      return;
    }

    String oauth_consumer_key = request.getParameter("oauth_consumer_key");
    if (BasicLTIUtil.isBlank(oauth_consumer_key)) {
      doError(request, response, theMap, "outcomes.missing", "oauth_consumer_key", null);
      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) {
      doError(request, response, theMap, "outcomes.sourcedid", "sourcedid", 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");
      doError(request, response, theMap, "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) {
      doError(request, response, theMap, "outcomes.sourcedid", "sourcedid", null);
      return;
    }

    // Check the message signature using OAuth
    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);
    OAuthMessage oam = OAuthServlet.getMessage(request, URL);
    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) {
      M_log.error(e.getLocalizedMessage(), e);
      base_string = null;
    }

    try {
      oav.validateMessage(oam, acc);
    } catch (Exception e) {
      M_log.warn("Provider failed to validate message");
      M_log.warn(e.getLocalizedMessage(), e);
      if (base_string != null) {
        M_log.warn(base_string);
      }
      doError(request, response, theMap, "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) {
      doError(request, response, theMap, "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) {
      doError(request, response, theMap, "outcomes.sourcedid", "sourcedid", null);
      return;
    }

    // Perform the message-specific handling
    if ("toolsetting".equals(message_type))
      processSetting(
          request, response, lti_message_type, site, siteId, placement_id, pitch, user_id, theMap);

    if ("roster".equals(message_type))
      processRoster(
          request, response, lti_message_type, site, siteId, placement_id, pitch, user_id, theMap);
  }
コード例 #3
0
 /**
  * Add a signature to the message.
  *
  * @throws URISyntaxException
  */
 public void sign(OAuthAccessor accessor) throws IOException, OAuthException, URISyntaxException {
   OAuthSignatureMethod.newSigner(this, accessor).sign(this);
 }
コード例 #4
0
 protected void validateSignature(OAuthMessage message, OAuthAccessor accessor)
     throws OAuthException, IOException, URISyntaxException {
   message.requireParameters(
       OAuth.OAUTH_CONSUMER_KEY, OAuth.OAUTH_SIGNATURE_METHOD, OAuth.OAUTH_SIGNATURE);
   OAuthSignatureMethod.newSigner(message, accessor).validate(message);
 }
コード例 #5
0
  protected void validate(Map payload, boolean isTrustedConsumer) throws LTIException {
    // check parameters
    String lti_message_type = (String) payload.get(BasicLTIConstants.LTI_MESSAGE_TYPE);
    String lti_version = (String) payload.get(BasicLTIConstants.LTI_VERSION);
    String oauth_consumer_key = (String) payload.get("oauth_consumer_key");
    String resource_link_id = (String) payload.get(BasicLTIConstants.RESOURCE_LINK_ID);
    String user_id = (String) payload.get(BasicLTIConstants.USER_ID);
    String context_id = (String) payload.get(BasicLTIConstants.CONTEXT_ID);

    boolean launch = true;
    if (BasicLTIUtil.equals(lti_message_type, "basic-lti-launch-request")) {
      launch = true;
    } else if (BasicLTIUtil.equals(lti_message_type, "ContentItemSelectionRequest")) {
      launch = false;
    } else {
      throw new LTIException("launch.invalid", "lti_message_type=" + lti_message_type, null);
    }

    if (!BasicLTIUtil.equals(lti_version, "LTI-1p0")) {
      throw new LTIException("launch.invalid", "lti_version=" + lti_version, null);
    }

    if (BasicLTIUtil.isBlank(oauth_consumer_key)) {
      throw new LTIException("launch.missing", "oauth_consumer_key", null);
    }

    if (launch && BasicLTIUtil.isBlank(resource_link_id)) {
      throw new LTIException("launch.missing", "resource_link_id", null);
    }

    if (BasicLTIUtil.isBlank(user_id)) {
      throw new LTIException("launch.missing", "user_id", null);
    }
    if (M_log.isDebugEnabled()) {
      M_log.debug("user_id=" + user_id);
    }

    // check tool_id
    String tool_id = (String) payload.get("tool_id");
    if (tool_id == null) {
      throw new LTIException("launch.tool_id.required", null, null);
    }

    // Trim off the leading slash and any trailing space
    tool_id = tool_id.substring(1).trim();
    if (M_log.isDebugEnabled()) {
      M_log.debug("tool_id=" + tool_id);
    }
    // store modified tool_id back in payload
    payload.put("tool_id", tool_id);
    final String allowedToolsConfig =
        ServerConfigurationService.getString("basiclti.provider.allowedtools", "");

    final String[] allowedTools = allowedToolsConfig.split(":");
    final List<String> allowedToolsList = Arrays.asList(allowedTools);

    if (launch && allowedTools != null && !allowedToolsList.contains(tool_id)) {
      throw new LTIException("launch.tool.notallowed", tool_id, null);
    }
    final Tool toolCheck = ToolManager.getTool(tool_id);
    if (launch && toolCheck == null) {
      throw new LTIException("launch.tool.notfound", tool_id, null);
    }

    // Check for the ext_sakai_provider_eid param. If set, this will contain the eid that we are to
    // use
    // in place of using the user_id parameter
    // WE still need that parameter though, so translate it from the given eid.
    boolean useProvidedEid = false;
    String ext_sakai_provider_eid = (String) payload.get(BasicLTIConstants.EXT_SAKAI_PROVIDER_EID);
    if (BasicLTIUtil.isNotBlank(ext_sakai_provider_eid)) {
      useProvidedEid = true;
      try {
        user_id = UserDirectoryService.getUserId(ext_sakai_provider_eid);
      } catch (Exception e) {
        M_log.error(e.getLocalizedMessage(), e);
        throw new LTIException(
            "launch.provided.eid.invalid", "ext_sakai_provider_eid=" + ext_sakai_provider_eid, e);
      }
    }

    if (M_log.isDebugEnabled()) {
      M_log.debug("ext_sakai_provider_eid=" + ext_sakai_provider_eid);
    }

    // Contextualize the context_id with the OAuth consumer key
    // Also use the resource_link_id for the context_id if we did not get a context_id
    // BLTI-31: if trusted, context_id is required and use the param without modification
    if (BasicLTIUtil.isBlank(context_id)) {
      if (isTrustedConsumer) {
        throw new LTIException("launch.missing", context_id, null);
      } else {
        context_id = "res:" + resource_link_id;
        payload.put(BasicLTIConstants.CONTEXT_ID, context_id);
      }
    }

    // Check if context_id is simply a ~. If so, get the id of that user's My Workspace site
    // and use that to construct the full context_id
    if (BasicLTIUtil.equals(context_id, "~")) {
      if (useProvidedEid) {
        String userSiteId = null;
        try {
          userSiteId = SiteService.getUserSiteId(user_id);
        } catch (Exception e) {
          M_log.warn("Failed to get My Workspace site for user_id:" + user_id);
          M_log.error(e.getLocalizedMessage(), e);
          throw new LTIException("launch.user.site.unknown", "user_id=" + user_id, e);
        }
        context_id = userSiteId;
        payload.put(BasicLTIConstants.CONTEXT_ID, context_id);
      }
    }

    if (M_log.isDebugEnabled()) {
      M_log.debug("context_id=" + context_id);
    }

    // Lookup the secret
    final String configPrefix = "basiclti.provider." + oauth_consumer_key + ".";
    final String oauth_secret = ServerConfigurationService.getString(configPrefix + "secret", null);
    if (oauth_secret == null) {
      throw new LTIException("launch.key.notfound", oauth_consumer_key, null);
    }
    final OAuthMessage oam = (OAuthMessage) payload.get("oauth_message");

    final String forcedURIScheme =
        ServerConfigurationService.getString("basiclti.provider.forcedurischeme", null);

    if (forcedURIScheme != null) {
      try {
        URI testURI = new URI(oam.URL);
        URI newURI = new URI(forcedURIScheme, testURI.getSchemeSpecificPart(), null);
        oam.URL = newURI.toString();
      } catch (URISyntaxException use) {
      }
    }
    final OAuthValidator oav = new SimpleOAuthValidator();
    final OAuthConsumer cons =
        new OAuthConsumer(
            "about:blank#OAuth+CallBack+NotUsed", oauth_consumer_key, oauth_secret, null);

    final OAuthAccessor acc = new OAuthAccessor(cons);

    String base_string = null;
    try {
      base_string = OAuthSignatureMethod.getBaseString(oam);
    } catch (Exception e) {
      M_log.error(e.getLocalizedMessage(), e);
      base_string = null;
    }

    try {
      oav.validateMessage(oam, acc);
    } catch (Exception e) {
      M_log.warn("Provider failed to validate message");
      M_log.warn(e.getLocalizedMessage(), e);
      if (base_string != null) {
        M_log.warn(base_string);
      }
      throw new LTIException("launch.no.validate", context_id, e);
    }

    final Session sess = SessionManager.getCurrentSession();

    if (sess == null) {
      throw new LTIException("launch.no.session", context_id, null);
    }
  }