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 6fb46a6294b2285aa55788014897968adc42aa7c..2b909cb1dcdee6d28942a34d341f620e194650fb 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 @@ -73,6 +73,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 1ef63a4f58b63c4a6473fc86665607115c747dcd..6b67fc9f2d62fd2f52d2915b0412df07168c43f5 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 @@ -37,7 +37,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 401e0d9041da50fee3151b47a7ec6d1763b4a294..b1c4d7c09ef878655ecb403f9978e4e9488528da 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 @@ -23,7 +23,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; @@ -126,6 +125,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); } 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 958a77076803eb5b77d334829355cafaca06a9cf..5afd6b3eed50cefb49d4b0b01dec6c02196988f2 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/AbstractDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/AbstractDao.java @@ -6,6 +6,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; @@ -22,11 +23,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)); @@ -42,7 +46,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 f5074bbdb513901ed2a9140eca489514c4fdaa95..c3e9ad42b9ad07870f437669ccad458bc26a2094 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/ContactDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/ContactDao.java @@ -1,5 +1,6 @@ package net.jami.datastore.dao; +import net.jami.jams.common.dao.StatementList; import net.jami.jams.common.objects.contacts.Contact; import java.util.LinkedHashMap; @@ -15,4 +16,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 9f7db3e553425bc8af3fdce2945c59446534c6c1..94e2fc99ea3c729b2501370e12527ab0be0593e9 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/DeviceDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/DeviceDao.java @@ -2,6 +2,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; @@ -59,4 +60,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 63545e5070ff15a148dd846b8df305fc13eeed82..35591e2a6f76f86bb6ba0d733f967dd682596fe8 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/SystemDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/SystemDao.java @@ -2,6 +2,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; @@ -57,4 +58,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 457f9074877c8752f4356a64e41122b19920b0ff..2ec325c04cccd3420a91e23a4c52f3f62df4ff5b 100644 --- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java +++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java @@ -51,6 +51,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 3fae34ca28b97fd2552cd2d76610c638ca9ac35d..7ffa8559029efac0003c025f140c0ea02daf662c 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 @@ -48,7 +48,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 1f8579677451a7f6d2bdaab10610bd1f25cdb086..91048b57b0d37def0ba9e27212c42022fb343b3c 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 @@ -3,8 +3,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/src/main/java/module-info.java b/jams-common/src/main/java/module-info.java index 4358de73c8dc950dee314af827dfc1df62752566..4be6ec881a3acfbea1fa52d65dd855b636ea0b8d 100644 --- a/jams-common/src/main/java/module-info.java +++ b/jams-common/src/main/java/module-info.java @@ -52,6 +52,7 @@ 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; 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 1b27f44b9cfd024f768cb8c7302418a22519c344..68c1314369c81ece50dcd5ddfde9251c3e0d047e 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 @@ -7,6 +7,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 329f003b8d5c5ded471bae9d7fc51e1b8053d514..ed5c90c3ffe2c7a750a574a179a9c47698cc8ee8 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 @@ -3,5 +3,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 3283283dd8d8e2b9fc2484f06931001d748857da..bfa65444657b5b402670881fe0a93d352d8abfc3 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 @@ -8,7 +8,6 @@ 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; 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 d5d53104dbda41eeb4b04e4ce6aae7d9189cfa9c..ebb6269bd5df314721312326911a1f04a669690e 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 @@ -3,11 +3,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; @@ -15,7 +14,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(" ") @@ -27,7 +26,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 d67273725f6635649bad1b7e382bd1101b74f8da..a572578cdfa01bba7f37ee8fde4477b16308a8e2 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 @@ -3,25 +3,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(" ") @@ -32,10 +31,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-server/src/main/java/module-info.java b/jams-server/src/main/java/module-info.java index ff32d400b7c3bc1f666899b05ebb1faf0fce6c5c..e8eff064935dffa28d51186710d73a45749408ea 100644 --- a/jams-server/src/main/java/module-info.java +++ b/jams-server/src/main/java/module-info.java @@ -10,6 +10,8 @@ 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; @@ -18,10 +20,19 @@ module jams.server { 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 48b23a5b2aa34ec97d31d2a49d669ae940c01128..dd1ff6112e4b6e8c56ae160079c9788a223ba390 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 @@ -10,6 +10,7 @@ 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; @@ -17,6 +18,7 @@ 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; @@ -37,6 +39,7 @@ 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(); @@ -80,6 +83,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 a7d8e8c714f38a9541a920198b00b59a2ca18fed..daaf7ec01faedded5635541847495e98d907fbab 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 @@ -15,7 +15,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. @@ -42,6 +41,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")) { @@ -74,6 +74,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 8916f9d27e93b6b6cdd68d03482c0b91eee367d0..e82870be59862379d3ae3efb844a2e5407fc092b 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 @@ -12,6 +12,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; @@ -92,6 +93,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 0b5640e9213ebf60055e79605a895b8bbd685f7f..673a5b136c2f9a6f42cc06f3dc50017a805b1353 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 @@ -60,7 +60,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 ea433ee5ed2420848a78507ddfa44a0ac6c5733e..9f2fb9eda5d7e74bd930d939fd16d9ed3422cf37 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 @@ -1,4 +1,55 @@ 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 59bc146717729cd36b853be788145030dad0a9ad..65da0414a20f13c397222b87aa8d9765ada399e3 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 @@ -1,4 +1,53 @@ 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/servlets/api/admin/devices/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java index dff1de3f6122db25ac1d73a2d918fac1b20b9324..4f67a0e77994987c2a67bef280e852b1bab009d9 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 @@ -1,31 +1,59 @@ 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 ba3162715ceea23e7aee24e86af6b50b75f84be2..b28b616c565aa546c025e53e8b7373cc8dae6aa5 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 @@ -1,19 +1,28 @@ 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 9b4cb4373ae36b90b7ff161334f75a22c2f7bc79..13411abe4b39631ae3138ab92c982d0203cea80c 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 @@ -1,37 +1,75 @@ 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 70047352ef4e6e72e6ce91ecb75cac7c0370acc7..0c71a02f55e0f466f3366c8fbae7f41a27bf170a 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 @@ -1,5 +1,6 @@ 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; @@ -8,12 +9,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 91dd970c7bacfbd888636313260f21a695d40781..622956d68d5e17e18b40786032a5005dcfa15494 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 @@ -11,7 +11,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; @@ -42,11 +44,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 ebea63526637a1857e6c378de8ec422134db024a..96c4f943c9dc32d12026370652eaf16457de8b38 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 @@ -7,7 +7,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 a41aaac61f45d84a90ba852d641cf0479151a24a..5bb3d82a889ad19b3f55f1bb24d766e4b2b4b4e1 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 @@ -46,9 +46,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/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/filters/AdminApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java index 0aa48f863c335a16930920c7cd996b034c279b09..7996b7cacff529fb003f394f628d7e53dc753e92 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 @@ -12,7 +12,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 e0fec4868ee33fd4d94a00546b1ba469baa10126..2ae16b84b95a1c2e3b22b250d48bb67eb806181b 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 @@ -11,7 +11,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 6865ba19f8269d377cfe57f1b8b2daca0931bf9d..427421595a53d9f54fa6a522f22f6c874f952010 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 @@ -49,6 +49,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 512c694dd5214312bc6b3152f4873765d8a2cd9f..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> @@ -140,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-----