<commit_before>/*
 * #%L
 * BroadleafCommerce Profile Web
 * %%
 * Copyright (C) 2009 - 2013 Broadleaf Commerce
 * %%
 * 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.
 * #L%
 */
package org.broadleafcommerce.profile.web.core.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.extension.ExtensionResultHolder;
import org.broadleafcommerce.common.web.AbstractBroadleafWebRequestProcessor;
import org.broadleafcommerce.common.web.BroadleafRequestCustomerResolverImpl;
import org.broadleafcommerce.profile.core.domain.Customer;
import org.broadleafcommerce.profile.core.service.CustomerService;
import org.broadleafcommerce.profile.web.core.CustomerState;
import org.broadleafcommerce.profile.web.core.CustomerStateRefresher;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;


/**
 * @author <NAME>
 * @see {@link CustomerStateFilter}
 */
@Component("blCustomerStateRequestProcessor")
public class CustomerStateRequestProcessor extends AbstractBroadleafWebRequestProcessor implements ApplicationEventPublisherAware {

    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    public static final String BLC_RULE_MAP_PARAM = "blRuleMap";

    @Resource(name="blCustomerService")
    protected CustomerService customerService;
    
    @Resource(name = "blCustomerMergeExtensionManager")
    protected CustomerMergeExtensionManager customerMergeExtensionManager;

    protected ApplicationEventPublisher eventPublisher;

    public static final String ANONYMOUS_CUSTOMER_SESSION_ATTRIBUTE_NAME = "_blc_anonymousCustomer";
    public static final String ANONYMOUS_CUSTOMER_ID_SESSION_ATTRIBUTE_NAME = "_blc_anonymousCustomerId";
    private static final String LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME = "_blc_lastPublishedEvent";
    public static final String OVERRIDE_CUSTOMER_SESSION_ATTR_NAME = "_blc_overrideCustomerId";
    public static final String ANONYMOUS_CUSTOMER_MERGED_SESSION_ATTRIBUTE_NAME = "_blc_anonymousCustomerMerged";

    @Override
    public void process(WebRequest request) {
        Customer customer = null;

        Long overrideId = (Long) request.getAttribute(OVERRIDE_CUSTOMER_SESSION_ATTR_NAME, WebRequest.SCOPE_GLOBAL_SESSION);
        if (overrideId != null) {
            customer = customerService.readCustomerById(overrideId);
        } else {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if ((authentication != null) && !(authentication instanceof AnonymousAuthenticationToken)) {
                String userName = authentication.getName();
                customer = (Customer) BroadleafRequestCustomerResolverImpl.getRequestCustomerResolver().getCustomer(request);
                if (userName != null && (customer == null || !userName.equals(customer.getUsername()))) {
                    // can only get here if the authenticated user does not match the user in session
                    customer = customerService.readCustomerByUsername(userName);
                    if (logger.isDebugEnabled() && customer != null) {
                        logger.debug("Customer found by username " + userName);
                    }
                }
                if (customer != null) {
                    ApplicationEvent lastPublishedEvent = (ApplicationEvent) request.getAttribute(LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME, WebRequest.SCOPE_REQUEST);
                    if (authentication instanceof RememberMeAuthenticationToken) {
                        // set transient property of customer
                        customer.setCookied(true);
                        boolean publishRememberMeEvent = true;
                        if (lastPublishedEvent != null && lastPublishedEvent instanceof CustomerAuthenticatedFromCookieEvent) {
                            CustomerAuthenticatedFromCookieEvent cookieEvent = (CustomerAuthenticatedFromCookieEvent) lastPublishedEvent;
                            if (userName.equals(cookieEvent.getCustomer().getUsername())) {
                                publishRememberMeEvent = false;
                            }
                        }
                        if (publishRememberMeEvent) {
                            CustomerAuthenticatedFromCookieEvent cookieEvent = new CustomerAuthenticatedFromCookieEvent(customer, this.getClass().getName()); 
                            eventPublisher.publishEvent(cookieEvent);
                            request.setAttribute(LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME, cookieEvent, WebRequest.SCOPE_REQUEST);
                        }                       
                    } else if (authentication instanceof UsernamePasswordAuthenticationToken) {
                        customer.setLoggedIn(true);
                        boolean publishLoggedInEvent = true;
                        if (lastPublishedEvent != null && lastPublishedEvent instanceof CustomerLoggedInEvent) {
                            CustomerLoggedInEvent loggedInEvent = (CustomerLoggedInEvent) lastPublishedEvent;
                            if (userName.equals(loggedInEvent.getCustomer().getUsername())) {
                                publishLoggedInEvent= false;
                            }
                        }
                        if (publishLoggedInEvent) {
                            CustomerLoggedInEvent loggedInEvent = new CustomerLoggedInEvent(customer, this.getClass().getName()); 
                            eventPublisher.publishEvent(loggedInEvent);
                            request.setAttribute(LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME, loggedInEvent, WebRequest.SCOPE_REQUEST);
                        }                        
                    } else {
                        customer = resolveAuthenticatedCustomer(authentication);
                    }
                }
            }
        }

        if (customer == null) {
            // This is an anonymous customer.
            // TODO: Handle a custom cookie (different than remember me) that is just for anonymous users.  
            // This can be used to remember their cart from a previous visit.
            // Cookie logic probably needs to be configurable - with TCS as the exception.

            customer = resolveAnonymousCustomer(request);
        } else {
            //Does this customer need to have an anonymous customer's data merged into it?
            customer = mergeCustomerIfRequired(request, customer);
        }
        CustomerState.setCustomer(customer);

        // Setup customer for content rule processing
        Map<String,Object> ruleMap = (Map<String, Object>) request.getAttribute(BLC_RULE_MAP_PARAM, WebRequest.SCOPE_REQUEST);
        if (ruleMap == null) {
            ruleMap = new HashMap<String,Object>();
        }
        ruleMap.put("customer", customer);
        request.setAttribute(BLC_RULE_MAP_PARAM, ruleMap, WebRequest.SCOPE_REQUEST);
        
    }
    
    /**
     * Allows the merging of anonymous customer data and / or session data, to the logged in customer, if required. 
     * This is written to only require it to happen once.
     * @param request
     * @param customer
     * @return
     */
    protected Customer mergeCustomerIfRequired(WebRequest request, Customer customer) {
        //Don't call this if it has already been called
        if (request.getAttribute(getAnonymousCustomerMergedSessionAttributeName(), WebRequest.SCOPE_GLOBAL_SESSION) == null) {
            Customer anonymousCustomer = getAnonymousCustomer(request);
            customer = copyAnonymousCustomerInfoToCustomer(request, anonymousCustomer, customer);

            //Set this so we don't do this every time.
            request.setAttribute(getAnonymousCustomerMergedSessionAttributeName(), Boolean.TRUE, WebRequest.SCOPE_GLOBAL_SESSION);
        }

        return customer;
    }

    /**
     * This allows the customer object to be augmented by information that may have been stored on the 
     * anonymous customer or session.  After login, a new instance of customer is created that is different from the 
     * anonymous customer.  In many cases, there are reasons that the anonymous customer may have had data associated with 
     * them that is required on the new customer.  For example, customer attributes, promotions, promo codes, etc. 
     * may have been associated with the anonymous customer, and we want them to be copied to this customer.  
     * The default implementation does not copy data. It simply provides a hook for implementors to extend / implement <commit_msg>#1015. Moved the setting of the session attribute to before the logic is actually executed.<commit_after>/*
 * #%L
 * BroadleafCommerce Profile Web
 * %%
 * Copyright (C) 2009 - 2013 Broadleaf Commerce
 * %%
 * 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.
 * #L%
 */
package org.broadleafcommerce.profile.web.core.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.extension.ExtensionResultHolder;
import org.broadleafcommerce.common.web.AbstractBroadleafWebRequestProcessor;
import org.broadleafcommerce.common.web.BroadleafRequestCustomerResolverImpl;
import org.broadleafcommerce.profile.core.domain.Customer;
import org.broadleafcommerce.profile.core.service.CustomerService;
import org.broadleafcommerce.profile.web.core.CustomerState;
import org.broadleafcommerce.profile.web.core.CustomerStateRefresher;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;


/**
 * @author <NAME>
 * @see {@link CustomerStateFilter}
 */
@Component("blCustomerStateRequestProcessor")
public class CustomerStateRequestProcessor extends AbstractBroadleafWebRequestProcessor implements ApplicationEventPublisherAware {

    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    public static final String BLC_RULE_MAP_PARAM = "blRuleMap";

    @Resource(name="blCustomerService")
    protected CustomerService customerService;
    
    @Resource(name = "blCustomerMergeExtensionManager")
    protected CustomerMergeExtensionManager customerMergeExtensionManager;

    protected ApplicationEventPublisher eventPublisher;

    public static final String ANONYMOUS_CUSTOMER_SESSION_ATTRIBUTE_NAME = "_blc_anonymousCustomer";
    public static final String ANONYMOUS_CUSTOMER_ID_SESSION_ATTRIBUTE_NAME = "_blc_anonymousCustomerId";
    private static final String LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME = "_blc_lastPublishedEvent";
    public static final String OVERRIDE_CUSTOMER_SESSION_ATTR_NAME = "_blc_overrideCustomerId";
    public static final String ANONYMOUS_CUSTOMER_MERGED_SESSION_ATTRIBUTE_NAME = "_blc_anonymousCustomerMerged";

    @Override
    public void process(WebRequest request) {
        Customer customer = null;

        Long overrideId = (Long) request.getAttribute(OVERRIDE_CUSTOMER_SESSION_ATTR_NAME, WebRequest.SCOPE_GLOBAL_SESSION);
        if (overrideId != null) {
            customer = customerService.readCustomerById(overrideId);
        } else {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if ((authentication != null) && !(authentication instanceof AnonymousAuthenticationToken)) {
                String userName = authentication.getName();
                customer = (Customer) BroadleafRequestCustomerResolverImpl.getRequestCustomerResolver().getCustomer(request);
                if (userName != null && (customer == null || !userName.equals(customer.getUsername()))) {
                    // can only get here if the authenticated user does not match the user in session
                    customer = customerService.readCustomerByUsername(userName);
                    if (logger.isDebugEnabled() && customer != null) {
                        logger.debug("Customer found by username " + userName);
                    }
                }
                if (customer != null) {
                    ApplicationEvent lastPublishedEvent = (ApplicationEvent) request.getAttribute(LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME, WebRequest.SCOPE_REQUEST);
                    if (authentication instanceof RememberMeAuthenticationToken) {
                        // set transient property of customer
                        customer.setCookied(true);
                        boolean publishRememberMeEvent = true;
                        if (lastPublishedEvent != null && lastPublishedEvent instanceof CustomerAuthenticatedFromCookieEvent) {
                            CustomerAuthenticatedFromCookieEvent cookieEvent = (CustomerAuthenticatedFromCookieEvent) lastPublishedEvent;
                            if (userName.equals(cookieEvent.getCustomer().getUsername())) {
                                publishRememberMeEvent = false;
                            }
                        }
                        if (publishRememberMeEvent) {
                            CustomerAuthenticatedFromCookieEvent cookieEvent = new CustomerAuthenticatedFromCookieEvent(customer, this.getClass().getName()); 
                            eventPublisher.publishEvent(cookieEvent);
                            request.setAttribute(LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME, cookieEvent, WebRequest.SCOPE_REQUEST);
                        }                       
                    } else if (authentication instanceof UsernamePasswordAuthenticationToken) {
                        customer.setLoggedIn(true);
                        boolean publishLoggedInEvent = true;
                        if (lastPublishedEvent != null && lastPublishedEvent instanceof CustomerLoggedInEvent) {
                            CustomerLoggedInEvent loggedInEvent = (CustomerLoggedInEvent) lastPublishedEvent;
                            if (userName.equals(loggedInEvent.getCustomer().getUsername())) {
                                publishLoggedInEvent= false;
                            }
                        }
                        if (publishLoggedInEvent) {
                            CustomerLoggedInEvent loggedInEvent = new CustomerLoggedInEvent(customer, this.getClass().getName()); 
                            eventPublisher.publishEvent(loggedInEvent);
                            request.setAttribute(LAST_PUBLISHED_EVENT_SESSION_ATTRIBUTED_NAME, loggedInEvent, WebRequest.SCOPE_REQUEST);
                        }                        
                    } else {
                        customer = resolveAuthenticatedCustomer(authentication);
                    }
                }
            }
        }

        if (customer == null) {
            // This is an anonymous customer.
            // TODO: Handle a custom cookie (different than remember me) that is just for anonymous users.  
            // This can be used to remember their cart from a previous visit.
            // Cookie logic probably needs to be configurable - with TCS as the exception.

            customer = resolveAnonymousCustomer(request);
        } else {
            //Does this customer need to have an anonymous customer's data merged into it?
            customer = mergeCustomerIfRequired(request, customer);
        }
        CustomerState.setCustomer(customer);

        // Setup customer for content rule processing
        Map<String,Object> ruleMap = (Map<String, Object>) request.getAttribute(BLC_RULE_MAP_PARAM, WebRequest.SCOPE_REQUEST);
        if (ruleMap == null) {
            ruleMap = new HashMap<String,Object>();
        }
        ruleMap.put("customer", customer);
        request.setAttribute(BLC_RULE_MAP_PARAM, ruleMap, WebRequest.SCOPE_REQUEST);
        
    }
    
    /**
     * Allows the merging of anonymous customer data and / or session data, to the logged in customer, if required. 
     * This is written to only require it to happen once.
     * @param request
     * @param customer
     * @return
     */
    protected Customer mergeCustomerIfRequired(WebRequest request, Customer customer) {
        //Don't call this if it has already been called
        if (request.getAttribute(getAnonymousCustomerMergedSessionAttributeName(), WebRequest.SCOPE_GLOBAL_SESSION) == null) {
            //Set this so we don't do this every time.
            request.setAttribute(getAnonymousCustomerMergedSessionAttributeName(), Boolean.TRUE, WebRequest.SCOPE_GLOBAL_SESSION);

            Customer anonymousCustomer = getAnonymousCustomer(request);
            customer = copyAnonymousCustomerInfoToCustomer(request, anonymousCustomer, customer);
        }

        return customer;
    }

    /**
     * This allows the customer object to be augmented by information that may have been stored on the 
     * anonymous customer or session.  After login, a new instance of customer is created that is different from the 
     * anonymous customer.  In many cases, there are reasons that the anonymous customer may have had data associated with 
     * them that is required on the new customer.  For example, customer attributes, promotions, promo codes, etc. 
     * may have been associated with the anonymous customer, and we want them to be copied to this customer.  
     * The default implementation does not copy data. It simply provides a hook for implementors to extend / implement 
{'fraction_non_alphanumeric': 0.06808789436548734, 'fraction_numerical': 0.0014111480697510332, 'mean_word_length': 2.794798240581373, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4370973', 'n_tokens_mistral': 4682, 'n_tokens_neox': 4447, 'n_words': 1262}