AccessControlXPage.java

/*
 * Copyright (c) 2002-2022, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.accesscontrol.web;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import fr.paris.lutece.plugins.accesscontrol.business.AccessControl;
import fr.paris.lutece.plugins.accesscontrol.business.AccessControlHome;
import fr.paris.lutece.plugins.accesscontrol.business.AccessController;
import fr.paris.lutece.plugins.accesscontrol.business.AccessControllerHome;
import fr.paris.lutece.plugins.accesscontrol.service.AccessControlServiceProvider;
import fr.paris.lutece.plugins.accesscontrol.service.IAccessControllerType;
import fr.paris.lutece.plugins.accesscontrol.service.IPersistentAccessControllerType;
import fr.paris.lutece.plugins.accesscontrol.util.BoolCondition;
import fr.paris.lutece.portal.business.accesscontrol.AccessControlSessionData;
import fr.paris.lutece.portal.service.accesscontrol.IAccessControlServiceProvider;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.message.SiteMessage;
import fr.paris.lutece.portal.service.message.SiteMessageException;
import fr.paris.lutece.portal.service.message.SiteMessageService;
import fr.paris.lutece.portal.service.security.UserNotSignedException;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
import fr.paris.lutece.portal.util.mvc.xpage.MVCApplication;
import fr.paris.lutece.portal.util.mvc.xpage.annotations.Controller;
import fr.paris.lutece.portal.web.xpages.XPage;
import fr.paris.lutece.util.url.UrlItem;

@Controller( xpageName = AccessControlXPage.XPAGE_NAME, pageTitleI18nKey = AccessControlXPage.MESSAGE_PAGE_TITLE, pagePathI18nKey = AccessControlXPage.MESSAGE_PATH )
public class AccessControlXPage extends MVCApplication
{
    private static final long serialVersionUID = -459438276022465973L;

    public static final String XPAGE_NAME = "accesscontrol";
    private static final String URL_PORTAL = "Portal.jsp";

    // Messages
    protected static final String MESSAGE_PAGE_TITLE = "accesscontrol.xpage.form.view.pageTitle";
    protected static final String MESSAGE_PATH = "accesscontrol.xpage.form.view.pagePathLabel";

    // Views
    public static final String VIEW_CONTROLLER = "controllerView";

    // Actions
    private static final String ACTION_VALIDATE_CONTROLLER = "doValidateController";
    private static final String ACTION_RETURN_CONTROLLER = "doReturnController";

    // Parameters
    public static final String PARAMETER_INIT = "init";
    public static final String PARAMETER_ID_ACCESS_CONTROL = "id_accesscontrol";
    public static final String PARAMETER_RESOURCE_ID = "id_resource";
    public static final String PARAMETER_RESOURCE_TYPE = "type_resource";

    // Marks
    private static final String MARK_CONTROLLER_HTML = "controller_html";
    private static final String MARK_FIRST_CONTROLLER = "first_controller";

    // Templates
    private static final String TEMPLATE_CONTROLLER_VIEW = "skin/plugins/accesscontrol/controller_view.html";

    private int _nCurrentControllerOrder;
    private Map<Integer, String> _accessControlResult;
    private List<AccessController> _controllerList;
    private AccessController _currentController;
    private AccessControl _accessControl;

    private int _idResource;
    private String _resourceType;

    private IAccessControlServiceProvider _service = SpringContextService.getBean( AccessControlServiceProvider.BEAN_NAME );

    private void init( HttpServletRequest request )
    {
        int idAccessControl = NumberUtils.toInt( request.getParameter( PARAMETER_ID_ACCESS_CONTROL ), -1 );
        _idResource = NumberUtils.toInt( request.getParameter( PARAMETER_RESOURCE_ID ), -1 );
        _resourceType = request.getParameter( PARAMETER_RESOURCE_TYPE );
        _nCurrentControllerOrder = 1;
        _accessControlResult = new HashMap<>( );
        _accessControl = AccessControlHome.findByPrimaryKey( idAccessControl );
        _controllerList = AccessControllerHome.getAccessControllersListByAccessControlId( idAccessControl );
    }

    /**
     * Return the default XPage with the list of all available Form
     * 
     * @param request
     *            The HttpServletRequest
     * @return the list of all available forms
     * @throws SiteMessageException
     */
    @View( value = VIEW_CONTROLLER )
    public XPage getControllerView( HttpServletRequest request ) throws SiteMessageException
    {
        if ( BooleanUtils.toBoolean( request.getParameter( PARAMETER_INIT ) ) )
        {
            init( request );
        }

        if ( CollectionUtils.isEmpty( _controllerList ) )
        {
            return null;
        }

        if ( _nCurrentControllerOrder > _controllerList.size( ) )
        {
            return validateAccessControlAndRedirect( request );
        }

        Locale locale = getLocale( request );

        _currentController = _controllerList.stream( ).filter( c -> c.getOrder( ) == _nCurrentControllerOrder ).findFirst( ).orElse( null );
        if ( _currentController == null )
        {
            return validateAccessControlAndRedirect( request );
        }
        IAccessControllerType currentControllerType = SpringContextService.getBean( _currentController.getType( ) );

        Map<String, Object> model = getModel( );
        model.put( MARK_CONTROLLER_HTML, currentControllerType.getControllerForm( request, locale, _currentController ) );
        model.put( MARK_FIRST_CONTROLLER, _nCurrentControllerOrder == 1 );

        XPage xPage = getXPage( TEMPLATE_CONTROLLER_VIEW, locale, model );
        xPage.setTitle( I18nService.getLocalizedString( MESSAGE_PAGE_TITLE, locale ) );
        xPage.setPathLabel( I18nService.getLocalizedString( MESSAGE_PATH, locale ) );

        return xPage;
    }

    private XPage validateAccessControlAndRedirect( HttpServletRequest request ) throws SiteMessageException
    {
        boolean result = true;
        for ( int i = 0; i < _controllerList.size( ); i++ )
        {
            int order = i + 1;
            boolean valid = _accessControlResult.get( order ) == null;

            AccessController controller = _controllerList.stream( ).filter( c -> c.getOrder( ) == order ).findFirst( ).orElse( null );

            if ( controller != null )
            {
                BoolCondition condition = BoolCondition.valueOf( controller.getBoolCond( ) );

                if ( condition == BoolCondition.AND )
                {
                    result = result && valid;
                }
                else
                    if ( condition == BoolCondition.OR )
                    {
                        result = result || valid;
                    }
            }
        }

        if ( result && _accessControlResult.size() == _controllerList.size() )
        {
            AccessControlSessionData sessionData = _service.getSessionDataForResource( request, _idResource, _resourceType );
            sessionData.setAccessControlResult( true );
            request.getSession( ).setAttribute( sessionData.getSessionKey( ), sessionData );

            return redirect( request, URL_PORTAL + "?" + sessionData.getReturnQueryString( ) );
        }

        UrlItem url = null;
        if ( StringUtils.isEmpty( _accessControl.getReturnUrl( ) ) )
        {
            url = new UrlItem( URL_PORTAL );
        }
        else
        {
            url = new UrlItem( _accessControl.getReturnUrl( ) );
        }
        Object [ ] messageArgs = new Object [ ] {
                _accessControlResult.values( ).stream( ).filter( Objects::nonNull ).collect( Collectors.joining( " - " ) )
        };
        SiteMessageService.setMessage( request, "accesscontrol.xpage.accesscontrol.sitemessage.error", messageArgs,
                "accesscontrol.xpage.accesscontrol.sitemessage.title", url.getUrl( ), null, SiteMessage.TYPE_ERROR );
        return null;
    }

    @Action( value = ACTION_VALIDATE_CONTROLLER )
    public XPage doValidateController( HttpServletRequest request ) throws UserNotSignedException
    {
        IAccessControllerType currentControllerType = SpringContextService.getBean( _currentController.getType( ) );
        String validationResult = currentControllerType.validate( request, _currentController );

        if ( currentControllerType instanceof IPersistentAccessControllerType && validationResult == null )
        {
            AccessControlSessionData sessionData = _service.getSessionDataForResource( request, _idResource, _resourceType );
            ( (IPersistentAccessControllerType) currentControllerType ).persistDataToSession( sessionData, request, getLocale( request ),
                    _currentController.getId( ) );
        }
        _accessControlResult.put( _currentController.getOrder( ), validationResult );
        _nCurrentControllerOrder++;
        return redirectView( request, VIEW_CONTROLLER );
    }

    @Action( value = ACTION_RETURN_CONTROLLER )
    public XPage doReturnController( HttpServletRequest request )
    {
        _nCurrentControllerOrder--;
        return redirectView( request, VIEW_CONTROLLER );
    }
}