/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.capedwarf.users; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; import org.codehaus.jackson.map.ObjectMapper; import org.jboss.capedwarf.common.io.Base64Utils; import org.jboss.capedwarf.common.servlet.ServletUtils; import org.jboss.capedwarf.shared.config.ApplicationConfiguration; import org.jboss.capedwarf.shared.config.OAuthConfiguration; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; /** * @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a> * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public class OAuthLoginProductionAuthHandler extends AuthHandler { private static final String STATE_ATTRIBUTE = "__capedwarf_oauth_login_state"; private static final String DESTINATION_URL_ATTRIBUTE = "__capedwarf_oauth_login_destination_url"; private static final String GMAIL_COM = "gmail.com"; private static final String URLS_ENDPOINT = "https://accounts.google.com/.well-known/openid-configuration"; private ObjectMapper objectMapper = new ObjectMapper(); private volatile Urls urls; public OAuthLoginProductionAuthHandler() { validateConfiguration(); } private void validateConfiguration() { if (getClientId() == null) { throw new IllegalStateException("OAuth clientId not configured in capedwarf-web.xml"); } if (getClientSecret() == null) { throw new IllegalStateException("OAuth clientSecret not configured in capedwarf-web.xml"); } } private Urls getUrls() { if (urls == null) { synchronized (this) { if (urls == null) { ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target(URLS_ENDPOINT); Response response = target.request().accept("application/json").get(); urls = response.readEntity(Urls.class); } } } return urls; } public void handleLoginRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { String authDomain = request.getParameter(AuthServlet.AUTH_DOMAIN_PARAM); String state = new BigInteger(130, new SecureRandom()).toString(32); String destinationUrl = request.getParameter(AuthServlet.DESTINATION_URL_PARAM); request.getSession().setAttribute(DESTINATION_URL_ATTRIBUTE, destinationUrl); request.getSession().setAttribute(STATE_ATTRIBUTE, state); String endpointUrl = getFullAuthorizationEndpointUrl(authDomain, state); ServletUtils.redirect(request, response, endpointUrl); } private String getFullAuthorizationEndpointUrl(String authDomain, String state) { return getAuthorizationEndpoint() + "?redirect_uri=" + getRedirectUrl() + "&client_id=" + getClientId() + "&scope=openid email" + "&response_type=code" + "&state=" + state + (authDomain == null ? "" : ("&hd=" + authDomain)); } private String getRedirectUrl() { return getReturnUrl(); } @Override public void handleOpenIDCallBackRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { String state = request.getParameter("state"); if (!state.equals(request.getSession().getAttribute(STATE_ATTRIBUTE))) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } String code = request.getParameter("code"); try { GoogleIdToken token = obtainGoogleIdToken(code); String email = token.getEmail(); String userId = token.getSubject(); String authDomain = GMAIL_COM; // appspot always returns gmail.com, even for custom domains boolean isAdmin = ApplicationConfiguration.getInstance().getCapedwarfConfiguration().isAdmin(email); setupUserPrincipal(request, email, userId, authDomain, isAdmin); String destination = (String) request.getSession().getAttribute(DESTINATION_URL_ATTRIBUTE); ServletUtils.forward(request, response, destination); } catch (Exception e) { throw new IOException(e); } } private GoogleIdToken obtainGoogleIdToken(String code) throws IOException { ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target(getTokenEndpoint()); Form form = new Form() .param("code", code) .param("client_id", getClientId()) .param("client_secret", getClientSecret()) .param("redirect_uri", getRedirectUrl()) .param("grant_type", "authorization_code"); Response clientResponse = target.request().accept("application/json").post(Entity.form(form)); OAuthLoginResponse oauthLoginResponse = clientResponse.readEntity(OAuthLoginResponse.class); String[] jwtParts = oauthLoginResponse.getIdToken().split("\\."); String content = jwtParts[1]; return objectMapper.readValue(Base64Utils.decodeWebSafe(content), GoogleIdToken.class); } private String getAuthorizationEndpoint() { return getUrls().getAuthorizationEndpoint(); } private String getTokenEndpoint() { return getUrls().getTokenEndpoint(); } private String getReturnUrl() { return AuthServlet.getServletUrl() + AuthServlet.CALLBACK_PATH; } private String getClientId() { return getOAuthConfig().getClientId(); } public String getClientSecret() { return getOAuthConfig().getClientSecret(); } private OAuthConfiguration getOAuthConfig() { return ApplicationConfiguration.getInstance().getCapedwarfConfiguration().getOAuthConfiguration(); } }