private OMElement handleSLORequest(MessageContext messageContext, LogoutRequest logoutRequest) { // Get the session index from the SLORequest and remove the relevant session. String sessionIndex = logoutRequest.getSessionIndexes().get(0).getSessionIndex(); String sessionId = CacheManager.getInstance().getSessionIndexMappingCache().get(sessionIndex); if (sessionId != null) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "Found a session id (md5 : '%s')for the given session index in the SLO request: '%s'. Clearing the session", GatewayUtils.getMD5Hash(sessionId), sessionIndex)); SessionStore.getInstance().removeSession(sessionId); CacheManager.getInstance().getSessionIndexMappingCache().remove(sessionIndex); } else { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "Couldn't find a session id for the given session index : '%s'", sessionIndex)); } OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace ns = fac.createOMNamespace("http://wso2.org/appm", "appm"); OMElement payload = fac.createOMElement("SLOResponse", ns); OMElement errorMessage = fac.createOMElement("message", ns); errorMessage.setText("SLORequest has been successfully processed by WSO2 App Manager"); payload.addChild(errorMessage); return payload; }
private Session getSession(MessageContext messageContext) { org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) messageContext).getAxis2MessageContext(); Map<String, Object> headers = (Map<String, Object>) axis2MessageContext.getProperty( org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS); String cookieHeaderValue = (String) headers.get(HTTPConstants.HEADER_COOKIE); if (cookieHeaderValue != null) { Map<String, String> cookies = parseRequestCookieHeader(cookieHeaderValue); if (cookies.get(AppMConstants.APPM_SAML2_COOKIE) != null) { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "Cookie '%s' is available in the request.", AppMConstants.APPM_SAML2_COOKIE)); } messageContext.setProperty( AppMConstants.APPM_SAML2_COOKIE, cookies.get(AppMConstants.APPM_SAML2_COOKIE)); } else { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "Cookie '%s' is not available in the request.", AppMConstants.APPM_SAML2_COOKIE)); } } } return GatewayUtils.getSession(messageContext, true); }
private void setSessionCookie(MessageContext messageContext, String cookieValue) { String setCookieString = String.format(SET_COOKIE_PATTERN, AppMConstants.APPM_SAML2_COOKIE, cookieValue, "/"); addTransportHeader(messageContext, HTTPConstants.HEADER_SET_COOKIE, setCookieString); if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "Cookie '%s' has been set in the response", AppMConstants.APPM_SAML2_COOKIE)); } }
@Override public boolean handleRequest(MessageContext messageContext) { Session session = getSession(messageContext); // Get and set relevant message context properties. org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) messageContext).getAxis2MessageContext(); String webAppContext = (String) messageContext.getProperty(RESTConstants.REST_API_CONTEXT); String webAppVersion = (String) messageContext.getProperty(RESTConstants.SYNAPSE_REST_API_VERSION); String fullResourceURL = (String) messageContext.getProperty(RESTConstants.REST_FULL_REQUEST_PATH); // If the request has come through the default App (the Synapse API without a version), // remove the version from the request URL when doing a redirection. String redirectionFriendlyFullRequestPath = getRedirectionReadyFullRequestPath(messageContext); messageContext.setProperty( AppMConstants.MESSAGE_CONTEXT_PROPERTY_REDIRECTION_FRIENDLY_FULL_REQUEST_PATH, redirectionFriendlyFullRequestPath); String baseURL = String.format("%s/%s/", webAppContext, webAppVersion); String relativeResourceURL = StringUtils.substringAfter(fullResourceURL, baseURL); String httpVerb = (String) axis2MessageContext.getProperty(Constants.Configuration.HTTP_METHOD); // Fetch the web app for the requested context and version. try { if (webApp == null) { int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); webApp = new DefaultAppRepository(null) .getWebAppByContextAndVersion(webAppContext, webAppVersion, tenantId); } } catch (AppManagementException e) { String errorMessage = String.format("Can't fetch the web for '%s' from the repository.", fullResourceURL); GatewayUtils.logAndThrowException(log, errorMessage, e); } messageContext.setProperty( AppMConstants.MESSAGE_CONTEXT_PROPERTY_APP_ID, webApp.getDatabaseId()); // Find a matched URI template. URITemplate matchedTemplate = GatewayUtils.findMatchedURITemplate(webApp, httpVerb, relativeResourceURL); messageContext.setProperty( AppMConstants.MESSAGE_CONTEXT_PROPERTY_MATCHED_URI_TEMPLATE, matchedTemplate); // If the request comes to the ACS URL, then it should be a SAML response or a request from the // IDP. if (isACSURL(relativeResourceURL)) { handleRequestToACSEndpoint(messageContext, session); // All requests to the ACS URL should be redirected to somewhere. e.g. IDP, app root URL return false; } // Handle logout requests. These requests don't need to be authenticated. if (GatewayUtils.isLogoutURL(webApp, relativeResourceURL)) { doLogout(session); redirectToIDPWithLogoutRequest(messageContext, session); return false; } if (GatewayUtils.isAnonymousAccessAllowed(webApp, matchedTemplate)) { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format("Request to '%s' is allowed for anonymous access", fullResourceURL)); } messageContext.setProperty( AppMConstants.MESSAGE_CONTEXT_PROPERTY_GATEWAY_SKIP_SECURITY, true); return true; } AuthenticationContext authenticationContext = session.getAuthenticationContext(); if (!authenticationContext.isAuthenticated()) { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format("Request to '%s' is not authenticated", fullResourceURL)); } session.setRequestedURL(redirectionFriendlyFullRequestPath); SessionStore.getInstance().updateSession(session); setSessionCookie(messageContext, session.getUuid()); requestAuthentication(messageContext); return false; } else { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "Request to '%s' is authenticated. Subject = '%s'", fullResourceURL, authenticationContext.getSubject())); } // If this web app has not been access before in this session, redirect to the IDP. // This is done to make sure SLO works. if (!session.hasAppBeenAccessedBefore(webApp.getUUID())) { GatewayUtils.logWithRequestInfo( log, messageContext, "This web app has not been accessed before in the current session. Doing SSO through IDP since it is needed to make SLO work."); session.setRequestedURL(redirectionFriendlyFullRequestPath); SessionStore.getInstance().updateSession(session); requestAuthentication(messageContext); return false; } // Set the session as a message context property. messageContext.setProperty(AppMConstants.APPM_SAML2_COOKIE, session.getUuid()); if (shouldSendSAMLResponseToBackend()) { Map<String, String> samlResponses = (Map<String, String>) session.getAttribute(SAMLUtils.SESSION_ATTRIBUTE_RAW_SAML_RESPONSES); String samlResponseForApp = null; if (samlResponses != null && (samlResponseForApp = samlResponses.get(webApp.getUUID())) != null) { addTransportHeader(messageContext, HTTP_HEADER_SAML_RESPONSE, samlResponseForApp); if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, "SAML response has been set in the request to the backend."); } } else { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, "Couldn't find the SAML response for the app in the session."); } } } if (isJWTEnabled()) { String jwtHeaderName = configuration.getFirstProperty(APISecurityConstants.API_SECURITY_CONTEXT_HEADER); Map<String, String> generatedJWTs = (Map<String, String>) session.getAttribute(SESSION_ATTRIBUTE_JWTS); String jwtForApp = null; if (generatedJWTs != null && (jwtForApp = generatedJWTs.get(webApp.getUUID())) != null) { addTransportHeader(messageContext, jwtHeaderName, jwtForApp); if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, "JWT has been set in the request to the backend."); } } else { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, "Couldn't find the generated JWT for the app in the session."); } } } return true; } }
private void redirectToIDPWithLogoutRequest(MessageContext messageContext, Session session) { LogoutRequest logoutRequest = SAMLUtils.buildLogoutRequest(webApp.getSaml2SsoIssuer(), session); GatewayUtils.logWithRequestInfo(log, messageContext, "Redirecting to the IDP for logging out."); GatewayUtils.redirectToIDPWithSAMLRequest(messageContext, logoutRequest); }
private boolean handleRequestToACSEndpoint(MessageContext messageContext, Session session) { String fullResourceURL = (String) messageContext.getProperty(RESTConstants.REST_FULL_REQUEST_PATH); // Build the incoming message. GatewayUtils.buildIncomingMessage(messageContext); IDPMessage idpMessage = null; try { idpMessage = SAMLUtils.processIDPMessage(messageContext); if (idpMessage.getSAMLResponse() == null && idpMessage.getSAMLRequest() == null) { String errorMessage = String.format( "A SAML request or response was not there in the request to the ACS URL ('%s')", fullResourceURL); GatewayUtils.logAndThrowException(log, errorMessage, null); } if (idpMessage.getRawSAMLResponse() != null) { // pass saml response and request for an authorized cookie to access admin services String authorizedAdminCookie = getAuthenticatedCookieFromIdP(idpMessage.getRawSAMLResponse()); if (authorizedAdminCookie == null) { String errorMessage = "Error while requesting the authorized cookie to access IDP admin services via " + "SAML2SSOAuthenticationService"; GatewayUtils.logAndThrowException(log, errorMessage, null); } // add to session if (session.getAttribute(AppMConstants.IDP_AUTHENTICATED_COOKIE) == null) { session.addAttribute(AppMConstants.IDP_AUTHENTICATED_COOKIE, authorizedAdminCookie); SessionStore.getInstance().updateSession(session); } } } catch (SAMLException e) { String errorMessage = String.format( "Error while processing the IDP call back request to the ACS URL ('%s')", fullResourceURL); log.error(errorMessage); if (log.isDebugEnabled()) { // Do not log the stack trace without checking isDebugEnabled, // because log can be filled by SAML Response XSW attacks. log.debug(errorMessage, e); } GatewayUtils.send401(messageContext, "Unauthorized SAML Response"); return false; } GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "%s is available in request.", idpMessage.getSAMLRequest() != null ? "SAMLRequest" : "SAMLResponse")); // If not configured, the SLO request URL and the SLO response URL is the ACS URL by default. // SLOResponse is handled in this method. But the SLORequest is handled generically in the rest // of the handler code. if (idpMessage.isSLOResponse()) { GatewayUtils.logWithRequestInfo(log, messageContext, "SAMLResponse in an SLO response."); GatewayUtils.redirectToURL(messageContext, GatewayUtils.getAppRootURL(messageContext)); return false; } else if (idpMessage.isSLORequest()) { GatewayUtils.logWithRequestInfo(log, messageContext, "SAMLRequest in an SLO request."); OMElement response = handleSLORequest(messageContext, (LogoutRequest) idpMessage.getSAMLRequest()); GatewayUtils.send200(messageContext, response); return false; } else { // Validate SAML response validity period if (!idpMessage.validateAssertionValidityPeriod()) { requestAuthentication(messageContext); return false; } // Validate SAML response signature, assertion signature and audience restrictions if (!idpMessage.validateSignatureAndAudienceRestriction( idpMessage.getSAMLResponse(), webApp, configuration)) { GatewayUtils.send401(messageContext, "Unauthorized SAML Response"); return false; } AuthenticationContext authenticationContext = getAuthenticationContextFromIDPCallback(idpMessage); if (authenticationContext.isAuthenticated()) {; if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, String.format( "SAML response is authenticated. Subject = '%s'", authenticationContext.getSubject())); } session.setAuthenticationContext(authenticationContext); if (shouldSendSAMLResponseToBackend()) { Map<String, String> samlResponses = (Map<String, String>) session.getAttribute(SAMLUtils.SESSION_ATTRIBUTE_RAW_SAML_RESPONSES); if (samlResponses == null) { samlResponses = new HashMap<String, String>(); session.addAttribute(SAMLUtils.SESSION_ATTRIBUTE_RAW_SAML_RESPONSES, samlResponses); } samlResponses.put(webApp.getUUID(), idpMessage.getRawSAMLResponse()); } // Get the SAML session index. String sessionIndex = (String) SAMLUtils.getSessionIndex((ResponseImpl) idpMessage.getSAMLResponse()); session.addAttribute(SAMLUtils.SESSION_ATTRIBUTE_SAML_SESSION_INDEX, sessionIndex); GatewayUtils.logWithRequestInfo( log, messageContext, String.format("Session index : %s", sessionIndex)); // Add Session Index -> Session ID to a cache. CacheManager.getInstance() .getSessionIndexMappingCache() .put(sessionIndex, session.getUuid()); // Mark this web app as an access web app in this session. session.addAccessedWebAppUUID(webApp.getUUID()); Map<String, Object> userAttributes = getUserAttributes((ResponseImpl) idpMessage.getSAMLResponse()); session.getAuthenticationContext().setAttributes(userAttributes); String roleAttributeValue = (String) userAttributes.get("http://wso2.org/claims/role"); if (roleAttributeValue != null) { String[] roles = roleAttributeValue.split(","); for (String role : roles) { session.getAuthenticationContext().addRole(role); } } // Generate the JWT and store in the session. if (isJWTEnabled()) { try { // Generate and store the JWT against the app. Map<String, String> generatedJWTs = (Map<String, String>) session.getAttribute(SESSION_ATTRIBUTE_JWTS); if (generatedJWTs == null) { generatedJWTs = new HashMap<String, String>(); session.addAttribute(SESSION_ATTRIBUTE_JWTS, generatedJWTs); } generatedJWTs.put( webApp.getUUID(), getJWTGenerator().generateToken(userAttributes, webApp, messageContext)); } catch (AppManagementException e) { String errorMessage = String.format( "Can't generate JWT for the subject : '%s'", authenticationContext.getSubject()); GatewayUtils.logAndThrowException(log, errorMessage, e); } } SessionStore.getInstance().updateSession(session); if (session.getRequestedURL() != null) { GatewayUtils.redirectToURL(messageContext, session.getRequestedURL()); } else { log.warn( String.format( "Original requested URL in the session is null. Redirecting to the app root URL.")); GatewayUtils.redirectToURL(messageContext, GatewayUtils.getAppRootURL(messageContext)); } return false; } else { if (log.isDebugEnabled()) { GatewayUtils.logWithRequestInfo( log, messageContext, "SAML response is not authenticated."); } requestAuthentication(messageContext); return false; } } }