Validate cookies  (#290)

* DMF-4477 : validate cookie sent to unomi

* transform classes as osgi services
diff --git a/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
index 6d0f689..7662792 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
@@ -25,16 +25,19 @@
 import org.apache.unomi.api.Profile;
 import org.apache.unomi.api.services.ConfigSharingService;
 import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.rest.service.RestServiceUtils;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import javax.jws.WebService;
-import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.*;
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import java.io.StringWriter;
@@ -43,20 +46,15 @@
 import java.util.Map;
 import java.util.Set;
 
-
 /**
  * A servlet filter to serve a context-specific Javascript containing the current request context object.
  */
 @WebService
-@CrossOriginResourceSharing(
-        allowAllOrigins = true,
-        allowCredentials = true
-)
+@CrossOriginResourceSharing(allowAllOrigins = true, allowCredentials = true)
 @Path("/")
 @Component(service = ClientEndpoint.class, property = "osgi.jaxrs.resource=true")
 public class ClientEndpoint {
 
-    private static final Logger logger = LoggerFactory.getLogger(ClientEndpoint.class.getName());
     private static final String CONTENT_DISPOSITION_HEADER_KEY = "Content-Disposition";
 
     private static final String FILE_NAME_WO_EXT = "my-profile";
@@ -70,6 +68,8 @@
     @Reference
     private ConfigSharingService configSharingService;
 
+    @Reference
+    private RestServiceUtils restServiceUtils;
 
     @Context
     HttpServletRequest request;
@@ -96,15 +96,8 @@
     }
 
     private Response donwloadCurrentProfile(String downloadFileType) throws JsonProcessingException {
-        String cookieProfileId = null;
-        Cookie[] cookies = request.getCookies();
-        if (cookies != null) {
-            for (Cookie cookie : cookies) {
-                if (configSharingService.getProperty("profileIdCookieName").equals(cookie.getName())) {
-                    cookieProfileId = cookie.getValue();
-                }
-            }
-        }
+        String cookieProfileId = restServiceUtils.getProfileIdCookieValue(request);
+
         if (cookieProfileId != null) {
             Profile currentProfile = profileService.load(cookieProfileId);
             if (currentProfile != null) {
@@ -133,9 +126,9 @@
         //using custom delimiter and quote character
         CSVWriter csvWriter = new CSVWriter(writer);
         if (vertical) {
-            csvWriter.writeNext(new String[]{"name", "value"});
+            csvWriter.writeNext(new String[] { "name", "value" });
             for (Map.Entry<String, Object> entry : currentProfile.getProperties().entrySet()) {
-                csvWriter.writeNext(new String[]{entry.getKey(), entry.getValue().toString().trim().replace("\n", "")});
+                csvWriter.writeNext(new String[] { entry.getKey(), entry.getValue().toString().trim().replace("\n", "") });
             }
         } else {
             Set<String> keySet = currentProfile.getProperties().keySet();
diff --git a/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
index 42769a8..5c6e2a8 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
@@ -36,9 +36,9 @@
 import org.apache.unomi.api.services.RulesService;
 import org.apache.unomi.api.utils.ValidationPattern;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
+import org.apache.unomi.rest.service.RestServiceUtils;
 import org.apache.unomi.utils.Changes;
 import org.apache.unomi.utils.HttpUtils;
-import org.apache.unomi.utils.ServletCommon;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -102,6 +102,8 @@
     private PersonalizationService personalizationService;
     @Reference
     private ConfigSharingService configSharingService;
+    @Reference
+    private RestServiceUtils restServiceUtils;
 
     @OPTIONS
     @Path("/context.json")
@@ -115,9 +117,8 @@
     public Response contextJSAsPost(@Valid ContextRequest contextRequest,
             @QueryParam("personaId") @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN) String personaId,
             @QueryParam("sessionId") @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN) String sessionId,
-            @QueryParam("timestamp") Long timestampAsLong,
-            @QueryParam("invalidateProfile") boolean invalidateProfile, @QueryParam("invalidateSession") boolean invalidateSession)
-            throws JsonProcessingException {
+            @QueryParam("timestamp") Long timestampAsLong, @QueryParam("invalidateProfile") boolean invalidateProfile,
+            @QueryParam("invalidateSession") boolean invalidateSession) throws JsonProcessingException {
         return contextJSAsGet(contextRequest, personaId, sessionId, timestampAsLong, invalidateProfile, invalidateSession);
     }
 
@@ -144,8 +145,8 @@
     public ContextResponse contextJSONAsGet(@Valid ContextRequest contextRequest,
             @QueryParam("personaId") @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN) String personaId,
             @QueryParam("sessionId") @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN) String sessionId,
-            @QueryParam("timestamp") Long timestampAsLong,
-            @QueryParam("invalidateProfile") boolean invalidateProfile, @QueryParam("invalidateSession") boolean invalidateSession) {
+            @QueryParam("timestamp") Long timestampAsLong, @QueryParam("invalidateProfile") boolean invalidateProfile,
+            @QueryParam("invalidateSession") boolean invalidateSession) {
         return contextJSONAsPost(contextRequest, personaId, sessionId, timestampAsLong, invalidateProfile, invalidateSession);
     }
 
@@ -155,8 +156,8 @@
     public ContextResponse contextJSONAsPost(@Valid ContextRequest contextRequest,
             @QueryParam("personaId") @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN) String personaId,
             @QueryParam("sessionId") @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN) String sessionId,
-            @QueryParam("timestamp") Long timestampAsLong,
-            @QueryParam("invalidateProfile") boolean invalidateProfile, @QueryParam("invalidateSession") boolean invalidateSession) {
+            @QueryParam("timestamp") Long timestampAsLong, @QueryParam("invalidateProfile") boolean invalidateProfile,
+            @QueryParam("invalidateSession") boolean invalidateSession) {
         Date timestamp = new Date();
         if (timestampAsLong != null) {
             timestamp = new Date(timestampAsLong);
@@ -191,7 +192,7 @@
         }
         if (profileId == null) {
             // Get profile id from the cookie
-            profileId = ServletCommon.getProfileIdCookieValue(request, (String) configSharingService.getProperty("profileIdCookieName"));
+            profileId = restServiceUtils.getProfileIdCookieValue(request);
         }
 
         if (profileId == null && sessionId == null && personaId == null) {
@@ -364,8 +365,7 @@
 
     private Changes handleRequest(ContextRequest contextRequest, Session session, Profile profile, ContextResponse data,
             ServletRequest request, ServletResponse response, Date timestamp) {
-        Changes changes = ServletCommon
-                .handleEvents(contextRequest.getEvents(), session, profile, request, response, timestamp, privacyService, eventService);
+        Changes changes = restServiceUtils.handleEvents(contextRequest.getEvents(), session, profile, request, response, timestamp);
         data.setProcessedEvents(changes.getProcessedItems());
 
         profile = changes.getProfile();
diff --git a/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
index c77b2fe..3a905f0 100644
--- a/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
@@ -28,8 +28,8 @@
 import org.apache.unomi.api.services.EventService;
 import org.apache.unomi.api.services.PrivacyService;
 import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.rest.service.RestServiceUtils;
 import org.apache.unomi.utils.Changes;
-import org.apache.unomi.utils.ServletCommon;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
@@ -71,6 +71,8 @@
     private PrivacyService privacyService;
     @Reference
     private ConfigSharingService configSharingService;
+    @Reference
+    private RestServiceUtils restServiceUtils;
 
     @Context
     HttpServletRequest request;
@@ -128,8 +130,7 @@
                 }
             }
             logger.debug("scope is now {}", scope);
-            String cookieProfileId = ServletCommon
-                    .getProfileIdCookieValue(request, (String) configSharingService.getProperty("profileIdCookieName"));
+            String cookieProfileId = restServiceUtils.getProfileIdCookieValue(request);
             if (StringUtils.isNotBlank(cookieProfileId)) {
                 profile = profileService.load(cookieProfileId);
             }
@@ -160,8 +161,7 @@
                 }
             } else {
                 // Session uses anonymous profile, try to find profile from cookie
-                String cookieProfileId = ServletCommon
-                        .getProfileIdCookieValue(request, (String) configSharingService.getProperty("profileIdCookieName"));
+                String cookieProfileId = restServiceUtils.getProfileIdCookieValue(request);
                 if (StringUtils.isNotBlank(cookieProfileId)) {
                     profile = profileService.load(cookieProfileId);
                 }
@@ -173,9 +173,8 @@
             }
         }
 
-        Changes changesObject = ServletCommon
-                .handleEvents(eventsCollectorRequest.getEvents(), session, profile, request, response, timestamp, privacyService,
-                        eventService);
+        Changes changesObject = restServiceUtils
+                .handleEvents(eventsCollectorRequest.getEvents(), session, profile, request, response, timestamp);
         int changes = changesObject.getChangeType();
         profile = changesObject.getProfile();
 
diff --git a/rest/src/main/java/org/apache/unomi/rest/RestServer.java b/rest/src/main/java/org/apache/unomi/rest/RestServer.java
index 33a5508..2403267 100644
--- a/rest/src/main/java/org/apache/unomi/rest/RestServer.java
+++ b/rest/src/main/java/org/apache/unomi/rest/RestServer.java
@@ -26,13 +26,11 @@
 import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor;
 import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationOutInterceptor;
 import org.apache.cxf.jaxrs.validation.ValidationExceptionMapper;
-import org.apache.cxf.validation.BeanValidationProvider;
-import org.apache.unomi.rest.validation.HibernateValidationProviderResolver;
-import org.apache.unomi.rest.validation.JAXRSBeanValidationInInterceptorOverride;
 import org.apache.unomi.rest.authentication.AuthenticationFilter;
 import org.apache.unomi.rest.authentication.AuthorizingInterceptor;
 import org.apache.unomi.rest.authentication.RestAuthenticationConfig;
-import org.hibernate.validator.HibernateValidator;
+import org.apache.unomi.rest.validation.JAXRSBeanValidationInInterceptorOverride;
+import org.apache.unomi.rest.validation.LocalBeanValidationProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Filter;
 import org.osgi.framework.ServiceReference;
@@ -71,7 +69,7 @@
     private long timeOfLastUpdate = System.currentTimeMillis();
     private Timer refreshTimer = null;
     private long startupDelay = 1000L;
-    private BeanValidationProvider beanValidationProvider;
+    private LocalBeanValidationProvider localBeanValidationProvider;
 
     final List<Object> serviceBeans = new CopyOnWriteArrayList<>();
 
@@ -94,6 +92,11 @@
         refreshServer();
     }
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    public void setLocalBeanValidationProvider(LocalBeanValidationProvider localBeanValidationProvider) {
+        this.localBeanValidationProvider = localBeanValidationProvider;
+    }
+
     public void removeExceptionMapper(ExceptionMapper exceptionMapper) {
         this.exceptionMappers.remove(exceptionMapper);
         timeOfLastUpdate = System.currentTimeMillis();
@@ -104,17 +107,6 @@
     public void activate(ComponentContext componentContext) throws Exception {
         this.bundleContext = componentContext.getBundleContext();
 
-        // This is a TCCL (Thread context class loader) hack to for the javax.el.FactoryFinder to use Class.forName(className)
-        // instead of tccl.loadClass(className) to load the class "com.sun.el.ExpressionFactoryImpl".
-        ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(null);
-            HibernateValidationProviderResolver validationProviderResolver = new HibernateValidationProviderResolver();
-            this.beanValidationProvider = new BeanValidationProvider(validationProviderResolver, HibernateValidator.class);
-        } finally {
-            Thread.currentThread().setContextClassLoader(currentContextClassLoader);
-        }
-
         Filter filter = bundleContext.createFilter("(osgi.jaxrs.resource=true)");
         jaxRSServiceTracker = new ServiceTracker(bundleContext, filter, new ServiceTrackerCustomizer() {
             @Override
@@ -224,10 +216,10 @@
         // Hibernate validator config
         jaxrsServerFactoryBean.setProvider(new ValidationExceptionMapper());
         JAXRSBeanValidationInInterceptor inInterceptor = new JAXRSBeanValidationInInterceptorOverride();
-        inInterceptor.setProvider(beanValidationProvider);
+        inInterceptor.setProvider(localBeanValidationProvider.get());
         jaxrsServerFactoryBean.setInInterceptors(Collections.singletonList(inInterceptor));
         JAXRSBeanValidationOutInterceptor outInterceptor = new JAXRSBeanValidationOutInterceptor();
-        outInterceptor.setProvider(beanValidationProvider);
+        outInterceptor.setProvider(localBeanValidationProvider.get());
         jaxrsServerFactoryBean.setOutInterceptors(Collections.singletonList(outInterceptor));
 
         // Register service beans (end points)
diff --git a/rest/src/main/java/org/apache/unomi/rest/service/RestServiceUtils.java b/rest/src/main/java/org/apache/unomi/rest/service/RestServiceUtils.java
new file mode 100644
index 0000000..e64db14
--- /dev/null
+++ b/rest/src/main/java/org/apache/unomi/rest/service/RestServiceUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.rest.service;
+
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.Session;
+import org.apache.unomi.utils.Changes;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.List;
+
+public interface RestServiceUtils {
+    String getProfileIdCookieValue(HttpServletRequest httpServletRequest);
+
+    Changes handleEvents(List<Event> events, Session session, Profile profile, ServletRequest request, ServletResponse response,
+            Date timestamp);
+}
diff --git a/rest/src/main/java/org/apache/unomi/utils/ServletCommon.java b/rest/src/main/java/org/apache/unomi/rest/service/impl/RestServiceUtilsImpl.java
similarity index 69%
rename from rest/src/main/java/org/apache/unomi/utils/ServletCommon.java
rename to rest/src/main/java/org/apache/unomi/rest/service/impl/RestServiceUtilsImpl.java
index 98e70a7..bee287b 100644
--- a/rest/src/main/java/org/apache/unomi/utils/ServletCommon.java
+++ b/rest/src/main/java/org/apache/unomi/rest/service/impl/RestServiceUtilsImpl.java
@@ -14,15 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.unomi.utils;
+package org.apache.unomi.rest.service.impl;
 
 import org.apache.unomi.api.Event;
 import org.apache.unomi.api.Persona;
 import org.apache.unomi.api.Profile;
 import org.apache.unomi.api.Session;
+import org.apache.unomi.api.services.ConfigSharingService;
 import org.apache.unomi.api.services.EventService;
 import org.apache.unomi.api.services.PrivacyService;
+import org.apache.unomi.rest.service.RestServiceUtils;
+import org.apache.unomi.rest.validation.LocalBeanValidationProvider;
+import org.apache.unomi.rest.validation.cookies.CookieWrapper;
+import org.apache.unomi.utils.Changes;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,39 +39,46 @@
 import java.util.Date;
 import java.util.List;
 
-/**
- * // This is duplicate of the class from the wab bundle, the original file will be removed once endpoints forwarded
- *
- * @author dgaillard
- */
-public class ServletCommon {
-    private static final Logger logger = LoggerFactory.getLogger(ServletCommon.class.getName());
+@Component(service = RestServiceUtils.class)
+public class RestServiceUtilsImpl implements RestServiceUtils {
 
+    private static final Logger logger = LoggerFactory.getLogger(RestServiceUtilsImpl.class.getName());
 
-    public static String getProfileIdCookieValue(HttpServletRequest httpServletRequest, String profileIdCookieName) {
+    @Reference
+    private ConfigSharingService configSharingService;
+
+    @Reference
+    private LocalBeanValidationProvider localBeanValidationProvider;
+
+    @Reference
+    private PrivacyService privacyService;
+
+    @Reference
+    private EventService eventService;
+
+    public String getProfileIdCookieValue(HttpServletRequest httpServletRequest) {
         String cookieProfileId = null;
 
         Cookie[] cookies = httpServletRequest.getCookies();
+
         if (cookies != null) {
             for (Cookie cookie : cookies) {
-                if (profileIdCookieName.equals(cookie.getName())) {
+                if (configSharingService.getProperty("profileIdCookieName").equals(cookie.getName())) {
+                    localBeanValidationProvider.get().validateBean(new CookieWrapper(cookie.getValue()));
                     cookieProfileId = cookie.getValue();
-                    // TODO : Replace this header protection by a more global one while working on input validation
-                    cookieProfileId = cookieProfileId.replaceAll("\\n\\r","");
                 }
             }
         }
-
         return cookieProfileId;
     }
 
-    public static Changes handleEvents(List<Event> events, Session session, Profile profile,
-                                       ServletRequest request, ServletResponse response, Date timestamp,
-                                       PrivacyService privacyService, EventService eventService) {
+    @Override
+    public Changes handleEvents(List<Event> events, Session session, Profile profile, ServletRequest request, ServletResponse response,
+            Date timestamp) {
         List<String> filteredEventTypes = privacyService.getFilteredEventTypes(profile);
 
-        String thirdPartyId = eventService.authenticateThirdPartyServer(((HttpServletRequest) request).getHeader("X-Unomi-Peer"),
-                request.getRemoteAddr());
+        String thirdPartyId = eventService
+                .authenticateThirdPartyServer(((HttpServletRequest) request).getHeader("X-Unomi-Peer"), request.getRemoteAddr());
 
         int changes = EventService.NO_CHANGE;
         // execute provided events if any
@@ -86,7 +99,8 @@
                         continue;
                     }
                     if (thirdPartyId != null && event.getItemId() != null) {
-                        eventToSend = new Event(event.getItemId(), event.getEventType(), session, profile, event.getSourceId(), event.getSource(), event.getTarget(), event.getProperties(), timestamp, event.isPersistent());
+                        eventToSend = new Event(event.getItemId(), event.getEventType(), session, profile, event.getSourceId(),
+                                event.getSource(), event.getTarget(), event.getProperties(), timestamp, event.isPersistent());
                     }
                     if (filteredEventTypes != null && filteredEventTypes.contains(event.getEventType())) {
                         logger.debug("Profile is filtering event type {}", event.getEventType());
@@ -99,8 +113,8 @@
 
                     eventToSend.getAttributes().put(Event.HTTP_REQUEST_ATTRIBUTE, request);
                     eventToSend.getAttributes().put(Event.HTTP_RESPONSE_ATTRIBUTE, response);
-                    logger.debug("Received event " + event.getEventType() + " for profile=" + profile.getItemId() + " session="
-                            + (session != null ? session.getItemId() : null) + " target=" + event.getTarget() + " timestamp=" + timestamp);
+                    logger.debug("Received event " + event.getEventType() + " for profile=" + profile.getItemId() + " session=" + (
+                            session != null ? session.getItemId() : null) + " target=" + event.getTarget() + " timestamp=" + timestamp);
                     changes |= eventService.send(eventToSend);
                     // If the event execution changes the profile we need to update it so the next event use the right profile
                     if ((changes & EventService.PROFILE_UPDATED) == EventService.PROFILE_UPDATED) {
@@ -118,4 +132,3 @@
         return new Changes(changes, processedEventsCnt, profile);
     }
 }
-
diff --git a/rest/src/main/java/org/apache/unomi/rest/validation/LocalBeanValidationProvider.java b/rest/src/main/java/org/apache/unomi/rest/validation/LocalBeanValidationProvider.java
new file mode 100644
index 0000000..5785142
--- /dev/null
+++ b/rest/src/main/java/org/apache/unomi/rest/validation/LocalBeanValidationProvider.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.rest.validation;
+
+import org.apache.cxf.validation.BeanValidationProvider;
+
+public interface LocalBeanValidationProvider {
+
+    BeanValidationProvider get();
+}
diff --git a/rest/src/main/java/org/apache/unomi/rest/validation/cookies/CookieWrapper.java b/rest/src/main/java/org/apache/unomi/rest/validation/cookies/CookieWrapper.java
new file mode 100644
index 0000000..87cad89
--- /dev/null
+++ b/rest/src/main/java/org/apache/unomi/rest/validation/cookies/CookieWrapper.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.rest.validation.cookies;
+
+import org.apache.unomi.api.utils.ValidationPattern;
+
+import javax.validation.constraints.Pattern;
+
+/**
+ * This class exists to allow to wrap a cookie value into an object and validate this object trough
+ * the bean validation
+ */
+public class CookieWrapper {
+
+    @Pattern(regexp = ValidationPattern.TEXT_VALID_CHARACTERS_PATTERN)
+    private String cookie;
+
+    public CookieWrapper(String cookie) {
+        this.cookie = cookie;
+    }
+
+}
diff --git a/rest/src/main/java/org/apache/unomi/rest/validation/impl/LocalBeanValidationProviderImpl.java b/rest/src/main/java/org/apache/unomi/rest/validation/impl/LocalBeanValidationProviderImpl.java
new file mode 100644
index 0000000..7362f5a
--- /dev/null
+++ b/rest/src/main/java/org/apache/unomi/rest/validation/impl/LocalBeanValidationProviderImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.rest.validation.impl;
+
+import org.apache.cxf.validation.BeanValidationProvider;
+import org.apache.unomi.rest.validation.HibernateValidationProviderResolver;
+import org.apache.unomi.rest.validation.LocalBeanValidationProvider;
+import org.hibernate.validator.HibernateValidator;
+import org.osgi.service.component.annotations.Component;
+
+@Component(service = LocalBeanValidationProvider.class)
+public class LocalBeanValidationProviderImpl implements LocalBeanValidationProvider {
+    private BeanValidationProvider beanValidationProvider;
+
+    public LocalBeanValidationProviderImpl() {
+        // This is a TCCL (Thread context class loader) hack to for the javax.el.FactoryFinder to use Class.forName(className)
+        // instead of tccl.loadClass(className) to load the class "com.sun.el.ExpressionFactoryImpl".
+        ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(null);
+            HibernateValidationProviderResolver validationProviderResolver = new HibernateValidationProviderResolver();
+            this.beanValidationProvider = new BeanValidationProvider(validationProviderResolver, HibernateValidator.class);
+        } finally {
+            Thread.currentThread().setContextClassLoader(currentContextClassLoader);
+        }
+    }
+
+    public BeanValidationProvider get() {
+        return beanValidationProvider;
+    }
+}