diff --git a/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java b/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..76a9dc264de2b633facd664630a851a4db5b9a11
--- /dev/null
+++ b/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java
@@ -0,0 +1,79 @@
+package net.jami.datastore.dao;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.datastore.main.DataStore;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.dao.connectivity.SQLConnection;
+import net.jami.jams.common.objects.user.Group;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+@Slf4j
+public class GroupDao extends AbstractDao<Group>{
+
+    public GroupDao() {
+        this.setTableName("groups");
+        this.setTClass(Group.class);
+    }
+
+    @Override
+    public boolean storeObject(Group object) {
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+        try{
+            PreparedStatement ps = connection.getConnection().prepareStatement("INSERT INTO groups " +
+                    "(name) VALUES (?)");
+            ps = object.getInsert(ps);
+            return ps.executeUpdate() != 0;
+        }
+        catch (SQLException e){
+            log.error("An error has occurred while trying to store a group: " + e.toString());
+            return false;
+        }
+        finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+
+    @Override
+    public boolean updateObject(StatementList update, StatementList constraints) {
+
+        String name = update.getStatements().get(0).getValue();
+        String oldName = constraints.getStatements().get(0).getValue();
+
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+
+        try{
+            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE groups SET name = ? WHERE name = ?");
+            ps.setString(1, name);
+            ps.setString(2, oldName);
+            return ps.executeUpdate() != 0;
+        }
+        catch (SQLException e){
+            log.error("An error has occurred while trying to update a group: " + e.toString());
+            return false;
+        }
+        finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+
+    @Override
+    public boolean deleteObject(StatementList constraints) {
+
+        String name = constraints.getStatements().get(0).getValue();
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+
+        try {
+            PreparedStatement ps = connection.getConnection().prepareStatement("DELETE FROM groups WHERE name = ?");
+            ps.setString(1, name);
+            return ps.executeUpdate() != 0;
+        } catch (SQLException e){
+            log.error("An error has occurred while trying to delete a group: " + e.toString());
+            return false;
+        } finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+
+}
diff --git a/datastore/src/main/java/net/jami/datastore/dao/PolicyDao.java b/datastore/src/main/java/net/jami/datastore/dao/PolicyDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3649752a6c239a471b263b4a76befcede927da9
--- /dev/null
+++ b/datastore/src/main/java/net/jami/datastore/dao/PolicyDao.java
@@ -0,0 +1,78 @@
+package net.jami.datastore.dao;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.datastore.main.DataStore;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.dao.connectivity.SQLConnection;
+import net.jami.jams.common.objects.user.Group;
+import net.jami.jams.common.objects.user.Policy;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+@Slf4j
+public class PolicyDao extends AbstractDao<Policy> {
+
+    public PolicyDao() {
+        this.setTableName("policies");
+        this.setTClass(Policy.class);
+    }
+
+    @Override
+    public boolean storeObject(Policy object) {
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+        try {
+            PreparedStatement ps = connection.getConnection().prepareStatement("INSERT INTO policies " +
+                    "(groupName, username, policyData) VALUES (?, ?, ?)");
+            ps = object.getInsert(ps);
+            return ps.executeUpdate() != 0;
+        } catch (Exception e) {
+            log.error("An error has occurred while trying to store a blueprint: " + e.toString());
+            return false;
+        } finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+
+    @Override
+    public boolean updateObject(StatementList update, StatementList constraints) {
+        String policyData = update.getStatements().get(0).getValue();
+        String groupName = update.getStatements().get(1).getValue();
+        String username = update.getStatements().get(2).getValue();
+        String oldGroupName = constraints.getStatements().get(0).getValue();
+
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+
+        try {
+            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE policies SET groupName = ?, username = ?, policyData = ? WHERE groupName = ?");
+            ps.setString(1, groupName);
+            ps.setString(2, username);
+            ps.setString(3, policyData);
+            ps.setString(4, oldGroupName);
+            return ps.executeUpdate() != 0;
+        } catch (Exception e) {
+            log.error("An error has occurred while trying to update a blueprint: " + e.toString());
+            return false;
+        } finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+
+    @Override
+    public boolean deleteObject(StatementList constraints) {
+
+        String name = constraints.getStatements().get(0).getValue();
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+
+        try {
+            PreparedStatement ps = connection.getConnection().prepareStatement("DELETE FROM policies WHERE groupName = ?");
+            ps.setString(1, name);
+            return ps.executeUpdate() != 0;
+        } catch (SQLException e){
+            log.error("An error has occurred while trying to delete a blueprint: " + e.toString());
+            return false;
+        } finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+}
diff --git a/datastore/src/main/java/net/jami/datastore/dao/UserDao.java b/datastore/src/main/java/net/jami/datastore/dao/UserDao.java
index 632b0d0c3aacf148b7b26924ac406dab698b5199..5f1284416b98f63a048a5493c15b997b5641cd39 100644
--- a/datastore/src/main/java/net/jami/datastore/dao/UserDao.java
+++ b/datastore/src/main/java/net/jami/datastore/dao/UserDao.java
@@ -30,7 +30,6 @@ import net.jami.jams.common.dao.connectivity.SQLConnection;
 import net.jami.jams.common.objects.user.User;
 
 import java.sql.PreparedStatement;
-import java.sql.SQLException;
 
 @Slf4j
 public class UserDao extends AbstractDao<User> {
@@ -45,10 +44,8 @@ public class UserDao extends AbstractDao<User> {
         SQLConnection connection = DataStore.connectionPool.getConnection();
         try {
             PreparedStatement ps = connection.getConnection().prepareStatement("INSERT INTO users " +
-                "(username, password, userType, realm, ethAddress, ethKey, jamiId,certificate, privatekey, accessLevel," +
-                "needsPasswordReset) " +
-                "VALUES " +
-                "(?, ?, ?, ?, ?, ?, ?,?, ?,?, ?)");
+                "(username, password, userType, realm, ethAddress, ethKey, jamiId,certificate, privatekey, accessLevel, needsPasswordReset) " +
+                " VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
             ps = object.getInsert(ps);
             return ps.executeUpdate() != 0;
         } catch (Exception e) {
diff --git a/datastore/src/main/java/net/jami/datastore/dao/UserGroupMappingsDao.java b/datastore/src/main/java/net/jami/datastore/dao/UserGroupMappingsDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e576cf818e59272bd9cceeccfa37a742691810d
--- /dev/null
+++ b/datastore/src/main/java/net/jami/datastore/dao/UserGroupMappingsDao.java
@@ -0,0 +1,55 @@
+package net.jami.datastore.dao;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.datastore.main.DataStore;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.dao.connectivity.SQLConnection;
+import net.jami.jams.common.objects.user.UserGroupMapping;
+
+import java.sql.PreparedStatement;
+
+@Slf4j
+public class UserGroupMappingsDao extends AbstractDao<UserGroupMapping>{
+
+    public UserGroupMappingsDao() {
+        this.setTableName("usergroupmappings");
+        this.setTClass(UserGroupMapping.class);
+    }
+
+    @Override
+    public boolean storeObject(UserGroupMapping object) {
+
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+        try {
+            PreparedStatement ps = connection.getConnection().prepareStatement("INSERT INTO usergroupmappings " +
+                    "(username, groups)" + " VALUES " + "(?,?)");
+            ps = object.getInsert(ps);
+            return ps.executeUpdate() != 0;
+        } catch (Exception e) {
+            log.error("An error has occurred while trying to store a user profile: " + e.toString());
+            return false;
+        } finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+
+    @Override
+    public boolean updateObject(StatementList update, StatementList constraints) {
+
+        String groups = update.getStatements().get(0).getValue();
+        String username = constraints.getStatements().get(0).getValue();
+
+        SQLConnection connection = DataStore.connectionPool.getConnection();
+        try {
+            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE usergroupmappings SET groups = ? WHERE username = ?");
+            ps.setString(1, groups);
+            ps.setString(2, username);
+            return ps.executeUpdate() != 0;
+        } catch (Exception e) {
+            log.error("An error has occurred while trying to update a user profile: " + e.toString());
+            return false;
+        } finally {
+            DataStore.connectionPool.returnConnection(connection);
+        }
+    }
+}
diff --git a/datastore/src/main/java/net/jami/datastore/dao/UserProfileDao.java b/datastore/src/main/java/net/jami/datastore/dao/UserProfileDao.java
index 19e0b9fcf38dac16edb1af4af61d183fba20a857..9e278045f8786c9c7a70b5aa769bd0879949bdf0 100644
--- a/datastore/src/main/java/net/jami/datastore/dao/UserProfileDao.java
+++ b/datastore/src/main/java/net/jami/datastore/dao/UserProfileDao.java
@@ -48,8 +48,8 @@ public class UserProfileDao extends AbstractDao<UserProfile> {
         SQLConnection connection = DataStore.connectionPool.getConnection();
         try {
             PreparedStatement ps = connection.getConnection().prepareStatement("INSERT INTO local_directory " +
-                "(username, firstName, lastName, email, profilePicture, organization, phoneNumber, phoneNumberExtension, faxNumber, mobileNumber)" +
-                " VALUES " + "(?,?,?,?,?,?,?,?,?,?)");
+                "(username, firstName, lastName, email, profilePicture, organization, phoneNumber, phoneNumberExtension, faxNumber, mobileNumber, groupMemberships)" +
+                " VALUES " + "(?,?,?,?,?,?,?,?,?,?,?)");
             ps = object.getInsert(ps);
             return ps.executeUpdate() != 0;
         } catch (Exception e) {
@@ -65,7 +65,7 @@ public class UserProfileDao extends AbstractDao<UserProfile> {
 
         SQLConnection connection = DataStore.connectionPool.getConnection();
         try {
-            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE local_directory SET firstname = ?, lastName = ?, email = ?, profilePicture = ?, organization = ?, phoneNumber = ?, phoneNumberExtension = ?, faxNumber = ?, mobileNumber = ? WHERE username = ?");
+            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE local_directory SET firstname = ?, lastName = ?, email = ?, profilePicture = ?, organization = ?, phoneNumber = ?, phoneNumberExtension = ?, faxNumber = ?, mobileNumber = ?, groupMemberships = ? WHERE username = ?");
             for (int i = 1; i < update.getStatements().size(); i++) {
                 ps.setString(i, update.getStatements().get(i).getValue());
             }
diff --git a/datastore/src/main/java/net/jami/datastore/main/DataStore.java b/datastore/src/main/java/net/jami/datastore/main/DataStore.java
index e9b1dc82c09fa162be0efd0842bbec1c4446164f..a76fe543199f8b65466ba664ad2736b85e1bd0ce 100644
--- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java
+++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java
@@ -24,12 +24,7 @@ package net.jami.datastore.main;
 
 import lombok.Getter;
 import lombok.Setter;
-import net.jami.datastore.dao.ContactDao;
-import net.jami.datastore.dao.DeviceDao;
-import net.jami.datastore.dao.JwtDao;
-import net.jami.datastore.dao.SystemDao;
-import net.jami.datastore.dao.UserDao;
-import net.jami.datastore.dao.UserProfileDao;
+import net.jami.datastore.dao.*;
 import net.jami.jams.common.authentication.AuthenticationSource;
 import net.jami.jams.common.authentication.AuthenticationSourceInfo;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
@@ -40,6 +35,7 @@ import net.jami.jams.common.objects.user.User;
 import net.jami.jams.common.objects.user.UserProfile;
 import org.flywaydb.core.Flyway;
 
+import java.util.Arrays;
 import java.util.List;
 
 @Getter
@@ -48,11 +44,14 @@ public class DataStore implements AuthenticationSource {
 
     public static ConnectionPool connectionPool;
     private UserDao userDao;
+    private GroupDao groupDao;
+    private PolicyDao policyDao;
     private DeviceDao deviceDao;
     private SystemDao systemDao;
     private ContactDao contactDao;
     private JwtDao jwtDao;
     private UserProfileDao userProfileDao;
+    private UserGroupMappingsDao userGroupMappingsDao;
 
     //Implicitly connect to derby.
     public DataStore(String connectionString) {
@@ -60,11 +59,14 @@ public class DataStore implements AuthenticationSource {
         flyway.migrate();
         connectionPool = new ConnectionPool(connectionString);
         userDao = new UserDao();
+        groupDao = new GroupDao();
+        policyDao = new PolicyDao();
         deviceDao = new DeviceDao();
         systemDao = new SystemDao();
         contactDao = new ContactDao();
         jwtDao = new JwtDao();
         userProfileDao = new UserProfileDao();
+        userGroupMappingsDao = new UserGroupMappingsDao();
     }
 
     public boolean userExists(String username){
@@ -127,7 +129,16 @@ public class DataStore implements AuthenticationSource {
         update.addStatement(new StatementElement("phoneNumberExtension","=",userProfile.getPhoneNumberExtension(),""));
         update.addStatement(new StatementElement("faxNumber","=",userProfile.getFaxNumber(),""));
         update.addStatement(new StatementElement("mobileNumber","=",userProfile.getMobileNumber(),""));
-
+        if (userProfile.getGroupMemberships() != null) {
+            String groups = "";
+            for (String s: userProfile.getGroupMemberships()) {
+                if (groups.equals(""))
+                    groups = s;
+                else
+                    groups = groups.concat("," + s);
+            }
+            update.addStatement(new StatementElement("groupMemberships","=", groups ,""));
+        }
         return userProfileDao.updateObject(update, null);
     }
 
diff --git a/datastore/src/main/resources/db/migration/V13__Users.sql b/datastore/src/main/resources/db/migration/V13__Users.sql
index 983f850bd02946e1cb23446d186b21b0be712f8b..3c568931739a9de6f40a453c442521fb8480f8b0 100644
--- a/datastore/src/main/resources/db/migration/V13__Users.sql
+++ b/datastore/src/main/resources/db/migration/V13__Users.sql
@@ -4,4 +4,4 @@ realm varchar(255),ethAddress varchar(255),
 ethKey varchar(255),jamiId varchar(255),
 certificate varchar(5000),privatekey varchar(5000),
 accessLevel varchar(10),needsPasswordReset varchar(10),
-PRIMARY KEY (username));
\ No newline at end of file
+PRIMARY KEY (username));
diff --git a/datastore/src/main/resources/db/migration/V14__Userprofile.sql b/datastore/src/main/resources/db/migration/V14__Userprofile.sql
index 534bfb98df4cc4a4ec96b58c3bd1ee1d7aefcbbc..27581fef8270bc7b110ec8fc57bbe4cd194dad13 100644
--- a/datastore/src/main/resources/db/migration/V14__Userprofile.sql
+++ b/datastore/src/main/resources/db/migration/V14__Userprofile.sql
@@ -4,4 +4,5 @@ email varchar(255),profilePicture CLOB,
 organization varchar(255),phoneNumber varchar(255),
 phoneNumberExtension varchar(255),
 faxNumber varchar(255),mobileNumber varchar(255),
+groupMemberships varchar(5000),
 PRIMARY KEY (username));
\ No newline at end of file
diff --git a/datastore/src/main/resources/db/migration/V15__Group.sql b/datastore/src/main/resources/db/migration/V15__Group.sql
new file mode 100644
index 0000000000000000000000000000000000000000..1b7d92f6ea416b446436d8ecc13c194464d5ea47
--- /dev/null
+++ b/datastore/src/main/resources/db/migration/V15__Group.sql
@@ -0,0 +1,2 @@
+CREATE TABLE groups (name varchar(255),
+                     PRIMARY KEY (name));
\ No newline at end of file
diff --git a/datastore/src/main/resources/db/migration/V16__Policy.sql b/datastore/src/main/resources/db/migration/V16__Policy.sql
new file mode 100644
index 0000000000000000000000000000000000000000..e39fcab348df9de31ad9027260d2da908a852765
--- /dev/null
+++ b/datastore/src/main/resources/db/migration/V16__Policy.sql
@@ -0,0 +1,3 @@
+CREATE TABLE policies (groupName varchar(255),
+username varchar(255), policyData varchar(5000),
+PRIMARY KEY (groupName));
\ No newline at end of file
diff --git a/datastore/src/main/resources/db/migration/V17__UserGroupMappings.sql b/datastore/src/main/resources/db/migration/V17__UserGroupMappings.sql
new file mode 100644
index 0000000000000000000000000000000000000000..2bcd6e18669b3eab87546bd5e9ba9aa337fb5236
--- /dev/null
+++ b/datastore/src/main/resources/db/migration/V17__UserGroupMappings.sql
@@ -0,0 +1,3 @@
+CREATE TABLE usergroupmappings (username varchar(255),
+groups varchar(5000),
+PRIMARY KEY (username));
\ No newline at end of file
diff --git a/datastore/src/test/java/net/jami/datastore/dao/DAOTest.java b/datastore/src/test/java/net/jami/datastore/dao/DAOTest.java
index 09468ff0f0e3bb8af78f6f61deca918477bf373b..3902fa90c9bac040745a92ad6e29b74ef2dc577a 100644
--- a/datastore/src/test/java/net/jami/datastore/dao/DAOTest.java
+++ b/datastore/src/test/java/net/jami/datastore/dao/DAOTest.java
@@ -22,6 +22,7 @@
 */
 package net.jami.datastore.dao;
 
+import com.jsoniter.JsonIterator;
 import net.jami.datastore.main.DataStore;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.dao.StatementElement;
@@ -36,6 +37,7 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 import java.io.InputStream;
+import java.util.ArrayList;
 
 class DAOTest {
 
@@ -58,7 +60,7 @@ class DAOTest {
     }
 
     @Test
-    void storeUser(){
+    void storeUser() throws Exception {
         User user = new User();
         user.setUsername("fsidokhine");
         user.setUserType(AuthenticationSourceType.LOCAL);
@@ -93,7 +95,7 @@ class DAOTest {
     }
 
     @Test
-    void storeAdminUser(){
+    void storeAdminUser() throws Exception{
         User user = new User();
         user.setUsername("admin");
         user.setUserType(AuthenticationSourceType.LOCAL);
@@ -108,9 +110,4 @@ class DAOTest {
         Assertions.assertNotNull(user1);
         Assertions.assertEquals(user1.getAccessLevel(),AccessLevel.ADMIN);
     }
-
-
-
-
-
 }
\ No newline at end of file
diff --git a/datastore/src/test/resources/db/migration/V13__Users.sql b/datastore/src/test/resources/db/migration/V13__Users.sql
index 983f850bd02946e1cb23446d186b21b0be712f8b..3c568931739a9de6f40a453c442521fb8480f8b0 100644
--- a/datastore/src/test/resources/db/migration/V13__Users.sql
+++ b/datastore/src/test/resources/db/migration/V13__Users.sql
@@ -4,4 +4,4 @@ realm varchar(255),ethAddress varchar(255),
 ethKey varchar(255),jamiId varchar(255),
 certificate varchar(5000),privatekey varchar(5000),
 accessLevel varchar(10),needsPasswordReset varchar(10),
-PRIMARY KEY (username));
\ No newline at end of file
+PRIMARY KEY (username));
diff --git a/datastore/src/test/resources/db/migration/V14__Userprofile.sql b/datastore/src/test/resources/db/migration/V14__Userprofile.sql
index 534bfb98df4cc4a4ec96b58c3bd1ee1d7aefcbbc..27581fef8270bc7b110ec8fc57bbe4cd194dad13 100644
--- a/datastore/src/test/resources/db/migration/V14__Userprofile.sql
+++ b/datastore/src/test/resources/db/migration/V14__Userprofile.sql
@@ -4,4 +4,5 @@ email varchar(255),profilePicture CLOB,
 organization varchar(255),phoneNumber varchar(255),
 phoneNumberExtension varchar(255),
 faxNumber varchar(255),mobileNumber varchar(255),
+groupMemberships varchar(5000),
 PRIMARY KEY (username));
\ No newline at end of file
diff --git a/datastore/src/test/resources/db/migration/V15__Group.sql b/datastore/src/test/resources/db/migration/V15__Group.sql
new file mode 100644
index 0000000000000000000000000000000000000000..1b7d92f6ea416b446436d8ecc13c194464d5ea47
--- /dev/null
+++ b/datastore/src/test/resources/db/migration/V15__Group.sql
@@ -0,0 +1,2 @@
+CREATE TABLE groups (name varchar(255),
+                     PRIMARY KEY (name));
\ No newline at end of file
diff --git a/datastore/src/test/resources/db/migration/V16__Policy.sql b/datastore/src/test/resources/db/migration/V16__Policy.sql
new file mode 100644
index 0000000000000000000000000000000000000000..1bf348d2975f653ace6693434d73806fc91b400f
--- /dev/null
+++ b/datastore/src/test/resources/db/migration/V16__Policy.sql
@@ -0,0 +1,3 @@
+CREATE TABLE policies (groupName bigint,
+userid bigint,
+PRIMARY KEY (groupName,userid));
\ No newline at end of file
diff --git a/datastore/src/test/resources/db/migration/V17__UserGroupMappings.sql b/datastore/src/test/resources/db/migration/V17__UserGroupMappings.sql
new file mode 100644
index 0000000000000000000000000000000000000000..d9b8deb1d998a7288617435c88902c52d1ed2817
--- /dev/null
+++ b/datastore/src/test/resources/db/migration/V17__UserGroupMappings.sql
@@ -0,0 +1,3 @@
+CREATE TABLE usergroupmappings (username varchar(255),
+groupMemberships CLOB,
+PRIMARY KEY (username));
\ No newline at end of file
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java
new file mode 100644
index 0000000000000000000000000000000000000000..42e6c69971e4604030cfb6beaeed000b68e54882
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java
@@ -0,0 +1,46 @@
+package net.jami.jams.common.objects.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.objects.roots.X509Entity;
+import net.jami.jams.common.serialization.database.DatabaseObject;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class Group implements DatabaseObject {
+    private String name;
+    private List<String> groupMembers;
+
+    public Group(ResultSet rs) throws SQLException {
+        this.name = rs.getString("name");
+        this.groupMembers = new ArrayList<>();
+    }
+
+    @Override
+    public PreparedStatement getInsert(PreparedStatement ps) throws SQLException {
+        ps.setString(1, name);
+        return ps;
+    }
+
+    @Override
+    public PreparedStatement getDelete(PreparedStatement ps) throws Exception {
+        return null;
+    }
+
+    @Override
+    public PreparedStatement getUpdate(PreparedStatement ps) throws Exception {
+        return null;
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/Policy.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/Policy.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e0b349c1813181750f3d2c0cead953b0e2f8f6c
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/Policy.java
@@ -0,0 +1,49 @@
+package net.jami.jams.common.objects.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import net.jami.jams.common.objects.roots.X509Entity;
+import net.jami.jams.common.serialization.database.DatabaseObject;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+import net.minidev.json.parser.JSONParser;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class Policy extends X509Entity implements DatabaseObject {
+
+    private String groupName;
+    private String username;
+    private String policyData;
+
+    public Policy(ResultSet rs) throws Exception {
+        this.groupName = rs.getString("groupName");
+        this.username = rs.getString("userId");
+        this.policyData = rs.getString("policyData");
+    }
+
+    @Override
+    public PreparedStatement getInsert(PreparedStatement ps) throws Exception {
+        ps.setString(1, username);
+        ps.setString(2, groupName);
+        ps.setString(3, policyData);
+        return ps;
+    }
+
+    @Override
+    public PreparedStatement getDelete(PreparedStatement ps) throws Exception {
+        return null;
+    }
+
+    @Override
+    public PreparedStatement getUpdate(PreparedStatement ps) throws Exception {
+        return null;
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java
index b7404ce215450c30fbe4fc54e94fa6a54b6a71ff..0fe93e3d8daad24b3aa149371a8fc3622d42701a 100644
--- a/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java
@@ -31,7 +31,6 @@ import net.jami.jams.common.objects.roots.BlockchainEntity;
 import net.jami.jams.common.objects.roots.X509Entity;
 import net.jami.jams.common.serialization.database.DatabaseObject;
 import net.jami.jams.common.utils.X509Utils;
-
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserGroupMapping.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserGroupMapping.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8814e25056173a4457e0fdd11161ada27a01589
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserGroupMapping.java
@@ -0,0 +1,55 @@
+package net.jami.jams.common.objects.user;
+
+import com.jsoniter.JsonIterator;
+import com.jsoniter.output.JsonStream;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import net.jami.jams.common.serialization.database.DatabaseObject;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Arrays;
+import java.util.List;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class UserGroupMapping implements DatabaseObject {
+
+    private String username;
+    private String groups;
+
+    public UserGroupMapping(ResultSet rs) throws Exception {
+        this.username = rs.getString("username");
+        this.groups = rs.getString("groups");
+    }
+
+    public void addGroup(String s) {
+        if (this.groups.equals(""))
+            this.groups = s;
+        else
+            this.groups = this.groups.concat("," + s);
+    }
+
+    @Override
+    public PreparedStatement getInsert(PreparedStatement ps) throws Exception {
+        ps.setString(1, username);
+        ps.setString(2, groups);
+        return ps;
+    }
+
+    @Override
+    public PreparedStatement getDelete(PreparedStatement ps) throws Exception {
+        return null;
+    }
+
+    @Override
+    public PreparedStatement getUpdate(PreparedStatement ps) throws Exception {
+        return null;
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
index 4161dcf4553b05193a5a499107430ab629337257..83833684912f5741a79fd71f4e9fa45af111dfbf 100644
--- a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
@@ -22,7 +22,9 @@
 */
 package net.jami.jams.common.objects.user;
 
+import com.jsoniter.JsonIterator;
 import com.jsoniter.annotation.JsonIgnore;
+import com.jsoniter.output.JsonStream;
 import ezvcard.Ezvcard;
 import ezvcard.VCard;
 import ezvcard.VCardVersion;
@@ -43,7 +45,10 @@ import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 
 @AllArgsConstructor
 @NoArgsConstructor
@@ -71,6 +76,7 @@ public class UserProfile implements DatabaseObject {
     private String faxNumber;
     private String mobileNumber;
     private String jamiId;
+    private List<String> groupMemberships;
 
     public UserProfile(ResultSet rs) throws Exception {
         this.username = rs.getString("username");
@@ -83,6 +89,7 @@ public class UserProfile implements DatabaseObject {
         this.phoneNumberExtension = rs.getString("phoneNumberExtension");
         this.faxNumber = rs.getString("faxNumber");
         this.mobileNumber = rs.getString("mobileNumber");
+        this.groupMemberships = Arrays.asList(JsonIterator.deserialize(rs.getString("groupMemberships"),String[].class));
     }
 
     @JsonIgnore
@@ -124,6 +131,9 @@ public class UserProfile implements DatabaseObject {
         ps.setString(8, phoneNumberExtension);
         ps.setString(9, faxNumber);
         ps.setString(10, mobileNumber);
+        if (groupMemberships == null)
+            groupMemberships = new ArrayList<>();
+        ps.setString(11, JsonStream.serialize(groupMemberships));
         return ps;
     }
 
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java
new file mode 100644
index 0000000000000000000000000000000000000000..438b27d825c98297487cb3b37e7159fb85cf0970
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java
@@ -0,0 +1,75 @@
+package net.jami.jams.server.core.workflows;
+
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.dao.StatementElement;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.user.Group;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserGroupMapping;
+import net.jami.jams.common.objects.user.UserProfile;
+import net.minidev.json.JSONArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static net.jami.jams.server.Server.dataStore;
+import static net.jami.jams.server.Server.userAuthenticationModule;
+
+@Slf4j
+public class AddUserToGroupFlow {
+
+    public static void addUserToGroup(String groupName, String username) {
+
+        try {
+
+            userAuthenticationModule.getAuthSources().forEach((k, v) -> {
+                        List<UserProfile> profiles = v.searchUserProfiles(username, "LOGON_NAME");
+                        if (!profiles.isEmpty()) {
+                            UserProfile profile = profiles.get(0);
+                            StatementList statementList = new StatementList();
+                            statementList.addStatement(new StatementElement("name", "=", username, ""));
+
+                            if (profile != null) {
+                                if (profile.getGroupMemberships() == null)
+                                    profile.setGroupMemberships(new ArrayList<>());
+                                profile.getGroupMemberships().add(groupName);
+                            }
+
+                            statementList = new StatementList();
+                            statementList.addStatement(new StatementElement("username", "=", username, ""));
+                            if (dataStore.getUserGroupMappingsDao().getObjects(statementList).isEmpty()) {
+                                // if the mapping doesn't exist, create it and add the group directly.
+                                UserGroupMapping newMapping = new UserGroupMapping();
+                                newMapping.setUsername(username);
+                                newMapping.setGroups("");
+                                newMapping.addGroup(groupName);
+                                dataStore.getUserGroupMappingsDao().storeObject(newMapping);
+                            } else {
+                                // otherwise, update the object.
+
+                                UserGroupMapping mapping = dataStore.getUserGroupMappingsDao().getObjects(statementList).get(0);
+                                mapping.addGroup(groupName);
+                                String newGroups = mapping.getGroups();
+
+                                StatementList update = new StatementList();
+                                StatementElement st0 = new StatementElement("groups","=", newGroups,"");
+                                update.addStatement(st0);
+                                StatementList constraint = new StatementList();
+                                StatementElement st1 = new StatementElement("username","=",username,"");
+                                constraint.addStatement(st1);
+                                dataStore.getUserGroupMappingsDao().updateObject(update, constraint);
+                            }
+                        }
+            });
+
+
+
+
+
+        } catch (Exception e) {
+            log.error("An error occurred while attempting to add " + username + " to group with name " + groupName);
+        }
+
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..c693ceb1a2e8dec7653704622d42124698f2012e
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java
@@ -0,0 +1,123 @@
+package net.jami.jams.server.servlets.api.admin.group;
+
+import com.jsoniter.output.JsonStream;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
+import net.jami.jams.common.dao.StatementElement;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.user.AccessLevel;
+import net.jami.jams.common.objects.user.Group;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserProfile;
+import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler;
+import net.jami.jams.server.core.workflows.AddUserToGroupFlow;
+import net.jami.jams.server.core.workflows.RegisterDeviceFlow;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static net.jami.jams.server.Server.dataStore;
+
+@WebServlet("/api/admin/group")
+public class GroupServlet extends HttpServlet {
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+        List<Group> groups = new ArrayList<>();
+        Group singleGroup = null;
+
+        if (!req.getParameter("groupName").equals("*")) {
+            StatementList statementList = new StatementList();
+            StatementElement st = new StatementElement("name", "=", req.getParameter("groupName"), "");
+
+            statementList.addStatement(st);
+            singleGroup = dataStore.getGroupDao().getObjects(statementList).get(0);
+        } else {
+            groups = dataStore.getGroupDao().getObjects(null);
+        }
+
+        if (singleGroup != null) {
+            Group finalSingleGroup = singleGroup;
+            List<UserProfile> profiles = dataStore.getUserProfileDao().getObjects(null).stream().filter(profile ->
+                    profile.getGroupMemberships().contains(finalSingleGroup.getName())).collect(Collectors.toList());
+
+            profiles.forEach(profile ->
+                    finalSingleGroup.getGroupMembers().add(profile.getUsername()));
+
+            resp.getOutputStream().write(JsonStream.serialize(finalSingleGroup).getBytes());
+            resp.setStatus(200);
+        } else if (!groups.isEmpty()) {
+            List<UserProfile> profiles = dataStore.getUserProfileDao().getObjects(null);
+            for (Group group: groups) {
+                for (UserProfile p: profiles) {
+                    if (p.getGroupMemberships().contains(group.getName()))
+                        group.getGroupMembers().add(p.getUsername());
+                }
+            }
+
+            resp.getOutputStream().write(JsonStream.serialize(groups).getBytes());
+            resp.setStatus(200);
+        } else {
+            resp.sendError(500, "An error occurred while attempting to obtain a group!");
+        }
+    }
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        String name = req.getParameter("newName");
+        StatementList update = new StatementList();
+        StatementElement st0 = new StatementElement("name", "=", name, "");
+        update.addStatement(st0);
+        StatementList constraint = new StatementList();
+        StatementElement st1 = new StatementElement("name", "=", name, "");
+        constraint.addStatement(st1);
+
+        AddUserToGroupFlow.addUserToGroup(name, req.getParameter("groupMembers"));
+
+        if (dataStore.getGroupDao().updateObject(update, constraint)) resp.setStatus(200);
+        else resp.sendError(500, "could not update the group's name!");
+    }
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        Group group = new Group();
+        group.setName(req.getParameter("name"));
+        group.setGroupMembers(new ArrayList<String>());
+        if (dataStore.getGroupDao().storeObject(group))
+            resp.setStatus(200);
+        else
+            resp.sendError(500, "Could not create a group successfully!");
+    }
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+
+        StatementElement statementElement = new StatementElement("groupName", "=", req.getParameter("groupName"), "");
+        StatementList constraint = new StatementList();
+        constraint.addStatement(statementElement);
+        if (dataStore.getGroupDao().deleteObject(constraint)) {
+            // if deletion was successful, cascade the deletion onto the users
+            List<UserProfile> validProfiles = dataStore.getUserProfileDao().getObjects(null).stream().filter(profile ->
+                    profile.getGroupMemberships().remove(Long.parseLong(req.getParameter("groupName")))).collect(Collectors.toList());
+
+            validProfiles.forEach(profile ->
+                    dataStore.updateUserProfile(profile));
+
+            resp.setStatus(200);
+        }
+
+        resp.sendError(500, "Could not delete the group successfully!");
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/PolicyServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/PolicyServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..e44cab8bd55dbab01b29086da9e692c20654af20
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/PolicyServlet.java
@@ -0,0 +1,79 @@
+package net.jami.jams.server.servlets.api.admin.group;
+
+import com.jsoniter.output.JsonStream;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
+import net.jami.jams.common.dao.StatementElement;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.user.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static net.jami.jams.server.Server.dataStore;
+
+@WebServlet("/api/admin/policy")
+public class PolicyServlet extends HttpServlet {
+
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+        StatementList statementList = new StatementList();
+        StatementElement st1 = new StatementElement("groupName","=",req.getParameter("groupName"),"");
+        statementList.addStatement(st1);
+        Policy policy = dataStore.getPolicyDao().getObjects(statementList).get(0);
+        resp.getOutputStream().write(JsonStream.serialize(policy).getBytes());
+    }
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        StatementList update = new StatementList();
+        StatementElement st0 = new StatementElement("groupName", "=", req.getParameter("groupName"), "");
+        update.addStatement(st0);
+        StatementElement st1 = new StatementElement("username", "=", req.getParameter("username"), "");
+        update.addStatement(st1);
+        StatementElement st2 = new StatementElement("policyData", "=", req.getParameter("policyData"), "");
+        update.addStatement(st2);
+        StatementList constraint = new StatementList();
+        StatementElement st3 = new StatementElement("groupName", "=", req.getParameter("groupName"), "");
+        constraint.addStatement(st3);
+        if (dataStore.getPolicyDao().updateObject(update, constraint)) resp.setStatus(200);
+        else resp.sendError(500, "could not update the group's name!");
+    }
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        Policy policy = new Policy();
+        policy.setGroupName(req.getParameter("groupName"));
+        policy.setUsername(req.getParameter("username"));
+        policy.setPolicyData(req.getParameter("policyData"));
+        dataStore.getPolicyDao().storeObject(policy);
+
+        resp.sendError(500, "Could not create a group successfully!");
+    }
+
+
+    @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        StatementElement statementElement = new StatementElement("groupName", "=", req.getParameter("groupName"), "");
+        StatementList constraint = new StatementList();
+        constraint.addStatement(statementElement);
+        if (dataStore.getPolicyDao().deleteObject(constraint))
+            resp.setStatus(200);
+
+
+        resp.sendError(500, "Could not delete the blueprint successfully!");
+    }
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
index b8e0d0ce54568014996fd7c3cc601b9052b8b08b..99044ade01c9cfe173aa17d2ca4d6afc350c0ce4 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
@@ -33,6 +33,7 @@ import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.objects.user.User;
 import net.jami.jams.common.annotations.JsonContent;
+import net.jami.jams.common.objects.user.UserGroupMapping;
 import net.jami.jams.common.objects.user.UserProfile;
 
 import java.io.IOException;
@@ -84,6 +85,28 @@ public class SearchDirectoryServlet extends HttpServlet {
                 userProfiles.add(profile);
             });
         });
+
+        // update user group mappings table
+        userProfiles.forEach(profile -> {
+            List<UserGroupMapping> mappings = dataStore.getUserGroupMappingsDao().getObjects(null);
+
+
+            for(UserGroupMapping mapping: mappings) {
+                List<String> list = new ArrayList<>();
+                if (mapping.getUsername().equals(profile.getUsername())) {
+                    String[] splits = mapping.getGroups().split(",");
+
+                    for (int i = 0; i < splits.length; i++) {
+                       list.add(splits[i]);
+                    }
+                }
+
+                if (!list.isEmpty())
+                    profile.setGroupMemberships(list);
+            }
+            dataStore.updateUserProfile(profile);
+        });
+
         resp.getOutputStream().write(JsonStream.serialize(userProfiles).getBytes());
     }
 }