Kerberos/Spnego/Tomcat/Jira 7 Integration : how to bypass login process?

Hello,

Today, my company uses a uniq repository to manage authentications and authorizations for the users to access all the applications including Jira 7. This repository provides a SOAP Web Service. The team I joined previously developed a seraph authenticator (deriving from DefaultAuthenticator) which uses the SOAP webservices and allows/forbids users to log in to Jira (the Java code to do so is contained in the overriden method authenticate) . Users are guided to secure/Dashboard.jspa. Login and password are typed, they click on Connect, and then the seraph authenticator runs and determines whether to allow/forbid the current user to log in.

Tomorrow, my company would like to manage SSO with Kerberos on Jira 7. Kerberos would only manage authentication. Authorizations would be managed by the existing repository : Jira would keep on accessing this authorizations repository via a SOAP Web Service.

I achieved to establish good communication between Tomcat and Spengo and Kerberos. Here’s the configuration :

context.xml file :

<Valve className="org.apache.catalina.authenticator.SpnegoAuthenticator" loginConfigName="spnego-server-jaas" alwaysUseSession="true" cache="true" />

<Realm className="org.apache.catalina.realm.JAASRealm" allRolesMode="strictAuthOnly" appName="spnego-server-jaas"                 userClassNames="javax.security.auth.kerberos.KerberosPrincipal" roleClassNames="javax.security.auth.kerberos.KerberosPrincipal"/>

web.xml file :

<security-constraint>
  <display-name>all_auth</display-name>
  <web-resource-collection>
        <web-resource-name/>
    <description/>
        <url-pattern>/secure/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>*</role-name>
  </auth-constraint>
</security-constraint>

<login-config>
        <auth-method>SPNEGO</auth-method>
</login-config>

JAVA_OPTS variable :
KERBEROS_CONFIG="-Djava.security.krb5.conf=${REP_APPLICATION}/ext/krb/krb5.conf -Djava.security.auth.login.config=${REP_APPLICATION}/ext/krb/spnego-server-jaas.conf"

spnego-server-jaas.conf :

spnego-server-jaas {
        com.sun.security.auth.module.Krb5LoginModule required
        doNotPrompt=true
        storeKey=true
        useKeyTab=true
        keyTab="absolute path to keytab file"
        principal="HTTP/ DNS SERVER @ REALM"
        debug=true
        isInitiator=false
        storePass=true;
};

I adapted the seraph authenticator (still derived from DefaultAuthenticator) (https://github.com/mschieder/jira-pvp-sso) and I declared it into the seraph-config.xml file :

    @Override
	public Principal getUser(HttpServletRequest request, HttpServletResponse response) {
		Principal user = null;

		try {
			if (request.getSession() != null
					&& request.getSession().getAttribute(DefaultAuthenticator.LOGGED_IN_KEY) != null) {
				LOGGER.info("URL = [" + request.getRequestURL() + "]" + "Session found; user already logged in");
				user = (Principal) request.getSession().getAttribute(DefaultAuthenticator.LOGGED_IN_KEY);
				return user;
			}

			SSOnCookie ssoCookie = SSOnCookie.getSSOCookie(request, response);

			if (ssoCookie == null || ssoCookie.isExpired()) {
				LOGGER.info("URL = [" + request.getRequestURL() + "]" + "Got SSOnCookie " + ssoCookie + "SSOCookie is null; redirecting");
				// user was not found, or not currently valid
				return null;
			}

			// Seamless login from intranet
			String username = ssoCookie.getLoginId();
			LOGGER.info("URL = [" + request.getRequestURL() + "]" + "Got SSOnCookie " + ssoCookie + "Got username = [" + username + "]");
			if (username != null) {
				user = getUser(username);
				LOGGER.info("Logged in via SSO, with User " + user);
				request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, user);
				request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
			}
		} catch (Exception e) { // catch class cast exceptions
			LOGGER.warn("Exception: " + e, e);
		}
		return user;
	}

SSOCookie.java :

/**
 * Simple SSOnCookie implementation as suggested here:
 * https://docs.atlassian.com/atlassian-seraph/latest/sso.html
 * 
 * @author Michael Schieder
 * 
 */
public class SSOnCookie {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SSOnCookie.class);
	

	private final static String COOKIE_NAME = "pvpJira";
	private String loginId;
	/**
	 * userid, usually with the syntax first.last@company.com
	 */
	public final static String HTTP_HEADER_USERID = "X-AUTHENTICATE-UserID";

	public SSOnCookie(String loginId) {
		this.loginId = loginId;
	}

	public static SSOnCookie getSSOCookie(HttpServletRequest request, HttpServletResponse response) {
		if (request.getCookies() != null) {
			for (Cookie next : request.getCookies()) {
				if (COOKIE_NAME.equals(next.getName())) {
					LOGGER.info(COOKIE_NAME + " a ete trouve dans l'objet Request");
					return new SSOnCookie(next.getValue());
				}
			}
		}

        String username = request.getHeader(SSOnCookie.HTTP_HEADER_USERID);
		LOGGER.info(SSOnCookie.HTTP_HEADER_USERID + " = [" + username + "]");
//		String username = request.getRemoteUser();
		LOGGER.info("RemoteUser = [" + request.getRemoteUser() + "]");
		SSOnCookie ssoCookie = null;
		if (username != null) {
			response.addCookie(new Cookie(COOKIE_NAME, username));
			ssoCookie = new SSOnCookie(username);
		}

		return ssoCookie;
	}

	public boolean isExpired() {
		return false;
	}

	public String getLoginId() {
		return loginId;
	}

}

The method getUser(username) contains the necessary code to call the SOAP Web Service to interrogate the authorizations repository.

The hardship I’m facing is that every time I call the JIRA URL, I need to type my login/password to log in Jira whereas Tomcat/Spnego/Kerberos has authenticated me. So what do I need to do to bypass the login process ?

Thanks for your interest.

Hello,

I finally solved my problem. I was inspired by https://github.com/AngusWarren/remoteuserauth.

I derived the seraph authenticator from JiraSeraphAuthenticator and rewrote my code as :

	public Principal getUser(HttpServletRequest request, HttpServletResponse response) {
		Principal user = null;
		if (request.getSession() != null
				&& request.getSession().getAttribute(JiraSeraphAuthenticator.LOGGED_IN_KEY) != null) {
			LOGGER.info("Session found; user already logged in");
			user = (Principal) request.getSession().getAttribute(JiraSeraphAuthenticator.LOGGED_IN_KEY);
			return user;
		}

		LOGGER.debug("Trying REMOTE_USER for SSO");
		String remoteuser = request.getRemoteUser();

		if (StringUtils.isEmpty(remoteuser)) {
			LOGGER.debug("remote_user is null");
			return null;
		}

		LOGGER.info("remoteuser = [" + remoteuser + "]");
		if (StringUtils.indexOf(remoteuser, '@') > -1) {
			String[] username = StringUtils.split(remoteuser, "@");
			if (ArrayUtils.isNotEmpty(username)) {
				LOGGER.debug("username = [" + username.length + "] username[0] = ["
						+ (username.length > 1 && StringUtils.isNotEmpty(username[0]) ? username[0] : "")
						+ "] username[1] = ["
						+ (username.length > 2 && StringUtils.isNotEmpty(username[1]) ? username[1] : "") + "]");
				if (StringUtils.isNotEmpty(username[0])) {
					user = getUser(username[0]);
				}
			}
		} else {
			user = getUser(remoteuser);
		}

		if (user != null) {
			LOGGER.info("Logging in with username : [" + user.getName() + "]");
			request.getSession().setAttribute(JiraSeraphAuthenticator.LOGGED_IN_KEY, user);
			request.getSession().setAttribute(JiraSeraphAuthenticator.LOGGED_OUT_KEY, null);
		}
		return user;
	}

The method getUser(username) contains the necessary code to call the SOAP Web Service to interrogate the authorizations repository.

It works fine !!