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

com.mantledillusion.vaadin.cotton.AccessHandler Maven / Gradle / Ivy

Go to download

Cotton is a Vaadin extension destined for the ultimate of developer convenience.

There is a newer version: 2.3.4
Show newest version
package com.mantledillusion.vaadin.cotton;

import com.mantledillusion.essentials.expression.Expression;
import com.mantledillusion.essentials.reflection.TypeEssentials;
import com.mantledillusion.injection.hura.core.annotation.injection.Inject;
import com.mantledillusion.injection.hura.core.annotation.injection.Qualifier;
import com.mantledillusion.injection.hura.core.annotation.instruction.Construct;
import com.mantledillusion.injection.hura.core.annotation.instruction.Optional;
import com.mantledillusion.metrics.trail.MetricsTrailSupport;
import com.mantledillusion.metrics.trail.api.Metric;
import com.mantledillusion.metrics.trail.api.MetricAttribute;
import com.mantledillusion.vaadin.cotton.exception.http400.Http403UnauthorizedException;
import com.mantledillusion.vaadin.cotton.metrics.CottonMetrics;
import com.mantledillusion.vaadin.cotton.viewpresenter.Restricted;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.router.BeforeLeaveEvent;
import com.vaadin.flow.router.BeforeLeaveListener;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

final class AccessHandler implements BeforeLeaveListener {

    private static class Destination {

        private final int priority;
        private final Class navigationTarget;
        private final RestrictionType restrictionType;
        private final List> restrictions;

        private Destination(Class navigationTarget, int priority,
                            RestrictionType restrictionType, List> restrictions) {
            this.navigationTarget = navigationTarget;
            this.priority = priority;
            this.restrictionType = restrictionType;
            this.restrictions = restrictions;
        }

        private Class getNavigationTarget() {
            return this.navigationTarget;
        }

        private int getPriority() {
            return this.priority;
        }

        private RestrictionType getRestrictionType() {
            return this.restrictionType;
        }

        private List> getRestrictions() {
            return this.restrictions;
        }
    }

    private enum RestrictionType {
        NONE,
        AUTHENTICATION,
        AUTHORIZATION
    }

    static final class ForwardingView extends Div {

        @Construct
        private ForwardingView() {

        }
    }

    static final String SID_NAVIGATION_HANDLER = "_navigationHandler";

    @Inject
    @Qualifier(CottonEnvironment.SID_LOGIN_PROVIDER)
    @Optional
    private LoginProvider provider;

    private final Map> forwardingRegistry = new HashMap<>();

    @Construct
    private AccessHandler() {}

    void register(String path, Class navigationTarget, int priority) {
        this.forwardingRegistry.computeIfAbsent(path, p -> new ArrayList<>()).add(toTarget(navigationTarget, priority));
        this.forwardingRegistry.get(path).sort(Comparator.comparingInt(Destination::getPriority));
    }

    private Destination toTarget(Class navigationTarget, int priority) {
        RestrictionType restrictionType = RestrictionType.NONE;
        List> restrictions = new ArrayList<>();
        for (Class type : TypeEssentials.getSuperClassesAnnotatedWith(navigationTarget, Restricted.class)) {
            Restricted restricted = type.getAnnotation(Restricted.class);
            if (StringUtils.isNotBlank(restricted.value())) {
                restrictions.add(Expression.parse(restricted.value()));
                restrictionType = RestrictionType.AUTHORIZATION;
            } else if (restrictionType == RestrictionType.NONE) {
                restrictionType = RestrictionType.AUTHENTICATION;
            }
        }
        return new Destination(navigationTarget, priority, restrictionType, restrictions);
    }

    @Override
    public void beforeLeave(BeforeLeaveEvent event) {
        if (this.provider != null && this.provider.loginView != null &&
                this.provider.loginView == event.getNavigationTarget()) {
            return;
        }

        List possibleDestinations;
        if (ForwardingView.class.isAssignableFrom(event.getNavigationTarget())) {
            possibleDestinations = this.forwardingRegistry.get(event.getLocation().getPath());
        } else {
            possibleDestinations = Collections.singletonList(
                    toTarget((Class) event.getNavigationTarget(), 0));
        }

        boolean isAuthenticationDesirable = possibleDestinations.stream().
                anyMatch(possibleDestination -> possibleDestination.getRestrictionType() != RestrictionType.NONE);
        boolean isAuthenticationNecessary = possibleDestinations.stream().
                allMatch(possibleDestination -> possibleDestination.getRestrictionType() != RestrictionType.NONE);
        AuthenticationHandler authenticationHandler = CottonSession.current().getAuthenticationHandler();
        if (isAuthenticationDesirable && !authenticationHandler.isLoggedIn()) {
            if (this.provider.userProvider != null && (this.provider.userProvider.isSilent() || isAuthenticationNecessary)) {
                User user = this.provider.userProvider.provide();
                if (user != null) {
                    authenticationHandler.login(user);
                }
            } else if (this.provider.loginView != null && isAuthenticationNecessary) {
                event.rerouteTo(this.provider.loginView);
                return;
            }
        }

        Destination destination = null;
        for (Destination possibleTarget: possibleDestinations) {
            if ((possibleTarget.getRestrictionType() == RestrictionType.NONE) || (authenticationHandler.isLoggedIn() && (
                    (possibleTarget.getRestrictionType() == RestrictionType.AUTHENTICATION) ||
                    (possibleTarget.getRestrictions().stream().allMatch(authenticationHandler::userHasRights))))) {
                destination = possibleTarget;
                break;
            }
        }

        if (destination == null) {
            MetricsTrailSupport.commit(CottonMetrics.SECURITY_ACCESS_DENIED.build(
                    new MetricAttribute("target", event.getNavigationTarget().getName()),
                    new MetricAttribute("user", authenticationHandler.isLoggedIn() ?
                            authenticationHandler.getUser().toString() : null)));

            event.rerouteToError(new Http403UnauthorizedException("Access to the view '"
                    + event.getNavigationTarget().getSimpleName() + "' is restricted"), null);
        } else {
            if (destination.getRestrictionType() != RestrictionType.NONE) {
                MetricsTrailSupport.commit(CottonMetrics.SECURITY_ACCESS_GRANTED.build(
                        new MetricAttribute("target", event.getNavigationTarget().getName()),
                        new MetricAttribute("user", authenticationHandler.getUser().toString())));
            }
            if (event.getNavigationTarget() != destination.getNavigationTarget()) {
                event.forwardTo(destination.getNavigationTarget());
            } else {
                Metric metric = CottonMetrics.SESSION_NAVIGATION.build(event.getLocation().getPath());
                String query = event.getLocation().getQueryParameters().getQueryString();
                if (!query.isEmpty()) {
                    for (Map.Entry param : fromParamAppender(query).entrySet()) {
                        metric.getAttributes().add(new MetricAttribute(param.getKey(), param.getValue()));
                    }
                }
                MetricsTrailSupport.commit(metric);
            }
        }
    }

    private static Map fromParamAppender(String query) {
        Map params = new HashMap<>();
        for (String param : query.split("&")) {
            String[] splitted = param.split("=");
            params.put(splitted[0], splitted[1]);
        }
        return params;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy