diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java index 2415b1cd1e21711615fd85c9a7b5407768f50cce..10485f16f577c465c71eec7d2873a987dc0c4f7b 100644 --- a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java +++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java @@ -95,6 +95,11 @@ public class ADConnector implements AuthenticationSource { return userProfileService.getUserProfile(queryString,field); } + @Override + public void setUserProfile(UserProfile userProfile) { + //does nothing as we cannot edit user profiles. + } + @Override public boolean authenticate(String username, String password) { try { diff --git a/api-doc/reference/Admin-API.v1.yaml b/api-doc/reference/Admin-API.v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9bb56b5ccde62a74c396cf6fb69d34c4e55f5328 --- /dev/null +++ b/api-doc/reference/Admin-API.v1.yaml @@ -0,0 +1,166 @@ +openapi: 3.0.0 +info: + title: Administration API + version: '1.0' + description: "This API is used to provide admin functions which are broader than the user's API and allow modifying users which are not oneself." +servers: + - url: 'http://localhost:8080' +paths: + /api/admin/device: + get: + summary: User device + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Device' + operationId: get-api-admin-device + requestBody: + description: '' + parameters: + - schema: + type: string + in: query + name: username + - schema: + type: string + in: query + name: deviceId + description: Returns the device information. + put: + summary: '' + operationId: put-api-admin-device + responses: + '200': + description: OK + delete: + summary: '' + operationId: delete-api-admin-device + responses: + '200': + description: OK + parameters: + - schema: + type: string + in: query + name: username + - schema: + type: string + in: query + name: deviceId + description: Revokes a device. + /api/admin/devices: + get: + summary: User devices + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Device' + operationId: get-api-admin-devices + parameters: + - schema: + type: string + in: query + name: username + /api/admin/directory/entry: + get: + summary: Your GET endpoint + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UserProfile' + operationId: get-api-admin-directory-entry + description: '' + put: + summary: '' + operationId: put-api-admin-directory-entry + responses: + '200': + description: OK + post: + summary: '' + operationId: post-api-admin-directory-entry + responses: + '200': + description: OK + /api/admin/user: + get: + summary: Your GET endpoint + tags: [] + responses: {} + operationId: get-api-admin-user + post: + summary: '' + operationId: post-api-admin-user + responses: + '200': + description: OK + delete: + summary: '' + operationId: delete-api-admin-user + responses: + '200': + description: OK + put: + summary: '' + operationId: put-api-admin-user + responses: + '200': + description: OK + /api/admin/users: + get: + summary: Your GET endpoint + tags: [] + responses: {} + operationId: get-api-admin-users +components: + schemas: + Device: + title: Device + type: object + properties: + deviceId: + type: string + displayName: + type: string + certificate: + type: string + description: "The user's Jami device." + UserProfile: + title: UserProfile + type: object + properties: + username: + type: string + firstName: + type: string + lastName: + type: string + phoneNumber: + type: string + phoneNumberExtension: + type: string + mobileNumber: + type: string + faxNumber: + type: string + profilePicture: + type: string + email: + type: string + organization: + type: string + description: "The user's extended information which is not critical to Jami operations, usually provided by LDAP, Active Directory or other backend." diff --git a/api-doc/reference/Install-API.v1.yaml b/api-doc/reference/Install-API.v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..03b15027dd565d4523768b30618c33b3af4fbcc2 --- /dev/null +++ b/api-doc/reference/Install-API.v1.yaml @@ -0,0 +1,38 @@ +openapi: 3.0.0 +info: + title: Install API + version: '1.0' + description: This API is used by the Web-UI installer in order to get JAMS up and running. +servers: + - url: 'http://localhost:3000' +paths: + /api/install/auth: + post: + summary: '' + operationId: post-api-install-auth + responses: + '200': + description: OK + /api/install/ca: + post: + summary: '' + operationId: post-api-install-ca + responses: + '200': + description: OK + /api/install/settings: + post: + summary: '' + operationId: post-api-install-settings + responses: + '200': + description: OK + /api/install/start: + post: + summary: '' + operationId: post-api-install-start + responses: + '200': + description: OK +components: + schemas: {} diff --git a/api-doc/reference/Nameserver-API.v1.yaml b/api-doc/reference/Nameserver-API.v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..baec648acd279ccffc6982dde2375a150fde71fa --- /dev/null +++ b/api-doc/reference/Nameserver-API.v1.yaml @@ -0,0 +1,36 @@ +openapi: 3.0.0 +info: + title: Nameserver API + version: '1.0' + description: This is the Jami-compatible implementation of the nameserver. +servers: + - url: 'http://localhost:8080' +paths: + '/api/nameserver/addr/{addr}': + parameters: + - schema: + type: string + name: addr + in: path + required: true + get: + summary: Address lookup + tags: [] + responses: {} + operationId: get-api-nameserver-addr-addr + '/api/nameserver/name/{name}': + parameters: + - schema: + type: string + name: name + in: path + required: true + get: + summary: Name lookup + tags: [] + responses: + '200': + description: OK + operationId: get-api-nameserver-name-name +components: + schemas: {} diff --git a/api-doc/reference/Security-API.v1.yaml b/api-doc/reference/Security-API.v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..05e04371a9b6cb270e7d0e9da2c43d8a9f947117 --- /dev/null +++ b/api-doc/reference/Security-API.v1.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + title: Security API + version: '1.0' +servers: + - url: 'http://localhost:8080' +paths: + /api/auth/login: + post: + summary: Login and Receive Auth Token + operationId: post-api-auth-login + responses: + '200': + description: OK +components: + schemas: {} diff --git a/api-doc/reference/User-API.v1.yaml b/api-doc/reference/User-API.v1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..07b6a4c77b933afdd75ed1daade127caaaa78e9d --- /dev/null +++ b/api-doc/reference/User-API.v1.yaml @@ -0,0 +1,113 @@ +openapi: 3.0.0 +info: + title: User API + version: '1.0' +servers: + - url: 'http://localhost:8080' +paths: + /api/auth/contacts: + get: + summary: Contacts endpoint + tags: [] + responses: {} + operationId: get-api-auth-contacts + '/api/auth/device/{deviceId}': + parameters: + - schema: + type: string + name: deviceId + in: path + required: true + get: + summary: Device operations + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './Admin-API.v1.yaml#/components/schemas/Device' + operationId: get-api-auth-device-deviceId + put: + summary: '' + operationId: put-api-auth-device-deviceId + responses: + '200': + description: OK + post: + summary: '' + operationId: post-api-auth-device-deviceId + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: [] + /api/auth/devices: + get: + summary: Get the list of devices + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: './Admin-API.v1.yaml#/components/schemas/Device' + operationId: get-api-auth-devices + /api/auth/directories: + get: + summary: Get the directories connect to JAMS + tags: [] + responses: {} + operationId: get-api-auth-directories + /api/auth/directory/entry: + get: + summary: Get the profile of a user + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: './Admin-API.v1.yaml#/components/schemas/UserProfile' + operationId: get-api-auth-directory-entry + /api/auth/directory/search: + get: + summary: Search the directory + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: './Admin-API.v1.yaml#/components/schemas/UserProfile' + '': + content: + application/json: + schema: + type: array + items: + $ref: './Admin-API.v1.yaml#/components/schemas/UserProfile' + operationId: get-api-auth-directory-search + /api/auth/user: + get: + summary: User endpoint + tags: [] + responses: {} + operationId: get-api-auth-user +components: + schemas: {} + securitySchemes: + API Key - 1: + name: API Key + type: apiKey + in: query diff --git a/authentication-module/src/main/java/net/jami/jams/authmodule/RegisterUserFlow.java b/authentication-module/src/main/java/net/jami/jams/authmodule/RegisterUserFlow.java index 6e38a49a91a421f99c5397968e5f0912c50e0fae..1efd2de164e6f128adbbd6ca38ebef5bd461e474 100644 --- a/authentication-module/src/main/java/net/jami/jams/authmodule/RegisterUserFlow.java +++ b/authentication-module/src/main/java/net/jami/jams/authmodule/RegisterUserFlow.java @@ -59,7 +59,7 @@ public class RegisterUserFlow { return false; } datastore.getUserDao().storeObject(user); - log.info("Create the user " + user.getUsername() + " because he did not exist before!"); + log.info("Created the user " + user.getUsername() + " because he did not exist before!"); return true; } } diff --git a/authentication-module/src/main/java/net/jami/jams/authmodule/UserAuthenticationModule.java b/authentication-module/src/main/java/net/jami/jams/authmodule/UserAuthenticationModule.java index cf0f51959f8b982fe27e68b91fd6282e2b4f8605..edf450716dc377e0950719fd2aa220bd181ccf5f 100644 --- a/authentication-module/src/main/java/net/jami/jams/authmodule/UserAuthenticationModule.java +++ b/authentication-module/src/main/java/net/jami/jams/authmodule/UserAuthenticationModule.java @@ -45,7 +45,6 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; -import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; @@ -148,6 +147,7 @@ public class UserAuthenticationModule implements AuthenticationModule { user.setAccessLevel(AccessLevel.USER); user.setRealm(key.getRealm()); user.setUserType(key.getType()); + //This is legal with a null ONLY because in this case there is no relation with a external server. RegisterUserFlow.createUser(user,null); return tokenController.getToken(user); } @@ -194,4 +194,18 @@ public class UserAuthenticationModule implements AuthenticationModule { return (RSAPublicKey) publicKey; } + @Override + public char[] getOTP(String username) { + + if(datastore.userExists(username)){ + StatementList statementList = new StatementList(); + StatementElement statementElement = new StatementElement("username","=",username,""); + statementList.addStatement(statementElement); + User user = datastore.getUserDao().getObjects(statementList).get(0); + return (user.getPassword()).toCharArray(); + } + + return new char[0]; + } + } diff --git a/datastore/src/main/java/net/jami/datastore/dao/AbstractDao.java b/datastore/src/main/java/net/jami/datastore/dao/AbstractDao.java index 7d708494b6a4b3d017d7a1e1896ae6ba4dd4e347..ffbca27b868807ce98d7fc2ef74ebb7ab41c2904 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/AbstractDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/AbstractDao.java @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; import net.jami.datastore.main.DataStore; import net.jami.jams.common.dao.SelectStatementBuilder; import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.dao.UpdateStatementBuilder; import net.jami.jams.common.dao.connectivity.SQLConnection; import java.sql.PreparedStatement; @@ -44,11 +45,14 @@ public abstract class AbstractDao<T> { @Getter @Setter private Class<T> tClass; + public abstract boolean storeObject(T object); + public abstract boolean deleteObject(LinkedHashMap<String,String> constraints); + public List<T> getObjects(StatementList constraints){ List<T> result = new ArrayList<>(); SQLConnection connection = DataStore.connectionPool.getConnection(); try{ - PreparedStatement ps = SelectStatementBuilder.buildStatement(tableName,constraints.getStatements(),connection); + PreparedStatement ps = SelectStatementBuilder.buildStatement(tableName,constraints,connection); ResultSet rs = ps.executeQuery(); while(rs.next()){ result.add(tClass.getConstructor(ResultSet.class).newInstance(rs)); @@ -64,7 +68,18 @@ public abstract class AbstractDao<T> { } } - - public abstract boolean storeObject(T object); - public abstract boolean deleteObject(LinkedHashMap<String,String> constraints); + public boolean updateObject(StatementList update, StatementList constraints){ + SQLConnection connection = DataStore.connectionPool.getConnection(); + try{ + PreparedStatement ps = UpdateStatementBuilder.buildStatement(tableName,update,constraints,connection); + return ps.execute(); + } + catch (Exception e){ + log.error("An error has occurred while trying to fetch a device: " + e.toString()); + return false; + } + finally { + DataStore.connectionPool.returnConnection(connection); + } + } } diff --git a/datastore/src/main/java/net/jami/datastore/dao/ContactDao.java b/datastore/src/main/java/net/jami/datastore/dao/ContactDao.java index b6c67e68cfd74e5890760cb7d4caa7fa36a6c1f5..1e33d86b870963fd13319a7f4ef1047d97fceb23 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/ContactDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/ContactDao.java @@ -22,6 +22,7 @@ */ package net.jami.datastore.dao; +import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.contacts.Contact; import java.util.LinkedHashMap; @@ -37,4 +38,9 @@ public class ContactDao extends AbstractDao<Contact> { public boolean deleteObject(LinkedHashMap<String, String> constraints) { return false; } + + @Override + public boolean updateObject(StatementList update, StatementList constraints) { + return false; + } } diff --git a/datastore/src/main/java/net/jami/datastore/dao/DeviceDao.java b/datastore/src/main/java/net/jami/datastore/dao/DeviceDao.java index d1e5b74eab8d024dc9cd95ba96da8f92040aff6f..6af5941495ee0edb7f6a933f9a93004e25a63f06 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/DeviceDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/DeviceDao.java @@ -24,6 +24,7 @@ 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.devices.Device; @@ -81,4 +82,9 @@ public class DeviceDao extends AbstractDao<Device> { public boolean deleteObject(LinkedHashMap<String, String> constraints) { return false; } + + @Override + public boolean updateObject(StatementList update, StatementList constraints) { + return false; + } } diff --git a/datastore/src/main/java/net/jami/datastore/dao/SystemDao.java b/datastore/src/main/java/net/jami/datastore/dao/SystemDao.java index c8d8ca660f2d415a481cb0094fb2262751e2939e..7cb9e1587a8ab624870a486a862c109cc27722be 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/SystemDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/SystemDao.java @@ -24,6 +24,7 @@ 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.system.SystemAccount; @@ -79,4 +80,9 @@ public class SystemDao extends AbstractDao<SystemAccount> { public boolean deleteObject(LinkedHashMap<String, String> constraints) { return false; } + + @Override + public boolean updateObject(StatementList update, StatementList constraints) { + return false; + } } 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 78db5a3d659094548efbc72ca2b20b338bdd2c62..41ed66eefbed2b3d301b0ed153d381eab663ba7f 100644 --- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java +++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java @@ -73,6 +73,11 @@ public class DataStore implements AuthenticationSource { return null; } + @Override + public void setUserProfile(UserProfile userProfile) { + //TODO: Implement this. + } + @Override public boolean authenticate(String username, String password) { StatementList statementList = new StatementList(); diff --git a/integration-test/install-server.py b/integration-test/install-server.py index 4add5375b079ca2552a6db951e14ffbbaebd68cd..e9a9de7ec000cdf8efd1203936a76a3ac0279d96 100644 --- a/integration-test/install-server.py +++ b/integration-test/install-server.py @@ -118,4 +118,13 @@ print(token) response = requests.get("http://localhost:8080/api/nameserver/name/aberaud",headers=header) print(response.status_code) +print(response.text) + + +response = requests.get("http://localhost:8080//api/auth/directories",headers=header) +print(response.status_code) +print(response.text) + +response = requests.get("http://localhost:8080/api/auth/user",headers=header) +print(response.status_code) print(response.text) \ No newline at end of file diff --git a/jami-nameserver/src/main/java/net/jami/jams/nameserver/PublicNameServer.java b/jami-nameserver/src/main/java/net/jami/jams/nameserver/PublicNameServer.java index a56776fe6ca256ffe3b3a25988d1e11b2b83fe8c..ef1c070656ff3a2fa6e806aac5691f1bc32d000d 100644 --- a/jami-nameserver/src/main/java/net/jami/jams/nameserver/PublicNameServer.java +++ b/jami-nameserver/src/main/java/net/jami/jams/nameserver/PublicNameServer.java @@ -70,7 +70,7 @@ public class PublicNameServer implements NameServer { responseData.append((char) con.getInputStream().read()); currentSize++; } - log.info("Reponse received from public nameserver {} ", responseData.toString()); + log.info("Response received from public nameserver {} ", responseData.toString()); return JsonIterator.deserialize(responseData.toString(),NameLookupResponse.class); } return null; diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/X509Worker.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/X509Worker.java index f62a027ee44efd82d28d2b006d3cf4917ab40c89..f09fda04757a466769e141344a16bf7a3a9e06c9 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/X509Worker.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/X509Worker.java @@ -25,8 +25,6 @@ package net.jami.jams.ca.workers; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPResp; import java.security.PrivateKey; import java.security.cert.X509Certificate; diff --git a/jams-common/pom.xml b/jams-common/pom.xml index 065b4c7495ea8df8fa4e347d69376cf681f860ff..5bc99ad07fec94f340f460b601b1f2e8eda156f0 100644 --- a/jams-common/pom.xml +++ b/jams-common/pom.xml @@ -27,6 +27,11 @@ <artifactId>xbean-classloader</artifactId> <version>${xbean.version}</version> </dependency> + <dependency> + <groupId>org.zeromq</groupId> + <artifactId>jeromq</artifactId> + <version>0.5.2</version> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/jams-common/src/main/java/module-info.java b/jams-common/src/main/java/module-info.java index f1558dbfc2b476c91a55165c239754d6d0561c4c..a96e0b86c98039385d28ef200f14b31238090a2f 100644 --- a/jams-common/src/main/java/module-info.java +++ b/jams-common/src/main/java/module-info.java @@ -74,9 +74,11 @@ module jams.common { exports net.jami.jams.common.authentication.local; exports net.jami.jams.common.objects.responses; exports net.jami.jams.common.cryptoengineapi.ocsp; + exports net.jami.jams.common.updater; requires jdk.crypto.cryptoki; requires java.base; requires java.sql; requires org.apache.xbean.classloader; + requires jeromq; } diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java index b9035222361d2316d675be06aed56a2468df53e0..9b3cb6203152e58d717bae92e182b4f197304b6b 100644 --- a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java @@ -29,6 +29,7 @@ public interface AuthenticationSource { boolean createUser(User user); UserProfile[] getUserProfile(String queryString, String field); + void setUserProfile(UserProfile userProfile); boolean authenticate(String username, String password); AuthenticationSourceInfo getInfo(); boolean test(); diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java index 380a6d9804aae24162d7a425d138c0f1f446c362..b01fb44d302271aad6cc2dba5485cd819b94e79d 100644 --- a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java @@ -25,5 +25,13 @@ package net.jami.jams.common.authentication; public enum AuthenticationSourceType { AD, LDAP, - LOCAL + LOCAL; + + public static AuthenticationSourceType fromString(String str){ + if(str.equals("AD")) return AD; + if(str.equals("LDAP")) return LDAP; + if(str.equals("LOCAL")) return LOCAL; + return null; + } + } diff --git a/jams-common/src/main/java/net/jami/jams/common/authmodule/AuthenticationModule.java b/jams-common/src/main/java/net/jami/jams/common/authmodule/AuthenticationModule.java index 273dfa67347c4ad15d4fadbfe8c1fca5ffbb8942..337a825a3d97db51ef0f24d4c013ffe801ad5097 100644 --- a/jams-common/src/main/java/net/jami/jams/common/authmodule/AuthenticationModule.java +++ b/jams-common/src/main/java/net/jami/jams/common/authmodule/AuthenticationModule.java @@ -27,10 +27,11 @@ import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.jami.NameServer; import net.jami.jams.common.objects.user.User; +import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; -import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public interface AuthenticationModule { @@ -41,4 +42,5 @@ public interface AuthenticationModule { boolean testModuleConfiguration(AuthenticationSourceType type, String configuration); boolean createUser(AuthenticationSourceType type, String realm, NameServer nameServer, User user); RSAPublicKey getAuthModulePubKey(); + char[] getOTP(String username); } diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java b/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java index 4d640f24b8c1a7263fd9e7ae31061ce580a2e714..c361e884579819fca538de04fdcedd2c33db2396 100644 --- a/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java +++ b/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java @@ -25,11 +25,10 @@ package net.jami.jams.common.dao; import net.jami.jams.common.dao.connectivity.SQLConnection; import java.sql.PreparedStatement; -import java.util.List; public class SelectStatementBuilder { - public static PreparedStatement buildStatement(String table, List<StatementElement> statementElements, + public static PreparedStatement buildStatement(String table, StatementList statementElements, SQLConnection connection) throws Exception { PreparedStatement ps = null; @@ -37,7 +36,7 @@ public class SelectStatementBuilder { stringBuilder.append("SELECT * FROM ").append(table); if(statementElements != null) { stringBuilder.append(" WHERE "); - for (StatementElement statementElement : statementElements) { + for (StatementElement statementElement : statementElements.getStatements()) { stringBuilder .append(statementElement.getColumn()) .append(" ") @@ -49,7 +48,7 @@ public class SelectStatementBuilder { } ps = connection.getConnection().prepareStatement(stringBuilder.toString()); int i = 1; - for (StatementElement statementElement : statementElements) { + for (StatementElement statementElement : statementElements.getStatements()) { ps.setString(i, statementElement.getValue()); i++; } diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java b/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java index 1c6db37ab6508ad8efe714e5570b7232f6b5926d..a80161771067daeacf52b2bab0e8521d2fa37159 100644 --- a/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java +++ b/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java @@ -25,25 +25,24 @@ package net.jami.jams.common.dao; import net.jami.jams.common.dao.connectivity.SQLConnection; import java.sql.PreparedStatement; -import java.util.List; public class UpdateStatementBuilder { - public static PreparedStatement buildStatement(String table, List<StatementElement> updateElements, - List<StatementElement> conditionalElements, + public static PreparedStatement buildStatement(String table, StatementList updateElements, + StatementList conditionalElements, SQLConnection connection) throws Exception { PreparedStatement ps = null; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("UPDATE ").append(table).append(" SET "); - for (int i = 0; i < updateElements.size(); i++) { - StatementElement statementElement = updateElements.get(i); + for (int i = 0; i < updateElements.getStatements().size(); i++) { + StatementElement statementElement = updateElements.getStatements().get(i); stringBuilder .append(statementElement.getColumn()) .append(" = ") .append("?"); - if (i != updateElements.size() - 1) stringBuilder.append(","); + if (i != updateElements.getStatements().size() - 1) stringBuilder.append(","); } stringBuilder.append(" WHERE "); - for (StatementElement statementElement : conditionalElements) { + for (StatementElement statementElement : conditionalElements.getStatements()) { stringBuilder .append(statementElement.getColumn()) .append(" ") @@ -54,10 +53,10 @@ public class UpdateStatementBuilder { .append(statementElement.getNextStatementRelation()); } ps = connection.getConnection().prepareStatement(stringBuilder.toString()); - //Now we have to feed this all the elements it hsould have. - updateElements.addAll(conditionalElements); + //Now we have to feed this all the elements it should have. + updateElements.getStatements().addAll(conditionalElements.getStatements()); int i = 1; - for (StatementElement statementElement : updateElements) { + for (StatementElement statementElement : updateElements.getStatements()) { ps.setString(i, statementElement.getValue()); i++; } diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/DeviceRevocationRequest.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/DeviceRevocationRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..2ef3f7d8514d8efcf6f09f213b394bd6f05fba17 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/DeviceRevocationRequest.java @@ -0,0 +1,17 @@ +package net.jami.jams.common.objects.requests; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class DeviceRevocationRequest { + + private String owner; + private String deviceId; + + public DeviceRevocationRequest(String username, String deviceId) { + this.owner = owner; + this.deviceId = deviceId; + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/responses/DeviceRevocationResponse.java b/jams-common/src/main/java/net/jami/jams/common/objects/responses/DeviceRevocationResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..31f8ae30e7467fab1939910275e024aa1cb1da8f --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/responses/DeviceRevocationResponse.java @@ -0,0 +1,19 @@ +package net.jami.jams.common.objects.responses; + +import lombok.Getter; +import lombok.Setter; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Getter +@Setter +public class DeviceRevocationResponse { + + + private boolean success; + private String errorDetails; + private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss'Z'X"); + private String timestamp = dateFormatter.format(new Date()); + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/updater/AppUpdater.java b/jams-common/src/main/java/net/jami/jams/common/updater/AppUpdater.java new file mode 100644 index 0000000000000000000000000000000000000000..56b431a20e8f43ef2ad37e70cedcab06e37542c4 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/updater/AppUpdater.java @@ -0,0 +1,11 @@ +package net.jami.jams.common.updater; + +public interface AppUpdater { + + String getCurrentVersion(); + String getLatestVersion(); + boolean downloadUpdates(); + + + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionStatusResponse.java b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionStatusResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..9776ca254be99c5bceaf2e4d1ae66d5c9eea6504 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionStatusResponse.java @@ -0,0 +1,12 @@ +package net.jami.jams.common.updater.subscription; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SubscriptionStatusResponse { + private Boolean subscribed; + private SubscriptionType subscriptionType; + private Long expiryDate; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionType.java b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionType.java new file mode 100644 index 0000000000000000000000000000000000000000..21467d99f190e03f3f09000da0d534acd1d21a99 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionType.java @@ -0,0 +1,5 @@ +package net.jami.jams.common.updater.subscription; + +public enum SubscriptionType { + COMMUNITY +} diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/LicenseUtils.java b/jams-common/src/main/java/net/jami/jams/common/utils/LicenseUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..5d55a05b787a19676b31454b52b524d1c7e656b3 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/utils/LicenseUtils.java @@ -0,0 +1,29 @@ +package net.jami.jams.common.utils; + + +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +@Slf4j +public class LicenseUtils { + + + public static String checkVersion(String destinationDir, String jarPath) throws IOException { + String resp = ""; + File file = new File(jarPath); + JarFile jar = new JarFile(file); + Manifest manifest = jar.getManifest(); + resp = manifest.getMainAttributes().getValue("Implementation-Version"); + System.out.println("Found version: " + resp); + return resp; + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/UpdateInterface.java b/jams-common/src/main/java/net/jami/jams/common/utils/UpdateInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..7bfcf4499a031bca37bc558e7f506bf64edcbcd9 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/utils/UpdateInterface.java @@ -0,0 +1,46 @@ +package net.jami.jams.common.utils; + +import lombok.Getter; +import lombok.Setter; +import org.zeromq.SocketType; +import org.zeromq.ZMQ; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; + +@Getter +@Setter +public class UpdateInterface extends Thread { + + private AtomicBoolean updateAvailable = new AtomicBoolean(false); + private volatile String versions; + private ZMQ.Context context = ZMQ.context(1); + private ZMQ.Socket sender = context.socket(SocketType.REQ); + private ZMQ.Socket receiver = context.socket(SocketType.SUB); + private final static Logger logger = Logger.getLogger(UpdateInterface.class.getName()); + + public UpdateInterface() { + receiver.connect("tcp://localhost:4572"); + sender.connect("tcp://localhost:4573"); + receiver.subscribe("UPDATE"); + this.start(); + } + + public void approveUpdate(){ + sender.send("DO-UPDATE"); + } + + @Override + public void run() { + while(true){ + try{ + receiver.recv(); + updateAvailable.set(true); + versions = receiver.recvStr(); + } + catch (Exception e){ + System.out.println(e.toString()); + } + } + } +} diff --git a/jams-launcher/pom.xml b/jams-launcher/pom.xml index 54ecb9f5d32d9e26da7c18bd05fb75afbe9b5883..9eb9bc4d220b2f0c28d578179eae6d92fea7f15c 100644 --- a/jams-launcher/pom.xml +++ b/jams-launcher/pom.xml @@ -14,37 +14,27 @@ <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-model</artifactId> - <version>3.2.5</version> + <version>${maven.model.version}</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> - <version>4.4.12</version> + <version>${apache.httpcore.version}</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> - <version>4.5.10</version> - </dependency> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>1.18.12</version> + <version>${apache.httpclient.version}</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> - <version>20.0</version> + <version>${google.guava.version}</version> </dependency> <dependency> <groupId>org.zeromq</groupId> <artifactId>jeromq</artifactId> - <version>0.5.2</version> - </dependency> - <dependency> - <groupId>com.blockfeed</groupId> - <artifactId>messaging</artifactId> - <version>1.0-SNAPSHOT</version> + <version>${jeromq.version}</version> </dependency> </dependencies> @@ -54,7 +44,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>2.3</version> + <version>${maven.shade.version}</version> <executions> <!-- Run shade goal on package phase --> <execution> diff --git a/jams-launcher/src/main/java/launcher/MessageReceiver.java b/jams-launcher/src/main/java/launcher/MessageReceiver.java index 05c97888f3c9e437f701c7bdae04e1c388270da8..26e390615748133e6b2271403ca04ba0d3fc752e 100644 --- a/jams-launcher/src/main/java/launcher/MessageReceiver.java +++ b/jams-launcher/src/main/java/launcher/MessageReceiver.java @@ -22,10 +22,12 @@ */ package launcher; +import lombok.extern.slf4j.Slf4j; import org.zeromq.ZMQ; import java.util.concurrent.atomic.AtomicBoolean; +@Slf4j public class MessageReceiver extends Thread { private ZMQ.Socket socket; @@ -44,7 +46,7 @@ public class MessageReceiver extends Thread { if(message.equals("DO-UPDATE")) atomicBoolean.set(true); } catch (Exception e){ - System.out.println("Some exception occurred!"); + log.error("Some exception occurred on the message receiving thread, details {}!",e.getMessage()); } } } diff --git a/jams-launcher/src/main/java/launcher/UpdateThread.java b/jams-launcher/src/main/java/launcher/UpdateThread.java index 0b6057eb630f596fea9f797869e1248090d7abfa..bf03bc700957202a4411b64ad4b60df0124f29d7 100644 --- a/jams-launcher/src/main/java/launcher/UpdateThread.java +++ b/jams-launcher/src/main/java/launcher/UpdateThread.java @@ -28,6 +28,7 @@ import com.jsoniter.JsonIterator; import com.jsoniter.any.Any; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; @@ -59,6 +60,7 @@ import java.util.logging.Logger; @Getter @Setter +@Slf4j public class UpdateThread extends Thread { private HashMap<String, String> localVersions = new HashMap<>(); @@ -68,7 +70,6 @@ public class UpdateThread extends Thread { private HashMap<String, String> remoteChecksums = new HashMap<>(); private String[] parentArgs; - private final static Logger logger = Logger.getLogger(UpdateThread.class.getName()); private static volatile String CORE_PACKAGE_MAIN_CLASS_NAME; private static volatile String UPDATE_SERVER_URL; private static volatile Long UPDATE_INTERVAL; @@ -93,7 +94,7 @@ public class UpdateThread extends Thread { messageReceiver = new MessageReceiver(receiver,doUpdate); messageReceiver.start(); } catch (Exception e) { - logger.warning("Could not create and bind publisher and/or receiver! Please contact software developer"); + log.warn("Could not create and bind publisher and/or receiver! Please contact software developer"); System.exit(-1); } @@ -105,7 +106,7 @@ public class UpdateThread extends Thread { UPDATE_INTERVAL = any.get("UPDATE_INTERVAL").toLong(); } catch (IOException e) { - logger.warning("Missing OEM configuration! Please contact software developer"); + log.warn("Missing OEM configuration! Please contact software developer"); System.exit(-1); } @@ -171,7 +172,7 @@ public class UpdateThread extends Thread { discoverVersions(folderExec); discoverVersions(folderLibs); } catch (Exception e) { - logger.warning(e.toString()); + log.warn(e.toString()); } } @@ -200,7 +201,7 @@ public class UpdateThread extends Thread { remoteChecksums.put(k, v.get("md5").toString()); }); } catch (Exception e) { - logger.warning("Could not establish connection to JAMS Update Center with error: " + e.toString()); + log.warn("Could not establish connection to JAMS Update Center with error: " + e.toString()); } } @@ -258,13 +259,13 @@ public class UpdateThread extends Thread { ((X509Certificate) c).checkValidity(); c.verify(ca.getPublicKey()); } catch (Exception e) { - logger.warning("Your license is no longer valid or has been tampered with - " + e.toString()); + log.warn("Your license is no longer valid or has been tampered with - " + e.toString()); return false; } sslContext = SSLContexts.custom().loadKeyMaterial(ks, "".toCharArray()).loadTrustMaterial(trustStore, null) .build(); } catch (Exception e) { - logger.warning("Could not read license file with error " + e.toString()); + log.warn("Could not read license file with error " + e.toString()); return false; } return true; @@ -283,14 +284,14 @@ public class UpdateThread extends Thread { HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build(); HttpResponse httpResponse = httpClient.execute(new HttpGet(UPDATE_SERVER_URL + "/updates/" + v)); if (httpResponse.getStatusLine().getStatusCode() == 200) { - logger.info(tmpFolder.getPath() + "/" + files.get(k)); + log.info(tmpFolder.getPath() + "/" + files.get(k)); FileOutputStream fos = new FileOutputStream(tmpFolder.getPath() + "/" + files.get(k)); if (k.equals(CORE_PACKAGE_MAIN_CLASS_NAME)) { httpResponse.getEntity().writeTo(fos); fos.close(); if (checksum(tmpFolder.getPath() + "/" + files.get(k)).equals(remoteChecksums.get(k))) { - logger.info("Successfully downloaded the core package!"); + log.info("Successfully downloaded the core package!"); remoteChecksums.remove(CORE_PACKAGE_MAIN_CLASS_NAME); } @@ -299,16 +300,16 @@ public class UpdateThread extends Thread { fos.close(); if (checksum(tmpFolder.getPath() + "/" + files.get(k)).equals(remoteChecksums.get(k))) { - logger.info("Successfully downloaded a library package!"); + log.info("Successfully downloaded a library package!"); remoteChecksums.remove(remoteChecksums.get(k)); } } } else { - logger.warning("The server declared an update but does not have the required files?!"); + log.warn("The server declared an update but does not have the required files?!"); } } catch (Exception e1) { - logger.warning("Could not download an update with error " + e1.toString()); + log.warn("Could not download an update with error " + e1.toString()); } }); } @@ -356,14 +357,14 @@ public class UpdateThread extends Thread { System.out.println("Murdered process with pid: " + AppStarter.getJamsPID()); // delete old files remoteExecs.forEach((k, v) -> { - logger.info(remoteExecs.get(k)); + log.info(remoteExecs.get(k)); if (k.equals(CORE_PACKAGE_MAIN_CLASS_NAME)) { File f = new File(System.getProperty("user.dir") + "/" + remoteExecs.get(k)); f.delete(); f = new File(System.getProperty("user.dir") + "/tmp/" + remoteExecs.get(k)); if (!f.renameTo(new File(System.getProperty("user.dir") + "/" + remoteExecs.get(k)))) - logger.warning("An error occurred while attempting to move the file!"); + log.warn("An error occurred while attempting to move the file!"); else f.delete(); } else { @@ -371,7 +372,7 @@ public class UpdateThread extends Thread { f.delete(); f = new File(System.getProperty("user.dir") + "/tmp/" + remoteExecs.get(k)); if (!f.renameTo(new File(System.getProperty("user.dir") + "/libs/" + remoteExecs.get(k)))) - logger.warning("An error occurred while attempting to move the file!"); + log.warn("An error occurred while attempting to move the file!"); else f.delete(); } @@ -397,7 +398,7 @@ public class UpdateThread extends Thread { try { FileUtils.deleteDirectory(System.getProperty("user.dir") + "/tmp/"); } catch (IOException e) { - logger.warning("An error occurred while attempting to delete /tmp/ folder!"); + log.warn("An error occurred while attempting to delete /tmp/ folder!"); } } @@ -406,7 +407,7 @@ public class UpdateThread extends Thread { HashCode hash = com.google.common.io.Files .hash(new File(filepath), Hashing.md5()); - logger.info("Calculated md5: " + hash.toString()); + log.warn("Calculated md5: " + hash.toString()); return hash.toString(); } @@ -432,7 +433,7 @@ public class UpdateThread extends Thread { }; timer.scheduleAtFixedRate(pollTask, 15000, UPDATE_INTERVAL); } catch (Exception e) { - logger.warning("error! " + e.toString()); + log.warn("error! " + e.toString()); } } } \ No newline at end of file diff --git a/jams-server/src/main/java/module-info.java b/jams-server/src/main/java/module-info.java index 5f8fd865bb663681db57337351eef8a0e6f812fd..29b097d812fbd815ec586a36be9f733d978ba460 100644 --- a/jams-server/src/main/java/module-info.java +++ b/jams-server/src/main/java/module-info.java @@ -32,15 +32,29 @@ module jams.server { requires javassist; requires datastore; requires org.apache.xbean.classloader; + requires org.bouncycastle.pkix; + requires org.bouncycastle.provider; requires jami.nameserver; requires jami.dht; requires nimbus.jose.jwt; requires java.desktop; + requires java.naming; + requires java.logging; + requires javax.servlet.api; exports net.jami.jams.server.servlets.general to org.apache.tomcat.embed.core; + exports net.jami.jams.server.servlets.filters to org.apache.tomcat.embed.core; + exports net.jami.jams.server.servlets.api.auth.login to org.apache.tomcat.embed.core; exports net.jami.jams.server.servlets.api.auth.device to org.apache.tomcat.embed.core; + exports net.jami.jams.server.servlets.api.auth.directory to org.apache.tomcat.embed.core; + exports net.jami.jams.server.servlets.api.auth.user to org.apache.tomcat.embed.core; + exports net.jami.jams.server.servlets.api.install to org.apache.tomcat.embed.core; + + exports net.jami.jams.server.servlets.general to org.apache.tomcat.embed.core; exports net.jami.jams.server.servlets.api.jaminameserver to org.apache.tomcat.embed.core; exports net.jami.jams.server.servlets.x509 to org.apache.tomcat.embed.core; -} \ No newline at end of file + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/Server.java b/jams-server/src/main/java/net/jami/jams/server/Server.java index 21f8c3344327a823c370d9d3a4d56d78ef39acf9..860871f7e14a5d4abc60ce80689153ff5c70da6b 100644 --- a/jams-server/src/main/java/net/jami/jams/server/Server.java +++ b/jams-server/src/main/java/net/jami/jams/server/Server.java @@ -32,12 +32,15 @@ import net.jami.jams.common.cryptoengineapi.CertificateAuthority; import net.jami.jams.common.jami.NameServer; import net.jami.jams.common.serialization.JsoniterRegistry; import net.jami.jams.common.server.ServerSettings; +import net.jami.jams.common.updater.AppUpdater; import net.jami.jams.common.utils.LibraryLoader; +import net.jami.jams.common.utils.UpdateInterface; import net.jami.jams.nameserver.LocalNameServer; import net.jami.jams.nameserver.PublicNameServer; import net.jami.jams.server.core.TomcatLauncher; import net.jami.jams.server.startup.AuthModuleLoader; import net.jami.jams.server.startup.CryptoEngineLoader; +import net.jami.jams.server.startup.UpdaterLoader; import java.io.File; import java.io.FileInputStream; @@ -47,7 +50,8 @@ import java.util.concurrent.atomic.AtomicBoolean; @Slf4j public class Server { - public static AtomicBoolean isInstalled = new AtomicBoolean(false); + public final static AtomicBoolean isInstalled = new AtomicBoolean(false); + public final static AtomicBoolean activated = new AtomicBoolean(false); static { JsoniterRegistry.initCodecs(); @@ -57,8 +61,10 @@ public class Server { //This one gets loaded via JAR, to make it more flexible. public static CertificateAuthority certificateAuthority; public static AuthenticationModule userAuthenticationModule; + public static AppUpdater appUpdater; public static NameServer nameServer; private static TomcatLauncher tomcatLauncher = null; + public static final UpdateInterface updateInterface = new UpdateInterface(); public static void main(String[] args) { //Start tomcat. @@ -99,6 +105,7 @@ public class Server { else nameServer = new LocalNameServer(dataStore,userAuthenticationModule,serverSettings.getServerPublicURI()); } else nameServer = new LocalNameServer(dataStore,userAuthenticationModule,serverSettings.getServerPublicURI()); + appUpdater = UpdaterLoader.loadUpdater(); log.info("All services are UP and ready for use..."); } catch (Exception e){ diff --git a/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java index 48294db227393fdca9f7a1cd9f0644d262876eda..e8643f8d84c29ad66f86941a146531486eab5ec7 100644 --- a/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java +++ b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java @@ -37,7 +37,6 @@ import java.net.URI; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import static net.jami.jams.server.core.TomcatConnectorFactory.getSSLConnectorWithTrustStore; //This class boots the tomcat server which provides the subsystem //for the API calls. @@ -64,6 +63,7 @@ public class TomcatLauncher { log.info("JAR Resource File = " + jarName); StandardContext context = (StandardContext) tomcat.addWebapp("", new File(System.getProperty("user.dir")).getAbsolutePath()); //Hack to prevent useless verbose messages. + context.getJarScanner().setJarScanFilter((jarScanType, s) -> false); WebResourceRoot resources = new StandardRoot(context); if (jarName.contains(".jar")) { @@ -96,6 +96,4 @@ public class TomcatLauncher { log.error("Web-server has failed to start - this is critical!"); } } - - } diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/ActivateSubscriptionWorkflow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/ActivateSubscriptionWorkflow.java new file mode 100644 index 0000000000000000000000000000000000000000..601a7e8ee76cd4bcd8505b5a8cd4bd475bf84fad --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/ActivateSubscriptionWorkflow.java @@ -0,0 +1,35 @@ +package net.jami.jams.server.core.workflows; + +import lombok.extern.slf4j.Slf4j; + +import java.io.FileOutputStream; +import java.security.KeyStore; + +@Slf4j +public class ActivateSubscriptionWorkflow { + + public static boolean activateSubscription(String data){ + try { + //TODO: Decode the the data into a certificate and private key. + + //TODO: Verify that the certificate has really been signed by SavoirFaireLinux and is valid. + + //Build a keystore from the data. + KeyStore ks = KeyStore.getInstance("JKS"); + char[] password = "changeit".toCharArray(); + ks.load(null, password); + ks.setKeyEntry("license",null,null); + FileOutputStream fos = new FileOutputStream("license.jks"); + ks.store(fos, password); + fos.close(); + log.info("Succesfully activated your license!"); + return true; + } + catch (Exception e){ + log.error("The activation process failed with error: {}",e.getMessage()); + return false; + } + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/InstallationFinalizer.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/InstallationFinalizer.java index 8a6c5ab952663a465847bd6fe4074eb7d99dcbbd..78e2a170a6118bcc958bf28310e75b563e18c847 100644 --- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/InstallationFinalizer.java +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/InstallationFinalizer.java @@ -34,6 +34,7 @@ import net.jami.jams.nameserver.PublicNameServer; import net.jami.jams.server.Server; import net.jami.jams.server.servlets.api.install.CachedObjects; import net.jami.jams.server.startup.AuthModuleLoader; +import net.jami.jams.server.startup.UpdaterLoader; import java.io.File; import java.io.FileOutputStream; @@ -114,6 +115,8 @@ public class InstallationFinalizer { ks.store(fos, password); fos.close(); log.info("Successfully built keystore for for tomcat!"); + appUpdater = UpdaterLoader.loadUpdater(); + log.info("Started subscription and update service!"); Server.isInstalled.set(true); log.info("The installation has completed successfully, you can now use JAMS!"); } catch (Exception e) { diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java index 4d4d5b64111063ca0f1ff107a89459f6c9fd902a..8c70a2f05ca25f8e524b72d0a78ace2614a033ae 100644 --- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java @@ -82,7 +82,4 @@ public class RegisterDeviceFlow { return null; } } - - - } diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java index aaa60f5fdbc17c00ef8d79498e2a248d58ce25f6..02590f9152a2287329a35490aee75c84fc28ea88 100644 --- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java @@ -22,5 +22,56 @@ */ 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.devices.Device; +import net.jami.jams.common.objects.requests.RevocationRequest; +import net.jami.jams.common.objects.requests.RevocationType; +import net.jami.jams.common.objects.responses.DeviceRevocationResponse; + +import static net.jami.jams.server.Server.certificateAuthority; +import static net.jami.jams.server.Server.dataStore; + +@Slf4j public class RevokeDeviceFlow { + + public static DeviceRevocationResponse revokeDevice(String username, String deviceId){ + DeviceRevocationResponse response = new DeviceRevocationResponse(); + try { + StatementList statementList = new StatementList(); + StatementElement st1 = new StatementElement("owner","=",username,"AND"); + StatementElement st2 = new StatementElement("deviceId","=",deviceId,""); + statementList.addStatement(st1); + statementList.addStatement(st2); + Device device = dataStore.getDeviceDao().getObjects(statementList).get(0); + if (device == null) { + log.error("Could not find device!"); + return null; + } + RevocationRequest request = new RevocationRequest(); + request.setRevocationType(RevocationType.DEVICE); + request.setIdentifier(device.getCertificate().getSerialNumber()); + certificateAuthority.revokeCertificate(request); + long statTime = System.currentTimeMillis(); + while(certificateAuthority.getLatestCRL().get() + .getRevokedCertificate(device.getCertificate().getSerialNumber()) == null){ + log.warn("Certificate has not yet appeared in CRL!"); + if(System.currentTimeMillis() - statTime > 1000){ + log.error("The certificate has not appeared within 1 second, we are considering the operation has failed"); + response.setSuccess(false); + return response; + } + } + //Finally we return the successful response + response.setSuccess(true); + return response; + } + catch (Exception e){ + log.error("An exception has occurred while trying to revoke a device with error {}", e.getMessage()); + response.setSuccess(false); + response.setErrorDetails(e.getMessage()); + return response; + } + } } diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java index 371de583c6c35e09878fd69ba8b0c1d5aa20051e..d12e325adb8a1dcb0289083df24191419922ba01 100644 --- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java @@ -22,5 +22,54 @@ */ 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.requests.RevocationRequest; +import net.jami.jams.common.objects.requests.RevocationType; +import net.jami.jams.common.objects.responses.DeviceRevocationResponse; +import net.jami.jams.common.objects.user.User; + +import static net.jami.jams.server.Server.certificateAuthority; +import static net.jami.jams.server.Server.dataStore; + +@Slf4j public class RevokeUserFlow { + + public static DeviceRevocationResponse revokeUser(String username){ + DeviceRevocationResponse response = new DeviceRevocationResponse(); + try { + StatementList statementList = new StatementList(); + StatementElement st1 = new StatementElement("username","=",username,""); + statementList.addStatement(st1); + User user = dataStore.getUserDao().getObjects(statementList).get(0); + if (user == null) { + log.error("Could not find user!"); + return null; + } + RevocationRequest request = new RevocationRequest(); + request.setRevocationType(RevocationType.USER); + request.setIdentifier(user.getCertificate().getSerialNumber()); + certificateAuthority.revokeCertificate(request); + long statTime = System.currentTimeMillis(); + while(certificateAuthority.getLatestCRL().get() + .getRevokedCertificate(user.getCertificate().getSerialNumber()) == null){ + log.warn("Certificate has not yet appeared in CRL!"); + if(System.currentTimeMillis() - statTime > 1000){ + log.error("The certificate has not appeared within 1 second, we are considering the operation has failed"); + response.setSuccess(false); + return response; + } + } + //Finally we return the successful response + response.setSuccess(true); + return response; + } + catch (Exception e){ + log.error("An exception has occurred while trying to revoke a device with error {}", e.getMessage()); + response.setSuccess(false); + response.setErrorDetails(e.getMessage()); + return response; + } + } } diff --git a/jams-server/src/main/java/net/jami/jams/server/licensing/LicenseService.java b/jams-server/src/main/java/net/jami/jams/server/licensing/LicenseService.java new file mode 100644 index 0000000000000000000000000000000000000000..14d58c9c23520ec845b9985db04afb3e5bfc8143 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/licensing/LicenseService.java @@ -0,0 +1,90 @@ +package net.jami.jams.server.licensing; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.server.Server; +import org.json.JSONObject; + +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.concurrent.atomic.AtomicBoolean; + +@Getter +@Setter +@Slf4j +public class LicenseService { + + private AtomicBoolean activationStatus = new AtomicBoolean(false); + private String licenseType = "COMMUNITY"; + + //Load the license. + public void loadLicense() { + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + //Assuming the file exists, we just read the file. + String b64License = new String(Files.readAllBytes(Path.of(System.getProperty("user.dir") + File.separator + "license.dat"))); + //Since this is base64, we need to decode it. + String strLicense = new String(Base64.getDecoder().decode(b64License)); + //Now we need to split it. This is actually easy. + int cutPoint = strLicense.indexOf("-----BEGIN PRIVATE KEY-----"); + String publicKey = strLicense.substring(0,cutPoint); + + InputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); + Certificate c = cf.generateCertificate(inputStream); + String dn = ((X509Certificate)c).getSubjectDN().toString(); + LdapName ln = new LdapName(dn); + byte[] array = null; + for(Rdn rdn : ln.getRdns()) { + try { + array = Base64.getDecoder().decode(((String)rdn.getValue()).getBytes()); + } catch (IllegalArgumentException e) { + + } + } + + //This is kept inside the resources/oem folder, this is a lot less violent than what we had before. + //TODO: You should re-use the same technique to validate whatever they stick in the textbox. + InputStream input = LicenseService.class.getClassLoader().getResourceAsStream("oem/ca.crt"); + if (input == null) { + System.out.println("No CA Found... this is critical!"); + System.exit(-1); + } + Certificate ca = cf.generateCertificate(input); + try{ + ((X509Certificate) c).checkValidity(); + c.verify(ca.getPublicKey()); + Server.setActivated(true); + if (array != null) { + JSONObject jsonObject = new JSONObject(new String(array)); + licenseType = (String) jsonObject.get("type"); + } + } + catch (Exception e){ + Server.setActivated(false); + licenseType = "COMMUNITY"; + log.warn("Your license is no longer valid or has been tampered with - " + e.toString()); + } + } + catch (Exception e){ + Server.activated.set(false); + licenseType = "COMMUNITY"; + //logger.warning("An exception occurred while checking your license: " + e.toString()); + } + } + + public String getLicenseType() { + loadLicense(); + return licenseType; + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java index 7b0143e546ed6ef5456d1e4f387f0d2c05cca5f2..ef068abbecc5a2f36db70cb26fa3c114c7d48a39 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java @@ -22,32 +22,60 @@ */ package net.jami.jams.server.servlets.api.admin.devices; +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.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.responses.DeviceRevocationResponse; +import net.jami.jams.server.core.workflows.RevokeDeviceFlow; import java.io.IOException; -@WebServlet("/api/admin/device/*") +import static net.jami.jams.server.Server.dataStore; + +@WebServlet("/api/admin/device") public class DeviceServlet extends HttpServlet { //Get a detailed device info. @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doGet(req, resp); + String username = req.getParameter("username"); + String deviceId = req.getParameter("deviceId"); + StatementList statementList = new StatementList(); + StatementElement st1 = new StatementElement("owner","=",username,"AND"); + StatementElement st2 = new StatementElement("deviceId","=",deviceId,""); + statementList.addStatement(st1); + statementList.addStatement(st2); + resp.getOutputStream().write(JsonStream.serialize(dataStore.getDeviceDao().getObjects(statementList).get(0)).getBytes()); } //Update device data. @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doPut(req, resp); + String username = req.getParameter("username"); + String deviceId = req.getParameter("deviceId"); + String deviceName = req.getParameter("deviceName"); + StatementList update = new StatementList(); + StatementElement st0 = new StatementElement("deviceName","=",deviceName,""); + update.addStatement(st0); + StatementList constraint = new StatementList(); + StatementElement st1 = new StatementElement("owner","=",username,"AND"); + StatementElement st2 = new StatementElement("deviceId","=",deviceId,""); + update.addStatement(st1); + update.addStatement(st2); + if(dataStore.getDeviceDao().updateObject(update,constraint)) resp.setStatus(200); + else resp.sendError(500,"could not update the device's information!"); } //Revoke/delete a device. @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doDelete(req, resp); + DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getParameter("username").toString(),req.getParameter("deviceId")); + if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes()); + else resp.sendError(500,"An exception has occurred while trying to revoke a device!"); } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java index 57a0b50c070d7afa2cde9b7ac9f656604494a380..05dca7927ea98d71bcff42f3a147a89e07b5043d 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java @@ -22,20 +22,29 @@ */ package net.jami.jams.server.servlets.api.admin.devices; +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.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; import java.io.IOException; -@WebServlet("/api/admin/devices/*") +import static net.jami.jams.server.Server.dataStore; + +@WebServlet("/api/admin/devices") public class DevicesServlet extends HttpServlet { //Get a list of devices for a user. @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doGet(req, resp); + String username = req.getParameter("username"); + StatementList statementList = new StatementList(); + StatementElement st1 = new StatementElement("owner","=",username,""); + statementList.addStatement(st1); + resp.getOutputStream().write(JsonStream.serialize(dataStore.getDeviceDao().getObjects(statementList)).getBytes()); } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..88d3d7e1aa2f0c5d3d45d2025939d6bc5a219340 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java @@ -0,0 +1,48 @@ +package net.jami.jams.server.servlets.api.admin.directory; + +import com.jsoniter.JsonIterator; +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 lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.authmodule.AuthModuleKey; +import net.jami.jams.common.objects.user.UserProfile; + +import java.io.IOException; + +import static net.jami.jams.server.Server.userAuthenticationModule; + +@WebServlet("/api/admin/directory/entry") +@Slf4j +public class DirectoryEntryServlet extends HttpServlet { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + //Create a user profile. + try { + String realm = req.getParameter("directory"); + UserProfile userProfile = JsonIterator.deserialize(req.getInputStream().readAllBytes(), UserProfile.class); + userAuthenticationModule.getAuthSources().get(new AuthModuleKey(realm, AuthenticationSourceType.LOCAL)) + .setUserProfile(userProfile); + resp.setStatus(200); + } + catch (Exception e){ + log.error("Could not store a user profile with error {}",e.getMessage()); + resp.sendError(500,e.getMessage()); + } + + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + //Update a user's profile. + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + //This method will probably never be implemented. + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java index 8d02e5c0b08014fd2dd27115ab63414ebd39738a..17a7176b37cefa93aaa5a881c0a904afed3dd514 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java @@ -22,38 +22,76 @@ */ package net.jami.jams.server.servlets.api.admin.users; +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.authentication.AuthenticationSourceType; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.responses.DeviceRevocationResponse; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.server.core.workflows.RevokeUserFlow; import java.io.IOException; -@WebServlet("/api/admin/user/*") +import static net.jami.jams.server.Server.*; + +@WebServlet("/api/admin/user") public class UserServlet extends HttpServlet { //Get the user profile. @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doGet(req, resp); + StatementList statementList = new StatementList(); + StatementElement st1 = new StatementElement("username","=",req.getParameter("username"),""); + statementList.addStatement(st1); + resp.getOutputStream().write(JsonStream.serialize(dataStore.getUserDao().getObjects(statementList)).getBytes()); } //Create an internal user - this is always technically available, because internal users have the right to exist. @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doPost(req, resp); + User user = new User(); + user.setUsername(req.getParameter("username")); + user.setPassword("TEMP-PASSWORD"); + user.setRealm("LOCAL"); + user.setUserType(AuthenticationSourceType.LOCAL); + if(userAuthenticationModule.createUser(user.getUserType(),user.getRealm(),nameServer,user)){ + resp.getOutputStream().write(JsonStream.serialize(user).getBytes()); + return; + } + resp.sendError(500,"Could not create a user successfully!"); } //Update user data. @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doPut(req, resp); + String username = req.getParameter("username"); + //Check if he is AD/LDAP - then return a 403, because we can't set such password. + StatementList select = new StatementList(); + StatementElement st = new StatementElement("username","=",username,""); + if(dataStore.getUserDao().getObjects(select).get(0).getUserType() != AuthenticationSourceType.LOCAL){ + resp.sendError(500,"The user is not a local user, therefore we cannot change his data!"); + return; + } + StatementList update = new StatementList(); + StatementElement st0 = new StatementElement("password","=",req.getParameter("password"),""); + update.addStatement(st0); + StatementList constraint = new StatementList(); + StatementElement st1 = new StatementElement("username","=",username,""); + update.addStatement(st1); + if(dataStore.getUserDao().updateObject(update,constraint)) resp.setStatus(200); + else resp.sendError(500,"could not update the users's data field!"); } //Revoke a user. @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doDelete(req, resp); + DeviceRevocationResponse devResponse = RevokeUserFlow.revokeUser(req.getParameter("username")); + if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes()); + else resp.sendError(500,"An exception has occurred while trying to revoke a device!"); } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java index ddafb44102861363ca2b80c2eb51dd28cfc74d99..d0ba9762ae8ed335b0db6c1abdd6f293c44d5a29 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java @@ -22,6 +22,7 @@ */ package net.jami.jams.server.servlets.api.admin.users; +import com.jsoniter.output.JsonStream; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; @@ -30,12 +31,14 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import static net.jami.jams.server.Server.dataStore; + @WebServlet("/api/admin/users") public class UsersServlet extends HttpServlet { //Returns a list of users. @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doGet(req, resp); + resp.getOutputStream().write(JsonStream.serialize(dataStore.getDeviceDao().getObjects(null).get(0)).getBytes()); } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java index b8ade2b83ec91da277ecdc9fefce376b93db3b44..18a3c81344769f92f7ab0090723aca91e15c3544 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java @@ -33,7 +33,9 @@ import net.jami.jams.common.dao.StatementElement; import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.requests.DeviceRegistrationRequest; import net.jami.jams.common.objects.responses.DeviceRegistrationResponse; +import net.jami.jams.common.objects.responses.DeviceRevocationResponse; import net.jami.jams.server.core.workflows.RegisterDeviceFlow; +import net.jami.jams.server.core.workflows.RevokeDeviceFlow; import java.io.IOException; @@ -64,11 +66,26 @@ public class DeviceServlet extends HttpServlet { @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doPut(req, resp); + String username = req.getAttribute("username").toString(); + String deviceId = req.getPathInfo().replace("/",""); + String deviceName = req.getParameter("deviceName"); + StatementList update = new StatementList(); + StatementElement st0 = new StatementElement("deviceName","=",deviceName,""); + update.addStatement(st0); + StatementList constraint = new StatementList(); + StatementElement st1 = new StatementElement("owner","=",username,"AND"); + StatementElement st2 = new StatementElement("deviceId","=",deviceId,""); + update.addStatement(st1); + update.addStatement(st2); + if(dataStore.getDeviceDao().updateObject(update,constraint)) resp.setStatus(200); + else resp.sendError(500,"could not update the device's information!"); } @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doDelete(req, resp); + String deviceId = req.getPathInfo().replace("/",""); + DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getAttribute("username").toString(),deviceId); + if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes()); + else resp.sendError(500,"An exception has occurred while trying to revoke a device!"); } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoriesServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoriesServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..8f5b05e18235c26d95ce7524b8a4a805910222c4 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoriesServlet.java @@ -0,0 +1,21 @@ +package net.jami.jams.server.servlets.api.auth.directory; + +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 java.io.IOException; + +import static net.jami.jams.server.Server.userAuthenticationModule; + +@WebServlet("/api/auth/directories") +public class DirectoriesServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.getOutputStream().write(JsonStream.serialize(userAuthenticationModule.getAuthSources().keySet()).getBytes()); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..8a28415519396b9a2b5066a894a7816d60b5a914 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java @@ -0,0 +1,47 @@ +package net.jami.jams.server.servlets.api.auth.directory; + +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.authentication.AuthenticationSourceType; +import net.jami.jams.common.authmodule.AuthModuleKey; +import net.jami.jams.common.objects.user.UserProfile; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static net.jami.jams.server.Server.userAuthenticationModule; + +//This is an endpoint to manipulate directory entry-data, this make sense only for local setups. + +@WebServlet("/api/auth/directory/entry") +public class DirectoryEntryServlet extends HttpServlet { + + //Get a profile from a directory, we are assuming here + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (req.getParameter("directory") != null && req.getParameter("directoryType") != null) { + UserProfile[] profiles = userAuthenticationModule.getAuthSources() + .get(new AuthModuleKey(req.getParameter("directory"), AuthenticationSourceType.fromString(req.getParameter("directoryType")))) + .getUserProfile(req.getParameter("username"), "LOGON_NAME"); + resp.getOutputStream().write(JsonStream.serialize(profiles[0]).getBytes()); + return; + } + List<UserProfile> userProfiles = new ArrayList<>(); + userAuthenticationModule.getAuthSources().forEach((k, v) -> { + UserProfile[] profiles = v.getUserProfile(req.getParameter("username"), "LOGON_NAME"); + if (profiles != null && profiles.length != 0) userProfiles.addAll(Arrays.asList(profiles)); + }); + resp.getOutputStream().write(JsonStream.serialize(userProfiles.get(0)).getBytes()); + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + //This should work to modify a profile only in the case of a LOCAL directory. + } +} 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 d4428d6e1b2784c2dd3087c329cb8d0134792ebd..69e318c33e4f376173589eeb3759b6f975420969 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 @@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import net.jami.jams.common.objects.user.UserProfile; -import net.jami.jams.server.Server; import java.io.IOException; import java.util.ArrayList; diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..c89b7bd944511ba3c00f6e35b08d594d7b31d498 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java @@ -0,0 +1,51 @@ +package net.jami.jams.server.servlets.api.auth.user; + +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.authentication.AuthenticationSourceType; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; + +import java.io.IOException; + +import static net.jami.jams.server.Server.dataStore; + +@WebServlet("/api/auth/user") +public class UserServlet extends HttpServlet { + + //User can "read" his own profile. + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String username = req.getAttribute("username").toString(); + StatementList select = new StatementList(); + StatementElement st = new StatementElement("username","=",username,""); + select.addStatement(st); + resp.getOutputStream().write(JsonStream.serialize(dataStore.getUserDao().getObjects(select).get(0)).getBytes()); + } + + //The user can update 3 fields: password,privatekey,publickey + //For now we do not consider the possibility for privatekey, publickey for other reasons. + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String username = req.getAttribute("username").toString(); + //Check if he is AD/LDAP - then return a 401, because we can't set such password. + StatementList select = new StatementList(); + StatementElement st = new StatementElement("username","=",username,""); + if(dataStore.getUserDao().getObjects(select).get(0).getUserType() != AuthenticationSourceType.LOCAL){ + resp.sendError(500,"The user is not a local user, therefore we cannot change his data!"); + return; + } + StatementList update = new StatementList(); + StatementElement st0 = new StatementElement("password","=",req.getParameter("password"),""); + update.addStatement(st0); + StatementList constraint = new StatementList(); + StatementElement st1 = new StatementElement("username","=",username,""); + update.addStatement(st1); + if(dataStore.getUserDao().updateObject(update,constraint)) resp.setStatus(200); + else resp.sendError(500,"could not update the users's data field!"); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java index 6a9e1340779dfb8a00ee2e29f98b58a21dc0c435..50f6f9e6e6b0987d2046450290ded0e9fabaf404 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java @@ -68,9 +68,7 @@ public class StartInstallServlet extends HttpServlet { //This is the ONLY case where we write directy to the DB @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - StatementList statementList = new StatementList(); - statementList.setStatements(null); - if(dataStore.getUserDao().getObjects(statementList).size() != 0){ + if(dataStore.getUserDao().getObjects(null).size() != 0){ resp.sendError(500,"We have tried to create an administrative account where one already exists!"); return; } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/LicenseServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/LicenseServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..de2518e1b2e002f8e5be1458852755164d38260d --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/LicenseServlet.java @@ -0,0 +1,39 @@ +package net.jami.jams.server.servlets.api.update; + +import com.jsoniter.output.JsonStream; +import net.jami.jams.ca.JamsCA; +import net.jami.jams.common.cryptoengineapi.CertificateAuthority; +import net.jami.jams.common.utils.LicenseUtils; +import net.jami.jams.server.Server; +import net.jami.jams.server.licensing.LicenseService; +import net.jami.jams.server.startup.CryptoEngineLoader; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; + + +@WebServlet("/api/auth/license") +public class LicenseServlet extends HttpServlet { + + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + try { + resp.setStatus(200); + HashMap<String,Object> payload = new HashMap<>(); + payload.put("isActive", Server.isActivated()); + payload.put("licenseType", new LicenseService().getLicenseType()); + payload.put("currentVersion", LicenseUtils.checkVersion(System.getProperty("user.dir") + "/tmpjar/", System.getProperty("user.dir") + "/jams-server.jar")); + resp.getOutputStream().write(JsonStream.serialize(payload).getBytes()); + } + catch (Exception e){ + resp.setStatus(403); + } + } + +} \ No newline at end of file diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/NeedsUpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/NeedsUpdateServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..cae916d93dc1f27aeb0a5e280ba50d3381d675f7 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/NeedsUpdateServlet.java @@ -0,0 +1,46 @@ +package net.jami.jams.server.servlets.api.update; + +import com.jsoniter.output.JsonStream; +import net.jami.jams.ca.JamsCA; +import net.jami.jams.server.Server; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.logging.Logger; + +@WebServlet("/api/checkupdate") +public class NeedsUpdateServlet extends HttpServlet { + + private final static Logger logger = Logger.getLogger(NeedsUpdateServlet.class.getName()); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + try { + HashMap<String,Object> payload = new HashMap<>(); + if(!Server.updateInterface.getVersions().equals("{}")) { + payload.put("updateAvailable", Server.updateInterface.getUpdateAvailable().get()); + payload.put("newVersions", Server.updateInterface.getVersions()); + } + else + payload.put("updateAvailable",false); + + resp.getOutputStream().write(JsonStream.serialize(payload).getBytes()); + resp.setStatus(200); + } + catch (Exception e){ + resp.setStatus(500); + } + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/StartUpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/StartUpdateServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..52b1e38be8fd68afd411a3c070d54a2b812abe71 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/StartUpdateServlet.java @@ -0,0 +1,29 @@ +package net.jami.jams.server.servlets.api.update; + +import net.jami.jams.ca.JamsCA; +import net.jami.jams.server.Server; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.logging.Logger; + +@WebServlet("/api/startupdate") +public class StartUpdateServlet extends HttpServlet { + + private final static Logger logger = Logger.getLogger(StartUpdateServlet.class.getName()); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + try { + Server.updateInterface.approveUpdate(); + resp.setStatus(200); + } + catch (Exception e){ + resp.setStatus(500); + } + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/SubscriptionServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/SubscriptionServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..67239407bd82517d61f4ddd67e39167ec0d81b96 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/SubscriptionServlet.java @@ -0,0 +1,29 @@ +package net.jami.jams.server.servlets.api.update; + +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 java.io.IOException; + +@WebServlet("/api/subscription") +public class SubscriptionServlet extends HttpServlet { + + //Get the subscription status (see: SubscriptionStatusResponse.class) + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + //Upload the license here, which is really just uploading a base64 representation of the keypair - and store it + //somewhere. + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + //Create the keystore based on the uploadaded keypair. + + + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..3d5464613c9184b396022e96448d36426cf3d246 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java @@ -0,0 +1,25 @@ +package net.jami.jams.server.servlets.api.update; + +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 java.io.IOException; + +@WebServlet("/api/update") +public class UpdateServlet extends HttpServlet { + + //Return the current version number and the available version number. + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + //This is the do-update button. + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/user/LocalUserExistsServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/user/LocalUserExistsServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..5313cd79d72265f002dc14143070480000abc62f --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/user/LocalUserExistsServlet.java @@ -0,0 +1,75 @@ +package net.jami.jams.server.servlets.api.user; + +import com.jsoniter.JsonIterator; +import com.jsoniter.any.Any; +import com.jsoniter.output.JsonStream; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.ca.JamsCA; +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.server.Server; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; + +@WebServlet("/api/user/exists") +@Slf4j +public class LocalUserExistsServlet extends HttpServlet { + + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + StringBuilder stringBuilder = new StringBuilder(); + + try { + int x = 0; + while (true) { + x = req.getInputStream().read(); + if(x == -1) break; + stringBuilder.append((char) x); + } + } + catch (Exception e) { + log.error("error decoding request body"); + } + + + if(stringBuilder.toString() != null) { + + Any userData = JsonIterator.deserialize(stringBuilder.toString()); + String username = userData.get("username").toString(); + try { + if (Server.dataStore.userExists(username)) { + + StatementList statementList = new StatementList(); + StatementElement statementElement = new StatementElement("username", "=", username, ""); + statementList.addStatement(statementElement); + User user = Server.dataStore.getUserDao().getObjects(statementList).get(0); + + if (user != null && user.getUserType() == AuthenticationSourceType.LOCAL) { + resp.setStatus(200); + HashMap<String, String> statusInfo = new HashMap<>(); + statusInfo.put("exists", "true"); + resp.getOutputStream().write(JsonStream.serialize(statusInfo).getBytes()); + } else { + resp.setStatus(500); + HashMap<String, String> statusInfo = new HashMap<>(); + statusInfo.put("exists", "false"); + resp.getOutputStream().write(JsonStream.serialize(statusInfo).getBytes()); + } + } + + } catch (Exception e) { + log.info(e.toString()); + resp.setStatus(500); + } + } + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/user/LocalUserNeedsResetServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/user/LocalUserNeedsResetServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..9650dd8234ced5c776c648474bce2711067077cc --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/user/LocalUserNeedsResetServlet.java @@ -0,0 +1,94 @@ +package net.jami.jams.server.servlets.api.user; + + +import com.jsoniter.output.JsonStream; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.ca.JamsCA; +import net.jami.jams.common.authentication.local.LocalAuthSettings; +import net.jami.jams.common.authmodule.AuthModuleKey; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.server.Server; +import net.jami.jams.server.servlets.api.install.CachedObjects; +import net.jami.jams.common.authentication.AuthenticationSourceType; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +@WebServlet("/api/user/needsreset") +@Slf4j +public class LocalUserNeedsResetServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain); + resp.setContentType("application/json"); + StringBuilder stringBuilder = new StringBuilder(); + + try { + int x = 0; + while (true) { + x = req.getInputStream().read(); + if(x == -1) break; + stringBuilder.append((char) x); + } + } + catch (Exception e){ + log.error("error decoding request body"); + } + + + if(stringBuilder.toString() != null) { + + try { + if (CachedObjects.localAuthSettings != null && req.getParameterMap().containsKey("username")) { + + HashMap<String,String> statusInfo = new HashMap<>(); + String username = req.getParameter("username"); + + if(Server.dataStore.userExists(username)){ + StatementList statementList = new StatementList(); + StatementElement statementElement = new StatementElement("username","=",username,""); + statementList.addStatement(statementElement); + User user = Server.dataStore.getUserDao().getObjects(statementList).get(0); + + if (user != null && user.getNeedsPasswordReset() && user.getUserType() == AuthenticationSourceType.LOCAL) { + // show the OTP modal + char[] otp = Server.userAuthenticationModule.getOTP(req.getParameter("username")); + statusInfo.put("needsReset", "true"); + statusInfo.put("otp", new String(otp)); + } else { + // change status for user, generate new password and update info + // Server.userAuthenticationModule.updateReset(user, 1); + user.setNeedsPasswordReset(false); + String newPW = generateRandomPassword(); + user.setPassword(newPW); + Server.dataStore.getUserDao().storeObject(user); + statusInfo.put("needsReset", "false"); + statusInfo.put("newPW", newPW); + } + } + + resp.getOutputStream().write(JsonStream.serialize(statusInfo).getBytes()); + resp.setStatus(200); + } + + } catch (Exception e) { + log.info(e.toString()); + resp.setStatus(500); + } + } + } + + // TODO : change to char array + public String generateRandomPassword() { + return new SecureRandom().ints(12, 48, 58).mapToObj(i -> String.valueOf((char)i)).collect(Collectors.joining()); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java index a109f437b264eee2a5583ba674656b86a709bf48..d0f324accc0534d568589127a7384b4b8b4159be 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java @@ -34,7 +34,6 @@ import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.server.Server; import java.io.IOException; -import java.util.Date; import static net.jami.jams.server.Server.userAuthenticationModule; import static net.jami.jams.server.servlets.filters.JWTValidator.verifyLevel; diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java index c25b7c9d7cca8ddfe34ae1218f532961cf8ddf4d..056965d0f1d51cfe9dcfb1b5f9d37e89d217f477 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java @@ -33,7 +33,6 @@ import lombok.extern.slf4j.Slf4j; import net.jami.jams.server.Server; import java.io.IOException; -import java.util.Date; import static net.jami.jams.server.Server.userAuthenticationModule; import static net.jami.jams.server.servlets.filters.JWTValidator.verifyValidity; diff --git a/jams-server/src/main/java/net/jami/jams/server/startup/UpdaterLoader.java b/jams-server/src/main/java/net/jami/jams/server/startup/UpdaterLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..bf0b9cb6d57f8ce08a1e4c2132f1f17942533e69 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/startup/UpdaterLoader.java @@ -0,0 +1,22 @@ +package net.jami.jams.server.startup; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.updater.AppUpdater; +import net.jami.jams.common.utils.LibraryLoader; + +@Slf4j +public class UpdaterLoader { + + public static AppUpdater loadUpdater() { + try { + Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.updater.JAMSUpdater"); + log.info("Updater service started..."); + return (AppUpdater) cls.getConstructor().newInstance(); + } + catch (Exception e){ + log.error("Could not load update module..."); + return null; + } + } + +} diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java index 1e734b75b670e74862172fa91308d70617a591bd..b6489bfa3dcb6293a8bfff24136f7033512da930 100644 --- a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java @@ -71,6 +71,11 @@ public class LDAPConnector implements AuthenticationSource { return userProfileService.getUserProfile(queryString,field); } + @Override + public void setUserProfile(UserProfile userProfile) { + //does nothing since we cannot edit LDAP profiles. + } + @Override public boolean authenticate(String username, String password) { return authenticationService.authenticateUser(username,password); diff --git a/pom.xml b/pom.xml index e55eb9726a217afc9d8e262b4cb5eb0eacd5ff85..2e60499d4adf8f50dc746160a781d9e96de07d2a 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,7 @@ <artifactId>jams3-parent</artifactId> <packaging>pom</packaging> <version>${revision}</version> + <name>Jami Account Management Server</name> <modules> <module>jams-server</module> <module>jams-common</module> @@ -18,7 +19,7 @@ <module>jami-dht</module> <module>authentication-module</module> <module>jami-nameserver</module> - <module>jams-launcher</module> + <module>updater</module> </modules> <properties> @@ -47,6 +48,11 @@ <maven.clean.version>3.1.0</maven.clean.version> <nimbus.jwt.version>8.17</nimbus.jwt.version> <asm.version>8.0</asm.version> + <jeromq.version>0.5.2</jeromq.version> + <maven.model.version>3.2.5</maven.model.version> + <apache.httpcore.version>4.4.12</apache.httpcore.version> + <apache.httpclient.version>4.5.10</apache.httpclient.version> + <google.guava.version>20.0</google.guava.version> </properties> <dependencies> @@ -135,4 +141,4 @@ </plugins> </build> -</project> \ No newline at end of file +</project> diff --git a/updater/pom.xml b/updater/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc45cafa89560c6bbb9fbaea72ba50efa10db524 --- /dev/null +++ b/updater/pom.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>jams3-parent</artifactId> + <groupId>net.jami</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>updater</artifactId> + <dependencies> + <dependency> + <groupId>net.jami</groupId> + <artifactId>jams-common</artifactId> + <version>${revision}</version> + <scope>compile</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven.shade.version}</version> + <executions> + <!-- Run shade goal on package phase --> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <outputFile>../jams/libs/${project.artifactId}.jar</outputFile> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + <transformers> + <!-- add Main-Class to manifest file --> + <transformer + implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <manifestEntries> + <Main-Class>net.jami.jams.server.Server</Main-Class> + <Implementation-Title>${project.artifactId}</Implementation-Title> + <Implementation-Version>${project.version}</Implementation-Version> + <Class-Path>.</Class-Path> + </manifestEntries> + </transformer> + <transformer + implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file diff --git a/updater/src/main/java/module-info.java b/updater/src/main/java/module-info.java new file mode 100644 index 0000000000000000000000000000000000000000..1ac87ad45e5d283e339fc567447f5b35347c98f0 --- /dev/null +++ b/updater/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module updater { + requires jams.common; + requires lombok; + requires org.slf4j; + +} \ No newline at end of file diff --git a/updater/src/main/java/net/jami/jams/updater/JAMSUpdater.java b/updater/src/main/java/net/jami/jams/updater/JAMSUpdater.java new file mode 100644 index 0000000000000000000000000000000000000000..b8a49d0157a3a2e0caf8c7b2151cf92477ce2a46 --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/JAMSUpdater.java @@ -0,0 +1,26 @@ +package net.jami.jams.updater; + +import net.jami.jams.common.updater.AppUpdater; + +public class JAMSUpdater implements AppUpdater { + + UpdateDownloader updateDownloader = new UpdateDownloader(); + + public JAMSUpdater() { + } + + @Override + public String getCurrentVersion() { + return null; + } + + @Override + public String getLatestVersion() { + return null; + } + + @Override + public boolean downloadUpdates() { + return updateDownloader.doUpdate(); + } +} diff --git a/updater/src/main/java/net/jami/jams/updater/SFLTrustStore.java b/updater/src/main/java/net/jami/jams/updater/SFLTrustStore.java new file mode 100644 index 0000000000000000000000000000000000000000..3f7b5cc2a32221b34608c992cd13d59d1bfe3916 --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/SFLTrustStore.java @@ -0,0 +1,51 @@ +package net.jami.jams.updater; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.utils.X509Utils; + +import javax.net.ssl.X509TrustManager; +import java.io.InputStream; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +@Slf4j +public class SFLTrustStore implements X509TrustManager { + + X509Certificate[] sflCertificate = new X509Certificate[1]; + + //TODO: This just returns the SavoirFaireLinux CA everywhere - get this from the OEM resources folder. + public SFLTrustStore() { + try { + InputStream is = SFLTrustStore.class.getClassLoader().getResourceAsStream("ca.crt"); + X509Certificate certificate = X509Utils.getCertificateFromPEMString(new String(is.readAllBytes())); + sflCertificate[0] = certificate; + } + catch (Exception e){ + log.error("Could not load the SavoirFaireLinux certificate with error: {}",e.getMessage()); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + boolean failedCheck = false; + for(int i=0; i < x509Certificates.length; i++){ + try { + x509Certificates[i].verify(sflCertificate[0].getPublicKey()); + } + catch (Exception e){ + throw new CertificateException("Failed to verify the server's identity..."); + } + } + } + + //Implement this. + @Override + public X509Certificate[] getAcceptedIssuers() { + return sflCertificate; + } +} diff --git a/updater/src/main/java/net/jami/jams/updater/UpdateCheckTask.java b/updater/src/main/java/net/jami/jams/updater/UpdateCheckTask.java new file mode 100644 index 0000000000000000000000000000000000000000..d41fd0d60aeea9fb7bfe828e808f26c1f2e8264c --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/UpdateCheckTask.java @@ -0,0 +1,45 @@ +package net.jami.jams.updater; + +import lombok.extern.slf4j.Slf4j; + +import javax.net.ssl.HttpsURLConnection; +import java.net.URL; +import java.util.List; +import java.util.TimerTask; + +import static net.jami.jams.updater.UpdateDaemon.UPDATE_SERVER_URI; + +@Slf4j +public class UpdateCheckTask extends TimerTask { + + private final List<String> files; + + protected UpdateCheckTask(List<String> files) { + this.files = files; + } + + @Override + public void run() { + try { + URL url = new URL(UPDATE_SERVER_URI); + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + if (con.getResponseCode() == 200) { + StringBuilder responseData = new StringBuilder(); + int respSize = Integer.parseInt(con.getHeaderField("Content-Length")); + int currentSize = 0; + while (currentSize < respSize) { + responseData.append((char) con.getInputStream().read()); + currentSize++; + } + log.info("Response received from update server {} ",con.getResponseCode()); + //TODO: Populate the files which "need" to be downloaded. + } else { + log.info("An error has occurred while checking for an update: {} ", con.getResponseCode()); + } + } + catch (Exception e){ + log.error("Could not check for updates with error: {}",e.getMessage()); + } + } +} diff --git a/updater/src/main/java/net/jami/jams/updater/UpdateDaemon.java b/updater/src/main/java/net/jami/jams/updater/UpdateDaemon.java new file mode 100644 index 0000000000000000000000000000000000000000..5eeb87147bf3f8a2a831abfc7e4ed4990be6e077 --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/UpdateDaemon.java @@ -0,0 +1,22 @@ +package net.jami.jams.updater; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; + +@Getter +@Setter +public class UpdateDaemon extends Timer { + + //TODO: This should come from the resources file. + public static final String UPDATE_SERVER_URI = "https://jami.net"; + public static final List<String> updateFiles = new ArrayList<>(); + + public UpdateDaemon() { + this.schedule(new UpdateCheckTask(updateFiles),0,150_000); + } + +} diff --git a/updater/src/main/java/net/jami/jams/updater/UpdateDownloader.java b/updater/src/main/java/net/jami/jams/updater/UpdateDownloader.java new file mode 100644 index 0000000000000000000000000000000000000000..a66798c1d23ebb97aa0058add2f4f557f3b4c650 --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/UpdateDownloader.java @@ -0,0 +1,78 @@ +package net.jami.jams.updater; + +import lombok.extern.slf4j.Slf4j; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import java.net.URL; + +import static net.jami.jams.updater.UpdateDaemon.UPDATE_SERVER_URI; +import static net.jami.jams.updater.UpdateDaemon.updateFiles; + +@Slf4j +public class UpdateDownloader { + + private SSLSocketFactory sslSocketFactory; + + //returns the activation status of the server. + public boolean getActivationStatus(){ + return loadLicense(); + } + + + public boolean doUpdate(){ + try { + if (!loadLicense()) { + log.warn("This server does not have a valid license, no files will be download and no update" + + " will take place..."); + return false; + } + for(String file : UpdateDaemon.updateFiles) { + URL url = new URL(UPDATE_SERVER_URI); + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + con.setSSLSocketFactory(sslSocketFactory); + con.setRequestMethod("GET"); + if (con.getResponseCode() == 200) { + StringBuilder responseData = new StringBuilder(); + int respSize = Integer.parseInt(con.getHeaderField("Content-Length")); + int currentSize = 0; + while (currentSize < respSize) { + responseData.append((char) con.getInputStream().read()); + currentSize++; + } + updateFiles.remove(file); + log.info("Successfully downloaded file..."); + } else { + log.info("An error has occurred while trying to download a file: {} ", con.getResponseCode()); + return false; + } + } + return true; + } + catch (Exception e){ + log.error("Could not check for updates with error: {}",e.getMessage()); + return false; + } + } + + private boolean loadLicense(){ + try { + //We assume the keystore already exists, because it gets created upon upload. + //Basically just load the JKS keystore here. + //TODO: Load keystore from file. + //Initialize the SSL context & load the SFL trust store. + SSLContext sslContext = SSLContext.getInstance("SSL"); + //sslContext.init(kmf.getKeyManagers(), new SFLTrustStore[]{new SFLTrustStore()},null); + log.info("License loaded successfully!"); + sslSocketFactory = sslContext.getSocketFactory(); + return true; + } + catch (Exception e){ + log.error("An error occurred while trying to load the license: {}",e.getMessage()); + return false; + } + } + + +} diff --git a/updater/src/main/resources/ca.crt b/updater/src/main/resources/ca.crt new file mode 100644 index 0000000000000000000000000000000000000000..4e4a1fdcdc732799485e301a3686560a26e1ef96 --- /dev/null +++ b/updater/src/main/resources/ca.crt @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGJTCCBA2gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBmzELMAkGA1UEBhMCQ0Ex +CzAJBgNVBAgTAlFDMREwDwYDVQQHEwhNb250cmVhbDEgMB4GA1UEChMXU2F2b2ly +LWZhaXJlIExpbnV4IEluYy4xDTALBgNVBAsTBEpBTVMxGjAYBgNVBAMTEUpBTVMg +TGljZW5zaW5nIENBMR8wHQYJKoZIhvcNAQkBFhBzdXBwb3J0QGphbWkubmV0MB4X +DTIwMDIxNzIzNDQwMFoXDTMwMDIxNzIzNDQwMFowgZsxCzAJBgNVBAYTAkNBMQsw +CQYDVQQIEwJRQzERMA8GA1UEBxMITW9udHJlYWwxIDAeBgNVBAoTF1Nhdm9pci1m +YWlyZSBMaW51eCBJbmMuMQ0wCwYDVQQLEwRKQU1TMRowGAYDVQQDExFKQU1TIExp +Y2Vuc2luZyBDQTEfMB0GCSqGSIb3DQEJARYQc3VwcG9ydEBqYW1pLm5ldDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANwPgPJLFvPUrP6e2H+OzZZuF3+3 +EqJ5/2a2khT+VziqkEwwm0DUQP6sABuNnZq9VeA8dBknLCCJpPCzmXjjn75hR2kB +B1jDKjJMBIs2rlXdy/EZ2668ndt3bi06I0GWBJUKzbchTtAW+J75SXtdCSiBR0pM +LnvhfyEQEF2tMO8xFqtEfjDxi5TFXoKZyZXgJ+Q6JAC2eRxdOFdP0V+FDArXnAkY +Y7/psBb45nKWut2EQP9fJacP8TWat7oXNgJ3c8JD+0NqwE6qZWVnC5ggS0PEFeo+ +MhQENoJua0UEVliKDHnXCms1AbjZu4/DLuuN1HqgrqTowGQ8DQf7WXa944u++ZLa +G8BJ3jCDoOvUpEkGwC81rmto5ehVq8y+TaElpjHR2btUcDpQ4c/dleSxBm8OkDLA +mkernsOsyIgfAy/sXKoCUZwpZsCy0+NUoCkKcljNh9YgI8apPg3fQ2r5/bheJZGe +evrCn0/ZB4KEN0VzdEiWR89AUsgw+tez6HIngKNPft2fmKR8rAbgs/Ls/pqItlkG +yOJw0DVhwHXtfMHk0P9AeaBqtvcjLbn4ZLEB2+rsceef/2quCenlGJ9V2xga2kpk +sfbosSF0qx/1IsVw8NlqRQCJ1ys5hQ94UF/QQt/+v3qw8eqWtmoIdPpo1UBQdQog +1PMJdcZaumsihKIhAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLpkHueN4c6HBF0E2tgSa+sviwNIMAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEE +BAMCAAcwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0B +AQsFAAOCAgEAMtqlnIed+BaKhH9b1qJBRnSQqLLIYCa1NrWVRZ1J6ynxF+/amBZE +X6BsgnjAXFk4U4LnMgYV9ZunhawZiOnh8YxUFGKNtlToh08GyjYAi+2br06plWaL +0bmJk2QSybBjfU1H7XgaDGHJy4AsRkpL+7vhSFLqsczEJRo0k4yG6MdMsJD0tc8O +pukUF0f2dsQyg9Br8EOiVF4jz4aKAOHRPURbb9V7FssnIHBvgWfbQGYuK3eVorek +MIdmzYliUbJc0MuHPwhRYgw7lrwQKGnJNJP9+5WBawP4IFJt5TlAyFyXm7W0Vfui +5szsy+aEAp3TPbNJ16gZKRzT/1kT4HaPiiKo5PJmBIonvT6A0XTIJvHIBAoGSORG +bNMf894jG299Xtavz7O3jxqGqkBFB6O/KLa4loVQn7G0mpDnStRP7xHEVEx/hNyA +PnRIap6ymiYx6anEr96wTpRcbhIX2XSTQk4Boz9og5AMv046bS/othVtAwk6BlXF ++RLe6XB3P7tiLIU0c8x5FdDZjid2igUDiTHWFmLT5SFTo3aCaSb7QVXO+YAxomBz +6RFQ2Hto+9kSyiU/4fgWdQAngDSupI4dTNBfp7EDEStqoa3ewgD3f4dWoeh0VIxN +Rl2PC6898uZF35FBrXOWjh8sx8tlCaflFOAdIfizVdDez2dDZtZlREY= +-----END CERTIFICATE-----