diff options
author | David Ostrovsky <david@ostrovsky.org> | 2015-04-18 22:11:31 +0200 |
---|---|---|
committer | David Ostrovsky <david@ostrovsky.org> | 2015-04-28 08:27:29 +0200 |
commit | 3ae7ec043f00515ee0f900b740487794311b74a1 (patch) | |
tree | a58dc0f025df7caa762ba5dd866da8aaa36f2d08 | |
parent | e6782847f87166c650ad0ec172c6e64b5be3c432 (diff) |
Hybrid OpenID/OAuth: Allow to link identity accross protocols
This change support all linking directions:
* From OpenID to OAuth
* From OAuth to OpenID
* From OAuth to OAuth
TEST PLAN:
1. Set up vanilla Gerrit site
2. Assign auth scheme to OpenID
3. Install gerrit-oauth-provider plugin
4. Configure GitHub or Google provider (or both)
5. Sign in with source identity
6. Click User => Settings => Identities => Link Another Identity
7. Select target identity from the login form
8. Confirm that the target identity is linked to the source identity
GitHub-Bug: https://github.com/davido/gerrit-oauth-provider/issues/12
Change-Id: I06e5cfc2ad1dde81050b951c0b7f602461af7992
3 files changed, 40 insertions, 7 deletions
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java index 93031fef62..e6bb25b382 100644 --- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java +++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java @@ -178,6 +178,7 @@ class LoginForm extends HttpServlet { || oauthSession.isOAuthFinal(req)) && !oauthSession.isLoggedIn()) { oauthSession.setServiceProvider(oauthProvider); + oauthSession.setLinkMode(link); oauthSession.login(req, res, oauthProvider); } } @@ -303,7 +304,7 @@ class LoginForm extends HttpServlet { oauthServiceProviders.byPlugin(pluginName); for (Map.Entry<String, Provider<OAuthServiceProvider>> e : m.entrySet()) { - addProvider(providers, pluginName, e.getKey(), + addProvider(providers, link, pluginName, e.getKey(), e.getValue().get().getName()); } } @@ -326,13 +327,18 @@ class LoginForm extends HttpServlet { } } - private static void addProvider(Element form, String pluginName, - String id, String serviceName) { + private static void addProvider(Element form, boolean link, + String pluginName, String id, String serviceName) { Element div = form.getOwnerDocument().createElement("div"); div.setAttribute("id", id); Element hyperlink = form.getOwnerDocument().createElement("a"); - hyperlink.setAttribute("href", String.format("?id=%s_%s", + StringBuilder u = new StringBuilder(String.format("?id=%s_%s", pluginName, id)); + if (link) { + u.append("&link"); + } + hyperlink.setAttribute("href", u.toString()); + hyperlink.setTextContent(serviceName + " (" + pluginName + " plugin)"); div.appendChild(hyperlink); diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java index a02f52d5d0..fb3d135edb 100644 --- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java +++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java @@ -27,11 +27,13 @@ import com.google.gerrit.httpd.CanonicalWebUrl; import com.google.gerrit.httpd.LoginUrlToken; import com.google.gerrit.httpd.WebSession; import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountException; import com.google.gerrit.server.account.AccountManager; import com.google.gerrit.server.account.AuthResult; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.servlet.SessionScoped; import org.apache.commons.codec.binary.Base64; @@ -55,19 +57,23 @@ class OAuthSessionOverOpenID { private static final SecureRandom randomState = newRandomGenerator(); private final String state; private final DynamicItem<WebSession> webSession; + private final Provider<IdentifiedUser> identifiedUser; private final AccountManager accountManager; private final CanonicalWebUrl urlProvider; private OAuthServiceProvider serviceProvider; private OAuthToken token; private OAuthUserInfo user; private String redirectToken; + private boolean linkMode; @Inject OAuthSessionOverOpenID(DynamicItem<WebSession> webSession, + Provider<IdentifiedUser> identifiedUser, AccountManager accountManager, CanonicalWebUrl urlProvider) { this.state = generateRandomState(); this.webSession = webSession; + this.identifiedUser = identifiedUser; this.accountManager = accountManager; this.urlProvider = urlProvider; } @@ -96,7 +102,6 @@ class OAuthSessionOverOpenID { log.debug("Login-Retrieve-User " + this); token = oauth.getAccessToken(new OAuthVerifier(request.getParameter("code"))); - user = oauth.getUserInfo(token); if (isLoggedIn()) { @@ -124,6 +129,7 @@ class OAuthSessionOverOpenID { try { String claimedIdentifier = user.getClaimedIdentity(); Account.Id actualId = accountManager.lookup(user.getExternalId()); + // Use case 1: claimed identity was provided during handshake phase if (!Strings.isNullOrEmpty(claimedIdentifier)) { Account.Id claimedId = accountManager.lookup(claimedIdentifier); if (claimedId != null && actualId != null) { @@ -153,6 +159,18 @@ class OAuthSessionOverOpenID { return; } } + } else if (linkMode) { + // Use case 2: link mode activated from the UI + try { + accountManager.link(identifiedUser.get().getAccountId(), areq); + } catch (OrmException e) { + log.error("Cannot link: " + user.getExternalId() + + " to user identity: " + identifiedUser.get().getAccountId()); + rsp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } finally { + linkMode = false; + } } areq.setUserName(user.getUserName()); areq.setEmailAddress(user.getEmailAddress()); @@ -213,4 +231,12 @@ class OAuthSessionOverOpenID { public OAuthServiceProvider getServiceProvider() { return serviceProvider; } + + public void setLinkMode(boolean linkMode) { + this.linkMode = linkMode; + } + + public boolean isLinkMode() { + return linkMode; + } } diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java index 53fc889305..dff456f2f7 100644 --- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java +++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java @@ -70,7 +70,9 @@ class OAuthWebFilterOverOpenID implements Filter { FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpSession httpSession = ((HttpServletRequest) request).getSession(false); - if (currentUserProvider.get().isIdentifiedUser()) { + OAuthSessionOverOpenID oauthSession = oauthSessionProvider.get(); + if (!oauthSession.isLinkMode() + && currentUserProvider.get().isIdentifiedUser()) { if (httpSession != null) { httpSession.invalidate(); } @@ -80,7 +82,6 @@ class OAuthWebFilterOverOpenID implements Filter { HttpServletResponse httpResponse = (HttpServletResponse) response; - OAuthSessionOverOpenID oauthSession = oauthSessionProvider.get(); OAuthServiceProvider service = ssoProvider == null ? oauthSession.getServiceProvider() : ssoProvider; |