diff --git a/jams-react-client/public/locales/en/translation.json b/jams-react-client/public/locales/en/translation.json index b40481b8e5f81d830162115c53174d9ce01271b3..ed419a166df1dc9a5e5fc5f0913e55d9bdb0751a 100644 --- a/jams-react-client/public/locales/en/translation.json +++ b/jams-react-client/public/locales/en/translation.json @@ -190,6 +190,7 @@ "are_you_sure_want_revoke": "Are you sure you want to revoke", "revoke": "Revoke", "revoke_user": "Revoke user", + "hide_revoked_users": "Hide revoked users", "edit_profile": "Edit profile", "change_password": "Change password", "minimum_3_characters": "Minimum 3 characters!", diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx b/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx index f5ca608038ae697a2253ea4abcb17858803dab91..579582e233cba47c946bbab3786d7538cecdfa76 100644 --- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx +++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx @@ -200,6 +200,7 @@ export interface UserProfile { faxNumber: string; mobileNumber: string; id: string; + revoked?: boolean; } export interface GroupMembership { @@ -236,6 +237,7 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ faxNumber: "", mobileNumber: "", id: "", + revoked: false, }); const [groupMemberships, setGroupMemberships] = useState<GroupMembership[]>( [] diff --git a/jams-react-client/src/views/Users/Users.tsx b/jams-react-client/src/views/Users/Users.tsx index a6bcee1bcc7e066604909a2bb2618eef5142743e..df9d554759271250c901a17328ba2be38c16a0fd 100644 --- a/jams-react-client/src/views/Users/Users.tsx +++ b/jams-react-client/src/views/Users/Users.tsx @@ -38,8 +38,7 @@ import CardAvatar from "components/Card/CardAvatar"; import CardBody from "components/Card/CardBody"; import Search from "@mui/icons-material/Search"; -import Checkbox from "@mui/material/Checkbox"; -import FormControlLabel from "@mui/material/FormControlLabel"; +import { Checkbox, FormControlLabel, Chip } from "@mui/material"; import axios from "axios"; import configApiCall from "api"; import auth from "auth"; @@ -98,13 +97,14 @@ const styles = { }, }; const useStyles = makeStyles(styles as any); +const label = i18next.t("revoked", "Revoked") as string; export default function Users() { const classes = useStyles(); const history = useHistory(); const [users, setUsers] = useState<UserProfile[]>([]); const [noUsersFound, setNoUsersFound] = useState(false); const [noMatchFound, setNoMatchFound] = useState(false); - const [showRevokedUsers, setShowRevokedUsers] = useState(false); + const [hideRevokedUsers, setHideRevokedUsers] = useState(true); const [searchValue, setSearchValue] = useState(""); @@ -200,8 +200,8 @@ export default function Users() { <FormControlLabel control={ <Checkbox - checked={showRevokedUsers} - onChange={() => setShowRevokedUsers(!showRevokedUsers)} + checked={hideRevokedUsers} + onChange={() => setHideRevokedUsers(!hideRevokedUsers)} inputProps={{ "aria-label": "primary checkbox" }} color="primary" /> @@ -211,7 +211,9 @@ export default function Users() { ? { marginLeft: "1rem" } : { marginLeft: "-8px" } } - label="Display revoked users" + label={ + i18next.t("hide_revoked_users", "Hide revoked users") as string + } /> <GridContainer> @@ -265,6 +267,7 @@ export default function Users() { } return 0; }) + .filter((user) => !hideRevokedUsers || !user.revoked) .map((user) => ( <GridItem xs={12} sm={6} md={3} lg={2} xl={2} key={user.username}> <Card profile> @@ -273,17 +276,32 @@ export default function Users() { style={{ height: "100%" }} > <CardBody profile> - <CardAvatar profile> - <img - src={ - user.profilePicture - ? "data:image/png;base64, " + user.profilePicture - : noProfilePicture - } - alt="..." - /> - </CardAvatar> - + <div + style={{ + display: "flex", + alignItems: "center", + justifyContent: "space-between", + }} + > + <CardAvatar profile> + <img + src={ + user.profilePicture + ? "data:image/png;base64, " + + user.profilePicture + : noProfilePicture + } + alt="..." + /> + </CardAvatar> + {user.revoked && ( + <Chip + label={i18next.t("revoked", "Revoked") as string} + variant="filled" + clickable={false} + /> + )} + </div> <GridContainer container direction="column" diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java index ad38a50a803869b5cbb8ca19d7d8b867440d2973..81dd41b448936fa8314cadf6a61fac9d8565fca9 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java @@ -16,6 +16,7 @@ */ package net.jami.jams.server.servlets.api.auth.directory; +import static net.jami.jams.server.Server.certificateAuthority; import static net.jami.jams.server.Server.dataStore; import static net.jami.jams.server.Server.nameServer; import static net.jami.jams.server.Server.userAuthenticationModule; @@ -43,6 +44,9 @@ import net.jami.jams.common.objects.user.User; import net.jami.jams.common.objects.user.UserProfile; import net.jami.jams.common.serialization.adapters.GsonFactory; +import org.bouncycastle.cert.X509CRLEntryHolder; +import org.bouncycastle.cert.X509CRLHolder; + import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; @@ -152,6 +156,23 @@ public class SearchDirectoryServlet extends HttpServlet { JsonObject obj = new JsonObject(); JsonArray profilesArray = gson.toJsonTree(userProfiles).getAsJsonArray(); + for (int i = 0; i < profilesArray.size(); i++) { + JsonObject profile = profilesArray.get(i).getAsJsonObject(); + String usernamerev = profile.get("username").getAsString(); + + User user = dataStore.getUserDao().getByUsername(usernamerev).orElse(null); + if (user != null) { + X509CRLHolder crl = certificateAuthority.getLatestCRL().get(); + if (crl != null) { + X509CRLEntryHolder revoked = + crl.getRevokedCertificate(user.getCertificate().getSerialNumber()); + profile.addProperty("revoked", revoked != null); + } else { + profile.addProperty("revoked", false); + } + } + } + obj.add("profiles", profilesArray); obj.addProperty("numPages", DataStore.NUM_PAGES);