diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java
new file mode 100644
index 00000000..ba057450
--- /dev/null
+++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 GeoSolutions S.A.S.
+ * http://www.geo-solutions.it
+ *
+ * GPLv3 + Classpath exception
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package it.geosolutions.geostore.core.dao.ldap.impl;
+
+import java.util.List;
+import javax.naming.directory.DirContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.ldap.control.SortControlDirContextProcessor;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.ldap.core.DirContextProcessor;
+import org.springframework.ldap.core.LdapTemplate;
+import com.googlecode.genericdao.search.Filter;
+import com.googlecode.genericdao.search.ISearch;
+import it.geosolutions.geostore.core.dao.ldap.impl.LdapBaseDAOImpl.NullDirContextProcessor;
+
+public abstract class LdapBaseDAOImpl {
+
+ public static final class NullDirContextProcessor implements DirContextProcessor {
+ public void postProcess(DirContext ctx) {
+ // Do nothing
+ }
+
+ public void preProcess(DirContext ctx) {
+ // Do nothing
+ }
+ }
+
+ protected String searchBase = "";
+ protected String baseFilter = "cn=*";
+ protected String nameAttribute = "cn";
+ protected String descriptionAttribute = "description";
+ protected boolean sortEnabled = false;
+
+ protected ContextSource contextSource;
+ protected LdapTemplate template;
+
+ public LdapBaseDAOImpl(ContextSource contextSource) {
+ this.contextSource = contextSource;
+ template = new LdapTemplate(contextSource);
+ }
+
+ public String getSearchBase() {
+ return searchBase;
+ }
+
+ public void setSearchBase(String searchBase) {
+ this.searchBase = searchBase;
+ }
+
+ public String getFilter() {
+ return baseFilter;
+ }
+
+ public void setFilter(String filter) {
+ this.baseFilter = filter;
+ }
+
+ public String getNameAttribute() {
+ return nameAttribute;
+ }
+
+ public void setNameAttribute(String nameAttribute) {
+ this.nameAttribute = nameAttribute;
+ }
+
+ public String getDescriptionAttribute() {
+ return descriptionAttribute;
+ }
+
+ public void setDescriptionAttribute(String descriptionAttribute) {
+ this.descriptionAttribute = descriptionAttribute;
+ }
+
+
+ protected DirContextProcessor getProcessorForSearch(ISearch search) {
+ if (sortEnabled && search.getSorts() != null && search.getSorts().size() == 1) {
+ return new SortControlDirContextProcessor(nameAttribute);
+ }
+ return new NullDirContextProcessor();
+ }
+
+ public boolean isSortEnabled() {
+ return sortEnabled;
+ }
+
+ public void setSortEnabled(boolean sortEnabled) {
+ this.sortEnabled = sortEnabled;
+ }
+
+ protected String combineFilters(String baseFilter, String ldapFilter) {
+ if ("".equals(baseFilter)) {
+ return ldapFilter;
+ }
+ if ("".equals(ldapFilter)) {
+ return baseFilter;
+ }
+ return "(& ("+baseFilter+") ("+ldapFilter+"))";
+ }
+
+ protected String getLdapFilter(ISearch search) {
+ String currentFilter = "";
+ for (Filter filter : search.getFilters()) {
+ currentFilter = combineFilters(currentFilter, getLdapFilter(filter));
+ }
+ return currentFilter;
+ }
+
+ private String getLdapFilter(Filter filter) {
+ switch(filter.getOperator()) {
+ case Filter.OP_EQUAL:
+ return filter.getProperty() + "=" + filter.getValue().toString();
+ //TODO: implement all operators
+ }
+ return "";
+ }
+
+ protected Expression getSearchExpression(List filters) {
+ String expression = "";
+ for (Filter filter: filters) {
+ expression = combineExpressions(expression, getSearchExpression(filter));
+ }
+ if ("".equals(expression)) {
+ expression = "true";
+ }
+ ExpressionParser parser = new SpelExpressionParser();
+ return parser.parseExpression(expression);
+ }
+
+ protected String combineExpressions(String expression, String searchExpression) {
+ if ("".equals(expression)) {
+ return searchExpression;
+ }
+ if ("".equals(searchExpression)) {
+ return expression;
+ }
+ return "("+expression+") && ("+searchExpression+")";
+ }
+
+ private String getSearchExpression(Filter filter) {
+ switch(filter.getOperator()) {
+ case Filter.OP_EQUAL:
+ return filter.getProperty() + "==" + filter.getValue().toString();
+ //TODO: implement all operators
+ }
+ return "";
+ }
+
+}
diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java
new file mode 100644
index 00000000..7da0193e
--- /dev/null
+++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2019 GeoSolutions S.A.S.
+ * http://www.geo-solutions.it
+ *
+ * GPLv3 + Classpath exception
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package it.geosolutions.geostore.core.dao.ldap.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.naming.directory.SearchControls;
+import org.apache.log4j.Logger;
+import org.springframework.ldap.control.SortControlDirContextProcessor;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DirContextProcessor;
+import org.springframework.ldap.core.support.AbstractContextMapper;
+import org.springframework.transaction.annotation.Transactional;
+import com.googlecode.genericdao.search.ISearch;
+import it.geosolutions.geostore.core.dao.UserDAO;
+import it.geosolutions.geostore.core.model.User;
+import it.geosolutions.geostore.core.model.UserAttribute;
+
+/**
+ * Class UserDAOImpl.
+ *
+ * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it)
+ * @author ETj (etj at geo-solutions.it)
+ */
+@Transactional(value = "geostoreTransactionManager")
+public class UserDAOImpl extends LdapBaseDAOImpl implements UserDAO {
+
+ private static final Logger LOGGER = Logger.getLogger(UserDAOImpl.class);
+
+ protected Map attributesMapper = new HashMap();
+
+ public UserDAOImpl(ContextSource contextSource) {
+ super(contextSource);
+ }
+
+ public Map getAttributesMapper() {
+ return attributesMapper;
+ }
+
+ public void setAttributesMapper(Map attributesMapper) {
+ this.attributesMapper = attributesMapper;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[])
+ */
+ @Override
+ public void persist(User... entities) {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#findAll()
+ */
+ @Override
+ public List findAll() {
+ return ldapSearch(baseFilter, new NullDirContextProcessor());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List search(ISearch search) {
+ return ldapSearch(combineFilters(baseFilter, getLdapFilter(search)), getProcessorForSearch(search));
+ }
+
+ protected List ldapSearch(String filter, DirContextProcessor processor) {
+ SearchControls controls = new SearchControls();
+ controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ return template.search(searchBase, filter, controls, new AbstractContextMapper() {
+ int counter = 1;
+ @Override
+ protected User doMapFromContext(DirContextOperations ctx) {
+ User user = new User();
+ user.setId((long)counter++);
+ user.setEnabled(true);
+ user.setName(ctx.getStringAttribute(nameAttribute));
+ List attributes = new ArrayList();
+ for (String ldapAttr : attributesMapper.keySet()) {
+ String value = ctx.getStringAttribute(ldapAttr);
+ String userAttr = attributesMapper.get(ldapAttr);
+ UserAttribute attr = new UserAttribute();
+ attr.setName(userAttr);
+ attr.setValue(value);
+ }
+ user.setAttribute(attributes);
+ return user;
+ }
+
+ }, processor);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object)
+ */
+ @Override
+ public User merge(User entity) {
+ return entity;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object)
+ */
+ @Override
+ public boolean remove(User entity) {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable)
+ */
+ @Override
+ public boolean removeById(Long id) {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable)
+ */
+ @Override
+ public User find(Long id) {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#save(T[])
+ */
+ @Override
+ public User[] save(User... entities) {
+ return entities;
+ }
+
+ @Override
+ public int count(ISearch search) {
+ return search(search).size();
+ }
+
+}
diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java
new file mode 100644
index 00000000..56069052
--- /dev/null
+++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 GeoSolutions S.A.S.
+ * http://www.geo-solutions.it
+ *
+ * GPLv3 + Classpath exception
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package it.geosolutions.geostore.core.dao.ldap.impl;
+
+import java.util.List;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import org.apache.log4j.Logger;
+import org.springframework.expression.Expression;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DirContextProcessor;
+import org.springframework.ldap.core.support.AbstractContextMapper;
+import org.springframework.transaction.annotation.Transactional;
+import com.googlecode.genericdao.search.Filter;
+import com.googlecode.genericdao.search.ISearch;
+import it.geosolutions.geostore.core.dao.UserGroupDAO;
+import it.geosolutions.geostore.core.model.UserGroup;
+import it.geosolutions.geostore.core.model.enums.GroupReservedNames;
+
+/**
+ * Class UserGroupDAOImpl.
+ *
+ * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it)
+ * @author ETj (etj at geo-solutions.it)
+ */
+@Transactional(value = "geostoreTransactionManager")
+public class UserGroupDAOImpl extends LdapBaseDAOImpl implements UserGroupDAO {
+
+
+
+ private static final Logger LOGGER = Logger.getLogger(UserGroupDAOImpl.class);
+
+ private boolean addEveryOneGroup = false;
+
+ public UserGroupDAOImpl(ContextSource contextSource) {
+ super(contextSource);
+ }
+
+ public boolean isAddEveryOneGroup() {
+ return addEveryOneGroup;
+ }
+
+ public void setAddEveryOneGroup(boolean addEveryOneGroup) {
+ this.addEveryOneGroup = addEveryOneGroup;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[])
+ */
+ @Override
+ public void persist(UserGroup... entities) {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#findAll()
+ */
+ @Override
+ public List findAll() {
+ return addEveryOne(ldapSearch(baseFilter, new NullDirContextProcessor()), null);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable)
+ */
+ @Override
+ public UserGroup find(Long id) {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List search(ISearch search) {
+ return addEveryOne(ldapSearch(combineFilters(baseFilter, getLdapFilter(search)), getProcessorForSearch(search)), search.getFilters());
+ }
+
+ protected List ldapSearch(String filter, DirContextProcessor processor) {
+ SearchControls controls = new SearchControls();
+ controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ return template.search(searchBase, filter, controls, new AbstractContextMapper() {
+ int counter = 1;
+ @Override
+ protected UserGroup doMapFromContext(DirContextOperations ctx) {
+ UserGroup group = new UserGroup();
+ group.setId((long)counter++);
+ group.setEnabled(true);
+ group.setGroupName(ctx.getStringAttribute(nameAttribute));
+ group.setDescription(ctx.getStringAttribute(descriptionAttribute));
+
+ return group;
+ }
+
+ }, processor);
+ }
+
+ private List addEveryOne(List groups, List filters) {
+ UserGroup everyoneGroup = new UserGroup();
+ everyoneGroup.setGroupName(GroupReservedNames.EVERYONE.groupName());
+ everyoneGroup.setId((long)(groups.size() + 1));
+ everyoneGroup.setEnabled(true);
+ if (filters == null || matchFilters(everyoneGroup, filters)) {
+ boolean everyoneFound = false;
+ for (UserGroup group : groups) {
+ if (group.getGroupName().equals(everyoneGroup.getGroupName())) {
+ everyoneFound = true;
+ }
+ }
+ if (!everyoneFound && addEveryOneGroup) {
+ groups.add(everyoneGroup);
+ }
+ }
+ return groups;
+ }
+
+ protected boolean matchFilters(UserGroup group, List filters) {
+ Expression matchExpression = getSearchExpression(filters);
+ return matchExpression.getValue(group, Boolean.class);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object)
+ */
+ @Override
+ public UserGroup merge(UserGroup entity) {
+ return entity;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object)
+ */
+ @Override
+ public boolean remove(UserGroup entity) {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable)
+ */
+ @Override
+ public boolean removeById(Long id) {
+ return true;
+ }
+
+ @Override
+ public UserGroup[] save(UserGroup... entities) {
+ return entities;
+ }
+
+ @Override
+ public int count(ISearch search) {
+ return search(search).size();
+ }
+
+}
diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java
index ab5e02ba..578a6333 100644
--- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java
+++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java
@@ -37,6 +37,7 @@ public class UserGroupList implements Iterable {
private List list;
public UserGroupList() {
+ this.list = new ArrayList();
}
/**