Package org.vaadin.spring.navigator

Source Code of org.vaadin.spring.navigator.SpringViewProvider$ViewProviderAccessDelegate

/*
* Copyright 2014 The original 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 org.vaadin.spring.navigator;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewProvider;
import com.vaadin.ui.UI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

/**
* A Vaadin {@link ViewProvider} that fetches the views from the Spring application context. The views
* must implement the {@link View} interface and be annotated with the {@link VaadinView} annotation.
* <p>
* Use like this:
* <pre>
*         &#64;VaadinUI
*         public class MyUI extends UI {
*
*              &#64;Autowired SpringViewProvider viewProvider;
*
*              protected void init(VaadinRequest vaadinRequest) {
*                  Navigator navigator = new Navigator(this, this);
*                  navigator.addProvider(viewProvider);
*                  setNavigator(navigator);
*                  // ...
*              }
*         }
*     </pre>
*
* View-based security can be provided by creating a Spring bean that implements the {@link org.vaadin.spring.navigator.SpringViewProvider.ViewProviderAccessDelegate} interface.
*
* @author Petter Holmström (petter@vaadin.com)
* @see VaadinView
*/
public class SpringViewProvider implements ViewProvider {

    /*
     * Note! This is a singleton bean!
     */

    // We can have multiple views with the same view name, as long as they belong to different UI subclasses
    private final Map<String, Set<String>> viewNameToBeanNamesMap = new ConcurrentHashMap<String, Set<String>>();
    private final ApplicationContext applicationContext;
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    public SpringViewProvider(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @PostConstruct
    void init() {
        logger.info("Looking up VaadinViews");
        int count = 0;
        final String[] viewBeanNames = applicationContext.getBeanNamesForAnnotation(VaadinView.class);
        for (String beanName : viewBeanNames) {
            final Class<?> type = applicationContext.getType(beanName);
            if (View.class.isAssignableFrom(type)) {
                final VaadinView annotation = applicationContext.findAnnotationOnBean(beanName, VaadinView.class);
                final String viewName = annotation.name();
                logger.debug("Found VaadinView bean [{}] with view name [{}]", beanName, viewName);
                if (applicationContext.isSingleton(beanName)) {
                    throw new IllegalStateException("VaadinView bean [" + beanName + "] must not be a singleton");
                }
                Set<String> beanNames = viewNameToBeanNamesMap.get(viewName);
                if (beanNames == null) {
                    beanNames = new ConcurrentSkipListSet<String>();
                    viewNameToBeanNamesMap.put(viewName, beanNames);
                }
                beanNames.add(beanName);
                count++;
            }
        }
        if (count == 0) {
            logger.warn("No VaadinViews found");
        } else if (count == 1) {
            logger.info("1 VaadinView found");
        } else {
            logger.info("{} VaadinViews found", count);
        }
    }

    @Override
    public String getViewName(String viewAndParameters) {
        logger.trace("Extracting view name from [{}]", viewAndParameters);
        String viewName = null;
        if (isViewNameValidForCurrentUI(viewAndParameters)) {
            viewName = viewAndParameters;
        } else {
            int lastSlash = -1;
            String viewPart = viewAndParameters;
            while ((lastSlash = viewPart.lastIndexOf('/')) > -1) {
                viewPart = viewPart.substring(0, lastSlash);
                logger.trace("Checking if [{}] is a valid view", viewPart);
                if (isViewNameValidForCurrentUI(viewPart)) {
                    viewName = viewPart;
                    break;
                }
            }
        }
        if (viewName == null) {
            logger.trace("Found no view name in [{}]", viewAndParameters);
        } else {
            logger.trace("[{}] is a valid view", viewName);
        }
        return viewName;
    }

    private boolean isViewNameValidForCurrentUI(String viewName) {
        final Set<String> beanNames = viewNameToBeanNamesMap.get(viewName);
        if (beanNames != null) {
            for (String beanName : beanNames) {
                if (isViewBeanNameValidForCurrentUI(beanName)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isViewBeanNameValidForCurrentUI(String beanName) {
        try {
            final Class<?> type = applicationContext.getType(beanName);

            Assert.isAssignable(View.class, type, "bean did not implement View interface");

            final UI currentUI = UI.getCurrent();
            final VaadinView annotation = applicationContext.findAnnotationOnBean(beanName, VaadinView.class);

            Assert.notNull(annotation, "class did not have a VaadinView annotation");

            final Map<String, ViewProviderAccessDelegate> accessDelegates = applicationContext.getBeansOfType(ViewProviderAccessDelegate.class);
            for (ViewProviderAccessDelegate accessDelegate : accessDelegates.values()) {
                if (!accessDelegate.isAccessGranted(beanName, currentUI)) {
                    logger.debug("Access delegate [{}] denied access to view class [{}]", accessDelegate, type.getCanonicalName());
                    return false;
                }
            }

            if (annotation.ui().length == 0) {
                logger.trace("View class [{}] with view name [{}] is available for all UI subclasses", type.getCanonicalName(), annotation.name());
                return true;
            } else {
                for (Class<? extends UI> validUI : annotation.ui()) {
                    if (validUI == currentUI.getClass()) {
                        logger.trace("View class [%s] with view name [{}] is available for UI subclass [{}]", type.getCanonicalName(), annotation.name(), validUI.getCanonicalName());
                        return true;
                    }
                }
            }
            return false;
        } catch (NoSuchBeanDefinitionException ex) {
            return false;
        }
    }

    @Override
    public View getView(String viewName) {
        final Set<String> beanNames = viewNameToBeanNamesMap.get(viewName);
        if (beanNames != null) {
            for (String beanName : beanNames) {
                if (isViewBeanNameValidForCurrentUI(beanName)) {
                    return (View) applicationContext.getBean(beanName);
                }
            }
        }
        logger.warn("Found no view with name [{}]", viewName);
        return null;
    }

    /**
     * Interface to be implemented by Spring beans that will be consulted before the Spring View provider
     * provides a view. If any of the view providers deny access, the view provider will act like no such
     * view ever existed.
     */
    public interface ViewProviderAccessDelegate {

        /**
         * Checks if the current user has access to the specified view and UI.
         *
         * @param beanName the bean name of the view, never {@code null}.
         * @param ui       the UI, never {@code null}.
         * @return true if access is granted, false if access is denied.
         */
        boolean isAccessGranted(String beanName, UI ui);
    }
}
TOP

Related Classes of org.vaadin.spring.navigator.SpringViewProvider$ViewProviderAccessDelegate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.