@@ -23,11 +23,13 @@
import org.broadleafcommerce.core.offer.domain.OfferItemCriteria;
import org.broadleafcommerce.core.offer.domain.OfferRule;
import org.broadleafcommerce.core.offer.service.discount.CandidatePromotionItems;
import org.broadleafcommerce.core.offer.service.discount.domain.PromotableCandidateItemOffer;
import org.broadleafcommerce.core.offer.service.discount.domain.PromotableOrder;
import org.broadleafcommerce.core.offer.service.discount.domain.PromotableOrderItem;
import org.broadleafcommerce.core.offer.service.type.OfferRuleType;
import org.broadleafcommerce.core.offer.service.type.OfferType;
import org.broadleafcommerce.core.order.service.type.FulfillmentType;
import org.broadleafcommerce.common.money.Money;
import org.broadleafcommerce.common.time.SystemTime;
import org.broadleafcommerce.profile.core.domain.Customer;
import org.hibernate.tool.hbm2x.StringUtils;
@@ -57,16 +59,95 @@ protected CandidatePromotionItems couldOfferApplyToOrderItems(Offer offer, List<
if (!candidates.isMatchedQualifier()) {
break;
}
}
}
}

if (offer.getType().equals(OfferType.ORDER_ITEM) && offer.getTargetItemCriteria() != null) {
checkForItemRequirements(candidates, offer.getTargetItemCriteria(), promotableOrderItems, false);
}

if (candidates.isMatchedQualifier()) {
if (! meetsItemQualifierSubtotal(offer, candidates)) {
candidates.setMatchedQualifier(false);
}
}

return candidates;
}

private boolean isEmpty(Collection<? extends Object> collection) {
return (collection == null || collection.size() == 0);
}

private boolean hasPositiveValue(Money money) {
return (money != null && money.greaterThan(Money.ZERO));
}

protected boolean meetsItemQualifierSubtotal(Offer offer, CandidatePromotionItems candidateItem) {
Money qualifyingSubtotal = offer.getQualifyingItemSubTotal();
if (! hasPositiveValue(qualifyingSubtotal)) {
if (LOG.isTraceEnabled()) {
LOG.trace("Offer " + offer.getName() + " does not have an item subtotal requirement.");
}
return true;
}

if (isEmpty(offer.getQualifyingItemCriteria())) {
if (OfferType.ORDER_ITEM.equals(offer.getType())) {
if (LOG.isWarnEnabled()) {
LOG.warn("Offer " + offer.getName() + " has a subtotal item requirement but no item qualification criteria.");
}
return false;
} else {
// Checking if targets meet subtotal for item offer with no item criteria.
Money accumulatedTotal = new Money(0);
for (PromotableOrderItem orderItem : candidateItem.getCandidateTargets()) {
Money itemPrice = orderItem.getCurrentPrice().multiply(orderItem.getQuantity());
accumulatedTotal = accumulatedTotal.add(itemPrice);
if (accumulatedTotal.greaterThan(qualifyingSubtotal)) {
if (LOG.isTraceEnabled()) {
LOG.trace("Offer " + offer.getName() + " meets qualifying item subtotal.");
}
return true;
}
}
}

if (LOG.isDebugEnabled()) {
LOG.debug("Offer " + offer.getName() + " does not meet qualifying item subtotal.");
}
} else {
if (candidateItem.getCandidateQualifiersMap() != null) {
Money accumulatedTotal = new Money(0);
Set<PromotableOrderItem> usedItems = new HashSet<PromotableOrderItem>();
for (OfferItemCriteria criteria : candidateItem.getCandidateQualifiersMap().keySet()) {
List<PromotableOrderItem> promotableItems = candidateItem.getCandidateQualifiersMap().get(criteria);
if (promotableItems != null) {
for (PromotableOrderItem item : promotableItems) {
if (!usedItems.contains(item)) {
usedItems.add(item);
Money itemPrice = item.getCurrentPrice().multiply(item.getQuantity());
accumulatedTotal = accumulatedTotal.add(itemPrice);
if (accumulatedTotal.greaterThan(qualifyingSubtotal)) {
if (LOG.isTraceEnabled()) {
LOG.trace("Offer " + offer.getName() + " meets the item subtotal requirement.");
}
return true;
}
}
}
}
}
}
}

if (LOG.isTraceEnabled()) {
LOG.trace("Offer " + offer.getName() + " does not meet the item subtotal qualifications.");
}
return false;

}

protected void checkForItemRequirements(CandidatePromotionItems candidates, OfferItemCriteria criteria, List<PromotableOrderItem> promotableOrderItems, boolean isQualifier) {
boolean matchFound = false;
int criteriaQuantity = criteria.getQuantity();
@@ -24,7 +24,6 @@
import org.broadleafcommerce.core.offer.domain.OrderItemAdjustment;
import org.broadleafcommerce.core.offer.service.discount.CandidatePromotionItems;
import org.broadleafcommerce.core.offer.service.discount.ItemOfferComparator;
import org.broadleafcommerce.core.offer.service.discount.OrderItemPriceComparator;
import org.broadleafcommerce.core.offer.service.discount.PromotionDiscount;
import org.broadleafcommerce.core.offer.service.discount.domain.PromotableCandidateItemOffer;
import org.broadleafcommerce.core.offer.service.discount.domain.PromotableCandidateOrderOffer;
@@ -38,6 +37,7 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
@@ -52,7 +52,8 @@ public class ItemOfferProcessorImpl extends OrderOfferProcessorImpl implements I
/* (non-Javadoc)
* @see org.broadleafcommerce.core.offer.service.processor.ItemOfferProcessor#filterItemLevelOffer(org.broadleafcommerce.core.order.domain.Order, java.util.List, java.util.List, org.broadleafcommerce.core.offer.domain.Offer)
*/
public void filterItemLevelOffer(PromotableOrder order, List<PromotableCandidateItemOffer> qualifiedItemOffers, Offer offer) {
@Override
public void filterItemLevelOffer(PromotableOrder order, List<PromotableCandidateItemOffer> qualifiedItemOffers, Offer offer) {
boolean isNewFormat = (offer.getQualifyingItemCriteria() != null && offer.getQualifyingItemCriteria().size() > 0) || offer.getTargetItemCriteria() != null;
boolean itemLevelQualification = false;
boolean offerCreated = false;
@@ -101,7 +102,7 @@ public void filterItemLevelOffer(PromotableOrder order, List<PromotableCandidate
candidateOffer = createCandidateItemOffer(qualifiedItemOffers, offer, null);
}
for (PromotableOrderItem candidateItem : candidates.getCandidateTargets()) {
PromotableCandidateItemOffer itemOffer = (PromotableCandidateItemOffer) candidateOffer.clone();
PromotableCandidateItemOffer itemOffer = candidateOffer.clone();
itemOffer.setOrderItem(candidateItem);
candidateItem.addCandidateItemOffer(itemOffer);
}
@@ -135,6 +136,7 @@ protected PromotableCandidateItemOffer createCandidateItemOffer(List<PromotableC
/* (non-Javadoc)
* @see org.broadleafcommerce.core.offer.service.processor.ItemOfferProcessor#applyAllItemOffers(java.util.List, java.util.List)
*/
@Override
public boolean applyAllItemOffers(List<PromotableCandidateItemOffer> itemOffers, PromotableOrder order) {
// Iterate through the collection of CandidateItemOffers. Remember that each one is an offer that may apply to a
// particular OrderItem. Multiple CandidateItemOffers may contain a reference to the same OrderItem object.
@@ -153,6 +155,8 @@ public boolean applyAllItemOffers(List<PromotableCandidateItemOffer> itemOffers,
isLegacyFormat = true;
appliedItemOffersCount = applyLegacyAdjustments(appliedItemOffersCount, itemOffer, beforeCount, orderItem);
} else {
// TODO: Add filter for item-subtotal
skipOfferIfSubtotalRequirementNotMet(order, itemOffer);
appliedItemOffersCount = applyAdjustments(order, appliedItemOffersCount, itemOffer, beforeCount);
}
}
@@ -166,6 +170,74 @@ public boolean applyAllItemOffers(List<PromotableCandidateItemOffer> itemOffers,
}
return itemOffersApplied;
}


protected boolean skipOfferIfSubtotalRequirementNotMet(PromotableOrder order, PromotableCandidateItemOffer itemOffer) {
if (itemOffer.getOffer().getQualifyingItemSubTotal() == null || itemOffer.getOffer().getQualifyingItemSubTotal().lessThanOrEqual(Money.ZERO)) {
return false;
}

/*
boolean notCombinableOfferApplied = false;
boolean offerApplied = false;
List<PromotableOrderItem> allSplitItems = order.getAllSplitItems();
for (PromotableOrderItem targetItem : allSplitItems) {
notCombinableOfferApplied = targetItem.isNotCombinableOfferApplied();
if (!offerApplied) {
offerApplied = targetItem.isHasOrderItemAdjustments();
}
if (notCombinableOfferApplied) {
break;
}
}
if (
!notCombinableOfferApplied && (
(
(itemOffer.getOffer().isCombinableWithOtherOffers() || itemOffer.getOffer().isTotalitarianOffer() == null || !itemOffer.getOffer().isTotalitarianOffer())
//&& itemOffer.getOffer().isStackable()
)
|| !offerApplied
)
)
{
// At this point, we should not have any official adjustment on the order
// for this item.
applyItemQualifiersAndTargets(itemOffer, order);
allSplitItems = order.getAllSplitItems();
for (PromotableOrderItem splitItem : allSplitItems) {
for (PromotionDiscount discount : splitItem.getPromotionDiscounts()) {
if (discount.getPromotion().equals(itemOffer.getOffer())) {
applyOrderItemAdjustment(itemOffer, splitItem);
break;
}
}
}
}
// check if not combinable offer is better than sale price; if no, remove the not combinable offer so
// that another offer may be applied to the item
if ((!itemOffer.getOffer().isCombinableWithOtherOffers() || (itemOffer.getOffer().isTotalitarianOffer() != null && itemOffer.getOffer().isTotalitarianOffer())) && appliedItemOffersCount > beforeCount) {
Money adjustmentTotal = new Money(0D);
Money saleTotal = new Money(0D);
for (PromotableOrderItem splitItem : allSplitItems) {
adjustmentTotal = adjustmentTotal.add(splitItem.getCurrentPrice().multiply(splitItem.getQuantity()));
saleTotal = saleTotal.add(splitItem.getPriceBeforeAdjustments(true).multiply(splitItem.getQuantity()));
}
if (adjustmentTotal.greaterThanOrEqual(saleTotal)) {
// adjustment price is not best price, remove adjustments for this item
for (PromotableOrderItem splitItem : allSplitItems) {
if (splitItem.isHasOrderItemAdjustments()) {
appliedItemOffersCount--;
}
}
order.getSplitItems().clear();
}
}
return appliedItemOffersCount;
*/
return false;
}


protected int applyAdjustments(PromotableOrder order, int appliedItemOffersCount, PromotableCandidateItemOffer itemOffer, int beforeCount) {
boolean notCombinableOfferApplied = false;
@@ -323,7 +395,6 @@ protected int applyLegacyAdjustments(int appliedItemOffersCount, PromotableCandi

protected void applyItemQualifiersAndTargets(PromotableCandidateItemOffer itemOffer, PromotableOrder order) {
Offer promotion = itemOffer.getOffer();
OrderItemPriceComparator priceComparator = new OrderItemPriceComparator(promotion.getApplyDiscountToSalePrice());
boolean matchFound = false;
do {
matchFound = false;
@@ -337,8 +408,8 @@ protected void applyItemQualifiersAndTargets(PromotableCandidateItemOffer itemOf
for (OfferItemCriteria itemCriteria : itemOffer.getCandidateQualifiersMap().keySet()) {
List<PromotableOrderItem> chargeableItems = itemOffer.getCandidateQualifiersMap().get(itemCriteria);

// Sort the items so that the highest priced ones are at the top
Collections.sort(chargeableItems, priceComparator);
Collections.sort(chargeableItems, getQualifierItemComparator(promotion.getApplyDiscountToSalePrice()));

// Calculate the number of qualifiers needed that will not receive the promotion.
// These will be reserved first before the target is assigned.
int qualifierQtyNeeded = itemCriteria.getQuantity();
@@ -366,7 +437,7 @@ protected void applyItemQualifiersAndTargets(PromotableCandidateItemOffer itemOf
}
checkTargets :{
List<PromotableOrderItem> chargeableItems = itemOffer.getCandidateTargets();
Collections.sort(chargeableItems, priceComparator);
Collections.sort(chargeableItems, getTargetItemComparator(promotion.getApplyDiscountToSalePrice()));
for (PromotableOrderItem chargeableItem : chargeableItems) {
// Mark Targets
if (receiveQtyNeeded > 0) {
@@ -425,6 +496,56 @@ protected void applyItemQualifiersAndTargets(PromotableCandidateItemOffer itemOf
}
}
}

/**
* Used in {@link #applyItemQualifiersAndTargets(PromotableCandidateItemOffer, PromotableOrder)} allow for customized
* sorting for which qualifier items should be attempted to be used first for a promotion. Default behavior
* is to sort descending, so higher-value items are attempted to be qualified first.
*
* @param applyToSalePrice - whether or not the Comparator should use the sale price for comparison
* @return
*/
protected Comparator<PromotableOrderItem> getQualifierItemComparator(final boolean applyToSalePrice) {
return new Comparator<PromotableOrderItem>() {
@Override
public int compare(PromotableOrderItem o1, PromotableOrderItem o2) {
Money price = o1.getPriceBeforeAdjustments(applyToSalePrice);
Money price2 = o2.getPriceBeforeAdjustments(applyToSalePrice);

// highest amount first
return price2.compareTo(price);
}
};
}

/**
* <p>
* Used in {@link #applyItemQualifiersAndTargets(PromotableCandidateItemOffer, PromotableOrder)} allow for customized
* sorting for which target items the promotion should be attempted to be applied to first. Default behavior is to
* sort descending, so higher-value items get the promotion over lesser-valued items.
* </p>
* <p>
* Note: By default, both the {@link #getQualifierItemComparator(boolean)} and this target comparator are sorted
* in descending order. This means that higher-valued items can be paired with higher-valued items and lower-valued
* items can be paired with lower-valued items. This also ensures that you will <b>not</b> have the scenario where 2 lower-valued
* items can be used to qualify a higher-valued target.
* </p>
*
* @param applyToSalePrice - whether or not the Comparator should use the sale price for comparison
* @return
*/
protected Comparator<PromotableOrderItem> getTargetItemComparator(final boolean applyToSalePrice) {
return new Comparator<PromotableOrderItem>() {
@Override
public int compare(PromotableOrderItem o1, PromotableOrderItem o2) {
Money price = o1.getPriceBeforeAdjustments(applyToSalePrice);
Money price2 = o2.getPriceBeforeAdjustments(applyToSalePrice);

// highest amount first
return price2.compareTo(price);
}
};
}

/**
* Private method used by applyAllItemOffers to create an OrderItemAdjustment from a CandidateItemOffer
@@ -440,6 +561,7 @@ protected void applyOrderItemAdjustment(PromotableCandidateItemOffer itemOffer,
orderItem.addOrderItemAdjustment(promotableOrderItemAdjustment); //This is how we can tell if an item has been discounted
}

@Override
public void filterOffers(PromotableOrder order, List<Offer> filteredOffers, List<PromotableCandidateOrderOffer> qualifiedOrderOffers, List<PromotableCandidateItemOffer> qualifiedItemOffers) {
// set order subtotal price to total item price without adjustments
order.setSubTotal(order.calculateOrderItemsFinalPrice(true));
@@ -452,6 +574,7 @@ public void filterOffers(PromotableOrder order, List<Offer> filteredOffers, List
}
}

@Override
@SuppressWarnings("unchecked")
public void applyAndCompareOrderAndItemOffers(PromotableOrder order, List<PromotableCandidateOrderOffer> qualifiedOrderOffers, List<PromotableCandidateItemOffer> qualifiedItemOffers) {
if (!qualifiedItemOffers.isEmpty()) {
@@ -254,6 +254,8 @@ public boolean applyAllOrderOffers(List<PromotableCandidateOrderOffer> orderOffe
} else {
if (!order.containsNotStackableOrderOffer() || !order.isHasOrderAdjustments()) {
boolean alreadyContainsTotalitarianOffer = order.isTotalitarianOfferApplied();

// TODO: Add filter for item-subtotal
applyOrderOffer(order, orderOffer);
orderOffersApplied = true;
if (
@@ -16,12 +16,13 @@

package org.broadleafcommerce.core.search.service;

import java.util.List;

import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.search.domain.SearchIntercept;
import org.broadleafcommerce.core.search.domain.SearchSynonym;

import java.util.List;

@Deprecated
public interface SearchService {

public void rebuildProductIndex();
@@ -16,10 +16,6 @@

package org.broadleafcommerce.core.search.service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.catalog.service.CatalogService;
@@ -35,10 +31,16 @@
import org.compass.core.engine.SearchEngineIndexManager;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.util.ArrayList;
import java.util.List;

/**
* @author dmclain
*
*/
@Deprecated
@Service("blSearchService")
public class SearchServiceCompassImpl implements SearchService {

@@ -99,7 +99,7 @@ public void verify() {
}

public void testFilterItemLevelOffer() {
EasyMock.expect(offerDaoMock.createCandidateItemOffer()).andReturn(new CandidateItemOfferImpl()).times(3);
EasyMock.expect(offerDaoMock.createCandidateItemOffer()).andReturn(new CandidateItemOfferImpl()).times(4);

replay();

@@ -132,6 +132,41 @@ public void testFilterItemLevelOffer() {
//we don't know the targets yet, so there's only one CandidateItemOffer for now
assertTrue(qualifiedOffers.size() == 1 && qualifiedOffers.get(0).getOffer().equals(offers.get(0)) && qualifiedOffers.get(0).getCandidateQualifiersMap().size() == 1);

// Add a subtotal requirement that will be met by the item offer.
qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>();
offers = dataProvider.createItemBasedOfferWithItemCriteria(
"order.subTotal.getAmount()>20",
OfferDiscountType.PERCENT_OFF,
"([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))",
"([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))"
);

offers.get(0).setQualifyingItemSubTotal(new Money(1));
itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0));

//test that the valid order item offer is included
//there is a qualifier and the item qualifying criteria requires only 1, therefore there will be only one qualifier in the qualifiers map
//we don't know the targets yet, so there's only one CandidateItemOffer for now
assertTrue(qualifiedOffers.size() == 1 && qualifiedOffers.get(0).getOffer().equals(offers.get(0)) && qualifiedOffers.get(0).getCandidateQualifiersMap().size() == 1);


// Add a subtotal requirement that will not be met by the item offer.
qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>();
offers = dataProvider.createItemBasedOfferWithItemCriteria(
"order.subTotal.getAmount()>20",
OfferDiscountType.PERCENT_OFF,
"([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))",
"([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))"
);

offers.get(0).setQualifyingItemSubTotal(new Money(99999));
itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0));

// Since the item qualification subTotal is not met, the qualified offer size should
// be zero.
assertTrue(qualifiedOffers.size() == 0);


qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>();
offers = dataProvider.createItemBasedOfferWithItemCriteria(
"order.subTotal.getAmount()>20",
@@ -0,0 +1,91 @@
/*
* Copyright 2012 the original author or 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.broadleafcommerce.profile.web.core.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Cookie used to protected against session fixation attacks
*
* @see SessionFixationProtectionFilter
*
* @author Andre Azzolini (apazzolini)
*/
public class SessionFixationProtectionCookie {
protected final Log logger = LogFactory.getLog(getClass());

public static final String COOKIE_NAME = "ActiveID";

public static String readActiveID(HttpServletRequest request) {
String cookieData = null;

try {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(COOKIE_NAME)) {
cookieData = cookie.getValue();
break;
}
}
}
} catch(Exception e) {
// Do nothing -- we'll be returning null
}

return cookieData;
}

public static void writeActiveID(HttpServletRequest request, HttpServletResponse response, String data) {
if (data != null) {
Cookie cookie = new Cookie(COOKIE_NAME, data);
cookie.setMaxAge(-1);
cookie.setSecure(true);
cookie.setPath("/");
response.addCookie(cookie);
}
}

public static void remove(HttpServletRequest request, HttpServletResponse response) {
if (request != null && request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (cookie.getName().equals(COOKIE_NAME)) {
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setSecure(true);
cookie.setValue("-1");
response.addCookie(cookie);
}
}
}
}

public static void forceRemove(HttpServletRequest request, HttpServletResponse response) {
Cookie cookie = new Cookie(COOKIE_NAME, "");
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setSecure(true);
cookie.setValue("-1");
response.addCookie(cookie);
}

}
@@ -0,0 +1,106 @@
/*
* Copyright 2012 the original author or 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.broadleafcommerce.profile.web.core.security;

import org.apache.commons.lang.StringUtils;
import org.broadleafcommerce.common.encryption.EncryptionModule;
import org.broadleafcommerce.common.security.RandomGenerator;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;

/**
* Filter used to protected against session fixation attacks while still keeping the same session id on both
* http and https protocols. Uses a secondary, https cookie that must be present on every https request for a
* given session after the first request. If it's not present and equal to what we expect, we will redirect the
* user to "/" and remove his session cookie.
*
* @author Andre Azzolini (apazzolini)
*/
@Component("blSessionFixationProtectionFilter")
public class SessionFixationProtectionFilter extends GenericFilterBean {
protected static final String SESSION_ATTR = "SFP-ActiveID";

@Resource(name = "blEncryptionModule")
protected EncryptionModule encryptionModule;

@Override
public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) sRequest;
HttpServletResponse response = (HttpServletResponse) sResponse;
HttpSession session = request.getSession(false);

if (SecurityContextHolder.getContext() == null) {
chain.doFilter(request, response);
}

String activeIdSessionValue = (String) session.getAttribute(SESSION_ATTR);

if (StringUtils.isNotBlank(activeIdSessionValue) && request.isSecure()) {
// The request is secure and and we've set a session fixation protection cookie
String activeIdCookieValue = SessionFixationProtectionCookie.readActiveID(request);
String decryptedActiveIdValue = encryptionModule.decrypt(activeIdCookieValue);

if (!activeIdSessionValue.equals(decryptedActiveIdValue)) {
abortUser(request, response);
}
} else if (request.isSecure()) {
// The request is secure, but we haven't set a session fixation protection cookie yet
String token;
try {
token = RandomGenerator.generateRandomId("SHA1PRNG", 32);
} catch (NoSuchAlgorithmException e) {
throw new ServletException(e);
}

String encryptedActiveIdValue = encryptionModule.encrypt(token);

session.setAttribute(SESSION_ATTR, token);
SessionFixationProtectionCookie.writeActiveID(request, response, encryptedActiveIdValue);
}

chain.doFilter(request, response);
}

protected void abortUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
SecurityContextHolder.clearContext();
SessionFixationProtectionCookie.remove(request, response);

Cookie cookie = new Cookie("JSESSIONID", "");
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setSecure(false);
cookie.setValue("-1");
response.addCookie(cookie);

response.sendRedirect("/");
}

}
@@ -14,7 +14,6 @@
<mapping-file>config/bc/jpa/domain/Phone.orm.xml</mapping-file>
<mapping-file>config/bc/jpa/domain/Role.orm.xml</mapping-file>
<mapping-file>config/bc/jpa/domain/State.orm.xml</mapping-file>
<mapping-file>config/bc/jpa/domain/User.orm.xml</mapping-file>
<class>org.broadleafcommerce.profile.core.domain.AddressImpl</class>
<class>org.broadleafcommerce.profile.core.domain.ChallengeQuestionImpl</class>
<class>org.broadleafcommerce.profile.core.domain.CountryImpl</class>