Use OpenID PAPE extension to force reauthentication
Site administrators relying on OpenID can now enable the PAPE extension, requiring users to reauthenticate with their provider before establishing a new session with the Gerrit server. This resolves issue 521 by allowing a site administrator to set auth.maxOpenIdSessionAge to 0. In this configuration the Google Accounts provider will always prompt for a password, which gives the user a chance to sign-out of Google's account system and sign-in as a different user before they return to the Gerrit installation. Bug: issue 521 Change-Id: I656d6fd31831a71edf15319b6d94503ac93f6f36 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -112,6 +112,30 @@ end with `$`) or be a simple prefix (any other string).
|
|||||||
By default, the list contains two values, `http://` and `https://`,
|
By default, the list contains two values, `http://` and `https://`,
|
||||||
allowing Gerrit to trust any OpenID it receives.
|
allowing Gerrit to trust any OpenID it receives.
|
||||||
|
|
||||||
|
[[auth.maxOpenIdSessionAge]]auth.maxOpenIdSessionAge::
|
||||||
|
+
|
||||||
|
Time in seconds before an OpenID provider must force the user
|
||||||
|
to authenticate themselves again before authentication to this
|
||||||
|
Gerrit server. Currently this is only a polite request, and users
|
||||||
|
coming from providers that don't support the PAPE extension will
|
||||||
|
be accepted anyway. In the future it may be enforced, rejecting
|
||||||
|
users coming from providers that don't honor the max session age.
|
||||||
|
+
|
||||||
|
If set to 0, the provider will always force the user to authenticate
|
||||||
|
(e.g. supply their password). Values should use common unit suffixes
|
||||||
|
to express their setting:
|
||||||
|
+
|
||||||
|
* s, sec, second, seconds
|
||||||
|
* m, min, minute, minutes
|
||||||
|
* h, hr, hour, hours
|
||||||
|
* d, day, days
|
||||||
|
* w, week, weeks (`1 week` is treated as `7 days`)
|
||||||
|
* mon, month, months (`1 month` is treated as `30 days`)
|
||||||
|
* y, year, years (`1 year` is treated as `365 days`)
|
||||||
|
|
||||||
|
+
|
||||||
|
Default is -1, permitting infinite time between authentications.
|
||||||
|
|
||||||
[[auth.httpHeader]]auth.httpHeader::
|
[[auth.httpHeader]]auth.httpHeader::
|
||||||
+
|
+
|
||||||
HTTP header to trust the username from, or unset to select HTTP basic
|
HTTP header to trust the username from, or unset to select HTTP basic
|
||||||
|
@@ -28,6 +28,7 @@ import com.google.gerrit.server.account.AccountManager;
|
|||||||
import com.google.gerrit.server.cache.Cache;
|
import com.google.gerrit.server.cache.Cache;
|
||||||
import com.google.gerrit.server.cache.SelfPopulatingCache;
|
import com.google.gerrit.server.cache.SelfPopulatingCache;
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
|
import com.google.gerrit.server.config.ConfigUtil;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
import com.google.gwtorm.client.KeyUtil;
|
import com.google.gwtorm.client.KeyUtil;
|
||||||
@@ -50,6 +51,9 @@ import org.openid4java.message.ParameterList;
|
|||||||
import org.openid4java.message.ax.AxMessage;
|
import org.openid4java.message.ax.AxMessage;
|
||||||
import org.openid4java.message.ax.FetchRequest;
|
import org.openid4java.message.ax.FetchRequest;
|
||||||
import org.openid4java.message.ax.FetchResponse;
|
import org.openid4java.message.ax.FetchResponse;
|
||||||
|
import org.openid4java.message.pape.PapeMessage;
|
||||||
|
import org.openid4java.message.pape.PapeRequest;
|
||||||
|
import org.openid4java.message.pape.PapeResponse;
|
||||||
import org.openid4java.message.sreg.SRegMessage;
|
import org.openid4java.message.sreg.SRegMessage;
|
||||||
import org.openid4java.message.sreg.SRegRequest;
|
import org.openid4java.message.sreg.SRegRequest;
|
||||||
import org.openid4java.message.sreg.SRegResponse;
|
import org.openid4java.message.sreg.SRegResponse;
|
||||||
@@ -62,6 +66,7 @@ import java.io.IOException;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
@@ -98,6 +103,9 @@ class OpenIdServiceImpl implements OpenIdService {
|
|||||||
private final ConsumerManager manager;
|
private final ConsumerManager manager;
|
||||||
private final SelfPopulatingCache<String, List> discoveryCache;
|
private final SelfPopulatingCache<String, List> discoveryCache;
|
||||||
|
|
||||||
|
/** Maximum age, in seconds, before forcing re-authentication of account. */
|
||||||
|
private final int papeMaxAuthAge;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
OpenIdServiceImpl(final Provider<WebSession> cf,
|
OpenIdServiceImpl(final Provider<WebSession> cf,
|
||||||
final Provider<IdentifiedUser> iu,
|
final Provider<IdentifiedUser> iu,
|
||||||
@@ -135,6 +143,8 @@ class OpenIdServiceImpl implements OpenIdService {
|
|||||||
urlProvider = up;
|
urlProvider = up;
|
||||||
accountManager = am;
|
accountManager = am;
|
||||||
manager = new ConsumerManager();
|
manager = new ConsumerManager();
|
||||||
|
papeMaxAuthAge = (int) ConfigUtil.getTimeUnit(config, //
|
||||||
|
"auth", null, "maxOpenIdSessionAge", -1, TimeUnit.SECONDS);
|
||||||
|
|
||||||
discoveryCache = new SelfPopulatingCache<String, List>(openidCache) {
|
discoveryCache = new SelfPopulatingCache<String, List>(openidCache) {
|
||||||
@Override
|
@Override
|
||||||
@@ -177,6 +187,12 @@ class OpenIdServiceImpl implements OpenIdService {
|
|||||||
fetch.addAttribute("Email", SCHEMA_EMAIL, true);
|
fetch.addAttribute("Email", SCHEMA_EMAIL, true);
|
||||||
aReq.addExtension(fetch);
|
aReq.addExtension(fetch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (0 <= papeMaxAuthAge) {
|
||||||
|
final PapeRequest pape = PapeRequest.createPapeRequest();
|
||||||
|
pape.setMaxAuthAge(papeMaxAuthAge);
|
||||||
|
aReq.addExtension(pape);
|
||||||
|
}
|
||||||
} catch (MessageException e) {
|
} catch (MessageException e) {
|
||||||
callback.onSuccess(new DiscoveryResult(false));
|
callback.onSuccess(new DiscoveryResult(false));
|
||||||
return;
|
return;
|
||||||
@@ -277,6 +293,28 @@ class OpenIdServiceImpl implements OpenIdService {
|
|||||||
SRegResponse sregRsp = null;
|
SRegResponse sregRsp = null;
|
||||||
FetchResponse fetchRsp = null;
|
FetchResponse fetchRsp = null;
|
||||||
|
|
||||||
|
if (0 <= papeMaxAuthAge) {
|
||||||
|
PapeResponse ext;
|
||||||
|
boolean unsupported = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ext = (PapeResponse) authRsp.getExtension(PapeMessage.OPENID_NS_PAPE);
|
||||||
|
} catch (MessageException err) {
|
||||||
|
// Far too many providers are unable to provide PAPE extensions
|
||||||
|
// right now. Instead of blocking all of them log the error and
|
||||||
|
// let the authentication complete anyway.
|
||||||
|
//
|
||||||
|
log.error("Invalid PAPE response " + openidIdentifier + ": " + err);
|
||||||
|
unsupported = true;
|
||||||
|
ext = null;
|
||||||
|
}
|
||||||
|
if (!unsupported && ext == null) {
|
||||||
|
log.error("No PAPE extension response from " + openidIdentifier);
|
||||||
|
cancelWithError(req, rsp, "OpenID provider does not support PAPE.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (authRsp.hasExtension(SRegMessage.OPENID_NS_SREG)) {
|
if (authRsp.hasExtension(SRegMessage.OPENID_NS_SREG)) {
|
||||||
final MessageExtension ext =
|
final MessageExtension ext =
|
||||||
authRsp.getExtension(SRegMessage.OPENID_NS_SREG);
|
authRsp.getExtension(SRegMessage.OPENID_NS_SREG);
|
||||||
|
Reference in New Issue
Block a user