/** * Validate the provided trust token. * * @param token : The token to validate * @return boolean : True if the token is valid, False otherwise */ @Override public boolean testTrustToken(JsonSessionState session, String token) { String[] parts = StringUtils.split(token, ":"); // Check the length if (parts.length != 4) { log.error("TOKEN: Should have 4 parts, not {} : '{}'", parts.length, token); return false; } // Check the parts String username = parts[0]; String timestamp = parts[1]; String publicKey = parts[2]; String userToken = parts[3]; if (username.isEmpty() || timestamp.isEmpty() || publicKey.isEmpty() || userToken.isEmpty()) { log.error("TOKEN: One or more parts are empty : '{}'", token); return false; } // Make sure the publicKey is valid if (!tokens.containsKey(publicKey)) { log.error("TOKEN: Invalid public key : '{}'", publicKey); return false; } String privateKey = tokens.get(publicKey); Long expiry = tokenExpiry.get(publicKey); // Check for valid timestamp & expiry timestamp = getFormattedTime(timestamp); if (timestamp == null) { log.error("TOKEN: Invalid timestamp : '{}'", timestamp); return false; } Long tokenTime = Long.valueOf(timestamp); Long currentTime = Long.valueOf(getFormattedTime(null)); Long age = currentTime - tokenTime; if (age > expiry) { log.error("Token is passed its expiry : {}s old", age); return false; } // Now validate the token itself String tokenSeed = username + ":" + timestamp + ":" + privateKey; String expectedToken = DigestUtils.md5Hex(tokenSeed); if (userToken.equals(expectedToken)) { // The token is valid session.set("username", username); session.set("source", TRUST_TOKEN_PREFIX + publicKey); // Store it in case we redirect later session.set("validToken", token); return true; } // Token was not valid log.error("TOKEN: Invalid token, hash does not match: '{}'", userToken); return false; }
@Override public void ssoPrepareLogin(JsonSessionState session, String returnAddress, String server) throws Exception { logger.trace( String.format("ssoPrepareLogin, Return Address: %s Server: %s", returnAddress, server)); session.set(RETURN_ADDRESS, returnAddress); }
/** * Get user details from SSO connection and set them in the user session. * * @return boolean: Flag whether a user was actually logged in or not. */ @Override public boolean ssoCheckUserDetails(JsonSessionState session) { // After the SSO roun-trip, restore any old query parameters we lost List<String> currentParams = request.getParameterNames(); // Cast a copy of keySet() to array to avoid errors as we modify String[] oldParams = session.keySet().toArray(new String[0]); // Loop through session data... for (String key : oldParams) { // ... looking for SSO stored params if (key.startsWith(SSO_STORAGE_PREFIX)) { // Remove our prefix... String oldParam = key.replace(SSO_STORAGE_PREFIX, ""); // ... and check if it survived the trip if (!currentParams.contains(oldParam)) { // No it didn't, add it to form data... the parameters are // already accessible from there in Jython String data = (String) session.get(key); formData.set(oldParam, data); // Don't forget to clear it from the session session.remove(key); } } } // Check our SSO providers for valid logins for (String ssoId : sso.keySet()) { sso.get(ssoId).ssoCheckUserDetails(session); GenericUser user = (GenericUser) sso.get(ssoId).getUserObject(session); if (user != null) { session.set("username", user.getUsername()); session.set("source", ssoId); return true; } } return false; }
@SuppressWarnings("unchecked") private void addAttr(String key, String value, JsonSessionState session) { Object o = session.get(key); List<String> l; if (o == null) { session.set(key, l = new ArrayList<String>()); } else { l = (List<String>) o; } List<String> vals; if (SHIB_ATTRIBUTE_DELIMITER != null) { vals = Arrays.asList(value.split(SHIB_ATTRIBUTE_DELIMITER)); logger.trace(String.format("Adding: %s : %s", key, vals)); } else { vals = new ArrayList<String>(); vals.add(value); } l.addAll(vals); }
/** * Initialize the SSO Service, prepare a login if required * * @param session The server session data * @throws Exception if any errors occur */ @Override public String ssoInit(JsonSessionState session) throws Exception { // Keep track of the user switching portals for // link building in other methods String portalId = (String) session.get("portalId", defaultPortal); ssoLoginUrl = serverUrlBase + portalId + SSO_LOGIN_PAGE; // Find out what page we are on String path = request.getAttribute("RequestURI").toString(); String currentAddress = serverUrlBase + path; // Store the portal URL, might be required by implementers to build // an interface (images etc). session.set("ssoPortalUrl", serverUrlBase + portalId); // Makes sure all SSO plugins get initialised for (String ssoId : sso.keySet()) { sso.get(ssoId).ssoInit(session, rg.getHTTPServletRequest()); } // Are we logging in right now? String ssoId = request.getParameter("ssoId"); // If this isn't the login page... if (!currentAddress.contains(SSO_LOGIN_PAGE)) { // Store the current address for use later session.set("returnAddress", currentAddress); // We might still be logging in from a deep link if (ssoId == null) { // No we're not, finished now return null; } else { // Yes it's a deep link, store any extra query params // since they probably won't survive the round-trip // through SSO. for (String param : request.getParameterNames()) { if (!param.equals("ssoId")) { // Store all the other parameters session.set(SSO_STORAGE_PREFIX + param, request.getParameter(param)); } } } } // Get the last address to return the user to String returnAddress = (String) session.get("returnAddress"); if (returnAddress == null) { // Or use the home page returnAddress = serverUrlBase + portalId + "/home"; } // Which SSO provider did the user request? if (ssoId == null) { log.error("==== SSO: SSO ID not found!"); return null; } if (!sso.containsKey(ssoId)) { log.error("==== SSO: SSO ID invalid: '{}'!", ssoId); return null; } // The main event... finally sso.get(ssoId).ssoPrepareLogin(session, returnAddress, serverUrlBase); return ssoId; }
/** * Wrapper method for other SSO methods provided by the security manager. If desired, the security * manager can take care of the integration using the default usage pattern, rather then calling * them individually. * * @param session : The session of the current request * @param formData : FormData object for the current request * @return boolean : True if SSO has redirected, in which case no response should be sent by * Dispatch, otherwise False. */ @Override public boolean runSsoIntegration(JsonSessionState session, FormData formData) { this.formData = formData; // Used in integrating with thrid party systems. They can send us a // user, we will log them in via a SSO round-trip, then send them back // to the external system String returnUrl = request.getParameter("returnUrl"); if (returnUrl != null) { log.info("External redirect requested: '{}'", returnUrl); session.set("ssoReturnUrl", returnUrl); } // The URL parameters can contain a trust token String utoken = request.getParameter("token"); String stoken = (String) session.get("validToken"); String token = null; // Or an 'old' token still in the session if (stoken != null) { token = stoken; } // But give the URL priority if (utoken != null) { token = utoken; } if (token != null) { // Valid token if (testTrustToken(session, token)) { // Dispatch can continue return false; } // Invalid token // Given that trust tokens are designed for system integration // we are going to fail with a non-branded error message try { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid or expired security token!"); } catch (IOException ex) { log.error("Error sending 403 response to client!"); } // We don't want Dispatch to send a response return true; } // Single Sign-On integration try { // Instantiate with access to the session String ssoId = ssoInit(session); if (ssoId != null) { // We are logging in, so send them to the SSO portal String ssoUrl = ssoGetRemoteLogonURL(session, ssoId); if (ssoUrl != null) { log.info("Redirect to external URL: '{}'", ssoUrl); response.sendRedirect(ssoUrl); return true; } } else { // Otherwise, check if we have user's details boolean valid = ssoCheckUserDetails(session); // If we validly logged in an SSO user, check for an // external redirect to third party systems if (valid) { returnUrl = (String) session.get("ssoReturnUrl"); if (returnUrl != null) { log.info("Redirect to external URL: '{}'", returnUrl); session.remove("ssoReturnUrl"); response.sendRedirect(returnUrl); return true; } } } } catch (Exception ex) { log.error("SSO Error!", ex); } return false; }