mirror of
				https://git.jami.net/savoirfairelinux/jami-jams.git
				synced 2025-10-30 07:57:19 +08:00 
			
		
		
		
	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
This commit is contained in:
		| @ -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 | ||||
|  | ||||
							
								
								
									
										14
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								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 | ||||
|  | ||||
| @ -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 main() -> None: | ||||
|     here = Path(__file__).parent | ||||
|     versions_file = here / "versions.json" | ||||
|  | ||||
|     class_name = sys.argv[1] | ||||
|     version = sys.argv[2] | ||||
|     filename = sys.argv[3] | ||||
|  | ||||
|     versions = read_versions(versions_file) | ||||
|  | ||||
| def get_md5_hash(filename: str) -> str: | ||||
|     md5_hash = hashlib.md5() | ||||
|  | ||||
|     with open(here / "jams" / filename, "rb") as jar: | ||||
|         md5_hash.update(jar.read()) | ||||
|  | ||||
|     return md5_hash.hexdigest() | ||||
|  | ||||
|  | ||||
| def generate_versions(class_name: str, version: str, filename: str) -> None: | ||||
|     versions_file = here / "versions.json" | ||||
|  | ||||
|     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() | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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: "", | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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 })); | ||||
|  | ||||
|  | ||||
| @ -413,7 +413,6 @@ export default function Users(props) { | ||||
|               xs={12} | ||||
|               sm={12} | ||||
|               md={2} | ||||
|               wrap="nowrap" | ||||
|               key={contact.uri} | ||||
|               style={{ display: contact.display }} | ||||
|             > | ||||
|  | ||||
| @ -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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
|         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; | ||||
|             } | ||||
|         // 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); | ||||
|         } | ||||
|         resp.setStatus(500); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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)); | ||||
|             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++; | ||||
|             Collection<LdapEntry> entries = getEntriesPage(res, page); | ||||
|  | ||||
|                 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)); | ||||
|                 } | ||||
|             if (entries.isEmpty()) return new ArrayList<>(); | ||||
|  | ||||
|                 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); | ||||
|                 } | ||||
|  | ||||
|                 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) { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Léo Banno-Cloutier
					Léo Banno-Cloutier