From daae2398c8486615b74f76f5ad4d37be078c925a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Banno-Cloutier?= <leo.banno-cloutier@savoirfairelinux.com> Date: Tue, 15 Aug 2023 11:21:53 -0400 Subject: [PATCH] refactor: minor changes - jams-react-client: fix request spam on devices tab - don't send hashed password in response when creating a user Change-Id: I3c576003d57ef84ff523ebbf14ac611793a8b750 --- .dockerignore | 11 +++ Dockerfile | 14 ++-- generate-versions.py | 51 +++++++++--- .../ca/workers/csr/builders/UserBuilder.java | 68 ++++++++-------- .../requests/DeviceRevocationRequest.java | 39 ---------- .../adapters/ContactAdapter.java | 1 - .../net/jami/jams/common/utils/X509Utils.java | 2 + .../src/components/Devices/Devices.tsx | 12 ++- .../src/components/Grid/GridItem.tsx | 21 +++-- jams-react-client/src/globalUrls.tsx | 2 +- .../src/views/Blueprint/PolicyDataContext.tsx | 2 +- .../views/Blueprint/policyData.constants.tsx | 17 +++- .../src/views/Blueprint/updatePolicyData.tsx | 4 +- .../src/views/Contacts/Contacts.tsx | 1 - .../core/workflows/RegisterDeviceFlow.java | 5 +- .../api/admin/contacts/ContactServlet.java | 48 ++---------- .../api/admin/update/SubscriptionServlet.java | 29 +++---- .../auth/directory/DirectoryEntryServlet.java | 71 ++++++++--------- .../api/image/FileHandlerServlet.java | 8 +- .../server/servlets/filters/FilterUtils.java | 2 - .../connector/service/UserProfileService.java | 78 ++++++++++--------- 21 files changed, 226 insertions(+), 260 deletions(-) delete mode 100644 jams-common/src/main/java/net/jami/jams/common/objects/requests/DeviceRevocationRequest.java diff --git a/.dockerignore b/.dockerignore index c8561256..99519023 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,7 @@ .git .gitignore +.gitmodules +.gitreview Dockerfile .dockerignore @@ -7,4 +9,13 @@ Dockerfile **/node_modules **/target +.gradle +.idea +.vscode + +README.md +derby.log + +extras jams +jams-server/src/main/resources/webapp diff --git a/Dockerfile b/Dockerfile index ffd06171..654a2500 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,9 +22,9 @@ COPY jams-server/pom.xml jams-server/pom.xml # RUN mvn dependency:resolve --fail-never # RUN mvn dependency:go-offline --fail-never -am RUN mvn install -pl ad-connector,authentication-module,datastore,jami-dht,jami-nameserver,jams-ca,jams-common,jams-launcher,ldap-connector,jams-server -am -DskipTests -COPY . . FROM build as dev +COPY . . WORKDIR /app RUN mkdir -p /app/jams-server/src/main/resources/webapp \ && echo '<h1>Dev build, this is a placeholder index.html. Please connect to <a href="http://localhost:3000">localhost:3000</a> instead</h1>' \ @@ -38,18 +38,16 @@ CMD java -jar jams-server.jar 8080 \ FROM build as prod WORKDIR /app/jams-react-client +COPY jams-react-client . RUN npm run build -RUN mkdir -p ../jams-server/src/main/resources/webapp \ - && mv build/* ../jams-server/src/main/resources/webapp WORKDIR /app +COPY . . +RUN mkdir -p jams-server/src/main/resources/webapp \ + && mv jams-react-client/build/* jams-server/src/main/resources/webapp RUN mvn package ENV JAMS_VERSION=3.5 -RUN python3 generate-versions.py net.jami.jams.ca.JamsCA $JAMS_VERSION libs/cryptoengine.jar -RUN python3 generate-versions.py net.jami.jams.authmodule.UserAuthenticationModule $JAMS_VERSION libs/authentication-module.jar -RUN python3 generate-versions.py net.jami.jams.server.Server $JAMS_VERSION jams-server.jar -RUN python3 generate-versions.py net.jami.jams.ad.connector.ADConnector $JAMS_VERSION libs/ad-connector.jar -RUN python3 generate-versions.py net.jami.jams.ldap.connector.LDAPConnector $JAMS_VERSION libs/ldap-connector.jar +RUN python3 generate-versions.py $JAMS_VERSION RUN ./build-doc.sh WORKDIR /app/jams diff --git a/generate-versions.py b/generate-versions.py index a460bdc1..b28d4e16 100644 --- a/generate-versions.py +++ b/generate-versions.py @@ -4,6 +4,9 @@ import sys from pathlib import Path +here = Path(__file__).parent + + def read_versions(versions_file: Path) -> dict: if not versions_file.exists(): return {} @@ -12,31 +15,57 @@ def read_versions(versions_file: Path) -> dict: return json.load(f) +def get_md5_hash(filename: str) -> str: + md5_hash = hashlib.md5() -def main() -> None: - here = Path(__file__).parent - versions_file = here / "versions.json" + with open(here / "jams" / filename, "rb") as jar: + md5_hash.update(jar.read()) - class_name = sys.argv[1] - version = sys.argv[2] - filename = sys.argv[3] + return md5_hash.hexdigest() - versions = read_versions(versions_file) - md5_hash = hashlib.md5() +def generate_versions(class_name: str, version: str, filename: str) -> None: + versions_file = here / "versions.json" - with open(here / "jams" / filename, "rb") as jar: - md5_hash.update(jar.read()) + versions = read_versions(versions_file) versions[class_name] = { "version": version, "filename": filename, - "md5": md5_hash.hexdigest(), + "md5": get_md5_hash(filename), } with open(versions_file, "w") as f: json.dump(versions, f, indent=4) +def main() -> None: + if len(sys.argv) == 2: + version = sys.argv[1] + + class_to_filename = { + "net.jami.jams.ca.JamsCA": "libs/cryptoengine.jar", + "net.jami.jams.authmodule.UserAuthenticationModule": "libs/authentication-module.jar", + "net.jami.jams.server.Server": "jams-server.jar", + "net.jami.jams.ad.connector.ADConnector": "libs/ad-connector.jar", + "net.jami.jams.ldap.connector.LDAPConnector": "libs/ldap-connector.jar", + } + + for class_name, filename in class_to_filename.items(): + generate_versions(class_name, version, filename) + + return + + if len(sys.argv) != 4: + print(f"Usage: {sys.argv[0]} (<version> | <class_name> <version> <filename>)") + sys.exit(1) + + class_name = sys.argv[1] + version = sys.argv[2] + filename = sys.argv[3] + + generate_versions(class_name, version, filename) + + if __name__ == "__main__": main() diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java index 86f7437f..3d3827fd 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java @@ -44,6 +44,8 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.SecureRandom; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.util.Date; @Slf4j @@ -51,7 +53,6 @@ public class UserBuilder { public static User generateUser(User user) { try { - long now = System.currentTimeMillis(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(4096); KeyPair keyPair = keyPairGenerator.generateKeyPair(); @@ -60,23 +61,14 @@ public class UserBuilder { MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1) .digest(keyPair.getPublic().getEncoded()); user.getX509Fields().setUid(Hex.encodeHexString(digest)); - - X509v3CertificateBuilder builder = - new X509v3CertificateBuilder( - new JcaX509CertificateHolder(JamsCA.CA.getCertificate()).getSubject(), - new BigInteger(128, new SecureRandom()), - new Date(now - SHIFT), - new Date(now + JamsCA.userLifetime), - new X500Name(user.getX509Fields().getDN()), - SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); - user.setPrivateKey(keyPair.getPrivate()); - user.setCertificate( - CertificateSigner.signCertificate( - JamsCA.CA.getPrivateKey(), builder, ExtensionLibrary.userExtensions)); - log.info( - "New user certificate: Not valid after: " - + user.getCertificate().getNotAfter()); + + String dn = user.getX509Fields().getDN(); + SubjectPublicKeyInfo publicKeyInfo = + SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); + X509Certificate certificate = + generateSignedCertificate(dn, JamsCA.userLifetime, publicKeyInfo); + user.setCertificate(certificate); return user; } catch (Exception e) { log.error("Could not generate a user certificate: " + e); @@ -89,28 +81,18 @@ public class UserBuilder { } public static User refreshUser(User user, long userLifeTime) { - long now = System.currentTimeMillis(); X509Fields x509 = new X509Fields(); x509.setCommonName(user.getUsername()); x509.setUid(user.getJamiId()); user.setX509Fields(x509); try { - X509v3CertificateBuilder builder = - new X509v3CertificateBuilder( - new JcaX509CertificateHolder(JamsCA.CA.getCertificate()).getSubject(), - new BigInteger(128, new SecureRandom()), - new Date(now - SHIFT), - new Date(now + userLifeTime), - new X500Name(user.getX509Fields().getDN()), - new JcaX509CertificateHolder(user.getCertificate()) - .getSubjectPublicKeyInfo()); - user.setCertificate( - CertificateSigner.signCertificate( - JamsCA.CA.getPrivateKey(), builder, ExtensionLibrary.userExtensions)); - log.info( - "Refreshed user certificate: Not valid after: " - + user.getCertificate().getNotAfter()); + String dn = user.getX509Fields().getDN(); + SubjectPublicKeyInfo publicKeyInfo = + new JcaX509CertificateHolder(user.getCertificate()).getSubjectPublicKeyInfo(); + X509Certificate certificate = + generateSignedCertificate(dn, userLifeTime, publicKeyInfo); + user.setCertificate(certificate); return user; } catch (Exception e) { @@ -118,4 +100,24 @@ public class UserBuilder { return null; } } + + private static X509Certificate generateSignedCertificate( + String dn, long userLifeTime, SubjectPublicKeyInfo publicKeyInfo) + throws CertificateEncodingException { + long now = System.currentTimeMillis(); + X509v3CertificateBuilder builder = + new X509v3CertificateBuilder( + new JcaX509CertificateHolder(JamsCA.CA.getCertificate()).getSubject(), + new BigInteger(128, new SecureRandom()), + new Date(now - SHIFT), + new Date(now + userLifeTime), + new X500Name(dn), + publicKeyInfo); + + X509Certificate certificate = + CertificateSigner.signCertificate( + JamsCA.CA.getPrivateKey(), builder, ExtensionLibrary.userExtensions); + log.info("User certificate: Not valid after: {}", certificate.getNotAfter()); + return certificate; + } } 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 deleted file mode 100644 index 21a1b903..00000000 --- a/jams-common/src/main/java/net/jami/jams/common/objects/requests/DeviceRevocationRequest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2020 by Savoir-faire Linux - * Authors: William Enright <william.enright@savoirfairelinux.com> - * Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> - * Johnny Flores <johnny.flores@savoirfairelinux.com> - * Mohammed Raza <mohammed.raza@savoirfairelinux.com> - * Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -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/serialization/adapters/ContactAdapter.java b/jams-common/src/main/java/net/jami/jams/common/serialization/adapters/ContactAdapter.java index 80fb7a17..a8ba9a52 100644 --- a/jams-common/src/main/java/net/jami/jams/common/serialization/adapters/ContactAdapter.java +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/adapters/ContactAdapter.java @@ -25,7 +25,6 @@ public class ContactAdapter implements JsonSerializer<Contact>, JsonDeserializer contact.setUri(input.get("uri").getAsString()); contact.setConversationId(input.get("conversationId").getAsString()); - long timeAdded = 0L; if (input.has("added")) { timeAdded = input.get("added").getAsLong(); diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java b/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java index def3a3ec..1276d4db 100644 --- a/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java +++ b/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java @@ -66,6 +66,8 @@ public class X509Utils { private static final String PPK_TAIL = "\n-----END PUBLIC KEY-----"; public static PrivateKey getKeyFromPEMString(String keyString) { + if (keyString.isEmpty()) return null; + try { PEMParser parser = new PEMParser(new StringReader(keyString)); Object parsedObject = parser.readObject(); diff --git a/jams-react-client/src/components/Devices/Devices.tsx b/jams-react-client/src/components/Devices/Devices.tsx index 535aad00..84403f62 100755 --- a/jams-react-client/src/components/Devices/Devices.tsx +++ b/jams-react-client/src/components/Devices/Devices.tsx @@ -40,7 +40,7 @@ import i18next from "i18next"; const useStyles = makeStyles(styles); -export default function Devices(props) { +export default function Devices({ username }) { const classes = useStyles(); const history = useHistory(); @@ -49,9 +49,7 @@ export default function Devices(props) { const [displayName, setDisplayName] = useState(""); const [openEdit, setOpenEdit] = useState(false); const [openRevoke, setOpenRevoke] = useState(false); - const userData = { - username: props.username, - }; + const userData = { username }; useEffect(() => { auth.checkDirectoryType(() => { @@ -94,7 +92,7 @@ export default function Devices(props) { }); } }); - }, [history, selectedDevice, userData]); + }, []); function getDeviceStatus(device) { if (!device.revoked) { @@ -134,7 +132,7 @@ export default function Devices(props) { const handleUpdate = () => { if (auth.hasAdminScope()) { const data = { - username: props.username, + username, deviceId: selectedDevice.deviceId, deviceName: displayName, }; @@ -188,7 +186,7 @@ export default function Devices(props) { const handleDeviceRevoke = () => { if (auth.hasAdminScope()) { const data = { - username: props.username, + username, deviceId: selectedDevice.deviceId, }; axios( diff --git a/jams-react-client/src/components/Grid/GridItem.tsx b/jams-react-client/src/components/Grid/GridItem.tsx index 2161d6b8..922404b8 100644 --- a/jams-react-client/src/components/Grid/GridItem.tsx +++ b/jams-react-client/src/components/Grid/GridItem.tsx @@ -1,9 +1,7 @@ -import React from "react"; -// nodejs library to set properties for components -import PropTypes from "prop-types"; -// @mui/material components +import React, { FC } from "react"; import { makeStyles } from "@mui/styles"; -import Grid from "@mui/material/Grid"; +import { Grid, GridTypeMap } from "@mui/material"; +import { OverridableComponent } from "@mui/material/OverridableComponent"; const styles = { grid: { @@ -13,16 +11,17 @@ const styles = { const useStyles = makeStyles(styles); -export default function GridItem(props) { +interface GridItemProps extends OverridableComponent<GridTypeMap> { + children: React.ReactNode; +} + +const GridItem: FC<GridItemProps> = ({ children, ...rest }) => { const classes = useStyles(); - const { children, ...rest } = props; return ( <Grid item {...rest} className={classes.grid}> {children} </Grid> ); -} - -GridItem.propTypes = { - children: PropTypes.node, }; + +export default GridItem; diff --git a/jams-react-client/src/globalUrls.tsx b/jams-react-client/src/globalUrls.tsx index 12efdb95..12013152 100644 --- a/jams-react-client/src/globalUrls.tsx +++ b/jams-react-client/src/globalUrls.tsx @@ -2,7 +2,7 @@ const uri = ""; const current_uri = window.location.href; const backend_address = new URL( process.env.NODE_ENV === "development" - ? "http://localhost:8080" + ? window.location.origin.replace(/\d+$/, "") + "8080" : window.location.href ); const url_path = backend_address.protocol + "//" + backend_address.hostname; diff --git a/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx b/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx index 3a642e6f..653a6560 100644 --- a/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx +++ b/jams-react-client/src/views/Blueprint/PolicyDataContext.tsx @@ -39,7 +39,7 @@ export const PolicyDataContextProvider: FC<Props> = ({ children, }) => { const [policyData, setPolicyData] = useState(DEFAULT_POLICY_DATA); - const [snackbar, setSnackbar] = useState({ + const [snackbar, setSnackbar] = useState<SnackbarProps>({ open: false, severity: "success", message: "", diff --git a/jams-react-client/src/views/Blueprint/policyData.constants.tsx b/jams-react-client/src/views/Blueprint/policyData.constants.tsx index 9e204d59..09f3e5da 100644 --- a/jams-react-client/src/views/Blueprint/policyData.constants.tsx +++ b/jams-react-client/src/views/Blueprint/policyData.constants.tsx @@ -19,9 +19,7 @@ export const DEFAULT_UI_CUSTOMIZATION = { logoSize: 100, }; -export type UiCustomization = typeof DEFAULT_UI_CUSTOMIZATION; - -export const DEFAULT_POLICY_DATA = { +const DEFAULT_POLICY_DATA_PERMISSIONS = { videoEnabled: true, publicInCalls: false, autoAnswer: false, @@ -30,7 +28,9 @@ export const DEFAULT_POLICY_DATA = { rendezVous: false, blueprintModerators: [], +}; +const DEFAULT_POLICY_DATA_CONFIGURATION = { upnpEnabled: true, selectedTurnOption: "defaultTurn", @@ -41,8 +41,17 @@ export const DEFAULT_POLICY_DATA = { selectedDHTProxyOption: "defaultDHTProxy", proxyServer: "dhtproxy.jami.net", dhtProxyListUrl: "", +}; +export const DEFAULT_POLICY_DATA = { + ...DEFAULT_POLICY_DATA_PERMISSIONS, + ...DEFAULT_POLICY_DATA_CONFIGURATION, uiCustomization: DEFAULT_UI_CUSTOMIZATION, }; -export type PolicyData = typeof DEFAULT_POLICY_DATA; +export type UiCustomization = typeof DEFAULT_UI_CUSTOMIZATION; +export type PolicyDataPermissions = typeof DEFAULT_POLICY_DATA_PERMISSIONS; +export type PolicyDataNetwork = typeof DEFAULT_POLICY_DATA_CONFIGURATION; +export interface PolicyData extends PolicyDataPermissions, PolicyDataNetwork { + uiCustomization: UiCustomization; +} diff --git a/jams-react-client/src/views/Blueprint/updatePolicyData.tsx b/jams-react-client/src/views/Blueprint/updatePolicyData.tsx index 984df536..8066a59d 100644 --- a/jams-react-client/src/views/Blueprint/updatePolicyData.tsx +++ b/jams-react-client/src/views/Blueprint/updatePolicyData.tsx @@ -186,8 +186,8 @@ export const _updatePolicyData = ( policyData: PolicyData, setPolicyData: Dispatch<SetStateAction<PolicyData>>, field: string, - value: string, - setSnackbar: (snackbar: any) => void + value: any, + setSnackbar: (snackbar: SnackbarProps) => void ) => { setPolicyData((state) => ({ ...state, [field]: value })); diff --git a/jams-react-client/src/views/Contacts/Contacts.tsx b/jams-react-client/src/views/Contacts/Contacts.tsx index 5736bdc6..cd138279 100644 --- a/jams-react-client/src/views/Contacts/Contacts.tsx +++ b/jams-react-client/src/views/Contacts/Contacts.tsx @@ -413,7 +413,6 @@ export default function Users(props) { xs={12} sm={12} md={2} - wrap="nowrap" key={contact.uri} style={{ display: contact.display }} > 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 ed742f18..7bcac7f4 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 @@ -102,9 +102,8 @@ public class RegisterDeviceFlow { }); return response; } catch (Exception e) { - log.error( - "An exception has occurred while trying to enroll a device with error {}", - e.getMessage()); + log.error("An exception has occurred while trying to enroll a device"); + e.printStackTrace(); return null; } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/contacts/ContactServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/contacts/ContactServlet.java index 64117277..c1a30341 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/contacts/ContactServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/contacts/ContactServlet.java @@ -38,15 +38,9 @@ import net.jami.jams.common.objects.contacts.Contact; import net.jami.jams.common.objects.user.AccessLevel; import net.jami.jams.common.serialization.adapters.GsonFactory; import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler; -import net.jami.jams.common.utils.ContactMerger; - -import org.json.JSONObject; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Scanner; @WebServlet("/api/admin/contacts") public class ContactServlet extends HttpServlet { @@ -68,8 +62,8 @@ public class ContactServlet extends HttpServlet { @JsonContent protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - List<Contact> contactList = - dataStore.getContactDao().getByOwner(req.getParameter("username")); + String username = req.getParameter("username"); + List<Contact> contactList = dataStore.getContactDao().getByOwner(username); resp.getOutputStream().write(gson.toJson(contactList).getBytes()); } @@ -90,28 +84,9 @@ public class ContactServlet extends HttpServlet { @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Scanner s = new Scanner(req.getInputStream()).useDelimiter("\\A"); - String res = s.hasNext() ? s.next() : ""; - final JSONObject obj = new JSONObject(res); - - Contact contact = new Contact(); - // TODO: Replace with mergetool. - contact.setDisplayName(obj.get("displayName").toString()); - contact.setTimestamp(System.currentTimeMillis() / 1000); - contact.setStatus('A'); - contact.setOwner(req.getParameter("username")); - contact.setUri(obj.get("uri").toString()); - - List<Contact> localList = - dataStore.getContactDao().getByOwner(req.getParameter("username")); - List<Contact> remoteList = new ArrayList<>(); - remoteList.add(contact); - List<Contact> result = ContactMerger.mergeContacts(localList, remoteList); - - if (dataStore.getContactDao().storeContactList(result)) resp.setStatus(200); - else - TomcatCustomErrorHandler.sendCustomError( - resp, 500, "could not store a contact due to server-side error"); + String username = req.getParameter("username"); + net.jami.jams.server.servlets.api.auth.contacts.ContactServlet.addContact( + req, resp, username); } /** @@ -155,15 +130,8 @@ public class ContactServlet extends HttpServlet { @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - List<Contact> localList = - dataStore.getContactDao().getByOwner(req.getParameter("username")); - List<Contact> remoteList = Arrays.asList(gson.fromJson(req.getReader(), Contact[].class)); - - remoteList.forEach(contact -> contact.setOwner(req.getParameter("username"))); - List<Contact> result = ContactMerger.mergeContacts(localList, remoteList); - - if (!dataStore.getContactDao().storeContactList(result)) - TomcatCustomErrorHandler.sendCustomError(resp, 500, "Could not store contacts!"); - else resp.getOutputStream().write(gson.toJson(result).getBytes()); + String username = req.getParameter("username"); + net.jami.jams.server.servlets.api.auth.contacts.ContactServlet.addContacts( + req, resp, username); } } diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java index 89ea65e1..52602d18 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java @@ -23,6 +23,7 @@ package net.jami.jams.server.servlets.api.admin.update; import com.google.gson.Gson; +import com.google.gson.JsonObject; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; @@ -38,8 +39,6 @@ import net.jami.jams.common.serialization.adapters.GsonFactory; import net.jami.jams.server.Server; import net.jami.jams.server.licensing.LicenseService; -import org.json.JSONObject; - import java.io.FileWriter; import java.io.IOException; @@ -68,22 +67,18 @@ public class SubscriptionServlet extends HttpServlet { @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) @JsonContent protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String license = new String(req.getInputStream().readAllBytes()); - final JSONObject obj = new JSONObject(license); - license = obj.getString("base64License"); + JsonObject jsonObject = gson.fromJson(req.getReader(), JsonObject.class); + String license = jsonObject.get("base64License").getAsString(); + + // create .dat file to be used later + FileWriter fw = new FileWriter("license.dat"); + fw.write(license); + fw.close(); - if (license != null || !license.isBlank()) { - // create .dat file to be used later - FileWriter fw = new FileWriter("license.dat"); - fw.write(license); - fw.close(); - LicenseService licenseService = new LicenseService(); - licenseService.loadLicense(); - if (Server.activated.get()) { - resp.setStatus(200); - return; - } + LicenseService licenseService = new LicenseService(); + licenseService.loadLicense(); + if (Server.activated.get()) { + resp.setStatus(200); } - resp.setStatus(500); } } 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 index 04291124..dfb2cab4 100644 --- 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 @@ -37,7 +37,6 @@ 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.User; import net.jami.jams.common.objects.user.UserProfile; import net.jami.jams.common.serialization.adapters.GsonFactory; @@ -203,55 +202,49 @@ public class DirectoryEntryServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String directory = req.getParameter("directory"); + String directoryType = req.getParameter("directoryType"); + + String format = req.getParameter("format"); + boolean isInVCardFormat = format != null && format.equals("vcard"); + String jamiId = req.getParameter("jamiId"); - if (jamiId != null) { - User user = dataStore.getUserDao().getByJamiId(jamiId).get(0); - List<UserProfile> userProfiles = new ArrayList<>(); - userAuthenticationModule - .getAuthSources() - .forEach( - (k, v) -> { - userProfiles.addAll( - v.searchUserProfiles( - user.getUsername(), - "LOGON_NAME", - Optional.empty())); - }); - if (req.getParameter("format") != null && req.getParameter("format").equals("vcard")) { - resp.getOutputStream().write(userProfiles.get(0).getAsVCard().getBytes()); - } else resp.getOutputStream().write(gson.toJson(userProfiles.get(0)).getBytes()); - return; - } - if (req.getParameter("directory") != null && req.getParameter("directoryType") != null) { - List<UserProfile> profiles = + + String username = + jamiId != null + ? dataStore.getUserDao().getByJamiId(jamiId).get(0).getUsername() + : req.getParameter("username"); + + if (directory != null && directoryType != null) { + AuthModuleKey authModuleKey = + new AuthModuleKey( + directory, AuthenticationSourceType.fromString(directoryType)); + + List<UserProfile> userProfiles = userAuthenticationModule .getAuthSources() - .get( - new AuthModuleKey( - req.getParameter("directory"), - AuthenticationSourceType.fromString( - req.getParameter("directoryType")))) - .searchUserProfiles( - req.getParameter("username"), "LOGON_NAME", Optional.empty()); - if (req.getParameter("format") != null && req.getParameter("format").equals("vcard")) { - resp.getOutputStream().write(profiles.get(0).getAsVCard().getBytes()); - } else resp.getOutputStream().write(gson.toJson(profiles.get(0)).getBytes()); + .get(authModuleKey) + .searchUserProfiles(username, "LOGON_NAME", Optional.empty()); + + UserProfile userProfile = userProfiles.get(0); + String result = isInVCardFormat ? userProfile.getAsVCard() : gson.toJson(userProfile); + resp.getOutputStream().write(result.getBytes()); return; } + List<UserProfile> userProfiles = new ArrayList<>(); userAuthenticationModule .getAuthSources() + .values() .forEach( - (k, v) -> { + v -> { userProfiles.addAll( - v.searchUserProfiles( - req.getParameter("username"), - "LOGON_NAME", - Optional.empty())); + v.searchUserProfiles(username, "LOGON_NAME", Optional.empty())); }); - if (req.getParameter("format") != null && req.getParameter("format").equals("vcard")) { - resp.getOutputStream().write(userProfiles.get(0).getAsVCard().getBytes()); - } else resp.getOutputStream().write(gson.toJson(userProfiles.get(0)).getBytes()); + + UserProfile userProfile = userProfiles.get(0); + String result = isInVCardFormat ? userProfile.getAsVCard() : gson.toJson(userProfile); + resp.getOutputStream().write(result.getBytes()); } @Override diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java index b5358130..a4e1b265 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java @@ -1,5 +1,7 @@ package net.jami.jams.server.servlets.api.image; +import com.google.gson.Gson; + import jakarta.servlet.ServletException; import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.WebServlet; @@ -9,6 +11,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.Part; import lombok.extern.slf4j.Slf4j; + import net.jami.jams.common.serialization.adapters.GsonFactory; import java.io.File; @@ -20,8 +23,6 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; -import com.google.gson.Gson; - @MultipartConfig @Slf4j @WebServlet("/api/image/filehandler/*") @@ -87,7 +88,8 @@ public class FileHandlerServlet extends HttpServlet { } Map<String, String> map = new HashMap<>(); - String url = "/api/image/filehandler/" + blueprintName + "/" + imageType + "/" + fileName; + String url = + "/api/image/filehandler/" + blueprintName + "/" + imageType + "/" + fileName; map.put("url", url); Gson gson = GsonFactory.createGson(); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java index 5ba99ad9..fa299778 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java @@ -95,9 +95,7 @@ public class FilterUtils { } String username = token.getJWTClaimsSet().getSubject(); - log.info("Getting user from database"); User user = dataStore.getUserDao().getByUsername(username).orElseThrow(); - log.info("User retrieved from database: {}", user); if (!user.getAccessLevelName().equals("ADMIN") && certificateAuthority.getLatestCRL().get() != null) { diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java index 56c177fa..af8fe25f 100644 --- a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java @@ -38,7 +38,11 @@ import org.ldaptive.SearchRequest; import org.ldaptive.SearchResponse; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Slf4j @@ -58,45 +62,23 @@ public class UserProfileService { queryString.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); connection = connectionFactory.getConnection(); - try { - connection.open(); - SearchOperation search = new SearchOperation(connectionFactory); - SearchResponse res = search.execute(buildRequest(queryString, field, exactMatch)); - - DataStore.NUM_PAGES = - (Integer) res.getEntries().size() / DataStore.RESULTS_PER_PAGE; - if (res.getEntries().size() % DataStore.RESULTS_PER_PAGE != 0) - DataStore.NUM_PAGES++; - - if (page.isPresent() && !res.getEntries().isEmpty()) { - if (res.getEntries().size() < DataStore.RESULTS_PER_PAGE) - res = res.subResult(0, res.getEntries().size()); - else if (page.get() * DataStore.RESULTS_PER_PAGE > res.getEntries().size()) - res = - res.subResult( - (page.get() - 1) * DataStore.RESULTS_PER_PAGE, - res.getEntries().size()); - else - res = - res.subResult( - (page.get() - 1) * DataStore.RESULTS_PER_PAGE, - (page.get() * DataStore.RESULTS_PER_PAGE)); - } + connection.open(); + SearchOperation search = new SearchOperation(connectionFactory); + SearchResponse res = search.execute(buildRequest(queryString, field, exactMatch)); - if (res.getEntries().size() == 0) return new ArrayList<>(); - List<UserProfile> profilesFromResponse = - res.getEntries().stream() - .map(UserProfileService::profileFromResponse) - .collect(Collectors.toList()); - for (UserProfile p : profilesFromResponse) { - dataStore.getUserProfileDao().insertIfNotExists(p); - } + Collection<LdapEntry> entries = getEntriesPage(res, page); + + if (entries.isEmpty()) return new ArrayList<>(); - return profilesFromResponse; - } catch (Exception e) { - log.error("Could not search LDAP directory with error " + e); - return null; + List<UserProfile> profilesFromResponse = + entries.stream() + .map(UserProfileService::profileFromResponse) + .collect(Collectors.toList()); + for (UserProfile p : profilesFromResponse) { + dataStore.getUserProfileDao().insertIfNotExists(p); } + + return profilesFromResponse; } catch (Exception e) { log.info("Failed to search LDAP directory with error " + e); return null; @@ -105,6 +87,28 @@ public class UserProfileService { } } + private Collection<LdapEntry> getEntriesPage(SearchResponse res, Optional<Integer> page) { + int size = res.getEntries().size(); + + DataStore.NUM_PAGES = (Integer) size / DataStore.RESULTS_PER_PAGE; + if (size % DataStore.RESULTS_PER_PAGE != 0) DataStore.NUM_PAGES++; + + if (page.isEmpty() || size == 0) { + return res.getEntries(); + } + + if (size < DataStore.RESULTS_PER_PAGE) res = res.subResult(0, size); + else if (page.get() * DataStore.RESULTS_PER_PAGE > size) + res = res.subResult((page.get() - 1) * DataStore.RESULTS_PER_PAGE, size); + else + res = + res.subResult( + (page.get() - 1) * DataStore.RESULTS_PER_PAGE, + (page.get() * DataStore.RESULTS_PER_PAGE)); + + return res.getEntries(); + } + public static SearchRequest buildRequest(String queryString, String field, boolean exactMatch) { if (!exactMatch) { -- GitLab