Skip to content

Commit

Permalink
Merge branch 'main' into sharkbook.ai
Browse files Browse the repository at this point in the history
  • Loading branch information
holmbergius committed Dec 21, 2024
2 parents d8e9679 + f208f0d commit 91ea22e
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 57 deletions.
44 changes: 23 additions & 21 deletions frontend/src/components/DataTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,6 @@ const customStyles = {
},
};

const conditionalRowStyles = (theme) => [
{
when: (row) => row.tableID % 2 === 0,
style: {
backgroundColor: "#ffffff", // Light gray color
"&:hover": {
backgroundColor: theme?.primaryColors?.primary300 || "#e0f7fa",
},
},
},
{
when: (row) => row.tableID % 2 !== 0,
style: {
backgroundColor: "#f2f2f2", // White color
"&:hover": {
backgroundColor: theme?.primaryColors?.primary300 || "#e0f7fa",
},
},
},
];

const MyDataTable = ({
title = "",
columnNames = [],
Expand All @@ -52,6 +31,7 @@ const MyDataTable = ({
style = {},
tabs = [],
isLoading = false,
extraStyles = [],
onSelectedRowsChange = () => {},
onRowClicked = () => {},
}) => {
Expand All @@ -61,6 +41,28 @@ const MyDataTable = ({
const perPageOptions = [10, 20, 30, 40, 50];
const intl = useIntl();

const conditionalRowStyles = (theme) =>
[
{
when: (row) => row.tableID % 2 === 0,
style: {
backgroundColor: "#ffffff", // Light gray color
"&:hover": {
backgroundColor: theme?.primaryColors?.primary300 || "#e0f7fa",
},
},
},
{
when: (row) => row.tableID % 2 !== 0,
style: {
backgroundColor: "#f2f2f2", // White color
"&:hover": {
backgroundColor: theme?.primaryColors?.primary300 || "#e0f7fa",
},
},
},
].concat(extraStyles);

const wrappedColumns = useMemo(
() =>
columnNames.map((col) => {
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/pages/EncounterSearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useSearchParams } from "react-router-dom";
import { useIntl } from "react-intl";
import axios from "axios";
import { get } from "lodash";
import ThemeColorContext from "../ThemeColorProvider";

const columns = [
{ name: "INDIVIDUAL_ID", selector: "individualDisplayName" },
Expand All @@ -31,6 +32,7 @@ export default function EncounterSearch() {
const [paramsFormFilters, setParamsFormFilters] = useState([]);
const paramsObject = Object.fromEntries(searchParams.entries()) || {};
const [formFilters, setFormFilters] = useState([]);
const theme = React.useContext(ThemeColorContext);

const regularQuery = searchParams.get("regularQuery");

Expand Down Expand Up @@ -272,10 +274,20 @@ export default function EncounterSearch() {
onPerPageChange={queryID ? setSearchIdResultPerPage1 : setPerPage}
setSort={setSort}
loading={false}
extraStyles={[
{
when: (row) => row.access === "none",
style: {
backgroundColor: theme?.statusColors?.yellow100 || "#fff3cd",
"&:hover": {
backgroundColor: theme?.primaryColors?.primary300 || "#e0f7fa",
},
},
},
]}
onRowClicked={(row) => {
const url = `/encounters/encounter.jsp?number=${row.id}`;
window.open(url, "_blank");
// window.location.href = url;
}}
onSelectedRowsChange={(selectedRows) => {
console.log("Selected Rows: ", selectedRows);
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/ecocean/Encounter.java
Original file line number Diff line number Diff line change
Expand Up @@ -4311,6 +4311,21 @@ public void opensearchDocumentSerializer(JsonGenerator jgen, Shepherd myShepherd
}
}

// given a doc from opensearch, can user access it?
public static boolean opensearchAccess(org.json.JSONObject doc, User user,
Shepherd myShepherd) {
if ((doc == null) || (user == null)) return false;
if (doc.optBoolean("publiclyReadable", false)) return true;
if (doc.optString("submitterUserId", "__FAIL__").equals(user.getId())) return true;
if (user.isAdmin(myShepherd)) return true;
org.json.JSONArray viewUsers = doc.optJSONArray("viewUsers");
if (viewUsers == null) return false;
for (int i = 0; i < viewUsers.length(); i++) {
if (viewUsers.optString(i, "__FAIL__").equals(user.getId())) return true;
}
return false;
}

@Override public long getVersion() {
return Util.getVersionFromModified(modified);
}
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/org/ecocean/EncounterQueryProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ public static String queryStringBuilder(HttpServletRequest request, StringBuffer
return failed;
}
// Encounter enc = myShepherd.getEncounter(hId);
encIds.add(hId);
boolean hasAccess = Encounter.opensearchAccess(h.optJSONObject("_source"), user,
myShepherd);
if (hasAccess) encIds.add(hId);
}
} catch (Exception ex) {
ex.printStackTrace();
Expand Down Expand Up @@ -1597,8 +1599,12 @@ public static EncounterQueryResult processQuery(Shepherd myShepherd, HttpServlet
return new EncounterQueryResult(rEncounters, searchQuery.toString(),
"OpenSearch id " + searchQueryId);
}
Encounter enc = myShepherd.getEncounter(hId);
if (enc != null) rEncounters.add(enc);
boolean hasAccess = Encounter.opensearchAccess(h.optJSONObject("_source"), user,
myShepherd);
if (hasAccess) {
Encounter enc = myShepherd.getEncounter(hId);
if (enc != null) rEncounters.add(enc);
}
}
} catch (Exception ex) {
ex.printStackTrace();
Expand Down
56 changes: 28 additions & 28 deletions src/main/java/org/ecocean/OpenSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -653,35 +653,35 @@ public static boolean getPermissionsNeeded(Shepherd myShepherd) {
public static JSONObject querySanitize(JSONObject query, User user, Shepherd myShepherd)
throws IOException {
if ((query == null) || (user == null)) throw new IOException("empty query or user");
// do not add permissions clause when we are admin, as user has no restriction
if (user.isAdmin(myShepherd)) return query;
// if (!Collaboration.securityEnabled("context0")) TODO do we want to allow everything searchable?
/*
JSONObject permClause = new JSONObject("{\"bool\": {\"should\": [] }}");
"{\"bool\": {\"should\": [{\"term\": {\"publiclyReadable\": true}}, {\"term\": {\"viewUsers\": \""
+ user.getId() + "\"}} ] }}");
*/
JSONArray shouldArr = new JSONArray();
shouldArr.put(new JSONObject("{\"term\": {\"publiclyReadable\": true}}"));
shouldArr.put(new JSONObject("{\"term\": {\"submitterUserId\": \"" + user.getId() +
"\"}}"));
shouldArr.put(new JSONObject("{\"term\": {\"viewUsers\": \"" + user.getId() + "\"}}"));
JSONObject pshould = new JSONObject();
pshould.put("should", shouldArr);
JSONObject permClause = new JSONObject();
permClause.put("bool", pshould);
JSONObject newQuery = new JSONObject(query.toString());
try {
JSONArray filter = newQuery.getJSONObject("query").getJSONObject("bool").getJSONArray(
"filter");
filter.put(permClause);
} catch (Exception ex) {
System.out.println(
"OpenSearch.querySanitize() failed to find placement for permissions in query=" +
query + "; cause: " + ex);
throw new IOException("unable to find placement for permissions clause in query");
// see issue 958 - now we let query pass as-is for anyone, results are scrubbed later e.g. sanitizeDoc() below
return query;
}

// takes raw search result doc and presents only data user should see
public static JSONObject sanitizeDoc(final JSONObject sourceDoc, String indexName,
Shepherd myShepherd, User user)
throws IOException {
if ((user == null) || (sourceDoc == null)) throw new IOException("null user or sourceDoc");
JSONObject clean = new JSONObject();
// this is just punting future classes to later development (should never happen)
if (!"encounter".equals(indexName)) return clean;
boolean hasAccess = Encounter.opensearchAccess(sourceDoc, user, myShepherd);
if (hasAccess) {
clean = new JSONObject(sourceDoc.toString());
clean.remove("viewUsers");
clean.put("access", "full");
return clean;
}
clean.put("access", "none");
String[] okFields = new String[] {
"id", "version", "indexTimestamp", "version", "individualId", "individualDisplayName",
"occurrenceId", "otherCatalogNumbers", "dateSubmitted", "date", "locationId",
"locationName", "taxonomy", "assignedUsername", "numberAnnotations"
};
for (String fieldName : okFields) {
if (sourceDoc.has(fieldName)) clean.put(fieldName, sourceDoc.get(fieldName));
}
return newQuery;
return clean;
}

public static boolean indexingActive() {
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/org/ecocean/api/SearchApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,8 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
JSONObject doc = h.optJSONObject("_source");
if (doc == null)
throw new IOException("failed to parse doc in hits[" + i + "]");
// these are kind of noisy
doc.remove("viewUsers");
doc.remove("editUsers");
hitsArr.put(doc);
hitsArr.put(OpenSearch.sanitizeDoc(doc, indexName, myShepherd,
currentUser));
}
response.setHeader("X-Wildbook-Total-Hits", Integer.toString(totalHits));
response.setHeader("X-Wildbook-Search-Query-Id", searchQueryId);
Expand Down

0 comments on commit 91ea22e

Please sign in to comment.