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

de.yourinspiration.jexpresso.basicauth.BasicAuthentation Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package de.yourinspiration.jexpresso.basicauth;

import io.netty.handler.codec.http.HttpMethod;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import org.pmw.tinylog.Logger;

import de.yourinspiration.jexpresso.MiddlewareHandler;
import de.yourinspiration.jexpresso.Next;
import de.yourinspiration.jexpresso.Request;
import de.yourinspiration.jexpresso.Response;
import de.yourinspiration.jexpresso.baseauth.GrantedAuthority;
import de.yourinspiration.jexpresso.baseauth.PasswordEncoder;
import de.yourinspiration.jexpresso.baseauth.UserDetails;
import de.yourinspiration.jexpresso.baseauth.UserDetailsService;
import de.yourinspiration.jexpresso.baseauth.UserNotFoundException;
import de.yourinspiration.jexpresso.http.HttpStatus;

/**
 * A middleware for JExpresso providing HTTP Basic Authentication.
 * 
 * @author Marcel Härle
 *
 */
public class BasicAuthentation implements MiddlewareHandler {

    public static final String USER_DETAILS_ATTR = "userDetails";

    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    private final List securityRoutes = new ArrayList<>();

    /**
     * Constructs a new HTTP Basic Authentiation middleware.
     * 
     * @param userDetailsService
     *            the user details service to retrieve user credentials
     * @param passwordEncoder
     *            the password encoder to check the passwords
     */
    public BasicAuthentation(final UserDetailsService userDetailsService, final PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * Provide HTTP Basic Authentication for the given path and methods.
     * 
     * @param path
     *            the path to be secured
     * @param authorities
     *            the granted authorities
     * @param methods
     *            the methods to be secured
     */
    public void securePath(final String path, final String authorities, final HttpMethod... methods) {
        securityRoutes.add(new SecurityRoute(path, authorities, methods));
    }

    @Override
    public void handle(Request request, Response response, Next next) {
        if (checkSecurityProviders(request, response)) {
            next.next();
        } else {
            next.cancel();
        }
    }

    private boolean checkSecurityProviders(final Request request, final Response response) {
        final String path = getUri(request);
        final HttpMethod method = request.method();

        for (SecurityRoute route : securityRoutes) {
            if (route.matchesPathAndMethod(path, method)) {
                final boolean authenticated = checkAuthentication(request, userDetailsService, passwordEncoder);
                if (!authenticated) {
                    handleUnauthenticated(response);
                    return false;
                } else {
                    return true;
                }
            }
        }

        return true;
    }

    private String getUri(final Request request) {
        if (request.path().contains("?")) {
            return request.path().substring(0, request.path().indexOf("?"));
        } else {
            return request.path();
        }
    }

    private void handleUnauthenticated(final Response response) {
        response.status(HttpStatus.UNAUTHORIZED);
        response.set("WWW-Authenticate", "Basic realm=\"sparkle realm\"");
        response.type("text/plain");
        response.send("");
    }

    private boolean checkAuthentication(Request request, final UserDetailsService userDetailsService,
            final PasswordEncoder passwordEncoder) {
        boolean authenticated = false;

        final String authorization = request.get("Authorization");

        if (authorization != null && authorization.startsWith("Basic")) {
            // Authorization: Basic base64credentials
            final String base64Credentials = authorization.substring("Basic".length()).trim();
            final String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                    Charset.forName("UTF-8"));

            // credentials = username:password
            final String[] values = credentials.split(":", 2);

            try {
                final UserDetails userDetails = userDetailsService.loadUserByUsername(values[0]);

                if (userDetails != null && passwordEncoder.checkpw(values[1], userDetails.getPassword())) {
                    final String authorities = getAuthoritiesForRoute(request.path(), request.method());

                    if (hasGrantedAuthoriy(userDetails, authorities)) {
                        request.attribute(USER_DETAILS_ATTR, userDetails);
                        authenticated = true;
                    }
                }
            } catch (UserNotFoundException e) {
                Logger.debug("User not found", e);
            }
        }

        return authenticated;
    }

    private String getAuthoritiesForRoute(final String path, final HttpMethod method) {
        for (SecurityRoute securityRoute : securityRoutes) {
            if (securityRoute.matchesPathAndMethod(path, method)) {
                return securityRoute.getAuthorities();
            }
        }
        return "";
    }

    private boolean hasGrantedAuthoriy(final UserDetails userDetails, final String authorities) {
        if ("".equals(authorities.trim())) {
            return true;
        }

        for (GrantedAuthority grantedAuthority : userDetails.getAuthorities()) {
            for (String authority : authorities.split(",")) {
                if (grantedAuthority.getAuthority().equalsIgnoreCase(authority)) {
                    return true;
                }
            }
        }

        return false;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy