All Downloads are FREE. Search and download functionalities are using the official Maven repository.

leap.oauth2.wac.OAuth2WebAppSecurityInterceptor Maven / Gradle / Ivy

There is a newer version: 0.7.13b
Show newest version
/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package leap.oauth2.wac;

import leap.core.annotation.Inject;
import leap.lang.Strings;
import leap.lang.http.QueryStringBuilder;
import leap.lang.intercepting.State;
import leap.lang.logging.Log;
import leap.lang.logging.LogFactory;
import leap.lang.net.Urls;
import leap.lang.servlet.Servlets;
import leap.oauth2.OAuth2Params;
import leap.oauth2.RequestOAuth2Params;
import leap.oauth2.proxy.UserAgentForwardedResolver;
import leap.oauth2.wac.auth.WacResponseHandler;
import leap.web.App;
import leap.web.AppInitializable;
import leap.web.Request;
import leap.web.Response;
import leap.web.security.SecurityConfigurator;
import leap.web.security.SecurityInterceptor;
import leap.web.security.authc.AuthenticationManager;
import leap.web.security.login.LoginContext;
import leap.web.security.logout.LogoutContext;
import leap.web.security.logout.LogoutManager;
import leap.web.view.View;
import leap.web.view.ViewSource;

import java.io.PrintWriter;

public class OAuth2WebAppSecurityInterceptor implements SecurityInterceptor, AppInitializable {

    private static final Log log = LogFactory.get(OAuth2WebAppSecurityInterceptor.class);

    protected @Inject OAuth2WebAppConfig    config;
    protected @Inject SecurityConfigurator  sc;
    protected @Inject ViewSource            vs;
    protected @Inject AuthenticationManager am;
    protected @Inject LogoutManager         lom;
    protected @Inject WacResponseHandler[]  handlers;
    protected @Inject UserAgentForwardedResolver proxyResolver;
    
    protected String redirectPath;
    protected String logoutPath;
    protected View   defaultErrorView;
    
    @Override
    public void postAppInit(App app) throws Throwable {
        if(config.isEnabled()) {
            String redirectUri = config.getClientRedirectUri();
            if(redirectUri.startsWith("/")) {
                redirectPath = Servlets.getRequestPathFromUri(redirectUri);
            }else{
                redirectPath = Servlets.getRequestPathFromUri(redirectUri, app.getContextPath());
                // when redirectPath is empty,set it to contextPath
                if(Strings.isEmpty(redirectPath)){
                	redirectPath = "/";
                }
            }
            sc.ignore(redirectPath);
            app.routes().get(redirectPath, (req, resp) -> handleAuthzServerLoginResponse(req, resp));

            String logoutUri = config.getClientLogoutUri();
            if(!Strings.isEmpty(logoutUri)) {
                if(logoutUri.startsWith("/")){
                    logoutPath = Servlets.getRequestPathFromUri(logoutUri);
                }else{
                    logoutPath = Servlets.getRequestPathFromUri(logoutUri, app.getContextPath());
                    // when logoutPath is empty,set it to root contextPath
                    if(Strings.isEmpty(logoutPath)){
                    	logoutPath = "/";
                    }
                }
                sc.ignore(logoutPath);
                app.routes().get(logoutPath,(req,resp) -> handleAuthzServerLogoutNotification(req, resp));
            }

            if(!Strings.isEmpty(config.getErrorView())) {
                defaultErrorView = vs.getView(config.getErrorView());
            }
        }
    }

    @Override
    public State prePromoteLogin(Request request, Response response, LoginContext context) throws Throwable {
        if(config.isOAuth2LoginEnabled()) {
            //Check cyclic redirect.
            if(!Strings.isEmpty(request.getParameter("oauth2_redirect"))) {
                throw new IllegalStateException("Cannot promote login for oauth2 redirect request : " + request.getUri());
            }else{
            	context.setLoginUrl(buildRemoteLoginUrl(request));
            }
        }
        return State.CONTINUE;
    }

    @Override
    public State preLogout(Request request, Response response, LogoutContext context) throws Throwable {
        if(config.isEnabled() && config.isOAuth2LogoutEnabled()) {
            Boolean reqeustedLogout = (Boolean)request.getAttribute("oauth2_logout");
            if(null != reqeustedLogout) {
                return State.CONTINUE;
            }

            String remoteLogoutParam = request.getParameter("remote_logout");
            if("0".equals(remoteLogoutParam)) {
                return State.CONTINUE;
            }else{
                response.sendRedirect(buildRemoteLogoutUrl(request));
                return State.INTERCEPTED;
            }
        }
        return State.CONTINUE;
    }

    protected void handleAuthzServerLogoutNotification(Request request, Response response) throws Throwable {
        log.debug("Logout by oauth2 authorization server");
        am.logoutImmediately(request, response);
    }

    protected void handleAuthzServerLoginResponse(Request request, Response response) throws Throwable {
        String logoutParam = request.getParameter("oauth2_logout");
        if(!Strings.isEmpty(logoutParam)) {
            request.setAttribute("oauth2_logout", Boolean.TRUE);
            lom.logout(request, response);
        }else{
            OAuth2Params params = new RequestOAuth2Params(request);
            if(params.isError()) {
                handleOAuth2ServerError(request, response, params);
            }else{
                handleOAuth2ServerSuccess(request, response, params);
            }
        }
    }
    
    protected void handleOAuth2ServerError(Request request, Response response, OAuth2Params params) throws Throwable {
        if(null != defaultErrorView) {
            View view = request.getViewSource().getView(config.getErrorView(), request.getLocale());
            if(null == view) {
                view = defaultErrorView;
            }
            
            view.render(request, response);
        }else{
            printError(response, params.getError(), params.getErrorDescription());
        }
    }
    
    protected void printError(Response response, String error, String desc) throws Throwable {
        PrintWriter out = response.getWriter();
        out.write(error);
        
        if(!Strings.isEmpty(desc)) {
            out.write(":");
            out.write(desc);
        }
    }
    
    protected void handleOAuth2ServerSuccess(Request request, Response response, OAuth2Params params) throws Throwable {
        boolean processed = false;
        
        for (WacResponseHandler handler : handlers) {
            State state = handler.handleSuccessResponse(request, response, params);
            if (State.isProcessed(state)) {
                processed = true;
            }
            if (State.isIntercepted(state)) {
                return;
            }
        }
        
        if(!processed) {
            printError(response, "invalid_redirect", "cannot handle the response from oauth2 server");
        }
    }
    
    protected String buildRemoteLoginUrl(Request request){
        QueryStringBuilder qs = new QueryStringBuilder();
        
        qs.add(OAuth2Params.RESPONSE_TYPE, config.isAccessTokenEnabled() ? "code id_token" : "id_token");
        qs.add(OAuth2Params.CLIENT_ID,     config.getClientId());
        qs.add(OAuth2Params.REDIRECT_URI,  buildClientRedirectUri(request));
        qs.add(OAuth2Params.LOGOUT_URI,    buildClientLogoutUri(request));
        
        return "redirect:" + Urls.appendQueryString(config.getServerAuthorizationEndpointUrl(), qs.build());
    }
    
    protected String buildClientRedirectUri(Request request) {
        String url = null;
        
        if(!config.getClientRedirectUri().startsWith("/")) {
            url = config.getClientRedirectUri();
        }else{
            if(proxyResolver.isProxyRequest(request)){
                String contextUrl = proxyResolver.resolveUserAgentForwarded(request);
                if(!Strings.isEmpty(contextUrl)){
                    url = contextUrl + config.getClientRedirectUri();
                }else{
                    url = request.getContextUrl() + config.getClientRedirectUri();
                }
            }else{
                url = request.getContextUrl() + config.getClientRedirectUri();
            }
        }

        url = Urls.appendQueryString(url, "oauth2_redirect=1&" + sc.config().getReturnUrlParameterName() + "=" + Urls.encode(request.getUriWithQueryString()));
        
        return url;
    }

    protected String buildClientLogoutUri(Request request) {
        String url = config.getClientLogoutUri();

        if(Strings.isEmpty(url)) {
            return request.getContextUrl() + "/logout";
        }else if(url.startsWith("/")) {
            url = request.getContextUrl() + url;
        }
        return url;
    }

    protected String buildRemoteLogoutUrl(Request request) {
        QueryStringBuilder qs = new QueryStringBuilder();
        
        qs.add(OAuth2Params.CLIENT_ID,                config.getClientId());
        qs.add(OAuth2Params.POST_LOGOUT_REDIRECT_URI, buildLogoutRedirectUri(request));
        
        return Urls.appendQueryString(config.getServerLogoutEndpointUrl(), qs.build());
    }
    
    protected String buildLogoutRedirectUri(Request request) {
        String url = null;
        
        if(!config.getClientRedirectUri().startsWith("/")) {
            url = config.getClientRedirectUri();
        }else{
            url = request.getContextUrl() + config.getClientRedirectUri();
        }
        
        url = Urls.appendQueryString(url, "oauth2_logout=1");
        
        return url;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy