Ravi Nori has uploaded a new change for review. Change subject: core : Engine IDP ......................................................................
core : Engine IDP Add IDP for unified login service. Uses picketlink and picketbox with custom login module Change-Id: Ib549b3b563081a37b1fae3f437a73a208dd86db5 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/EnginePrincipal.java A backend/manager/modules/idp/pom.xml A backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineAttributeHandler.java A backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineLoginModule.java A backend/manager/modules/idp/src/main/webapp/WEB-INF/jboss-web.xml A backend/manager/modules/idp/src/main/webapp/WEB-INF/login-error.jsp A backend/manager/modules/idp/src/main/webapp/WEB-INF/login.jsp A backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-handlers.xml A backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-idfed.xml A backend/manager/modules/idp/src/main/webapp/WEB-INF/web.xml A backend/manager/modules/idp/src/main/webapp/hosted/index.jsp A backend/manager/modules/idp/src/main/webapp/index.jsp A backend/manager/modules/idpfilters/pom.xml A backend/manager/modules/idpfilters/src/main/java/org/ovirt/engine/core/idp/filters/EngineIDPLoginFilter.java M backend/manager/modules/pom.xml M ear/pom.xml M ear/src/main/application/META-INF/jboss-deployment-structure.xml M packaging/services/ovirt-engine/ovirt-engine.xml.in 18 files changed, 1,163 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/94/34194/1 diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/EnginePrincipal.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/EnginePrincipal.java new file mode 100644 index 0000000..9774871 --- /dev/null +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/EnginePrincipal.java @@ -0,0 +1,55 @@ +package org.ovirt.engine.core.aaa; + + +import java.io.Serializable; +import java.security.Principal; + +public class EnginePrincipal implements Principal, Serializable { + private static final long serialVersionUID = 7701951188631723261L; + private final String name; + private String engineSessionId; + + public EnginePrincipal(String name) { + this.name = name; + } + + @Override + public boolean equals(Object another) { + if (!(another instanceof Principal)) { + return false; + } + if (!(another instanceof EnginePrincipal)) { + return false; + } + String anotherName = ((Principal) another).getName(); + boolean equals; + if (name == null) { + equals = anotherName == null; + } else { + equals = name.equals(anotherName); + } + return equals; + } + + @Override + public int hashCode() { + return (name == null ? 0 : name.hashCode()); + } + + @Override + public String toString() { + return name; + } + + public String getName() { + return name; + } + + public String getEngineSessionId() { + return engineSessionId; + } + + public void setEngineSessionId(String engineSessionId) { + this.engineSessionId = engineSessionId; + } +} diff --git a/backend/manager/modules/idp/pom.xml b/backend/manager/modules/idp/pom.xml new file mode 100644 index 0000000..894529c --- /dev/null +++ b/backend/manager/modules/idp/pom.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>manager-modules</artifactId> + <groupId>org.ovirt.engine.core</groupId> + <version>3.6.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>idp</artifactId> + <packaging>war</packaging> + <name>ovirt-engine idp</name> + <description>oVirt engine idp</description> + + <dependencies> + + <dependency> + <groupId>org.picketbox</groupId> + <artifactId>picketbox</artifactId> + <version>4.0.7.Final</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.jboss.logging</groupId> + <artifactId>jboss-logging</artifactId> + <version>3.1.3.GA</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>common</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>aaa</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>bll</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>extensions-manager</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>utils</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.ovirt.engine.api</groupId> + <artifactId>ovirt-engine-extensions-api</artifactId> + <version>${org.ovirt.engine.api.ovirt-engine-extensions-api.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.jboss.spec.javax.servlet</groupId> + <artifactId>jboss-servlet-api_3.0_spec</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>utils</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>utils</artifactId> + <version>${engine.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.picketlink</groupId> + <artifactId>picketlink-core</artifactId> + <version>2.1.9.Final</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>branding</artifactId> + <version>${engine.version}</version> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>idpfilters</artifactId> + <version>${engine.version}</version> + </dependency> + + </dependencies> + + +</project> diff --git a/backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineAttributeHandler.java b/backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineAttributeHandler.java new file mode 100644 index 0000000..fd3130c --- /dev/null +++ b/backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineAttributeHandler.java @@ -0,0 +1,59 @@ +package org.ovirt.engine.core.idp; + +import org.ovirt.engine.core.aaa.EnginePrincipal; +import org.ovirt.engine.core.common.constants.SessionConstants; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; +import org.picketlink.identity.federation.core.exceptions.ProcessingException; +import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerRequest; +import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerResponse; +import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType; +import org.picketlink.identity.federation.web.constants.GeneralConstants; +import org.picketlink.identity.federation.web.core.HTTPContext; +import org.picketlink.identity.federation.web.handlers.saml2.SAML2AttributeHandler; + +import javax.servlet.http.HttpSession; +import java.security.Principal; +import java.util.Map; + + +public class EngineAttributeHandler extends SAML2AttributeHandler { + + protected static final Log log = LogFactory.getLog(EngineAttributeHandler.class); + @Override + public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { + super.handleRequestType(request, response); + log.info("EngineAttributeHandler . handleRequestType"); + // Do not handle log out request interaction + if (request.getSAML2Object() instanceof LogoutRequestType) + return; + + // only handle IDP side + if (getType() == HANDLER_TYPE.SP) + return; + + HTTPContext httpContext = (HTTPContext) request.getContext(); + HttpSession session = httpContext.getRequest().getSession(false); + + Principal userPrincipal = (Principal) session.getAttribute(GeneralConstants.PRINCIPAL_ID); + + if (userPrincipal == null) + userPrincipal = httpContext.getRequest().getUserPrincipal(); + + Map<String, Object> attribs = (Map<String, Object>) session.getAttribute(GeneralConstants.ATTRIBUTES); + if (attribs != null) { + log.info("EngineAttributeHandler . handleRequestType attr is not null "+ attribs.keySet()); + if (userPrincipal instanceof EnginePrincipal) { + log.info("EngineAttributeHandler . handleRequestType setting ENGINE_SESSION_ID in attribs to " + ((EnginePrincipal) userPrincipal).getEngineSessionId()); + String engineSessionId = ((EnginePrincipal) userPrincipal).getEngineSessionId(); + attribs.put(SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, engineSessionId); + session.setAttribute(SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, engineSessionId); + } + request.addOption(GeneralConstants.ATTRIBUTES, attribs); + session.setAttribute(GeneralConstants.ATTRIBUTES, attribs); + } else { + log.info("EngineAttributeHandler . handleRequestType attr is null"); + } + } + +} diff --git a/backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineLoginModule.java b/backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineLoginModule.java new file mode 100644 index 0000000..75f1b9c --- /dev/null +++ b/backend/manager/modules/idp/src/main/java/org/ovirt/engine/core/idp/EngineLoginModule.java @@ -0,0 +1,259 @@ +package org.ovirt.engine.core.idp; + +import java.io.IOException; +import java.security.Principal; +import java.security.acl.Group; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.InitialContext; +import javax.security.auth.login.LoginException; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.servlet.ServletException; +import org.jboss.security.SimpleGroup; + +import org.ovirt.engine.api.extensions.Base; +import org.ovirt.engine.api.extensions.ExtMap; +import org.ovirt.engine.api.extensions.aaa.Acct; +import org.ovirt.engine.api.extensions.aaa.Authn; +import org.ovirt.engine.core.aaa.AcctUtils; +import org.ovirt.engine.core.aaa.AuthType; +import org.ovirt.engine.core.aaa.AuthenticationProfile; +import org.ovirt.engine.core.aaa.AuthenticationProfileRepository; +import org.ovirt.engine.core.aaa.EnginePrincipal; +import org.ovirt.engine.core.aaa.filters.FiltersHelper; +import org.ovirt.engine.core.common.action.LoginUserParameters; +import org.ovirt.engine.core.common.action.VdcActionType; +import org.ovirt.engine.core.common.action.VdcLoginReturnValueBase; +import org.ovirt.engine.core.common.businessentities.aaa.DbUser; +import org.ovirt.engine.core.common.constants.SessionConstants; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +@SuppressWarnings("rawtypes") +public class EngineLoginModule extends org.jboss.security.auth.spi.UsernamePasswordLoginModule { + + protected static final Log log = LogFactory.getLog(EngineLoginModule.class); + + public EngineLoginModule() { + } + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map<String, ?> sharedState, Map<String, ?> options) { + super.initialize(subject, callbackHandler, sharedState, options); + } + + private transient SimpleGroup userRoles = new SimpleGroup("Roles"); + + /** + * Overridden to return an empty password string as typically one cannot + * obtain a user's password. We also override the validatePassword so + * this is ok. + * + * @return and empty password String + */ + protected String getUsersPassword() throws LoginException { + return ""; + } + + /** + * Overridden by subclasses to return the Groups that correspond to the + * to the role sets assigned to the user. Subclasses should create at + * least a Group named "Roles" that contains the roles assigned to the user. + * A second common group is "CallerPrincipal" that provides the application + * identity of the user rather than the security domain identity. + * + * @return Group[] containing the sets of roles + */ + protected Group[] getRoleSets() throws LoginException { + Group[] roleSets = {userRoles}; + return roleSets; + } + + /** + * Validate the inputPassword by creating a ldap InitialContext with the + * SECURITY_CREDENTIALS set to the password. + * + * @param inputPassword the password to validate. + * @param expectedPassword ignored + */ + protected boolean validatePassword(String inputPassword, String expectedPassword) { + System.out.println("validatePassword"); + boolean isValid = false; + if (inputPassword != null) { + // See if this is an empty password that should be disallowed + if (inputPassword.length() == 0) { + log.info("Rejecting empty password"); + return false; + } + + try { + // Validate the password by trying to create an initial context + String username = getUsername(); + if (username.equals("rnori") && inputPassword.equals("rnorip")) { + isValid = true; + addRole("manager"); + } else { + HashMap<String, Object> authResult = new HashMap<>(); + if (handleCredentials(authResult, username, inputPassword)) { + DbUser user = doLogin(authResult); + log.info("user = " + user.getLoginName() + ", role = " + user.getRole()); + isValid = true; + addRole("user"); + if (user.isAdmin()) { + addRole("admin"); + } + } + } + } catch (Throwable e) { + super.setValidateError(e); + } + } + System.out.println("isValid = " + isValid); + return isValid; + } + + private boolean handleCredentials(Map<String, Object> authResult, String user, String password) { + UserProfile userProfile = translateUser(user); + boolean authenticated = false; + if (userProfile == null || userProfile.profile == null) { + log.errorFormat("Error in obtaining profile {}", userProfile.profile); + } else { + ExtMap outputMap = userProfile.profile.getAuthn().invoke(new ExtMap().mput( + Base.InvokeKeys.COMMAND, + Authn.InvokeCommands.AUTHENTICATE_CREDENTIALS + ).mput( + Authn.InvokeKeys.USER, + userProfile.userName + ).mput( + Authn.InvokeKeys.CREDENTIALS, + password + ) + ); + if (outputMap.<Integer>get(Base.InvokeKeys.RESULT) == Base.InvokeResult.SUCCESS && + outputMap.<Integer>get(Authn.InvokeKeys.RESULT) == Authn.AuthResult.SUCCESS) { + authResult.put(FiltersHelper.Constants.REQUEST_AUTH_RECORD_KEY, + outputMap.<ExtMap>get(Authn.InvokeKeys.AUTH_RECORD)); + authResult.put(FiltersHelper.Constants.REQUEST_AUTH_TYPE_KEY, AuthType.CREDENTIALS); + authResult.put(FiltersHelper.Constants.REQUEST_PROFILE_KEY, userProfile.profile.getName()); + authenticated = true; + } else { + if (outputMap.<Integer>get(Base.InvokeKeys.RESULT) != Base.InvokeResult.SUCCESS + || outputMap.<Integer>get(Authn.InvokeKeys.RESULT) != Authn.AuthResult.SUCCESS) { + AcctUtils.reportRecords( + Acct.ReportReason.PRINCIPAL_LOGIN_FAILED, + userProfile.userName, + null, + null, + "Basic authentication failed for User %1$s.", + userProfile.userName + ); + } + log.errorFormat("Failure in authentication to profile {}. Invocation Result code is {}. Authn result code is {}", + userProfile.profile, + outputMap.<Integer>get(Base.InvokeKeys.RESULT), + outputMap.<Integer>get(Authn.InvokeKeys.RESULT) + ); + } + } + return authenticated; + } + + private UserProfile translateUser(String translateFrom) { + UserProfile result = translateUserProfileUpn(translateFrom); + if (result == null) { + result = translateUserRestApiSpecific(translateFrom); + } + if (result == null) { + result = new UserProfile(translateFrom, null); + } + return result; + } + + private UserProfile translateUserProfileUpn(String translateFrom) { + UserProfile result = null; + int separator = translateFrom.lastIndexOf("@"); + if (separator != -1) { + String profileName = translateFrom.substring(separator + 1); + AuthenticationProfile profile = AuthenticationProfileRepository.getInstance().getProfile(profileName); + result = profile != null ? new UserProfile(translateFrom.substring(0, separator), profile) : null; + } + return result; + } + + private UserProfile translateUserRestApiSpecific(String translateFrom) { + UserProfile result = null; + int separator = translateFrom.indexOf("\\"); + if (separator != -1) { + + String profileName = translateFrom.substring(0, separator); + AuthenticationProfile profile = AuthenticationProfileRepository.getInstance().getProfile(profileName); + result = profile != null ? new UserProfile(translateFrom.substring(separator + 1), profile) : null; + } + return result; + } + + public DbUser doLogin(Map<String, Object> authResult) throws IOException, + ServletException { + DbUser user = null; + try { + String profileName = (String) authResult.get(FiltersHelper.Constants.REQUEST_PROFILE_KEY); + + ExtMap authRecord = (ExtMap) authResult.get(FiltersHelper.Constants.REQUEST_AUTH_RECORD_KEY); + if (authRecord != null) { + InitialContext context = new InitialContext(); + try { + VdcLoginReturnValueBase returnValue = (VdcLoginReturnValueBase) FiltersHelper.getBackend(context).login(new + LoginUserParameters( + profileName, + authRecord, + VdcActionType.LoginUser, + (AuthType) authResult.get(FiltersHelper.Constants.REQUEST_AUTH_TYPE_KEY) + ) + ); + if (returnValue.getSucceeded()) { + authResult.put( + SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, + returnValue.getSessionId() + ); + log.info("returnValue.getSessionId() = " + returnValue.getSessionId()); + user = (DbUser) returnValue.getActionReturnValue(); + ((EnginePrincipal) this.getIdentity()).setEngineSessionId(returnValue.getSessionId()); + } + } finally { + context.close(); + } + } + } catch (Exception ex) { + log.error("Error in login to engine ", ex); + } + return user; + } + + private void addRole(String roleName) { + System.out.println("add role " + roleName); + if (roleName != null) { + try { + Principal p = super.createIdentity(roleName); + log.info("Assigning user to role " + roleName); + userRoles.addMember(p); + } catch (Exception e) { + log.info("Failure to create principal " + roleName); + } + } + } + + private static class UserProfile { + + private String userName; + private AuthenticationProfile profile; + + public UserProfile(String user, AuthenticationProfile profile) { + this.userName = user; + this.profile = profile; + } + } + +} diff --git a/backend/manager/modules/idp/src/main/webapp/WEB-INF/jboss-web.xml b/backend/manager/modules/idp/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 0000000..a6b8b88 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,11 @@ +<jboss-web> + <!-- You must have a security-domain configured in your JBoss EAP instance. The security-domain is necessary to provide user + authentication and roles mappings. --> + <security-domain>idp</security-domain> + + <!-- You must configure one of the PicketLink Authenticators to get enable SAML-based SSO. Identity Providers and Service Providers + have different authenticators. In this case we're configuring an Identity Provider, so we authenticator above is required. --> + <valve> + <class-name>org.picketlink.identity.federation.bindings.tomcat.idp.IDPWebBrowserSSOValve</class-name> + </valve> +</jboss-web> diff --git a/backend/manager/modules/idp/src/main/webapp/WEB-INF/login-error.jsp b/backend/manager/modules/idp/src/main/webapp/WEB-INF/login-error.jsp new file mode 100644 index 0000000..ce6fc89 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/WEB-INF/login-error.jsp @@ -0,0 +1,63 @@ +<%@ page pageEncoding="UTF-8" session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="obrand" uri="obrand" %> +<fmt:setLocale value="${locale}" /> +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <obrand:favicon /> + <title>Login Page</title> + <obrand:stylesheets /> +</head> +<body> +<div class="obrand_loginPageBackground"> + <a href="<obrand:messages key="obrand.common.vendor_url"/>" class="obrand_loginPageLogoImageLink"> + <span class="obrand_loginPageLogoImage"></span> + </a> + <div class="login-pf"> + <div class="container"> + <div class="row"> + + <div class="col-sm-12"> + <div id="brand"> + <div class="obrand_loginFormLogoImage"></div> + </div> + </div> + + <div class="col-sm-12"> + <div style="width:250px;"> + <form id="login_form" name="login_form" method="post" + action="j_security_check" enctype="application/x-www-form-urlencoded"> + <center> + <p> + Welcome to the <b> Identity Provider</b> + </p> + <p style="color: red">Login failed, please try again.</p> + </center> + + <div style="margin-left: 15px;width:250px;"> + <p> + <label for="username" style="width:70px;"> Username</label><input id="username" + type="text" name="j_username" size="20" style="color:black;"/> + </p> + <p> + <label for="password" style="width:70px;"> Password</label><input id="password" + type="password" name="j_password" value="" size="20" style="color:black;"/> + </p> + <center> + <input id="submit" type="submit" name="submit" value="Login" + class="buttonmed" /> + </center> + </div> + </form> + </div> + </div> + + </div> + </div> + </div> + </div> +</body> +</html> diff --git a/backend/manager/modules/idp/src/main/webapp/WEB-INF/login.jsp b/backend/manager/modules/idp/src/main/webapp/WEB-INF/login.jsp new file mode 100644 index 0000000..1ca3782 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/WEB-INF/login.jsp @@ -0,0 +1,63 @@ +<%@ page pageEncoding="UTF-8" session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="obrand" uri="obrand" %> +<fmt:setLocale value="${locale}" /> +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <obrand:favicon /> + <title>Login Page</title> + <obrand:stylesheets /> +</head> +<body> +<div class="obrand_loginPageBackground"> + <a href="<obrand:messages key="obrand.common.vendor_url"/>" class="obrand_loginPageLogoImageLink"> + <span class="obrand_loginPageLogoImage"></span> + </a> + <div class="login-pf"> + <div class="container"> + <div class="row"> + + <div class="col-sm-12"> + <div id="brand"> + <div class="obrand_loginFormLogoImage"></div> + </div> + </div> + + <div class="col-sm-12"> + <div style="width:250px;"> + <form id="login_form" name="login_form" method="post" + action="j_security_check" enctype="application/x-www-form-urlencoded"> + <center> + <p> + Welcome to the <b> Identity Provider</b> + </p> + <p>Please login to proceed.</p> + </center> + + <div style="margin-left: 15px;width:250px;"> + <p> + <label for="username" style="width:70px;"> Username</label><input id="username" + type="text" name="j_username" size="20" style="color:black;"/> + </p> + <p> + <label for="password" style="width:70px;"> Password</label><input id="password" + type="password" name="j_password" value="" size="20" style="color:black;"/> + </p> + <center> + <input id="submit" type="submit" name="submit" value="Login" + class="buttonmed" /> + </center> + </div> + </form> + </div> + </div> + + </div> + </div> + </div> + </div> +</body> +</html> diff --git a/backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-handlers.xml b/backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-handlers.xml new file mode 100644 index 0000000..07ae2d7 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-handlers.xml @@ -0,0 +1,9 @@ +<Handlers xmlns="urn:picketlink:identity-federation:handler:config:2.1"> + <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2IssuerTrustHandler" /> + <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler" /> + <Handler class="org.ovirt.engine.core.idp.EngineAttributeHandler" > + <Option Key="ATTRIBUTE_ KEYS" Value="picketlink.roles"/> + </Handler> + <Handler class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler" /> + <Handler class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler" /> +</Handlers> diff --git a/backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-idfed.xml b/backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-idfed.xml new file mode 100644 index 0000000..b478aa1 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/WEB-INF/picketlink-idfed.xml @@ -0,0 +1,6 @@ +<PicketLinkIDP xmlns="urn:picketlink:identity-federation:config:2.1"> + <IdentityURL>${idp.url::/ovirt-engine/idp/}</IdentityURL> + <Trust> + <Domains>localhost</Domains> + </Trust> +</PicketLinkIDP> diff --git a/backend/manager/modules/idp/src/main/webapp/WEB-INF/web.xml b/backend/manager/modules/idp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..6bf43a8 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,160 @@ +<?xml version="1.0"?> +<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + version="2.5"> + + <display-name>PicketLink Identity Provider</display-name> + + <description>PicketLink Identity Provider With a Basic Configuration</description> + + <listener> + <listener-class>org.picketlink.identity.federation.web.listeners.IDPHttpSessionListener</listener-class> + </listener> + + <!-- Application with context parameters --> + <context-param> + <param-name>obrandThemePath</param-name> + <param-value>/theme</param-value> + </context-param> + <context-param> + <param-name>obrandResourcePath</param-name> + <param-value>/theme-resource</param-value> + </context-param> + <context-param> + <param-name>obrandApplicationName</param-name> + <param-value>welcome</param-value> + </context-param> + + <!-- Branding Servlet --> + <servlet> + <servlet-name>BrandingServlet</servlet-name> + <servlet-class>org.ovirt.engine.core.branding.BrandingServlet</servlet-class> + </servlet> + <servlet-mapping> + <servlet-name>BrandingServlet</servlet-name> + <url-pattern>/theme/*</url-pattern> + </servlet-mapping> + + <!-- Branding Cascading Resource Servlet --> + <servlet> + <servlet-name>BrandingCascadingResourceServlet</servlet-name> + <servlet-class>org.ovirt.engine.core.branding.BrandingCascadingResourceServlet</servlet-class> + </servlet> + <servlet-mapping> + <servlet-name>BrandingCascadingResourceServlet</servlet-name> + <url-pattern>/theme-resource/*</url-pattern> + </servlet-mapping> + + + + <filter> + <filter-name>LoginFilter</filter-name> + <filter-class>org.ovirt.engine.core.idp.filters.EngineIDPLoginFilter</filter-class> + <init-param> + <param-name>login-as-admin</param-name> + <param-value>true</param-value> + </init-param> + </filter> + <filter-mapping> + <filter-name>LoginFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <!-- Filters --> + <!-- Locale Filter, determines the user locale --> + <filter> + <filter-name>LocaleFilter</filter-name> + <filter-class>org.ovirt.engine.core.utils.servlet.LocaleFilter</filter-class> + </filter> + <!-- Branding Filter, passes branding information to jsps --> + <filter> + <filter-name>BrandingFilter</filter-name> + <filter-class>org.ovirt.engine.core.branding.BrandingFilter</filter-class> + </filter> + + <!-- Header filter to automatically add some headers to each response --> + <filter> + <filter-name>HeaderFilter</filter-name> + <filter-class>org.ovirt.engine.core.utils.servlet.HeaderFilter</filter-class> + </filter> + + <filter-mapping> + <filter-name>HeaderFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <filter-mapping> + <filter-name>LocaleFilter</filter-name> + <url-pattern>/*</url-pattern> + <dispatcher>REQUEST</dispatcher> + <dispatcher>ERROR</dispatcher> + </filter-mapping> + + <filter-mapping> + <filter-name>BrandingFilter</filter-name> + <url-pattern>/*</url-pattern> + <dispatcher>FORWARD</dispatcher> + <dispatcher>REQUEST</dispatcher> + <dispatcher>ERROR</dispatcher> + </filter-mapping> + + + + <!-- Login Servlet --> + <servlet> + <servlet-name>LoginServlet</servlet-name> + <jsp-file>/WEB-INF/login.jsp</jsp-file> + </servlet> + + <servlet-mapping> + <servlet-name>LoginServlet</servlet-name> + <url-pattern>/login.jsp</url-pattern> + </servlet-mapping> + + <!-- LoginError Servlet --> + <servlet> + <servlet-name>LoginErrorServlet</servlet-name> + <jsp-file>/WEB-INF/login-error.jsp</jsp-file> + </servlet> + + <servlet-mapping> + <servlet-name>LoginErrorServlet</servlet-name> + <url-pattern>/login-error.jsp</url-pattern> + </servlet-mapping> + <!-- Define a security constraint that gives unlimited access to some static resources. --> + <security-constraint> + <web-resource-collection> + <web-resource-name>Theme</web-resource-name> + <url-pattern>/theme/*</url-pattern> + </web-resource-collection> + <web-resource-collection> + <web-resource-name>Resource</web-resource-name> + <url-pattern>/theme-resource/*</url-pattern> + </web-resource-collection> + </security-constraint> + + <!-- Define a Security Constraint on this application. --> + <security-constraint> + <web-resource-collection> + <web-resource-name>User Access</web-resource-name> + <url-pattern>/*</url-pattern> + </web-resource-collection> + <auth-constraint> + <role-name>user</role-name> + </auth-constraint> + </security-constraint> + <!-- Define the Login Configuration for this application. --> + <login-config> + <auth-method>FORM</auth-method> + <realm-name>PicketLink IDP Application</realm-name> + <form-login-config> + <form-login-page>/login.jsp</form-login-page> + <form-error-page>/login-error.jsp</form-error-page> + </form-login-config> + </login-config> + + <!-- Security roles referenced by this web application. --> + <security-role> + <role-name>user</role-name> + </security-role> +</web-app> diff --git a/backend/manager/modules/idp/src/main/webapp/hosted/index.jsp b/backend/manager/modules/idp/src/main/webapp/hosted/index.jsp new file mode 100644 index 0000000..405b741 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/hosted/index.jsp @@ -0,0 +1,44 @@ +<%@ page pageEncoding="UTF-8" session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="obrand" uri="obrand" %> +<fmt:setLocale value="${locale}" /> +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <obrand:favicon /> + <title>oVirt Engine</title> + <obrand:stylesheets /> +</head> +<body> +<div class="obrand_loginPageBackground"> + <a href="<obrand:messages key="obrand.common.vendor_url"/>" class="obrand_loginPageLogoImageLink"> + <span class="obrand_loginPageLogoImage"></span> + </a> + <div class="login-pf"> + <div class="container"> + <div class="row"> + + <div class="col-sm-12"> + <div id="brand"> + <div class="obrand_loginFormLogoImage"></div> + </div> + </div> + + <div class="col-sm-12 welcome-title-wrapper"> + <span class="welcome-title">Welcome to Open Virtualization Manager</span> + </div> + + <div class="col-sm-12"> + <a id="userportal" href="/userportal/?locale=en_US<">User Portal</a><br> + <a id="webadmin" href="/webadmin/?locale=en_US<">Administration Portal</a> + </div> + + </div> + </div> + </div> + </div> +</body> +</html> + diff --git a/backend/manager/modules/idp/src/main/webapp/index.jsp b/backend/manager/modules/idp/src/main/webapp/index.jsp new file mode 100644 index 0000000..405b741 --- /dev/null +++ b/backend/manager/modules/idp/src/main/webapp/index.jsp @@ -0,0 +1,44 @@ +<%@ page pageEncoding="UTF-8" session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="obrand" uri="obrand" %> +<fmt:setLocale value="${locale}" /> +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <obrand:favicon /> + <title>oVirt Engine</title> + <obrand:stylesheets /> +</head> +<body> +<div class="obrand_loginPageBackground"> + <a href="<obrand:messages key="obrand.common.vendor_url"/>" class="obrand_loginPageLogoImageLink"> + <span class="obrand_loginPageLogoImage"></span> + </a> + <div class="login-pf"> + <div class="container"> + <div class="row"> + + <div class="col-sm-12"> + <div id="brand"> + <div class="obrand_loginFormLogoImage"></div> + </div> + </div> + + <div class="col-sm-12 welcome-title-wrapper"> + <span class="welcome-title">Welcome to Open Virtualization Manager</span> + </div> + + <div class="col-sm-12"> + <a id="userportal" href="/userportal/?locale=en_US<">User Portal</a><br> + <a id="webadmin" href="/webadmin/?locale=en_US<">Administration Portal</a> + </div> + + </div> + </div> + </div> + </div> +</body> +</html> + diff --git a/backend/manager/modules/idpfilters/pom.xml b/backend/manager/modules/idpfilters/pom.xml new file mode 100644 index 0000000..ea122aa --- /dev/null +++ b/backend/manager/modules/idpfilters/pom.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>manager-modules</artifactId> + <groupId>org.ovirt.engine.core</groupId> + <version>3.6.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>idpfilters</artifactId> + <packaging>jar</packaging> + <name>ovirt-engine idpfilters</name> + <description>oVirt engine idpfilters</description> + + <dependencies> + + <dependency> + <groupId>org.picketbox</groupId> + <artifactId>picketbox</artifactId> + <version>4.0.7.Final</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.jboss.logging</groupId> + <artifactId>jboss-logging</artifactId> + <version>3.1.3.GA</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>common</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>bll</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>${engine.groupId}</groupId> + <artifactId>utils</artifactId> + <version>${engine.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.jboss.spec.javax.servlet</groupId> + <artifactId>jboss-servlet-api_3.0_spec</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.picketlink</groupId> + <artifactId>picketlink-core</artifactId> + <version>2.1.9.Final</version> + <scope>provided</scope> + </dependency> + + </dependencies> + + +</project> diff --git a/backend/manager/modules/idpfilters/src/main/java/org/ovirt/engine/core/idp/filters/EngineIDPLoginFilter.java b/backend/manager/modules/idpfilters/src/main/java/org/ovirt/engine/core/idp/filters/EngineIDPLoginFilter.java new file mode 100644 index 0000000..30072ec --- /dev/null +++ b/backend/manager/modules/idpfilters/src/main/java/org/ovirt/engine/core/idp/filters/EngineIDPLoginFilter.java @@ -0,0 +1,119 @@ +package org.ovirt.engine.core.idp.filters; + +import org.ovirt.engine.core.aaa.EnginePrincipal; +import org.ovirt.engine.core.bll.interfaces.BackendInternal; +import org.ovirt.engine.core.common.businessentities.aaa.DbUser; +import org.ovirt.engine.core.common.constants.SessionConstants; +import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; +import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; +import org.ovirt.engine.core.common.queries.VdcQueryType; +import org.ovirt.engine.core.utils.ejb.BeanProxyType; +import org.ovirt.engine.core.utils.ejb.BeanType; +import org.ovirt.engine.core.utils.ejb.EjbUtils; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; +import org.picketlink.identity.federation.web.constants.GeneralConstants; + +import java.io.IOException; +import java.security.Principal; +import java.util.List; +import java.util.Map; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +public class EngineIDPLoginFilter implements Filter { + + protected static final Log log = LogFactory.getLog(EngineIDPLoginFilter.class); + + FilterConfig filterConfig; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + this.filterConfig = filterConfig; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { + HttpServletRequest req = (HttpServletRequest) request; + Principal principal = req.getUserPrincipal(); + if (principal != null) { + HttpSession session = req.getSession(true); + String engineSessionId = null; + if (principal instanceof EnginePrincipal) { + engineSessionId = ((EnginePrincipal) principal).getEngineSessionId(); + } else { + Map<String, Object> attrMap = (Map<String, Object>) session.getAttribute(GeneralConstants.SESSION_ATTRIBUTE_MAP); + if (attrMap != null && attrMap.containsKey(SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY)) { + engineSessionId = ((List<String>) attrMap.get(SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY)).get(0); + } + } + log.info("LoginFilter.doFilter sessionId = " + session.getId() + ", engineSessionId = " + engineSessionId); + if (engineSessionId != null) { + request.setAttribute( + SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, + engineSessionId + ); + session.setAttribute( + SessionConstants.HTTP_SESSION_ENGINE_SESSION_ID_KEY, + engineSessionId + ); + runQuery(engineSessionId); + } + } + chain.doFilter(request, response); + } + + private boolean runQuery(String sessionID) { + boolean returnValue = false; + + BackendInternal backend = (BackendInternal) EjbUtils.findBean(BeanType.BACKEND, BeanProxyType.LOCAL); + VdcQueryParametersBase params = new VdcQueryParametersBase(); + params.setSessionId(sessionID); + + VdcQueryReturnValue queryReturnValue = backend.runInternalQuery(VdcQueryType.ValidateSession, params); + + if (queryReturnValue != null) { + returnValue = queryReturnValue.getSucceeded(); + + if (returnValue) { + DbUser user = queryReturnValue.getReturnValue(); + + // We get the user name only in case the validation succeeded, and the user is an administrator + if (user.isAdmin()) { + log.info("Admin user associated with the session = " + getUPN(user)); + } else { + log.info("User associated with the session = " + getUPN(user)); + } + } else { + log.info("Got no user for sessionId"); + } + } else { + log.error("Got NULL from backend.RunQuery"); + } + + return returnValue; + } + + private String getUPN(DbUser user) { + String retVal = user.getLoginName(); + if (!retVal.contains("@")) { + retVal = retVal + "@" + user.getDomain(); + } + return retVal; + } + + + @Override + public void destroy() { + } + +} + diff --git a/backend/manager/modules/pom.xml b/backend/manager/modules/pom.xml index 73d590c..bb8b3ab 100644 --- a/backend/manager/modules/pom.xml +++ b/backend/manager/modules/pom.xml @@ -22,6 +22,8 @@ <module>utils</module> <module>common</module> <module>dal</module> + <module>idp</module> + <module>idpfilters</module> <module>vdsbroker</module> <module>builtin-extensions</module> <module>scheduler</module> diff --git a/ear/pom.xml b/ear/pom.xml index 8bdb747..9c08d54 100644 --- a/ear/pom.xml +++ b/ear/pom.xml @@ -58,6 +58,13 @@ <dependency> <groupId>org.ovirt.engine.core</groupId> + <artifactId>idp</artifactId> + <version>${engine.version}</version> + <type>war</type> + </dependency> + + <dependency> + <groupId>org.ovirt.engine.core</groupId> <artifactId>docs</artifactId> <version>${engine.version}</version> <type>war</type> @@ -212,6 +219,13 @@ <webModule> <groupId>org.ovirt.engine.core</groupId> + <artifactId>idp</artifactId> + <bundleFileName>idp.war</bundleFileName> + <contextRoot>/ovirt-engine/idp</contextRoot> + </webModule> + + <webModule> + <groupId>org.ovirt.engine.core</groupId> <artifactId>docs</artifactId> <bundleFileName>docs.war</bundleFileName> <contextRoot>/ovirt-engine/docs</contextRoot> diff --git a/ear/src/main/application/META-INF/jboss-deployment-structure.xml b/ear/src/main/application/META-INF/jboss-deployment-structure.xml index cec2758..aa5714c 100644 --- a/ear/src/main/application/META-INF/jboss-deployment-structure.xml +++ b/ear/src/main/application/META-INF/jboss-deployment-structure.xml @@ -5,6 +5,8 @@ <module name="javax.enterprise.api"/> <module name="javax.inject.api"/> <module name="javax.interceptor.api"/> + <module name="org.picketbox" export="true" meta-inf="import"/> + <module name="org.picketlink" export="true" meta-inf="import"/> <module name="org.ovirt.engine.core.common" export="true" meta-inf="import"/> <module name="org.ovirt.engine.core.utils" export="true" meta-inf="import"/> <module name="org.ovirt.engine.core.dal" export="true" meta-inf="import"/> diff --git a/packaging/services/ovirt-engine/ovirt-engine.xml.in b/packaging/services/ovirt-engine/ovirt-engine.xml.in index 66220c4..5da88d0 100644 --- a/packaging/services/ovirt-engine/ovirt-engine.xml.in +++ b/packaging/services/ovirt-engine/ovirt-engine.xml.in @@ -269,6 +269,19 @@ <subsystem xmlns="urn:jboss:domain:security:1.1"> <security-domains> + <security-domain name="idp" cache-type="default"> + <authentication> + <login-module code="org.ovirt.engine.core.idp.EngineLoginModule" flag="required"> + <module-option name="debug" value="true"/> + <module-option name="principalClass" value="org.ovirt.engine.core.aaa.EnginePrincipal"/> + </login-module> + </authentication> + </security-domain> + <security-domain name="sp" cache-type="default"> + <authentication> + <login-module code="org.picketlink.identity.federation.bindings.jboss.auth.SAML2LoginModule" flag="required"/> + </authentication> + </security-domain> <security-domain name="other" cache-type="default"> <authentication> <login-module code="Remoting" flag="optional"> -- To view, visit http://gerrit.ovirt.org/34194 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ib549b3b563081a37b1fae3f437a73a208dd86db5 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