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:
Léo Banno-Cloutier
2023-08-15 11:21:53 -04:00
parent 00922a448e
commit daae2398c8
21 changed files with 226 additions and 260 deletions

View File

@ -1,5 +1,7 @@
.git .git
.gitignore .gitignore
.gitmodules
.gitreview
Dockerfile Dockerfile
.dockerignore .dockerignore
@ -7,4 +9,13 @@ Dockerfile
**/node_modules **/node_modules
**/target **/target
.gradle
.idea
.vscode
README.md
derby.log
extras
jams jams
jams-server/src/main/resources/webapp

View File

@ -22,9 +22,9 @@ COPY jams-server/pom.xml jams-server/pom.xml
# RUN mvn dependency:resolve --fail-never # RUN mvn dependency:resolve --fail-never
# RUN mvn dependency:go-offline --fail-never -am # 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 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 FROM build as dev
COPY . .
WORKDIR /app WORKDIR /app
RUN mkdir -p /app/jams-server/src/main/resources/webapp \ 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>' \ && 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 FROM build as prod
WORKDIR /app/jams-react-client WORKDIR /app/jams-react-client
COPY jams-react-client .
RUN npm run build RUN npm run build
RUN mkdir -p ../jams-server/src/main/resources/webapp \
&& mv build/* ../jams-server/src/main/resources/webapp
WORKDIR /app 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 RUN mvn package
ENV JAMS_VERSION=3.5 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 $JAMS_VERSION
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 ./build-doc.sh RUN ./build-doc.sh
WORKDIR /app/jams WORKDIR /app/jams

View File

@ -4,6 +4,9 @@ import sys
from pathlib import Path from pathlib import Path
here = Path(__file__).parent
def read_versions(versions_file: Path) -> dict: def read_versions(versions_file: Path) -> dict:
if not versions_file.exists(): if not versions_file.exists():
return {} return {}
@ -12,31 +15,57 @@ def read_versions(versions_file: Path) -> dict:
return json.load(f) return json.load(f)
def get_md5_hash(filename: str) -> str:
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)
md5_hash = hashlib.md5() md5_hash = hashlib.md5()
with open(here / "jams" / filename, "rb") as jar: with open(here / "jams" / filename, "rb") as jar:
md5_hash.update(jar.read()) 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] = { versions[class_name] = {
"version": version, "version": version,
"filename": filename, "filename": filename,
"md5": md5_hash.hexdigest(), "md5": get_md5_hash(filename),
} }
with open(versions_file, "w") as f: with open(versions_file, "w") as f:
json.dump(versions, f, indent=4) 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__": if __name__ == "__main__":
main() main()

View File

@ -44,6 +44,8 @@ import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Date; import java.util.Date;
@Slf4j @Slf4j
@ -51,7 +53,6 @@ public class UserBuilder {
public static User generateUser(User user) { public static User generateUser(User user) {
try { try {
long now = System.currentTimeMillis();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096); keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair(); KeyPair keyPair = keyPairGenerator.generateKeyPair();
@ -60,23 +61,14 @@ public class UserBuilder {
MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1) MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1)
.digest(keyPair.getPublic().getEncoded()); .digest(keyPair.getPublic().getEncoded());
user.getX509Fields().setUid(Hex.encodeHexString(digest)); 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.setPrivateKey(keyPair.getPrivate());
user.setCertificate(
CertificateSigner.signCertificate( String dn = user.getX509Fields().getDN();
JamsCA.CA.getPrivateKey(), builder, ExtensionLibrary.userExtensions)); SubjectPublicKeyInfo publicKeyInfo =
log.info( SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
"New user certificate: Not valid after: " X509Certificate certificate =
+ user.getCertificate().getNotAfter()); generateSignedCertificate(dn, JamsCA.userLifetime, publicKeyInfo);
user.setCertificate(certificate);
return user; return user;
} catch (Exception e) { } catch (Exception e) {
log.error("Could not generate a user certificate: " + 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) { public static User refreshUser(User user, long userLifeTime) {
long now = System.currentTimeMillis();
X509Fields x509 = new X509Fields(); X509Fields x509 = new X509Fields();
x509.setCommonName(user.getUsername()); x509.setCommonName(user.getUsername());
x509.setUid(user.getJamiId()); x509.setUid(user.getJamiId());
user.setX509Fields(x509); user.setX509Fields(x509);
try { try {
X509v3CertificateBuilder builder = String dn = user.getX509Fields().getDN();
new X509v3CertificateBuilder( SubjectPublicKeyInfo publicKeyInfo =
new JcaX509CertificateHolder(JamsCA.CA.getCertificate()).getSubject(), new JcaX509CertificateHolder(user.getCertificate()).getSubjectPublicKeyInfo();
new BigInteger(128, new SecureRandom()), X509Certificate certificate =
new Date(now - SHIFT), generateSignedCertificate(dn, userLifeTime, publicKeyInfo);
new Date(now + userLifeTime), user.setCertificate(certificate);
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());
return user; return user;
} catch (Exception e) { } catch (Exception e) {
@ -118,4 +100,24 @@ public class UserBuilder {
return null; 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;
}
} }

View File

@ -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;
}
}

View File

@ -25,7 +25,6 @@ public class ContactAdapter implements JsonSerializer<Contact>, JsonDeserializer
contact.setUri(input.get("uri").getAsString()); contact.setUri(input.get("uri").getAsString());
contact.setConversationId(input.get("conversationId").getAsString()); contact.setConversationId(input.get("conversationId").getAsString());
long timeAdded = 0L; long timeAdded = 0L;
if (input.has("added")) { if (input.has("added")) {
timeAdded = input.get("added").getAsLong(); timeAdded = input.get("added").getAsLong();

View File

@ -66,6 +66,8 @@ public class X509Utils {
private static final String PPK_TAIL = "\n-----END PUBLIC KEY-----"; private static final String PPK_TAIL = "\n-----END PUBLIC KEY-----";
public static PrivateKey getKeyFromPEMString(String keyString) { public static PrivateKey getKeyFromPEMString(String keyString) {
if (keyString.isEmpty()) return null;
try { try {
PEMParser parser = new PEMParser(new StringReader(keyString)); PEMParser parser = new PEMParser(new StringReader(keyString));
Object parsedObject = parser.readObject(); Object parsedObject = parser.readObject();

View File

@ -40,7 +40,7 @@ import i18next from "i18next";
const useStyles = makeStyles(styles); const useStyles = makeStyles(styles);
export default function Devices(props) { export default function Devices({ username }) {
const classes = useStyles(); const classes = useStyles();
const history = useHistory(); const history = useHistory();
@ -49,9 +49,7 @@ export default function Devices(props) {
const [displayName, setDisplayName] = useState(""); const [displayName, setDisplayName] = useState("");
const [openEdit, setOpenEdit] = useState(false); const [openEdit, setOpenEdit] = useState(false);
const [openRevoke, setOpenRevoke] = useState(false); const [openRevoke, setOpenRevoke] = useState(false);
const userData = { const userData = { username };
username: props.username,
};
useEffect(() => { useEffect(() => {
auth.checkDirectoryType(() => { auth.checkDirectoryType(() => {
@ -94,7 +92,7 @@ export default function Devices(props) {
}); });
} }
}); });
}, [history, selectedDevice, userData]); }, []);
function getDeviceStatus(device) { function getDeviceStatus(device) {
if (!device.revoked) { if (!device.revoked) {
@ -134,7 +132,7 @@ export default function Devices(props) {
const handleUpdate = () => { const handleUpdate = () => {
if (auth.hasAdminScope()) { if (auth.hasAdminScope()) {
const data = { const data = {
username: props.username, username,
deviceId: selectedDevice.deviceId, deviceId: selectedDevice.deviceId,
deviceName: displayName, deviceName: displayName,
}; };
@ -188,7 +186,7 @@ export default function Devices(props) {
const handleDeviceRevoke = () => { const handleDeviceRevoke = () => {
if (auth.hasAdminScope()) { if (auth.hasAdminScope()) {
const data = { const data = {
username: props.username, username,
deviceId: selectedDevice.deviceId, deviceId: selectedDevice.deviceId,
}; };
axios( axios(

View File

@ -1,9 +1,7 @@
import React from "react"; import React, { FC } from "react";
// nodejs library to set properties for components
import PropTypes from "prop-types";
// @mui/material components
import { makeStyles } from "@mui/styles"; 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 = { const styles = {
grid: { grid: {
@ -13,16 +11,17 @@ const styles = {
const useStyles = makeStyles(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 classes = useStyles();
const { children, ...rest } = props;
return ( return (
<Grid item {...rest} className={classes.grid}> <Grid item {...rest} className={classes.grid}>
{children} {children}
</Grid> </Grid>
); );
}
GridItem.propTypes = {
children: PropTypes.node,
}; };
export default GridItem;

View File

@ -2,7 +2,7 @@ const uri = "";
const current_uri = window.location.href; const current_uri = window.location.href;
const backend_address = new URL( const backend_address = new URL(
process.env.NODE_ENV === "development" process.env.NODE_ENV === "development"
? "http://localhost:8080" ? window.location.origin.replace(/\d+$/, "") + "8080"
: window.location.href : window.location.href
); );
const url_path = backend_address.protocol + "//" + backend_address.hostname; const url_path = backend_address.protocol + "//" + backend_address.hostname;

View File

@ -39,7 +39,7 @@ export const PolicyDataContextProvider: FC<Props> = ({
children, children,
}) => { }) => {
const [policyData, setPolicyData] = useState(DEFAULT_POLICY_DATA); const [policyData, setPolicyData] = useState(DEFAULT_POLICY_DATA);
const [snackbar, setSnackbar] = useState({ const [snackbar, setSnackbar] = useState<SnackbarProps>({
open: false, open: false,
severity: "success", severity: "success",
message: "", message: "",

View File

@ -19,9 +19,7 @@ export const DEFAULT_UI_CUSTOMIZATION = {
logoSize: 100, logoSize: 100,
}; };
export type UiCustomization = typeof DEFAULT_UI_CUSTOMIZATION; const DEFAULT_POLICY_DATA_PERMISSIONS = {
export const DEFAULT_POLICY_DATA = {
videoEnabled: true, videoEnabled: true,
publicInCalls: false, publicInCalls: false,
autoAnswer: false, autoAnswer: false,
@ -30,7 +28,9 @@ export const DEFAULT_POLICY_DATA = {
rendezVous: false, rendezVous: false,
blueprintModerators: [], blueprintModerators: [],
};
const DEFAULT_POLICY_DATA_CONFIGURATION = {
upnpEnabled: true, upnpEnabled: true,
selectedTurnOption: "defaultTurn", selectedTurnOption: "defaultTurn",
@ -41,8 +41,17 @@ export const DEFAULT_POLICY_DATA = {
selectedDHTProxyOption: "defaultDHTProxy", selectedDHTProxyOption: "defaultDHTProxy",
proxyServer: "dhtproxy.jami.net", proxyServer: "dhtproxy.jami.net",
dhtProxyListUrl: "", dhtProxyListUrl: "",
};
export const DEFAULT_POLICY_DATA = {
...DEFAULT_POLICY_DATA_PERMISSIONS,
...DEFAULT_POLICY_DATA_CONFIGURATION,
uiCustomization: DEFAULT_UI_CUSTOMIZATION, 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;
}

View File

@ -186,8 +186,8 @@ export const _updatePolicyData = (
policyData: PolicyData, policyData: PolicyData,
setPolicyData: Dispatch<SetStateAction<PolicyData>>, setPolicyData: Dispatch<SetStateAction<PolicyData>>,
field: string, field: string,
value: string, value: any,
setSnackbar: (snackbar: any) => void setSnackbar: (snackbar: SnackbarProps) => void
) => { ) => {
setPolicyData((state) => ({ ...state, [field]: value })); setPolicyData((state) => ({ ...state, [field]: value }));

View File

@ -413,7 +413,6 @@ export default function Users(props) {
xs={12} xs={12}
sm={12} sm={12}
md={2} md={2}
wrap="nowrap"
key={contact.uri} key={contact.uri}
style={{ display: contact.display }} style={{ display: contact.display }}
> >

View File

@ -102,9 +102,8 @@ public class RegisterDeviceFlow {
}); });
return response; return response;
} catch (Exception e) { } catch (Exception e) {
log.error( log.error("An exception has occurred while trying to enroll a device");
"An exception has occurred while trying to enroll a device with error {}", e.printStackTrace();
e.getMessage());
return null; return null;
} }
} }

View File

@ -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.objects.user.AccessLevel;
import net.jami.jams.common.serialization.adapters.GsonFactory; import net.jami.jams.common.serialization.adapters.GsonFactory;
import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler; 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.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Scanner;
@WebServlet("/api/admin/contacts") @WebServlet("/api/admin/contacts")
public class ContactServlet extends HttpServlet { public class ContactServlet extends HttpServlet {
@ -68,8 +62,8 @@ public class ContactServlet extends HttpServlet {
@JsonContent @JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { throws ServletException, IOException {
List<Contact> contactList = String username = req.getParameter("username");
dataStore.getContactDao().getByOwner(req.getParameter("username")); List<Contact> contactList = dataStore.getContactDao().getByOwner(username);
resp.getOutputStream().write(gson.toJson(contactList).getBytes()); resp.getOutputStream().write(gson.toJson(contactList).getBytes());
} }
@ -90,28 +84,9 @@ public class ContactServlet extends HttpServlet {
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
protected void doPut(HttpServletRequest req, HttpServletResponse resp) protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { throws ServletException, IOException {
Scanner s = new Scanner(req.getInputStream()).useDelimiter("\\A"); String username = req.getParameter("username");
String res = s.hasNext() ? s.next() : ""; net.jami.jams.server.servlets.api.auth.contacts.ContactServlet.addContact(
final JSONObject obj = new JSONObject(res); req, resp, username);
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");
} }
/** /**
@ -155,15 +130,8 @@ public class ContactServlet extends HttpServlet {
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
protected void doPost(HttpServletRequest req, HttpServletResponse resp) protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { throws ServletException, IOException {
List<Contact> localList = String username = req.getParameter("username");
dataStore.getContactDao().getByOwner(req.getParameter("username")); net.jami.jams.server.servlets.api.auth.contacts.ContactServlet.addContacts(
List<Contact> remoteList = Arrays.asList(gson.fromJson(req.getReader(), Contact[].class)); req, resp, username);
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());
} }
} }

View File

@ -23,6 +23,7 @@
package net.jami.jams.server.servlets.api.admin.update; package net.jami.jams.server.servlets.api.admin.update;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet; 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.Server;
import net.jami.jams.server.licensing.LicenseService; import net.jami.jams.server.licensing.LicenseService;
import org.json.JSONObject;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
@ -68,22 +67,18 @@ public class SubscriptionServlet extends HttpServlet {
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN}) @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
@JsonContent @JsonContent
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String license = new String(req.getInputStream().readAllBytes()); JsonObject jsonObject = gson.fromJson(req.getReader(), JsonObject.class);
final JSONObject obj = new JSONObject(license); String license = jsonObject.get("base64License").getAsString();
license = obj.getString("base64License");
if (license != null || !license.isBlank()) { // create .dat file to be used later
// create .dat file to be used later FileWriter fw = new FileWriter("license.dat");
FileWriter fw = new FileWriter("license.dat"); fw.write(license);
fw.write(license); fw.close();
fw.close();
LicenseService licenseService = new LicenseService(); LicenseService licenseService = new LicenseService();
licenseService.loadLicense(); licenseService.loadLicense();
if (Server.activated.get()) { if (Server.activated.get()) {
resp.setStatus(200); resp.setStatus(200);
return;
}
} }
resp.setStatus(500);
} }
} }

View File

@ -37,7 +37,6 @@ import lombok.extern.slf4j.Slf4j;
import net.jami.jams.common.authentication.AuthenticationSourceType; import net.jami.jams.common.authentication.AuthenticationSourceType;
import net.jami.jams.common.authmodule.AuthModuleKey; 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.objects.user.UserProfile;
import net.jami.jams.common.serialization.adapters.GsonFactory; import net.jami.jams.common.serialization.adapters.GsonFactory;
@ -203,55 +202,49 @@ public class DirectoryEntryServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { 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"); String jamiId = req.getParameter("jamiId");
if (jamiId != null) {
User user = dataStore.getUserDao().getByJamiId(jamiId).get(0); String username =
List<UserProfile> userProfiles = new ArrayList<>(); jamiId != null
userAuthenticationModule ? dataStore.getUserDao().getByJamiId(jamiId).get(0).getUsername()
.getAuthSources() : req.getParameter("username");
.forEach(
(k, v) -> { if (directory != null && directoryType != null) {
userProfiles.addAll( AuthModuleKey authModuleKey =
v.searchUserProfiles( new AuthModuleKey(
user.getUsername(), directory, AuthenticationSourceType.fromString(directoryType));
"LOGON_NAME",
Optional.empty())); List<UserProfile> userProfiles =
});
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 =
userAuthenticationModule userAuthenticationModule
.getAuthSources() .getAuthSources()
.get( .get(authModuleKey)
new AuthModuleKey( .searchUserProfiles(username, "LOGON_NAME", Optional.empty());
req.getParameter("directory"),
AuthenticationSourceType.fromString( UserProfile userProfile = userProfiles.get(0);
req.getParameter("directoryType")))) String result = isInVCardFormat ? userProfile.getAsVCard() : gson.toJson(userProfile);
.searchUserProfiles( resp.getOutputStream().write(result.getBytes());
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());
return; return;
} }
List<UserProfile> userProfiles = new ArrayList<>(); List<UserProfile> userProfiles = new ArrayList<>();
userAuthenticationModule userAuthenticationModule
.getAuthSources() .getAuthSources()
.values()
.forEach( .forEach(
(k, v) -> { v -> {
userProfiles.addAll( userProfiles.addAll(
v.searchUserProfiles( v.searchUserProfiles(username, "LOGON_NAME", Optional.empty()));
req.getParameter("username"),
"LOGON_NAME",
Optional.empty()));
}); });
if (req.getParameter("format") != null && req.getParameter("format").equals("vcard")) {
resp.getOutputStream().write(userProfiles.get(0).getAsVCard().getBytes()); UserProfile userProfile = userProfiles.get(0);
} else resp.getOutputStream().write(gson.toJson(userProfiles.get(0)).getBytes()); String result = isInVCardFormat ? userProfile.getAsVCard() : gson.toJson(userProfile);
resp.getOutputStream().write(result.getBytes());
} }
@Override @Override

View File

@ -1,5 +1,7 @@
package net.jami.jams.server.servlets.api.image; package net.jami.jams.server.servlets.api.image;
import com.google.gson.Gson;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.annotation.WebServlet;
@ -9,6 +11,7 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part; import jakarta.servlet.http.Part;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.jami.jams.common.serialization.adapters.GsonFactory; import net.jami.jams.common.serialization.adapters.GsonFactory;
import java.io.File; import java.io.File;
@ -20,8 +23,6 @@ import java.nio.file.Paths;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.google.gson.Gson;
@MultipartConfig @MultipartConfig
@Slf4j @Slf4j
@WebServlet("/api/image/filehandler/*") @WebServlet("/api/image/filehandler/*")
@ -87,7 +88,8 @@ public class FileHandlerServlet extends HttpServlet {
} }
Map<String, String> map = new HashMap<>(); 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); map.put("url", url);
Gson gson = GsonFactory.createGson(); Gson gson = GsonFactory.createGson();

View File

@ -95,9 +95,7 @@ public class FilterUtils {
} }
String username = token.getJWTClaimsSet().getSubject(); String username = token.getJWTClaimsSet().getSubject();
log.info("Getting user from database");
User user = dataStore.getUserDao().getByUsername(username).orElseThrow(); User user = dataStore.getUserDao().getByUsername(username).orElseThrow();
log.info("User retrieved from database: {}", user);
if (!user.getAccessLevelName().equals("ADMIN") if (!user.getAccessLevelName().equals("ADMIN")
&& certificateAuthority.getLatestCRL().get() != null) { && certificateAuthority.getLatestCRL().get() != null) {

View File

@ -38,7 +38,11 @@ import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResponse; import org.ldaptive.SearchResponse;
import java.nio.charset.StandardCharsets; 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; import java.util.stream.Collectors;
@Slf4j @Slf4j
@ -58,45 +62,23 @@ public class UserProfileService {
queryString.getBytes(StandardCharsets.UTF_8), queryString.getBytes(StandardCharsets.UTF_8),
StandardCharsets.ISO_8859_1); StandardCharsets.ISO_8859_1);
connection = connectionFactory.getConnection(); connection = connectionFactory.getConnection();
try { connection.open();
connection.open(); SearchOperation search = new SearchOperation(connectionFactory);
SearchOperation search = new SearchOperation(connectionFactory); SearchResponse res = search.execute(buildRequest(queryString, field, exactMatch));
SearchResponse res = search.execute(buildRequest(queryString, field, exactMatch));
DataStore.NUM_PAGES = Collection<LdapEntry> entries = getEntriesPage(res, page);
(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 (entries.isEmpty()) return new ArrayList<>();
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 (res.getEntries().size() == 0) return new ArrayList<>(); List<UserProfile> profilesFromResponse =
List<UserProfile> profilesFromResponse = entries.stream()
res.getEntries().stream() .map(UserProfileService::profileFromResponse)
.map(UserProfileService::profileFromResponse) .collect(Collectors.toList());
.collect(Collectors.toList()); for (UserProfile p : profilesFromResponse) {
for (UserProfile p : profilesFromResponse) { dataStore.getUserProfileDao().insertIfNotExists(p);
dataStore.getUserProfileDao().insertIfNotExists(p);
}
return profilesFromResponse;
} catch (Exception e) {
log.error("Could not search LDAP directory with error " + e);
return null;
} }
return profilesFromResponse;
} catch (Exception e) { } catch (Exception e) {
log.info("Failed to search LDAP directory with error " + e); log.info("Failed to search LDAP directory with error " + e);
return null; 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) { public static SearchRequest buildRequest(String queryString, String field, boolean exactMatch) {
if (!exactMatch) { if (!exactMatch) {