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:
Shawn O. Pearce
2010-04-24 17:25:29 -07:00
parent 4ba0b8a7cb
commit 89030bc3d0
2 changed files with 62 additions and 0 deletions

View File

@@ -112,6 +112,30 @@ end with `$`) or be a simple prefix (any other string).
By default, the list contains two values, `http://` and `https://`,
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::
+
HTTP header to trust the username from, or unset to select HTTP basic

View File

@@ -28,6 +28,7 @@ import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.SelfPopulatingCache;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gwt.user.client.rpc.AsyncCallback;
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.FetchRequest;
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.SRegRequest;
import org.openid4java.message.sreg.SRegResponse;
@@ -62,6 +66,7 @@ import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.servlet.http.Cookie;
@@ -98,6 +103,9 @@ class OpenIdServiceImpl implements OpenIdService {
private final ConsumerManager manager;
private final SelfPopulatingCache<String, List> discoveryCache;
/** Maximum age, in seconds, before forcing re-authentication of account. */
private final int papeMaxAuthAge;
@Inject
OpenIdServiceImpl(final Provider<WebSession> cf,
final Provider<IdentifiedUser> iu,
@@ -135,6 +143,8 @@ class OpenIdServiceImpl implements OpenIdService {
urlProvider = up;
accountManager = am;
manager = new ConsumerManager();
papeMaxAuthAge = (int) ConfigUtil.getTimeUnit(config, //
"auth", null, "maxOpenIdSessionAge", -1, TimeUnit.SECONDS);
discoveryCache = new SelfPopulatingCache<String, List>(openidCache) {
@Override
@@ -177,6 +187,12 @@ class OpenIdServiceImpl implements OpenIdService {
fetch.addAttribute("Email", SCHEMA_EMAIL, true);
aReq.addExtension(fetch);
}
if (0 <= papeMaxAuthAge) {
final PapeRequest pape = PapeRequest.createPapeRequest();
pape.setMaxAuthAge(papeMaxAuthAge);
aReq.addExtension(pape);
}
} catch (MessageException e) {
callback.onSuccess(new DiscoveryResult(false));
return;
@@ -277,6 +293,28 @@ class OpenIdServiceImpl implements OpenIdService {
SRegResponse sregRsp = 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)) {
final MessageExtension ext =
authRsp.getExtension(SRegMessage.OPENID_NS_SREG);