Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions api/src/org/labkey/api/action/SimpleErrorView.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

/**
* View that renders an error collection.
* User: adam
* Date: Sep 26, 2007
*/
public class SimpleErrorView extends JspView<Boolean>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ interface SSOAuthenticationConfiguration<AP extends SSOAuthenticationProvider<?>
{
LinkFactory getLinkFactory();
URLHelper getUrl(ViewContext ctx);
URLHelper getReauthUrl(ViewContext ctx);

/**
* Allows an SSO auth configuration to specify that it should be used automatically instead of showing the standard
Expand Down
66 changes: 64 additions & 2 deletions api/src/org/labkey/api/security/AuthenticationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
import org.labkey.api.usageMetrics.UsageMetricsService;
import org.labkey.api.util.DateUtil;
import org.labkey.api.util.ExceptionUtil;
import org.labkey.api.util.GUID;
import org.labkey.api.util.HeartBeat;
import org.labkey.api.util.HtmlString;
import org.labkey.api.util.HtmlStringBuilder;
Expand Down Expand Up @@ -549,7 +550,12 @@ public static abstract class BaseSsoValidateAction<FORM extends AuthenticationCo
public ModelAndView getView(FORM form, BindException errors) throws Exception
{
AuthenticationResponse response = validateAuthentication(form, errors);
return response.isReauth() ? getReauthView(response, errors) : getAuthView(response, errors);
}

// Normal primary authentication case
private ModelAndView getAuthView(AuthenticationResponse response, BindException errors)
{
// Show validation error(s), if any
if (errors.hasErrors() || !response.isAuthenticated())
{
Expand Down Expand Up @@ -586,6 +592,32 @@ public ModelAndView getView(FORM form, BindException errors) throws Exception
return new SimpleErrorView(errors, false);
}

// Reauthentication case for electronic signing and other sensitive operations. Check that reauthentication
// was successful and re-auth user matches session user. Not currently verifying that the same authentication
// configuration was used to reauthenticate.
private ModelAndView getReauthView(AuthenticationResponse response, BindException errors)
{
String errorMessage = null;

if (!response.isAuthenticated())
{
errorMessage = errors.hasErrors() ? errors.getMessage() : "Reauthentication failed";
}

LoginReturnProperties properties = getLoginReturnProperties(getViewContext().getRequestOrThrow());

// We lost the return URL. Could happen on local session timeout.
if (properties == null)
throw new NotFoundException("Reauthentication failed");

URLHelper url = properties.getReturnUrl();
@Nullable User reauthUser = UserManager.getUser(response.getValidEmail());

AuthenticationManager.setReauthUser(reauthUser, getUser(), getViewContext().getRequestOrThrow(), errorMessage, url);

throw new RedirectException(url);
}

@Override
protected String getCommandClassMethodName()
{
Expand Down Expand Up @@ -1769,8 +1801,35 @@ public static URLHelper getWelcomeURL()

public record Reauth(String token, User user){}
public static final String REAUTH_TOKEN_NAME = "reauthToken";
public static final String ERROR_MESSAGE = "errorMessage";

public static @Nullable User getAndClearReauthUser(HttpServletRequest request, @Nullable String token)
/**
* @param reauthUser Re-auth user to stash in session with the re-auth token
* @param sessionUser If not null, validate that this user and reauthUser are the same
* @param request Request from which to retrieve the session
* @param errorMessage Pre-existing error message to add to the URL
* @param redirectUrl URL to which the token (on success) or error message (on failure) gets added
*/
public static void setReauthUser(User reauthUser, @Nullable User sessionUser, HttpServletRequest request, @Nullable String errorMessage, URLHelper redirectUrl)
{
if (errorMessage == null && sessionUser != null && !sessionUser.equals(reauthUser))
{
errorMessage = "Reauthentication failed: wrong user reauthenticated";
}

if (errorMessage != null)
{
redirectUrl.addParameter(ERROR_MESSAGE, errorMessage);
}
else
{
String reauthToken = GUID.makeHash();
redirectUrl.addParameter(REAUTH_TOKEN_NAME, reauthToken);
request.getSession().setAttribute(REAUTH_TOKEN_NAME, new Reauth(reauthToken, reauthUser));
}
}

public static @Nullable User getAndClearReauthUser(HttpServletRequest request, @Nullable String token, @Nullable User sessionUser)
{
if (token != null)
{
Expand All @@ -1787,7 +1846,10 @@ public record Reauth(String token, User user){}
if (matches)
{
session.removeAttribute(REAUTH_TOKEN_NAME);
return reauth.user();
User reauthUser = reauth.user();

if (sessionUser == null || sessionUser.equals(reauthUser))
return reauthUser;
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions api/src/org/labkey/api/security/AuthenticationProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ class AuthenticationResponse
private @NotNull Map<String, String> _userAttributeMap = Collections.emptyMap(); // A case-insensitive map of attribute names and values associated with the user
private @NotNull Map<String, Object> _authenticationProperties = Collections.emptyMap();
private boolean _requireSecondary = true; // Require secondary authentication
private boolean _reauth = false;
private @Nullable String _successDetails = null; // An optional string describing how successful authentication took place, which will
// appear in the audit log. If null, the configuration's description will be used.

Expand Down Expand Up @@ -447,6 +448,17 @@ public AuthenticationResponse setRequireSecondary(boolean requireSecondary)
_requireSecondary = requireSecondary;
return this;
}

public boolean isReauth()
{
return _reauth;
}

public AuthenticationResponse setReauth(boolean reauth)
{
_reauth = reauth;
return this;
}
}

// FailureReasons are only reported to administrators (in the audit log and/or server log), NOT to users (and potential
Expand Down
3 changes: 2 additions & 1 deletion api/src/org/labkey/api/security/LoginUrls.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ public interface LoginUrls extends UrlProvider
ActionURL getLoginURL(URLHelper returnUrl);
ActionURL getRegisterURL(Container c, @Nullable URLHelper returnUrl);
ActionURL getLoginURL(Container c, @Nullable URLHelper returnUrl);
ActionURL getForceReauthURL(Container c, @Nullable URLHelper returnUrl);
ActionURL getForceReauthURL(Container c, boolean local, @Nullable URLHelper returnUrl);
ActionURL getLogoutURL(Container c);
ActionURL getLogoutURL(Container c, URLHelper returnUrl);
ActionURL getStopImpersonatingURL(Container c, @Nullable URLHelper returnUrl);
ActionURL getAgreeToTermsURL(Container c, URLHelper returnUrl);
ActionURL getSSORedirectURL(SSOAuthenticationConfiguration<?> configuration, URLHelper returnUrl, boolean skipProfile);
ActionURL getSSOReauthURL(SSOAuthenticationConfiguration<?> configuration, URLHelper returnUrl);
}
Loading