Skip to content

Commit

Permalink
Merge branch 'develop' into fb_importTemplates
Browse files Browse the repository at this point in the history
  • Loading branch information
XingY committed Dec 26, 2024
2 parents 7465bfa + 4d25de6 commit 69c7f7d
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 155 deletions.
1 change: 1 addition & 0 deletions api/src/org/labkey/api/exp/query/ExpMaterialTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum Column
SourceProtocolLSID,
StoredAmount,
Units,
IsPlated,
}

default void setSupportTableRules(boolean supportTableRules)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import org.labkey.api.attachments.AttachmentParent;
import org.labkey.api.reports.report.ReportUrls;
import org.labkey.api.reports.report.r.ParamReplacement;
import org.labkey.api.util.GUID;
import org.labkey.api.util.ImageUtil;
import org.labkey.api.util.PageFlowUtil;

import java.io.File;
Expand Down Expand Up @@ -64,10 +64,12 @@ protected String renderInternalAsString(File file)
{
File newFile = moveToTemp(file, "RReportPdf");
// file hasn't been saved yet
String key = "temp:" + GUID.makeGUID();
getViewContext().getRequest().getSession(true).setAttribute(key, newFile);
String key = ImageUtil.setFileInSession(getViewContext().getRequest(), newFile);
downloadUrl = PageFlowUtil.urlProvider(ReportUrls.class).urlStreamFile(getViewContext().getContainer()).
addParameters(PageFlowUtil.map("sessionKey", key, "deleteFile", "false", "attachment", "true")).getLocalURIString();
addParameters(PageFlowUtil.map(
ImageUtil.FILE_SESSION_PARAM, key,
ImageUtil.DELETE_FILE_PARAM, "false",
ImageUtil.ATTACHMENT_PARAM, "true")).getLocalURIString();
}
return downloadUrl;
}
Expand Down
8 changes: 3 additions & 5 deletions api/src/org/labkey/api/reports/report/r/view/ImageOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@

import org.apache.commons.lang3.BooleanUtils;
import org.labkey.api.reports.Report;
import org.labkey.api.reports.report.r.RReport;
import org.labkey.api.reports.report.ReportDescriptor;
import org.labkey.api.reports.report.ReportUrls;
import org.labkey.api.reports.report.ScriptOutput;
import org.labkey.api.reports.report.ScriptReportDescriptor;
import org.labkey.api.reports.report.r.AbstractParamReplacement;
import org.labkey.api.reports.report.r.ParamReplacement;
import org.labkey.api.reports.report.r.RReport;
import org.labkey.api.thumbnail.Thumbnail;
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.GUID;
import org.labkey.api.util.ImageUtil;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.view.ActionURL;
Expand Down Expand Up @@ -133,10 +132,9 @@ protected String renderInternalAsString(File file)

if (imgFile != null)
{
String key = "temp:" + GUID.makeGUID();
getViewContext().getRequest().getSession(true).setAttribute(key, imgFile);
String key = ImageUtil.setFileInSession(getViewContext().getRequest(), imgFile);
ActionURL url = PageFlowUtil.urlProvider(ReportUrls.class).urlStreamFile(getViewContext().getContainer());
url.addParameters(PageFlowUtil.map("sessionKey", key, "deleteFile", Boolean.toString(_deleteFile), "cacheFile", "true"));
url.addParameters(PageFlowUtil.map(ImageUtil.FILE_SESSION_PARAM, key, ImageUtil.DELETE_FILE_PARAM, Boolean.toString(_deleteFile), ImageUtil.CACHE_FILE_PARAM, "true"));
imgUrl = url.getLocalURIString();
}
}
Expand Down
69 changes: 39 additions & 30 deletions api/src/org/labkey/api/security/AuthFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,43 +125,52 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
}
}

// No startup failure, so check for SSL redirection
if (!req.getScheme().equalsIgnoreCase("https") && AppProps.getInstance().isSSLRequired())
if (AppProps.getInstance().isSSLRequired())
{
// We can't redirect posts (we'll lose the post body), so return an error code
if ("post".equalsIgnoreCase(req.getMethod()))
// No startup failure, so check for SSL redirection
if (!req.getScheme().equalsIgnoreCase("https"))
{
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Can't POST to an http URL; POSTs to this server require https");
return;
}
// We can't redirect posts (we'll lose the post body), so return an error code
if ("post".equalsIgnoreCase(req.getMethod()))
{
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Can't POST to an http URL; POSTs to this server require https");
return;
}

StringBuffer originalURL = req.getRequestURL();
if (!StringUtils.isBlank(req.getQueryString()))
{
originalURL.append("?");
originalURL.append(req.getQueryString());
}
URL url = new URL(originalURL.toString());
int port = AppProps.getInstance().getSSLPort();
StringBuffer originalURL = req.getRequestURL();
if (!StringUtils.isBlank(req.getQueryString()))
{
originalURL.append("?");
originalURL.append(req.getQueryString());
}
URL url = new URL(originalURL.toString());
int port = AppProps.getInstance().getSSLPort();

// Check the SSL configuration if this is the first time doing an SSL redirect. Note: The redirect and check must
// happen before ensureFirstRequestHandled() so AppProps gets initialized with the SSL scheme & port. That means
// this check can't be handled in a FirstRequestListener.
if (!_sslChecked)
{
HttpsUtil.checkSslRedirectConfiguration(req, port);
_sslChecked = true;
}
// Check the SSL configuration if this is the first time doing an SSL redirect. Note: The redirect and check must
// happen before ensureFirstRequestHandled() so AppProps gets initialized with the SSL scheme & port. That means
// this check can't be handled in a FirstRequestListener.
if (!_sslChecked)
{
HttpsUtil.checkSslRedirectConfiguration(req, port);
_sslChecked = true;
}

if (port == 443)
if (port == 443)
{
port = -1;
}
url = new URL("https", url.getHost(), port, url.getFile());
// Use 301 redirect instead of a 302 to indicate it's a permanent move
resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
resp.setHeader("Location", resp.encodeRedirectURL(url.toString()));
return;
}
else if (!AppProps.getInstance().isDevMode())
{
port = -1;
// Issue 51904: Strict-Transport-Security header when HTTPS is required
// Avoid setting when in dev mode to make it easier to toggle HTTPS on and off again for local deployments
resp.setHeader("Strict-Transport-Security", "max-age=31536000");
}
url = new URL("https", url.getHost(), port, url.getFile());
// Use 301 redirect instead of a 302 to indicate it's a permanent move
resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
resp.setHeader("Location", resp.encodeRedirectURL(url.toString()));
return;
}

// allow CSRFUtil early access to req/resp if it wants to write cookies
Expand Down
14 changes: 10 additions & 4 deletions api/src/org/labkey/api/security/UserManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1181,10 +1181,16 @@ public static List<String> parseUserListInput(Set<String> theList)
{
if (null == (name = StringUtils.trimToNull(name)))
continue;
User u = null;
try { u = getUser(new ValidEmail(name)); } catch (ValidEmail.InvalidEmailException ignored) {}
if (null == u)
u = getUserByDisplayName(name);
// First try by display name. See issue 40987
User u = getUserByDisplayName(name);
if (u == null)
{
try
{
u = getUser(new ValidEmail(name));
}
catch (ValidEmail.InvalidEmailException ignored) {}
}
parsed.add(null == u ? name : String.valueOf(u.getUserId()));
}
return parsed;
Expand Down
45 changes: 40 additions & 5 deletions api/src/org/labkey/api/util/ImageUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.labkey.api.util;

import jakarta.servlet.http.HttpServletRequest;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.hc.core5.http.ParseException;
Expand Down Expand Up @@ -53,6 +54,10 @@
public class ImageUtil
{
private static Logger LOG = LogManager.getLogger(ImageUtil.class);
public static final String FILE_SESSION_PARAM = "fileCacheKey";
public static final String DELETE_FILE_PARAM = "deleteFile";
public static final String CACHE_FILE_PARAM = "cacheFile";
public static final String ATTACHMENT_PARAM = "attachment";

static
{
Expand Down Expand Up @@ -248,6 +253,35 @@ public void setBaseURL(String url)
}
}

/**
* Retrieves a file cached in the session
*/
@Nullable
public static File getFileFromSession(HttpServletRequest request, String key)
{
Object o = request.getSession().getAttribute(key);
if (o instanceof File file && file.exists())
return file;

return null;
}

/**
* Adds a file to the request session and returns the generated session attribute key.
*/
@Nullable
public static String setFileInSession(HttpServletRequest request, File file)
{
if (file != null && file.exists())
{
String key = "temp:" + GUID.makeGUID();
request.getSession(true).setAttribute(key, file);

return key;
}
return null;
}

// LabKey user agent is used to resolve image resources (or others if required in the future) using the
// same session as the incoming request. Right now this occurs when we are generating a thumbnail for a Knitr
// R report
Expand All @@ -262,7 +296,7 @@ private LabKeyUserAgent(ViewContext context, String baseURL)
}

@Override
public org.xhtmlrenderer.resource.ImageResource getImageResource(java.lang.String uri)
public ImageResource getImageResource(String uri)
{
ImageResource ir;
String uriResolved = resolveURI(uri);
Expand All @@ -276,10 +310,11 @@ public org.xhtmlrenderer.resource.ImageResource getImageResource(java.lang.Strin
try
{
URLHelper helper = new URLHelper(uriResolved);
String sessionKey = helper.getParameter("sessionKey");
String deleteFile = helper.getParameter("deleteFile");
File file = (File) _context.getRequest().getSession().getAttribute(sessionKey);
if (file != null && file.exists())
String sessionKey = helper.getParameter(FILE_SESSION_PARAM);
String deleteFile = helper.getParameter(DELETE_FILE_PARAM);

File file = getFileFromSession(_context.getRequest(), sessionKey);
if (file != null)
{
try
{
Expand Down
31 changes: 31 additions & 0 deletions experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,37 @@ public void addQueryFieldKeys(Set<FieldKey> keys)
if (InventoryService.get() != null && (st == null || !st.isMedia()))
defaultCols.addAll(InventoryService.get().addInventoryStatusColumns(st == null ? null : st.getMetricUnit(), this, getContainer(), _userSchema.getUser()));

UserSchema plateUserSchema = QueryService.get().getUserSchema(_userSchema.getUser(), getContainer(), "plate");
SQLFragment sql;
if (plateUserSchema != null)
{
String rowIdField = ExprColumn.STR_TABLE_ALIAS + "." + Column.RowId.name();
SQLFragment existsSubquery = new SQLFragment()
.append("SELECT 1 FROM ")
.append(plateUserSchema.getTable("Well"), "well")
.append(" WHERE well.sampleid = ").append(rowIdField);

sql = new SQLFragment()
.append("CASE WHEN EXISTS (")
.append(existsSubquery)
.append(") THEN 'Plated' ELSE 'Not Plated'")
.append(" END");
}
else
{
sql = new SQLFragment("SELECT NULL");
}
var col = new ExprColumn(this, Column.IsPlated.name(), sql, JdbcType.VARCHAR);
col.setDescription("Whether the sample that has been plated, if plating is supported.");
col.setUserEditable(false);
col.setReadOnly(true);
col.setShownInDetailsView(false);
col.setShownInInsertView(false);
col.setShownInUpdateView(false);
if (plateUserSchema != null)
col.setURL(DetailsURL.fromString("plate-isPlated.api?sampleId=${" + Column.RowId.name() + "}"));
addColumn(col);

addVocabularyDomains();

addColumn(Column.Properties);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ else if ("summary".equalsIgnoreCase(auditLevel))
if (sampleType != null)
log.info("Sample Type matching the 'name' capture group was resolved : " + sampleName);
else
log.info("Sample Type matching the 'name' capture group was not resolved : " + sampleName);
{
log.error("Sample Type matching the 'name' capture group was not resolved : " + sampleName);
return new RecordedActionSet();
}
}
else if (params.containsKey(SAMPLE_ID_KEY))
{
Expand All @@ -127,7 +130,10 @@ else if (params.containsKey(SAMPLE_ID_KEY))
if (sampleType != null)
log.info("Sample Type matching the 'id' capture group was resolved : " + sampleType.getName());
else
log.info("Sample Type matching the 'id' capture group was not resolved : " + params.get(SAMPLE_ID_KEY));
{
log.error("Sample Type matching the 'id' capture group was not resolved : " + params.get(SAMPLE_ID_KEY));
return new RecordedActionSet();
}
}

if (sampleType == null)
Expand Down
13 changes: 7 additions & 6 deletions query/src/org/labkey/query/reports/ReportsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
import org.labkey.api.util.ExceptionUtil;
import org.labkey.api.util.HtmlString;
import org.labkey.api.util.HtmlStringBuilder;
import org.labkey.api.util.ImageUtil;
import org.labkey.api.util.JsonUtil;
import org.labkey.api.util.MimeMap;
import org.labkey.api.util.PageFlowUtil;
Expand Down Expand Up @@ -1523,14 +1524,14 @@ public static class StreamFileAction extends SimpleViewAction<Object>
@Override
public ModelAndView getView(Object o, BindException errors) throws Exception
{
String sessionKey = (String) getViewContext().get("sessionKey");
String deleteFile = (String) getViewContext().get("deleteFile");
String attachment = (String) getViewContext().get("attachment");
String cacheFile = (String) getViewContext().get("cacheFile");
String sessionKey = (String) getViewContext().get(ImageUtil.FILE_SESSION_PARAM);
String deleteFile = (String) getViewContext().get(ImageUtil.DELETE_FILE_PARAM);
String attachment = (String) getViewContext().get(ImageUtil.ATTACHMENT_PARAM);
String cacheFile = (String) getViewContext().get(ImageUtil.CACHE_FILE_PARAM);
if (sessionKey != null)
{
File file = (File) getViewContext().getRequest().getSession().getAttribute(sessionKey);
if (file != null && file.isFile())
File file = ImageUtil.getFileFromSession(getViewContext().getRequest(), sessionKey);
if (file != null)
{
Map<String, String> responseHeaders = Collections.emptyMap();
if (BooleanUtils.toBoolean(cacheFile))
Expand Down
Loading

0 comments on commit 69c7f7d

Please sign in to comment.