Skip to content

Commit c5baffb

Browse files
Merge 25.11 to 26.3
2 parents e54eed7 + c74963f commit c5baffb

2 files changed

Lines changed: 117 additions & 24 deletions

File tree

laboratory/api-src/org/labkey/api/laboratory/assay/DefaultAssayParser.java

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,19 @@
2020
import org.apache.commons.beanutils.ConversionException;
2121
import org.apache.commons.lang3.StringUtils;
2222
import org.apache.logging.log4j.Level;
23-
import org.apache.logging.log4j.Logger;
2423
import org.apache.logging.log4j.LogManager;
24+
import org.apache.logging.log4j.Logger;
2525
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
2626
import org.json.JSONArray;
2727
import org.json.JSONObject;
28+
import org.labkey.api.assay.AssayProvider;
29+
import org.labkey.api.assay.AssayService;
2830
import org.labkey.api.collections.CaseInsensitiveHashMap;
2931
import org.labkey.api.data.Container;
32+
import org.labkey.api.data.ContainerManager;
33+
import org.labkey.api.data.ContainerType;
3034
import org.labkey.api.data.ConvertHelper;
31-
import org.labkey.api.data.DbSchema;
32-
import org.labkey.api.data.RuntimeSQLException;
3335
import org.labkey.api.data.SimpleFilter;
34-
import org.labkey.api.data.Table;
3536
import org.labkey.api.data.TableInfo;
3637
import org.labkey.api.data.TableSelector;
3738
import org.labkey.api.exp.PropertyDescriptor;
@@ -44,14 +45,14 @@
4445
import org.labkey.api.laboratory.LaboratoryService;
4546
import org.labkey.api.query.BatchValidationException;
4647
import org.labkey.api.query.FieldKey;
48+
import org.labkey.api.query.QueryService;
49+
import org.labkey.api.query.UserSchema;
4750
import org.labkey.api.query.ValidationException;
4851
import org.labkey.api.reader.ColumnDescriptor;
4952
import org.labkey.api.reader.ExcelFactory;
5053
import org.labkey.api.reader.Readers;
5154
import org.labkey.api.reader.TabLoader;
5255
import org.labkey.api.security.User;
53-
import org.labkey.api.assay.AssayProvider;
54-
import org.labkey.api.assay.AssayService;
5556
import org.labkey.api.util.FileType;
5657
import org.labkey.api.util.JsonUtil;
5758
import org.labkey.api.util.Pair;
@@ -466,10 +467,16 @@ protected void saveTemplate(ViewContext ctx, int templateId, long runId) throws
466467
{
467468
try
468469
{
469-
//validate the template exists
470-
TableInfo ti = DbSchema.get("laboratory").getTable("assay_run_templates");
470+
//validate the template exists. Note: this should always act against the current container, even if the container is a workbook (e.g., it should not allow cross-workbook actions)
471+
UserSchema us = QueryService.get().getUserSchema(ctx.getUser(), ctx.getContainer(), "laboratory");
472+
if (us == null)
473+
{
474+
throw new IllegalStateException("The laboratory schema is not available in container: " + ctx.getContainer().getPath());
475+
}
476+
477+
TableInfo ti = us.getTable("assay_run_templates");
471478
TableSelector ts = new TableSelector(ti, new SimpleFilter(FieldKey.fromString("rowid"), templateId), null);
472-
if (ts.getRowCount() == 0)
479+
if (!ts.exists())
473480
{
474481
throw new BatchValidationException(Collections.singletonList(new ValidationException("Unknown template: " + templateId)), null);
475482
}
@@ -478,11 +485,15 @@ protected void saveTemplate(ViewContext ctx, int templateId, long runId) throws
478485
row.put("runid", runId);
479486
row.put("status", "Complete");
480487

481-
Table.update(ctx.getUser(), ti, row, templateId);
488+
ti.getUpdateService().updateRows(ctx.getUser(), ctx.getContainer(), Arrays.asList(row), Arrays.asList(Map.of("rowid", templateId)), null, null);
489+
}
490+
catch (BatchValidationException e)
491+
{
492+
throw e;
482493
}
483-
catch (RuntimeSQLException e)
494+
catch (Exception e)
484495
{
485-
throw new BatchValidationException(Collections.singletonList(new ValidationException(e.getSQLException().getMessage())), null);
496+
throw new BatchValidationException(Collections.singletonList(new ValidationException(e.getMessage())), null);
486497
}
487498
}
488499

@@ -584,8 +595,12 @@ protected Map<String, Map<String, Object>> getTemplateRowMap(ImportContext conte
584595
if (templateId == null)
585596
return ret;
586597

587-
TableInfo ti = DbSchema.get("laboratory").getTable("assay_run_templates");
588-
598+
UserSchema us = QueryService.get().getUserSchema(context.getViewContext().getUser(), context.getViewContext().getContainer().getContainerFor(ContainerType.DataType.tabParent), "laboratory");
599+
if (us == null)
600+
{
601+
throw new IllegalStateException("Could not find the laboratory schema in container: " + context.getViewContext().getContainer().getContainerFor(ContainerType.DataType.tabParent).getPath());
602+
}
603+
TableInfo ti = us.getTable("assay_run_templates");
589604
TableSelector ts = new TableSelector(ti, new SimpleFilter(FieldKey.fromString("rowid"), templateId), null);
590605
Map<String, Object>[] maps = ts.getMapArray();
591606
if (maps.length == 0)
@@ -595,6 +610,18 @@ protected Map<String, Map<String, Object>> getTemplateRowMap(ImportContext conte
595610

596611
Map<String, Object> map = maps[0];
597612
JSONObject templateJson = new JSONObject((String)map.get("json"));
613+
614+
// This enforces that the request and existing record are from the same container, including for workbook/parents:
615+
Container rowContainer = ContainerManager.getForId(String.valueOf(map.get("container")));
616+
if (rowContainer == null)
617+
{
618+
throw new IllegalStateException("Unable to determine the container for template: " + templateId);
619+
}
620+
else if (!rowContainer.equals(context.getViewContext().getContainer()))
621+
{
622+
throw new IllegalStateException("Template is from the wrong container: " + templateId);
623+
}
624+
598625
JSONArray rows = templateJson.getJSONArray("ResultRows");
599626
for (JSONObject row : JsonUtil.toJSONObjectList(rows))
600627
{

laboratory/src/org/labkey/laboratory/assay/AssayHelper.java

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
import org.labkey.api.collections.CollectionUtils;
3333
import org.labkey.api.data.Container;
3434
import org.labkey.api.data.ContainerManager;
35-
import org.labkey.api.data.RuntimeSQLException;
35+
import org.labkey.api.data.ContainerType;
36+
import org.labkey.api.data.SimpleFilter;
3637
import org.labkey.api.data.TSVMapWriter;
37-
import org.labkey.api.data.Table;
3838
import org.labkey.api.data.TableInfo;
3939
import org.labkey.api.data.TableSelector;
4040
import org.labkey.api.exp.ChangePropertyDescriptorException;
@@ -51,9 +51,14 @@
5151
import org.labkey.api.laboratory.assay.AssayDataProvider;
5252
import org.labkey.api.laboratory.assay.AssayImportMethod;
5353
import org.labkey.api.query.BatchValidationException;
54+
import org.labkey.api.query.FieldKey;
55+
import org.labkey.api.query.QueryService;
56+
import org.labkey.api.query.UserSchema;
5457
import org.labkey.api.query.ValidationException;
5558
import org.labkey.api.security.User;
59+
import org.labkey.api.security.permissions.UpdatePermission;
5660
import org.labkey.api.util.FileUtil;
61+
import org.labkey.api.util.PageFlowUtil;
5762
import org.labkey.api.util.Pair;
5863
import org.labkey.api.view.ActionURL;
5964
import org.labkey.api.view.ViewContext;
@@ -66,6 +71,7 @@
6671
import java.io.File;
6772
import java.io.IOException;
6873
import java.util.ArrayList;
74+
import java.util.Arrays;
6975
import java.util.Collections;
7076
import java.util.HashMap;
7177
import java.util.List;
@@ -125,35 +131,52 @@ public Map<String, Object> saveTemplate(User u, Container c, ExpProtocol protoco
125131
{
126132
validateTemplate(u, c, protocol, templateId, title, importMethod, json);
127133

128-
TableInfo ti = LaboratorySchema.getInstance().getSchema().getTable(LaboratorySchema.TABLE_ASSAY_RUN_TEMPLATES);
134+
UserSchema us = QueryService.get().getUserSchema(u, c, "laboratory");
135+
if (us == null)
136+
{
137+
throw new IllegalStateException("Could not find the laboratory schema in container: " + c.getPath());
138+
}
139+
140+
TableInfo ti = us.getTable(LaboratorySchema.TABLE_ASSAY_RUN_TEMPLATES);
129141
Map<String, Object> row = new HashMap<>();
130142
row.put("assayId", protocol.getRowId());
131143
row.put("title", title);
132144
row.put("importMethod", importMethod);
133145
row.put("json", json.toString());
134-
row.put("container", c.getId());
135146

136147
if (templateId == null)
137148
{
138-
row = Table.insert(u, ti, row);
149+
BatchValidationException bve = new BatchValidationException();
150+
List<Map<String, Object>> rows = ti.getUpdateService().insertRows(u, c, Arrays.asList(row), bve, null, null);
151+
if (bve.hasErrors())
152+
{
153+
throw bve;
154+
}
155+
156+
return rows.get(0);
139157
}
140158
else
141159
{
142160
row.put("rowid", templateId);
143-
row = Table.update(u, ti, row, templateId);
161+
List<Map<String, Object>> rows = ti.getUpdateService().updateRows(u, c, Arrays.asList(row), Arrays.asList(Map.of("rowid", templateId)), null, null);
162+
return rows.get(0);
144163
}
145-
146-
return row;
147164
}
148-
catch (RuntimeSQLException e)
165+
catch (BatchValidationException e)
166+
{
167+
// Expected validation failures (e.g. from validateTemplate() or insertRows()) should propagate to the
168+
// client as-is, not be logged as server errors.
169+
throw e;
170+
}
171+
catch (Exception e)
149172
{
150173
_log.error(e.getMessage(), e);
151174
errors.addRowError(new ValidationException(e.getMessage()));
152175
throw errors;
153176
}
154177
}
155178

156-
public void validateTemplate(User u, Container c, ExpProtocol protocol, Integer templateId, String title, String importMethod, JSONObject json) throws BatchValidationException
179+
public void validateTemplate(User u, Container c, ExpProtocol protocol, @Nullable Integer templateId, String title, String importMethod, JSONObject json) throws BatchValidationException
157180
{
158181
BatchValidationException errors = new BatchValidationException();
159182

@@ -178,6 +201,49 @@ public void validateTemplate(User u, Container c, ExpProtocol protocol, Integer
178201
throw errors;
179202
}
180203

204+
// Verify if this template exists and permissions. This expects any existing row to be present in the current container:
205+
if (templateId != null)
206+
{
207+
// This queries the current container+workbooks to identify the existence of rows in other reasonable containers:
208+
UserSchema us = QueryService.get().getUserSchema(u, c.getContainerFor(ContainerType.DataType.tabParent), "laboratory");
209+
TableInfo ti = us.getTable("assay_run_templates");
210+
TableSelector ts = new TableSelector(ti, PageFlowUtil.set("container"), new SimpleFilter(FieldKey.fromString("rowId"), templateId), null);
211+
if (ts.exists())
212+
{
213+
Container rowContainer = ContainerManager.getForId(ts.getObject(String.class));
214+
if (rowContainer == null)
215+
{
216+
errors.addRowError(new ValidationException("Unable to determine the container for template: " + templateId));
217+
throw errors;
218+
}
219+
220+
if (!rowContainer.hasPermission("AssayHelper.validateTemplate()", u, UpdatePermission.class))
221+
{
222+
errors.addRowError(new ValidationException("The current user does not have permission to edit template: " + templateId));
223+
throw errors;
224+
}
225+
226+
if (!c.equals(rowContainer))
227+
{
228+
errors.addRowError(new ValidationException("Template " + templateId + " is not from this folder"));
229+
throw errors;
230+
}
231+
}
232+
else
233+
{
234+
errors.addRowError(new ValidationException("Unable to find template with ID: " + templateId));
235+
throw errors;
236+
}
237+
}
238+
else
239+
{
240+
if (!c.hasPermission("AssayHelper.validateTemplate()", u, UpdatePermission.class))
241+
{
242+
errors.addRowError(new ValidationException("The current user does not have permission to creates templates in folder: " + c.getName()));
243+
throw errors;
244+
}
245+
}
246+
181247
method.validateTemplate(u, c, protocol, templateId, title, json, errors);
182248

183249
if (errors.hasErrors())

0 commit comments

Comments
 (0)