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

leap.web.security.SecurityRequestInterceptor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013 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.web.security;

import leap.core.annotation.Inject;
import leap.core.annotation.M;
import leap.core.security.Authentication;
import leap.core.security.Authorization;
import leap.core.security.annotation.*;
import leap.core.web.RequestIgnore;
import leap.lang.Assert;
import leap.lang.Strings;
import leap.lang.intercepting.State;
import leap.lang.logging.Log;
import leap.lang.logging.LogFactory;
import leap.web.*;
import leap.web.action.ActionContext;
import leap.web.cors.CorsHandler;
import leap.web.route.Route;
import leap.web.route.RouteProcessor;
import leap.web.security.csrf.CSRF;
import leap.web.security.csrf.CsrfHandler;
import leap.web.security.path.*;
import leap.web.security.permission.PermissionManager;

public class SecurityRequestInterceptor implements RequestInterceptor,AppListener, RouteProcessor {

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

    protected @Inject @M SecurityConfig         config;
    protected @Inject @M SecurityConfigurator   configurator;
    protected @Inject @M PermissionManager      perm;
    protected @Inject @M SecuredPathSource      pathSource;
    protected @Inject @M SecurityHandler        handler;
    protected @Inject @M CsrfHandler            csrf;
    protected @Inject @M CorsHandler            cors;

    protected SecuredPathBuilder spb(Route route) {
        SecuredPathBuilder spb = route.getExtension(SecuredPathBuilder.class);
        if(null == spb) {
            spb = new DefaultSecuredPathBuilder(route);
            route.setExtension(SecuredPathBuilder.class, spb);
        }
        return spb;
    }

	@Override
    public void postAppStart(App app) throws Throwable {
	    for(Route route : app.routes()) {
            processRoute(route);
	    }
    }

    @Override
    public void processRoute(Route route) {
        if(null != route.getAction()){
            Ignore ignore = route.getAction().searchAnnotation(Ignore.class);
            if(null != ignore && ignore.value()){
                configurator.ignore(route.getPathTemplate().getTemplate());
            }
        }

        if(null != route.getAllowAnonymous()) {
            spb(route).setAllowAnonymous(route.getAllowAnonymous());
        }

        if(null != route.getAllowClientOnly()) {
            spb(route).setAllowClientOnly(route.getAllowClientOnly());
        }

        if(null != route.getAllowRememberMe()) {
            spb(route).setAllowRememberMe(route.getAllowRememberMe());
        }

        if(null != route.getPermissions()) {
            spb(route).setPermissions(route.getPermissions());
        }

        if(null != route.getRoles()) {
            spb(route).setRoles(route.getRoles());
        }

        config.getPathPrefixFailureHandlers().forEach((prefix, handler) -> {
            if(Strings.startsWith(route.getPathTemplate().getTemplate(), prefix)) {
                log.debug("Set failure handler for path prefix '{}'", route.getPathTemplate());
                spb(route).setFailureHandler(handler);
            }
        });

        SecuredPathBuilder spb = route.removeExtension(SecuredPathBuilder.class);
        if(null != spb) {
            route.setExtension(SecuredPath.class, spb.build());
        }
    }

    @Override
    public State preHandleRequest(Request request, Response response, ActionContext ac) throws Throwable {
		//Web security do not enabled.
		if(!config.isEnabled()){
			log.debug("Web security not enabled, ignore the interceptor");
			return State.CONTINUE;
		}

        //csrf
		if(State.isIntercepted(csrf.handleRequest(request, response))){
		    return State.INTERCEPTED;
		}

        //cors
        if(config.isCorsIgnored() && cors.isPreflightRequest(request)) {
            return State.CONTINUE;
        }
		
		//TODO : cache 
		//Check is the request ignored.
		for(RequestIgnore ignore : config.getIgnores()) {
			if(ignore.matches(request)) {
				return State.CONTINUE;
			}
		}
		
		DefaultSecurityContextHolder context = new DefaultSecurityContextHolder(config, perm, request, ac);
        context.setSecuredPath(resolveSecuredPath(request,response,context,ac.getRoute()));
		return preHandleRequest(request, response, context);
    }

    protected State preHandleRequest(Request request, Response response, DefaultSecurityContextHolder context) throws Throwable {
        request.setSecurityContext(context);

        //Handles request if login
        if(config.isLoginEnabled() && handleLoginRequest(request, response, context)){
            return State.INTERCEPTED;
        }

        //Handles request if logout.
        if(config.isLogoutEnabled() && handleLogoutRequest(request, response, context)) {
            return State.INTERCEPTED;
        }
        
        //Resolve authentication.
        State state = resolveAuthentication(request,response,context);
        if(state.isIntercepted()){
            return state;
        }

        //Disable csrf if anonymous access.
        if(!context.getAuthentication().isAuthenticated()) {
            CSRF.ignore(request);
        }

        return State.CONTINUE;
    }

    @Override
    public State handleRoute(Request request, Response response, Route route, ActionContext ac) throws Throwable {
        return handleSecurity(request, response, route);
    }

    @Override
    public State handleNoRoute(Request request, Response response) throws Throwable {
        return handleSecurity(request, response, null);
    }

    protected State handleSecurity(Request request, Response response, Route route) throws Throwable {
        DefaultSecurityContextHolder context = DefaultSecurityContextHolder.tryGet(request);
        if(null == context || context.isHandled()) {
            return State.CONTINUE;
        }
        context.markHandled();

        //Resolve secured path.
        context.setSecuredPath(resolveSecuredPath(request, response, context, route));

        //Check authentication
        State state = checkAuthentication(request, response, context);
        if(state.isIntercepted()) {
            return state;
        }

        //Resolve authorization.
        state = resolveAuthorization(request, response, context);
        if(state.isIntercepted()) {
            return state;
        }

        //Check authorization.
        return checkAuthorization(request, response, context);
    }

	/**
	 * Returns true if the request already handled.
	 */
	protected State resolveAuthentication(Request request, Response response, DefaultSecurityContextHolder context) throws Throwable {
	    SecurityInterceptor[] interceptors = config.getInterceptors();
		for(SecurityInterceptor interceptor : interceptors) {
			if(interceptor.preResolveAuthentication(request, response, context).isIntercepted()){
				log.debug("Intercepted by interceptor : {}", interceptor.getClass());
				return State.INTERCEPTED;
			}
		}

		Authentication authc = context.getAuthentication();
        if(null == authc) {
            log.debug("Resolving authentication...");
            authc = handler.resolveAuthentication(request, response, context);

            Assert.notNull(authc,"'Authentication' must not be null");
            context.setAuthentication(authc);
        }else{
            log.debug("Authentication already resolved by interceptor -> {}", authc);
        }

		if(log.isDebugEnabled()) {
			if(authc.isAuthenticated()) {
				log.debug("Request authenticated to : {}", authc);
			}else{
				log.debug("Request not authenticated!");
			}
		}

        request.setAuthentication(authc);
        request.setUser(authc.getUser());

		for(SecurityInterceptor interceptor : interceptors) {
			if(interceptor.postResolveAuthentication(request, response, context).isIntercepted()){
				return State.INTERCEPTED;
			}
		}

		if(authc != context.getAuthentication()){
		    authc = context.getAuthentication();
            context.setAuthentication(authc);
            request.setAuthentication(authc);
            request.setUser(authc.getUser());
        }

        return State.CONTINUE;
	}

    protected boolean handleLoginRequest(Request request, Response response, DefaultSecurityContextHolder context) throws Throwable {
        return handler.handleLoginRequest(request, response, context);
    }

    protected boolean handleLogoutRequest(Request request, Response response, DefaultSecurityContextHolder context) throws Throwable {
        return handler.handleLogoutRequest(request, response, context);
    }

    protected SecuredPath resolveSecuredPath(Request request, Response response, DefaultSecurityContextHolder context, Route route) throws Throwable {
        SecuredPath p;
        for(SecurityInterceptor interceptor : config.getInterceptors()) {
            if((p = interceptor.resolveSecuredPath(context)) != null){
                return p;
            }
        }

        SecuredPath p1 = null == route ? null : route.getExtension(SecuredPath.class);
        SecuredPath p2 = pathSource.getSecuredPath(context, request);

        if(null == p1) {
            return p2;
        }

        if(null == p2) {
            return p1;
        }

        return new MergedSecuredPath(route, p1, p2);
    }

    protected State checkAuthentication(Request request, Response response, DefaultSecurityContextHolder context) throws Throwable {
		if(!handler.checkAuthentication(request, response, context)){
			handler.handleAuthenticationDenied(request, response, context);
			return State.INTERCEPTED;
		}
		return State.CONTINUE;
    }

    protected State resolveAuthorization(Request request,Response response,DefaultSecurityContextHolder context) throws Throwable {
        SecurityInterceptor[] interceptors = config.getInterceptors();
        for(SecurityInterceptor si : interceptors) {
            if(State.isIntercepted(si.preResolveAuthorization(request, response, context))) {
                return State.INTERCEPTED;
            }
        }

        Authorization authz = context.getAuthorization();
        if(null == authz) {
            log.debug("Resolving authorization...");
            authz = handler.resolveAuthorization(request,response,context);
            Assert.notNull(authz,"The authorization must not be null");
            context.setAuthorization(authz);
        }else{
            log.debug("Authorization already resolved by interceptor -> {}", authz);
        }

        for(SecurityInterceptor si : interceptors) {
            if(State.isIntercepted(si.postResolveAuthorization(request, response, context))) {
                return State.INTERCEPTED;
            }
        }

        return State.CONTINUE;
    }
	
	/**
	 * Returns true if current request handled by this interceptor.
	 */
	protected State checkAuthorization(Request request, Response response, DefaultSecurityContextHolder context) throws Throwable {
        if(!handler.checkAuthorization(request, response, context)) {
            handler.handleAuthorizationDenied(request, response, context);
            return State.INTERCEPTED;
        }
		return State.CONTINUE;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy