Ravi Nori has uploaded a new change for review. Change subject: aaa: Introduce sso query service ......................................................................
aaa: Introduce sso query service The sso query service can be used to get ticket for a token, validate a session using sso token, invalidate a session and get details of session user associated with a token. Change-Id: Ic2f20efa531b8311218a204d942fbfca1fc615d6 Bug-Url: https://bugzilla.redhat.com/1092744 Signed-off-by: Ravi Nori <rn...@redhat.com> --- A backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/SSOQueryServiceUtils.java M backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/EngineSessionTokenAuthenticationFilter.java M backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java M backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/SSORestApiLoginFilter.java M backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOLoginServlet.java M backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOPostLoginServlet.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/constants/SessionConstants.java D backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/LoginCredentialsServlet.java M backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOContextListener.java A backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOQueryServlet.java M backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOSessionListener.java D backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SessionValidationServlet.java M backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/AuthenticationUtils.java M backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/SSOUtils.java M backend/manager/modules/enginesso/src/main/webapp/WEB-INF/web.xml M frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java 16 files changed, 341 insertions(+), 210 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/99/38499/1 diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/SSOQueryServiceUtils.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/SSOQueryServiceUtils.java new file mode 100644 index 0000000..a6c8577 --- /dev/null +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/SSOQueryServiceUtils.java @@ -0,0 +1,98 @@ +package org.ovirt.engine.core.aaa; + +import org.ovirt.engine.core.aaa.filters.FiltersHelper; +import org.ovirt.engine.core.utils.EngineLocalConfig; +import org.ovirt.engine.core.uutils.net.HttpURLConnectionBuilder; + +import javax.net.ssl.TrustManagerFactory; +import javax.servlet.http.HttpServletRequest; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +public class SSOQueryServiceUtils { + + private static final String ssoQueryService = "http://localhost/ovirt-engine/sso/query-service?"; + + public static String getFromSsoQueryService(HttpServletRequest req, String uri) throws Exception { + HttpURLConnection connection = null; + try { + connection = create(new URL(String.format("%sversion=%s&%s", ssoQueryService, FiltersHelper.VERSION, uri))); + connection.setDoInput(true); + connection.setDoOutput(false); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + try (InputStream input = connection.getInputStream()) { + copy(input, os); + } + connection.connect(); + return new String(os.toByteArray(), "UTF-8"); + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + } + + public static String postToSsoQueryService(HttpServletRequest req, String uri) throws Exception { + return postToSsoQueryService(req, uri, false); + } + + public static String postToSsoQueryService(HttpServletRequest req, String uri, boolean sendHeaderAuth) throws Exception { + HttpURLConnection connection = null; + try { + connection = create(new URL(String.format("%sversion=%s&%s", ssoQueryService, FiltersHelper.VERSION, uri))); + connection.setDoInput(true); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + if (sendHeaderAuth) { + String headerAuthorization = req.getHeader(FiltersHelper.Constants.HEADER_AUTHORIZATION); + connection.setRequestProperty(FiltersHelper.Constants.HEADER_AUTHORIZATION, headerAuthorization); + } + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + connection.setRequestProperty("Content-Language", "en-US"); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + try (InputStream input = connection.getInputStream()) { + copy(input, os); + } + connection.connect(); + return new String(os.toByteArray(), "UTF-8"); + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + } + + private static HttpURLConnection create(URL url) throws IOException, GeneralSecurityException { + return new HttpURLConnectionBuilder(url).setHttpsProtocol("TLSv1") + .setReadTimeout(0) + .setTrustManagerAlgorithm(TrustManagerFactory.getDefaultAlgorithm()) + .setTrustStore(EngineLocalConfig.getInstance().getProperty("ENGINE_PKI_TRUST_STORE")) + .setTrustStorePassword(EngineLocalConfig.getInstance().getPKITrustStorePassword()) + .setTrustStoreType(KeyStore.getDefaultType()) + .setURL(url) + .setVerifyChain(true) + .setVerifyHost(false).create(); + } + + private static long copy(final InputStream input, final OutputStream output) throws IOException { + final byte[] buffer = new byte[8*1024]; + long count = 0; + int n; + while ((n = input.read(buffer)) != -1) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + +} diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/EngineSessionTokenAuthenticationFilter.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/EngineSessionTokenAuthenticationFilter.java index 0ddc9cd..7718845 100644 --- a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/EngineSessionTokenAuthenticationFilter.java +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/EngineSessionTokenAuthenticationFilter.java @@ -27,9 +27,9 @@ if (token != null) { request.setAttribute(SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, FiltersHelper.getTokenContent(token)); } - token = req.getHeader(SessionConstants.SSO_SESSION_ID_KEY); + token = req.getHeader(SessionConstants.SSO_TOKEN_KEY); if (token != null) { - ((HttpServletRequest) request).getSession(true).setAttribute(SessionConstants.SSO_SESSION_ID_KEY, token); + ((HttpServletRequest) request).getSession(true).setAttribute(SessionConstants.SSO_TOKEN_KEY, token); } } chain.doFilter(request, response); diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java index 2011b9b..91f20bc 100644 --- a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java @@ -1,6 +1,5 @@ package org.ovirt.engine.core.aaa.filters; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -19,6 +18,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.http.HeaderElement; import org.apache.http.message.BasicHeaderValueParser; +import org.ovirt.engine.core.aaa.SSOQueryServiceUtils; import org.ovirt.engine.core.common.constants.SessionConstants; import org.ovirt.engine.core.common.interfaces.BackendLocal; import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; @@ -31,6 +31,7 @@ public class FiltersHelper { + public static final String VERSION = "0"; private final static Logger log = LoggerFactory.getLogger(FiltersHelper.class); public static class Constants { @@ -77,29 +78,15 @@ } public static boolean isSessionValid(HttpServletRequest req) throws NamingException { - String sessionId = (String) req.getSession(true).getAttribute(SessionConstants.SSO_SESSION_ID_KEY); - HttpURLConnection connection = null; + String sessionId = (String) req.getSession(true).getAttribute(SessionConstants.SSO_TOKEN_KEY); boolean isValid = false; if (StringUtils.isNotEmpty(sessionId)) { try { - connection = create(new URL("http://localhost/ovirt-engine/sso/validate-session?sso_session_id=" + sessionId)); - connection.setDoInput(true); - connection.setDoOutput(false); - if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { - isValid = true; - } - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - try (InputStream input = connection.getInputStream()) { - copy(input, os); - } - connection.connect(); - } + SSOQueryServiceUtils.getFromSsoQueryService(req, + String.format("command=sso-token-validate&sso_token=%s", sessionId)); + isValid = true; } catch (Exception e) { log.error("Session not valid session id = " + sessionId, e.getMessage()); - } finally { - if (connection != null) { - connection.disconnect(); - } } } return isValid; diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/SSORestApiLoginFilter.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/SSORestApiLoginFilter.java index b873a6d..fd125fe 100644 --- a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/SSORestApiLoginFilter.java +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/SSORestApiLoginFilter.java @@ -1,20 +1,18 @@ package org.ovirt.engine.core.aaa.filters; import org.ovirt.engine.api.extensions.ExtMap; +import org.ovirt.engine.core.aaa.SSOQueryServiceUtils; import org.ovirt.engine.core.aaa.TicketUtils; import org.ovirt.engine.core.common.action.CreateUserSessionParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.constants.SessionConstants; -import org.ovirt.engine.core.utils.EngineLocalConfig; import org.ovirt.engine.core.utils.serialization.json.JsonObjectDeserializer; -import org.ovirt.engine.core.uutils.net.HttpURLConnectionBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.naming.InitialContext; import javax.naming.NamingException; -import javax.net.ssl.TrustManagerFactory; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -23,14 +21,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.security.GeneralSecurityException; -import java.security.KeyStore; import java.util.HashMap; import java.util.Map; @@ -59,55 +50,12 @@ } } - protected HttpURLConnection create(URL url) throws IOException, GeneralSecurityException { - return new HttpURLConnectionBuilder(url).setHttpsProtocol("TLSv1") - .setReadTimeout(0) - .setTrustManagerAlgorithm(TrustManagerFactory.getDefaultAlgorithm()) - .setTrustStore(EngineLocalConfig.getInstance().getProperty("ENGINE_PKI_TRUST_STORE")) - .setTrustStorePassword(EngineLocalConfig.getInstance().getPKITrustStorePassword()) - .setTrustStoreType(KeyStore.getDefaultType()) - .setURL(url) - .setVerifyChain(true) - .setVerifyHost(false).create(); - } - - protected static long copy(final InputStream input, final OutputStream output) throws IOException { - final byte[] buffer = new byte[8*1024]; - long count = 0; - int n; - while ((n = input.read(buffer)) != -1) { - output.write(buffer, 0, n); - count += n; - } - return count; - } - private void authenticateWithSSO(HttpServletRequest req) throws ServletException { - String headerAuthorization = req.getHeader(FiltersHelper.Constants.HEADER_AUTHORIZATION); - HttpURLConnection connection = null; try { - connection = create(new URL("http://localhost/ovirt-engine/sso/login-credentials")); - connection.setDoInput(true); - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - connection.setRequestProperty(FiltersHelper.Constants.HEADER_AUTHORIZATION, headerAuthorization); - connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - connection.setRequestProperty("Content-Language", "en-US"); - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - try (InputStream input = connection.getInputStream()) { - copy(input, os); - } - connection.connect(); - createUserSession(req, new String(os.toByteArray(), "UTF-8")); - } + createUserSession(req, SSOQueryServiceUtils.postToSsoQueryService(req, "command=authn-credentials&ticket=1", true)); } catch (Exception e) { log.error("Exception obtaining ticket from sso", e.getMessage()); - } finally { - if (connection != null) { - connection.disconnect(); - } } - } private void createUserSession(HttpServletRequest req, String ticket) { @@ -135,8 +83,8 @@ } HttpSession httpSession = req.getSession(true); httpSession.setAttribute( - SessionConstants.SSO_SESSION_ID_KEY, - payload.get(SessionConstants.SSO_SESSION_ID_KEY)); + SessionConstants.SSO_TOKEN_KEY, + payload.get(SessionConstants.SSO_TOKEN_KEY)); httpSession.setAttribute( SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, queryRetVal.getActionReturnValue()); diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOLoginServlet.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOLoginServlet.java index 4581f19..68e1411 100644 --- a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOLoginServlet.java +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOLoginServlet.java @@ -1,5 +1,7 @@ package org.ovirt.engine.core.aaa.servlet; +import org.ovirt.engine.core.aaa.filters.FiltersHelper; + import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -33,10 +35,11 @@ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.sendRedirect(String.format("%s&opaque=%s&post_action_url=%s&version=0", + response.sendRedirect(String.format("%s&opaque=%s&post_action_url=%s&version=%s", ssoUrl, response.encodeURL(request.getParameter("app_url")), - response.encodeURL(postActionUrl))); + response.encodeURL(postActionUrl), + FiltersHelper.VERSION)); } } diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOPostLoginServlet.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOPostLoginServlet.java index 3b15b48..c3985de 100644 --- a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOPostLoginServlet.java +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/servlet/SSOPostLoginServlet.java @@ -2,6 +2,7 @@ import org.apache.commons.lang.StringUtils; import org.ovirt.engine.api.extensions.ExtMap; +import org.ovirt.engine.core.aaa.SSOQueryServiceUtils; import org.ovirt.engine.core.aaa.TicketUtils; import org.ovirt.engine.core.aaa.filters.FiltersHelper; import org.ovirt.engine.core.common.action.CreateUserSessionParameters; @@ -40,14 +41,17 @@ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (StringUtils.isEmpty(request.getParameter("ticket"))) { - throw new RuntimeException("No ticket found in request."); + if (StringUtils.isEmpty(request.getParameter(SessionConstants.SSO_TOKEN_KEY))) { + throw new RuntimeException("No sso token found in request."); } JsonObjectDeserializer deserializer = new JsonObjectDeserializer(); InitialContext ctx = null; try { - Map<String, Object> payload = deserializer.deserialize(TicketUtils.getTicketDecoder().decode(request.getParameter("ticket")), HashMap.class); + String ssoToken = request.getParameter(SessionConstants.SSO_TOKEN_KEY); + String ticket = SSOQueryServiceUtils.postToSsoQueryService(request, + String.format("command=sso-get-token&%s=%s", SessionConstants.SSO_TOKEN_KEY, ssoToken)); + Map<String, Object> payload = deserializer.deserialize(TicketUtils.getTicketDecoder().decode(ticket), HashMap.class); ctx = new InitialContext(); String username = (String) payload.get("userName"); VdcReturnValueBase queryRetVal = FiltersHelper.getBackend(ctx).runAction(VdcActionType.CreateUserSession, @@ -61,8 +65,8 @@ } HttpSession httpSession = request.getSession(true); httpSession.setAttribute( - SessionConstants.SSO_SESSION_ID_KEY, - payload.get(SessionConstants.SSO_SESSION_ID_KEY)); + SessionConstants.SSO_TOKEN_KEY, + payload.get(SessionConstants.SSO_TOKEN_KEY)); httpSession.setAttribute( SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, queryRetVal.getActionReturnValue()); diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/constants/SessionConstants.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/constants/SessionConstants.java index 690fcdc..6153354 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/constants/SessionConstants.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/constants/SessionConstants.java @@ -3,7 +3,7 @@ public class SessionConstants { public static final String HTTP_SESSION_ENGINE_SESSION_ID_KEY = "ovirt_aaa_engineSessionId"; - public static final String SSO_SESSION_ID_KEY = "sso_session_id"; + public static final String SSO_TOKEN_KEY = "sso_token"; public static final String REQUEST_ASYNC_KEY = "ovirt_aaa_restapi_async"; } diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/LoginCredentialsServlet.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/LoginCredentialsServlet.java deleted file mode 100644 index 9dedf89..0000000 --- a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/LoginCredentialsServlet.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.ovirt.engine.core.sso.servlets; - -import org.ovirt.engine.core.sso.utils.AuthenticationUtils; -import org.ovirt.engine.core.sso.utils.Credentials; -import org.ovirt.engine.core.sso.utils.SSOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.OutputStream; - -public class LoginCredentialsServlet extends HttpServlet { - - private static final long serialVersionUID = 4414518331391974859L; - private static Logger log = LoggerFactory.getLogger(LoginCredentialsServlet.class); - - @Override - protected void service(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - try { - Credentials credentials = SSOUtils.getUserCredentialsFromHeader(request); - if (credentials == null) { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } else { - AuthenticationUtils.handleCredentials( - request.getSession(true), - credentials.getUsername(), - credentials.getPassword(), - credentials.getProfile()); - try (OutputStream os = response.getOutputStream()) { - os.write(SSOUtils.issueTicket(request.getSession(true), request).getBytes("UTF-8")); - } - } - } catch (Exception e) { - log.error("Unable to authenticate {}", e.getMessage()); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - } - -} diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOContextListener.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOContextListener.java index ec7e518..046043a 100644 --- a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOContextListener.java +++ b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOContextListener.java @@ -16,7 +16,8 @@ import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; -import java.util.TreeSet; +import java.util.HashMap; +import java.util.Map; public class SSOContextListener implements ServletContextListener { @@ -56,7 +57,7 @@ } catch (Exception e) { throw new RuntimeException("Unable to instantiate TicketEncoder", e); } - ctx.setAttribute(SSOUtils.SSO_SESSIONS, new TreeSet<String>()); + ctx.setAttribute(SSOUtils.SSO_SESSIONS, new HashMap<String, Map<String, Object>>()); } private TicketEncoder getTicketEncoder(ServletContext ctx, SSOLocalConfig localConfig, int lifetime) throws Exception { diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOQueryServlet.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOQueryServlet.java new file mode 100644 index 0000000..bde1045 --- /dev/null +++ b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOQueryServlet.java @@ -0,0 +1,144 @@ +package org.ovirt.engine.core.sso.servlets; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; +import org.ovirt.engine.core.sso.utils.AuthenticationUtils; +import org.ovirt.engine.core.sso.utils.Credentials; +import org.ovirt.engine.core.sso.utils.SSOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.nio.charset.Charset; +import java.security.GeneralSecurityException; +import java.sql.SQLException; +import java.util.Map; + +public class SSOQueryServlet extends HttpServlet { + + private static Logger log = LoggerFactory.getLogger(SSOQueryServlet.class); + + private static final long serialVersionUID = -5918206701007271897L; + private static String COMMAND = "command"; + private static final String AUTHN_CREDENTIALS = "authn-credentials"; + private static final String GET_TOKEN = "sso-get-token"; + private static final String TOKEN_QUERY = "sso-token-query"; + private static final String INVALIDATE_SESSION = "sso-token-invalidate"; + private static final String VALIDATE_SESSION = "sso-token-validate"; + private static final String VERSION = "version"; + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String command = request.getParameter(COMMAND); + String version = request.getParameter(VERSION); + try { + if (!VERSION.equals(command)) { + if (StringUtils.isEmpty(version)) { + throw new Exception("No version found in request."); + } + if (!SSOUtils.versionSupported(version)) { + throw new Exception("Unsupported version found in request."); + } + } + switch (command) { + case AUTHN_CREDENTIALS: + authCredentials(request, response); + break; + case GET_TOKEN: + getToken(request, response); + break; + case INVALIDATE_SESSION: + HttpSession existingSession = request.getSession(false); + if (existingSession != null) { + existingSession.invalidate(); + } + break; + case TOKEN_QUERY: + getTokenUser(request, response); + break; + case VALIDATE_SESSION: + validateSession(request, response); + break; + case VERSION: + response.getWriter().print(SSOUtils.SSO_VERSION); + break; + default: + log.error("Unknown command " + command); + response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); + break; + } + } catch (Exception ex) { + log.debug("Exception in handling command " + command, ex); + response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); + } + } + + private void getTokenUser(HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException, SQLException { + String ssoToken = request.getParameter("sso_token"); + if (StringUtils.isEmpty(ssoToken) || + !((Map<String, Map>) request.getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).containsKey(new String(Base64.decodeBase64(ssoToken), Charset.forName("UTF-8")))) { + response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); + } else { + response.getWriter().print(SSOUtils.getUserDetailsJson(request.getServletContext(), + new String(Base64.decodeBase64(ssoToken), Charset.forName("UTF-8")))); + response.setStatus(HttpURLConnection.HTTP_OK); + } + } + + private void validateSession(HttpServletRequest request, HttpServletResponse response) throws IOException { + String ssoToken = request.getParameter("sso_token"); + if (StringUtils.isEmpty(ssoToken) || + !((Map<String, Map>) request.getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).containsKey(new String(Base64.decodeBase64(ssoToken), Charset.forName("UTF-8")))) { + response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); + } else { + response.getWriter().print(true); + response.setStatus(HttpURLConnection.HTTP_OK); + } + } + + private void getToken(HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException, SQLException { + String ssoToken = request.getParameter("sso_token"); + if (StringUtils.isEmpty(ssoToken) || + !((Map<String, Map>) request.getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).containsKey(new String(Base64.decodeBase64(ssoToken), Charset.forName("UTF-8")))) { + response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); + } else { + try (OutputStream os = response.getOutputStream()) { + os.write(SSOUtils.issueTicket(request.getServletContext(), new String(Base64.decodeBase64(ssoToken), Charset.forName("UTF-8")), request).getBytes("UTF-8")); + } + } + } + + private void authCredentials(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + Credentials credentials = SSOUtils.getUserCredentialsFromHeader(request); + if (credentials == null) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } else { + AuthenticationUtils.handleCredentials( + request.getSession(true), + credentials.getUsername(), + credentials.getPassword(), + credentials.getProfile()); + String issueTicket = request.getParameter("ticket"); + if (StringUtils.isEmpty(issueTicket) || "0".equals(issueTicket)) { + response.getWriter().print(SSOUtils.getSsoToken(request.getSession(true).getId())); + } else { + try (OutputStream os = response.getOutputStream()) { + os.write(SSOUtils.issueTicket(request.getServletContext(), request.getSession(true).getId(), request).getBytes("UTF-8")); + } + } + } + } catch (Exception e) { + log.error("Unable to authenticate {}", e.getMessage()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } + } +} diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOSessionListener.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOSessionListener.java index 934a1eb..12933a6 100644 --- a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOSessionListener.java +++ b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SSOSessionListener.java @@ -4,16 +4,17 @@ import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; -import java.util.Set; +import java.util.HashMap; +import java.util.Map; public class SSOSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { - ((Set<String>) se.getSession().getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).add(se.getSession().getId()); + ((Map<String, Map>) se.getSession().getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).put(se.getSession().getId(), new HashMap<String, Object>()); } @Override public void sessionDestroyed(HttpSessionEvent se) { - ((Set<String>) se.getSession().getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).remove(se.getSession().getId()); + ((Map<String, Map>) se.getSession().getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).remove(se.getSession().getId()); } } diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SessionValidationServlet.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SessionValidationServlet.java deleted file mode 100644 index c910f2f..0000000 --- a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/servlets/SessionValidationServlet.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.ovirt.engine.core.sso.servlets; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringUtils; -import org.ovirt.engine.core.sso.utils.SSOUtils; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.nio.charset.Charset; -import java.util.Set; - -public class SessionValidationServlet extends HttpServlet { - private static final long serialVersionUID = -5918206701007271897L; - - @Override - protected void service(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - String sessionId = request.getParameter("sso_session_id"); - if (StringUtils.isEmpty(sessionId) || - !((Set<String>) request.getServletContext().getAttribute(SSOUtils.SSO_SESSIONS)).contains(new String(Base64.decodeBase64(sessionId), Charset.forName("UTF-8")))) { - response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - } else { - response.getWriter().print(true); - response.setStatus(HttpURLConnection.HTTP_OK); - } - } - -} diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/AuthenticationUtils.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/AuthenticationUtils.java index 05e9c81..fc41a79 100644 --- a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/AuthenticationUtils.java +++ b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/AuthenticationUtils.java @@ -15,6 +15,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Properties; public class AuthenticationUtils { @@ -98,10 +99,11 @@ Authz.InvokeKeys.QUERY_FLAGS, Authz.QueryFlags.RESOLVE_GROUPS | Authz.QueryFlags.RESOLVE_GROUPS_RECURSIVE )); - session.setAttribute(SSOUtils.SSO_PROFILE_ATTR_NAME, profile); - session.setAttribute(SSOUtils.SSO_AUTHZ_ATTR_NAME, authz.getContext().get(Base.ContextKeys.INSTANCE_NAME)); - session.setAttribute(SSOUtils.SSO_AUTH_RECORD_ATTR_NAME, authRecord); - session.setAttribute(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME, output.get(Authz.InvokeKeys.PRINCIPAL_RECORD)); + Map<String, Object> sessionData = SSOUtils.getSessionData(session.getServletContext(), session.getId()); + sessionData.put(SSOUtils.SSO_PROFILE_ATTR_NAME, profile); + sessionData.put(SSOUtils.SSO_AUTHZ_ATTR_NAME, authz.getContext().get(Base.ContextKeys.INSTANCE_NAME)); + sessionData.put(SSOUtils.SSO_AUTH_RECORD_ATTR_NAME, authRecord); + sessionData.put(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME, output.get(Authz.InvokeKeys.PRINCIPAL_RECORD)); } public static List<String> getAvailableProfiles(ServletContext ctx) { diff --git a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/SSOUtils.java b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/SSOUtils.java index cbf2366..a0f16a9 100644 --- a/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/SSOUtils.java +++ b/backend/manager/modules/enginesso/src/main/java/org/ovirt/engine/core/sso/utils/SSOUtils.java @@ -9,6 +9,7 @@ import org.ovirt.engine.api.extensions.aaa.Authz; import org.ovirt.engine.core.uutils.crypto.ticket.TicketEncoder; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -48,7 +49,8 @@ public static final String LOGIN_PHASE4_URI = "/login-phase4"; public static boolean isUserAuthenticated(HttpSession session) { - return session.getAttribute(SSO_PRINCIPAL_RECORD_ATTR_NAME) != null; + Map<String, Object> sessionData = SSOUtils.getSessionData(session.getServletContext(), session.getId()); + return sessionData.get(SSO_PRINCIPAL_RECORD_ATTR_NAME) != null; } public static void redirectToModule(HttpSession session, @@ -62,19 +64,49 @@ redirectUrl.append("&opaque="); redirectUrl.append(response.encodeURL(SSOUtils.getParameter(request, OPAQUE))); } - redirectUrl.append("&ticket="); - redirectUrl.append(response.encodeURL(issueTicket(session, request))); + redirectUrl.append("&sso_token="); + redirectUrl.append(response.encodeURL(getSsoToken(session.getId()))); response.sendRedirect(redirectUrl.toString()); } catch (Exception ex) { throw new RuntimeException(ex); } } - public static String issueTicket(HttpSession session, + public static String getSsoToken(String sessionId) { + return new String(Base64.encodeBase64(sessionId.getBytes())); + } + + public static String issueTicket(ServletContext ctx, + String token, HttpServletRequest request) throws SQLException, IOException, GeneralSecurityException { - SSOConfig config = (SSOConfig) session.getServletContext().getAttribute(SSO_CONFIG); - String authzName = (String) session.getAttribute(SSOUtils.SSO_AUTHZ_ATTR_NAME); - ExtMap principalRecord = (ExtMap) session.getAttribute(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME); + Map<String, Object> sessionData = SSOUtils.getSessionData(ctx, token); + ExtMap principalRecord = (ExtMap) sessionData.get(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME); + Map<String, Object> payload = getUserDetails(ctx, token); + + payload.put("version", SSO_VERSION); + payload.put("principalRecord", principalRecord); + payload.put("authRecord", sessionData.get(SSOUtils.SSO_AUTH_RECORD_ATTR_NAME)); + payload.put("sso_token", new String(Base64.encodeBase64(token.getBytes()))); + + ObjectMapper mapper = new ObjectMapper().configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); + mapper.getSerializationConfig().addMixInAnnotations(ExtMap.class, JsonExtMapMixIn.class); + return ((TicketEncoder) request.getServletContext().getAttribute(SSO_TICKET_ENCODER)).encode(mapper.writeValueAsString(payload)); + } + + public static String getUserDetailsJson(ServletContext ctx, + String token) throws SQLException, IOException, GeneralSecurityException { + ObjectMapper mapper = new ObjectMapper().configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); + return mapper.writeValueAsString(getUserDetails(ctx, token)); + } + + public static Map<String, Object> getUserDetails(ServletContext ctx, + String token) throws SQLException, IOException, GeneralSecurityException { + Map<String, Object> sessionData = SSOUtils.getSessionData(ctx, token); + SSOConfig config = (SSOConfig) ctx.getAttribute(SSO_CONFIG); + String authzName = (String) sessionData.get(SSOUtils.SSO_AUTHZ_ATTR_NAME); + ExtMap principalRecord = (ExtMap) sessionData.get(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME); String userId = config.getSsoUserGroupManager().getUserIdByExternalId(authzName, principalRecord.<String>get(Authz.PrincipalRecord.ID)); if (userId == null) { userId = DEFAULT_USER_ID; @@ -83,27 +115,20 @@ String principal = principalRecord.get(Authz.PrincipalRecord.PRINCIPAL); Map<String, Object> payload = new HashMap<>(); payload.put("userId", userId); - payload.put("version", SSO_VERSION); payload.put("userName", principal != null ? principal : principalRecord.<String>get(Authz.PrincipalRecord.NAME)); payload.put("email", principalRecord.<String>get(Authz.PrincipalRecord.EMAIL)); payload.put("groupIds", getGroupIds(config.getSsoUserGroupManager(), authzName, principalRecord)); - payload.put("profile", session.getAttribute(SSOUtils.SSO_PROFILE_ATTR_NAME)); - payload.put("principalRecord", principalRecord); - payload.put("authRecord", session.getAttribute(SSOUtils.SSO_AUTH_RECORD_ATTR_NAME)); - payload.put("sso_session_id", new String(Base64.encodeBase64(session.getId().getBytes()))); - - ObjectMapper mapper = new ObjectMapper().configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); - mapper.getSerializationConfig().addMixInAnnotations(ExtMap.class, JsonExtMapMixIn.class); - return ((TicketEncoder) request.getServletContext().getAttribute(SSO_TICKET_ENCODER)).encode(mapper.writeValueAsString(payload)); + payload.put("profile", sessionData.get(SSOUtils.SSO_PROFILE_ATTR_NAME)); + return payload; } public static String getUserNameWithProfile(HttpServletRequest request) { HttpSession session = request.getSession(true); - ExtMap principalRecord = (ExtMap) session.getAttribute(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME); + Map<String, Object> sessionData = SSOUtils.getSessionData(session.getServletContext(), session.getId()); + ExtMap principalRecord = (ExtMap) sessionData.get(SSOUtils.SSO_PRINCIPAL_RECORD_ATTR_NAME); String principal = principalRecord.get(Authz.PrincipalRecord.PRINCIPAL); return principal != null ? principal : principalRecord.<String>get(Authz.PrincipalRecord.NAME) + "@" + - session.getAttribute(SSOUtils.SSO_PROFILE_ATTR_NAME); + sessionData.get(SSOUtils.SSO_PROFILE_ATTR_NAME); } private static List<String> getGroupIds(SSOUserGroupManager userGroupManager, String authzName, ExtMap principalRecord) throws SQLException { @@ -141,6 +166,10 @@ return Integer.parseInt(SSO_VERSION) >= Integer.parseInt(version); } + public static Map<String, Object> getSessionData(ServletContext ctx, String token) { + return ((Map<String, Map<String, Object>>) ctx.getAttribute(SSOUtils.SSO_SESSIONS)).get(token); + } + public static Credentials getUserCredentialsFromHeader(HttpServletRequest request) { String header = request.getHeader(SSOUtils.HEADER_AUTHORIZATION); Credentials credentials = null; diff --git a/backend/manager/modules/enginesso/src/main/webapp/WEB-INF/web.xml b/backend/manager/modules/enginesso/src/main/webapp/WEB-INF/web.xml index 54530be..99ac193 100644 --- a/backend/manager/modules/enginesso/src/main/webapp/WEB-INF/web.xml +++ b/backend/manager/modules/enginesso/src/main/webapp/WEB-INF/web.xml @@ -196,23 +196,13 @@ </servlet-mapping> <servlet> - <servlet-name>LoginCredentialsServlet</servlet-name> - <servlet-class>org.ovirt.engine.core.sso.servlets.LoginCredentialsServlet</servlet-class> + <servlet-name>SSOQueryServlet</servlet-name> + <servlet-class>org.ovirt.engine.core.sso.servlets.SSOQueryServlet</servlet-class> </servlet> <servlet-mapping> - <servlet-name>LoginCredentialsServlet</servlet-name> - <url-pattern>/login-credentials</url-pattern> - </servlet-mapping> - - <servlet> - <servlet-name>SessionValidationServlet</servlet-name> - <servlet-class>org.ovirt.engine.core.sso.servlets.SessionValidationServlet</servlet-class> - </servlet> - - <servlet-mapping> - <servlet-name>SessionValidationServlet</servlet-name> - <url-pattern>/validate-session</url-pattern> + <servlet-name>SSOQueryServlet</servlet-name> + <url-pattern>/query-service</url-pattern> </servlet-mapping> <servlet> diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java index 1c1b663..603a20c 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java @@ -75,7 +75,7 @@ private static final String ENGINE_AUTH_TOKEN_HEADER = "OVIRT-INTERNAL-ENGINE-AUTH-TOKEN"; //$NON-NLS-1$ private static final String SESSION_ID_KEY = "RestApiSessionId"; //$NON-NLS-1$ - public static final String SSO_SESSION_ID_KEY = "sso_session_id"; //$NON-NLS-1$ + public static final String SSO_TOKEN_KEY = "sso_token"; //$NON-NLS-1$ private static final int DEFAULT_ENGINE_SESSION_TIMEOUT = 30; // Heartbeat (delay) between REST API keep-alive requests @@ -148,7 +148,7 @@ /** * Build HTTP request to acquire new REST API session. */ - RequestBuilder createRequest(String engineAuthToken, String sso_session_id) { + RequestBuilder createRequest(String engineAuthToken, String ssoToken) { RequestBuilder builder = createRequest(); // Enforce expiry of existing session when acquiring new session @@ -157,7 +157,7 @@ // Map this (physical) REST API session to current user's (logical) Engine session builder.setHeader(ENGINE_AUTH_TOKEN_HEADER, engineAuthToken); - builder.setHeader(SSO_SESSION_ID_KEY, sso_session_id); + builder.setHeader(SSO_TOKEN_KEY, ssoToken); return builder; } @@ -194,11 +194,11 @@ * Acquires new REST API session that maps to current user's Engine session. */ public void acquireSession(final String engineAuthToken) { - retrieveFromHttpSession(SSO_SESSION_ID_KEY, new StorageCallback() { + retrieveFromHttpSession(SSO_TOKEN_KEY, new StorageCallback() { @Override - public void onSuccess(String sso_session_id) { - if (sso_session_id != null) { - sendRequest(createRequest(engineAuthToken, sso_session_id), new RestApiRequestCallback() { + public void onSuccess(String ssoToken) { + if (ssoToken != null) { + sendRequest(createRequest(engineAuthToken, ssoToken), new RestApiRequestCallback() { @Override protected void processResponse(Response response) { // Obtain session ID from response header, as we're unable to access the -- To view, visit https://gerrit.ovirt.org/38499 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ic2f20efa531b8311218a204d942fbfca1fc615d6 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Ravi Nori <rn...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches