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()); } }