Merge "Expose extension point for generic OAuth providers" into stable-2.10

This commit is contained in:
Saša Živkov 2015-03-13 13:31:48 +00:00 committed by Gerrit Code Review
commit c842693e80
24 changed files with 817 additions and 10 deletions

View File

@ -145,6 +145,16 @@ request is the exact string supplied in the dialog by the user.
The configured <<ldap.username,ldap.username>> identity is not used to obtain
account information.
+
* OAUTH
+
OAuth is a protocol that lets external apps request authorization to private
details in a user's account without getting their password. This is
preferred over Basic Authentication because tokens can be limited to specific
types of data, and can be revoked by users at any time.
+
Site owners have to register their application before getting started. Note
that provider specific plugins must be used with this authentication scheme.
+
* `DEVELOPMENT_BECOME_ANY_ACCOUNT`
+
*DO NOT USE*. Only for use in a development environment.

View File

@ -0,0 +1,74 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.auth.oauth;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import java.io.IOException;
/* Contract that OAuth provider must implement */
@ExtensionPoint
public interface OAuthServiceProvider {
/**
* Retrieve the request token.
*
* @return request token
*/
OAuthToken getRequestToken();
/**
* Returns the URL where you should redirect your users to authenticate
* your application.
*
* @param requestToken the request token you need to authorize
* @return the URL where you should redirect your users
*/
String getAuthorizationUrl(OAuthToken requestToken);
/**
* Retrieve the access token
*
* @param requestToken request token (obtained previously)
* @param verifier verifier code
* @return access token
*/
OAuthToken getAccessToken(OAuthToken requestToken, OAuthVerifier verifier);
/**
* After establishing of secure communication channel, this method supossed to
* access the protected resoure and retrieve the username.
*
* @param token
* @return OAuth user information
* @throws IOException
*/
OAuthUserInfo getUserInfo(OAuthToken token) throws IOException;
/**
* Returns the OAuth version of the service.
*
* @return oauth version as string
*/
String getVersion();
/**
* Returns the name of this service. This name is resented the user to choose
* between multiple service providers
*
* @return name of the service
*/
String getName();
}

View File

@ -0,0 +1,41 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.auth.oauth;
/* OAuth token */
public class OAuthToken {
private final String token;
private final String secret;
private final String raw;
public OAuthToken(String token, String secret, String raw) {
this.token = token;
this.secret = secret;
this.raw = raw;
}
public String getToken() {
return token;
}
public String getSecret() {
return secret;
}
public String getRaw() {
return raw;
}
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.auth.oauth;
public class OAuthUserInfo {
private final String externalId;
private final String userName;
private final String emailAddress;
private final String displayName;
public OAuthUserInfo(String externalId,
String userName,
String emailAddress,
String displayName) {
this.externalId = externalId;
this.userName = userName;
this.emailAddress = emailAddress;
this.displayName = displayName;
}
public String getExternalId() {
return externalId;
}
public String getUserName() {
return userName;
}
public String getEmailAddress() {
return emailAddress;
}
public String getDisplayName() {
return displayName;
}
}

View File

@ -0,0 +1,29 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.extensions.auth.oauth;
/* OAuth verifier */
public class OAuthVerifier {
private final String value;
public OAuthVerifier(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}

View File

@ -718,6 +718,15 @@ public class Gerrit implements EntryPoint {
});
break;
case OAUTH:
menuRight.addItem(C.menuSignIn(), new Command() {
@Override
public void execute() {
doSignIn(History.getToken());
}
});
break;
case OPENID_SSO:
menuRight.addItem(C.menuSignIn(), new Command() {
public void execute() {

View File

@ -112,6 +112,7 @@ class GerritConfigProvider implements Provider<GerritConfig> {
case CLIENT_SSL_CERT_LDAP:
case DEVELOPMENT_BECOME_ANY_ACCOUNT:
case OAUTH:
case OPENID:
case OPENID_SSO:
break;

View File

@ -35,7 +35,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Singleton
class HttpLogoutServlet extends HttpServlet {
public class HttpLogoutServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final DynamicItem<WebSession> webSession;
@ -44,7 +44,7 @@ class HttpLogoutServlet extends HttpServlet {
private final AuditService audit;
@Inject
HttpLogoutServlet(final AuthConfig authConfig,
protected HttpLogoutServlet(final AuthConfig authConfig,
final DynamicItem<WebSession> webSession,
@CanonicalWebUrl @Nullable final Provider<String> urlProvider,
final AccountManager accountManager,
@ -55,7 +55,7 @@ class HttpLogoutServlet extends HttpServlet {
this.audit = audit;
}
private void doLogout(final HttpServletRequest req,
protected void doLogout(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException {
webSession.get().logout();
if (logoutUrl != null) {

View File

@ -33,8 +33,10 @@ import com.google.gerrit.httpd.rpc.config.ConfigRestApiServlet;
import com.google.gerrit.httpd.rpc.doc.QueryDocumentationFilter;
import com.google.gerrit.httpd.rpc.group.GroupsRestApiServlet;
import com.google.gerrit.httpd.rpc.project.ProjectsRestApiServlet;
import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gwtexpui.server.CacheControlFilter;
import com.google.inject.Inject;
@ -64,10 +66,12 @@ class UrlModule extends ServletModule {
private final UrlConfig cfg;
private GerritUiOptions uiOptions;
private AuthConfig authConfig;
UrlModule(UrlConfig cfg, GerritUiOptions uiOptions) {
UrlModule(UrlConfig cfg, GerritUiOptions uiOptions, AuthConfig authConfig) {
this.cfg = cfg;
this.uiOptions = uiOptions;
this.authConfig = authConfig;
}
@Override
@ -81,8 +85,11 @@ class UrlModule extends ServletModule {
serve("/Gerrit/*").with(legacyGerritScreen());
}
serve("/cat/*").with(CatServlet.class);
serve("/logout").with(HttpLogoutServlet.class);
serve("/signout").with(HttpLogoutServlet.class);
if (authConfig.getAuthType() != AuthType.OAUTH) {
serve("/logout").with(HttpLogoutServlet.class);
serve("/signout").with(HttpLogoutServlet.class);
}
serve("/ssh_info").with(SshInfoServlet.class);
serve("/static/*").with(StaticServlet.class);

View File

@ -101,6 +101,8 @@ public class WebModule extends LifecycleModule {
install(new BecomeAnyAccountModule());
break;
case OAUTH:
// OAuth support is bound in WebAppInitializer and Daemon.
case OPENID:
case OPENID_SSO:
// OpenID support is bound in WebAppInitializer and Daemon.
@ -110,7 +112,7 @@ public class WebModule extends LifecycleModule {
throw new ProvisionException("Unsupported loginType: " + authConfig.getAuthType());
}
install(new UrlModule(urlConfig, uiOptions));
install(new UrlModule(urlConfig, uiOptions, authConfig));
install(new UiRpcModule());
install(new GerritRequestModule());
install(new GitOverHttpServlet.Module());

24
gerrit-oauth/BUCK Normal file
View File

@ -0,0 +1,24 @@
SRCS = glob(
['src/main/java/**/*.java'],
)
RESOURCES = glob(['src/main/resources/**/*'])
java_library(
name = 'oauth',
srcs = SRCS,
resources = RESOURCES,
deps = [
'//gerrit-common:annotations',
'//gerrit-extension-api:api',
'//gerrit-httpd:httpd',
'//gerrit-server:server',
'//lib:gson',
'//lib:guava',
'//lib/commons:codec',
'//lib/guice:guice',
'//lib/guice:guice-servlet',
'//lib/log:api',
],
provided_deps = ['//lib:servlet-api-3_1'],
visibility = ['PUBLIC'],
)

View File

@ -0,0 +1,57 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd.auth.oauth;
import com.google.gerrit.audit.AuditService;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.httpd.HttpLogoutServlet;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Singleton
class OAuthLogoutServlet extends HttpLogoutServlet {
private static final long serialVersionUID = 1L;
private final Provider<OAuthSession> oauthSession;
@Inject
OAuthLogoutServlet(AuthConfig authConfig,
DynamicItem<WebSession> webSession,
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
AccountManager accountManager,
AuditService audit,
Provider<OAuthSession> oauthSession) {
super(authConfig, webSession, urlProvider, accountManager, audit);
this.oauthSession = oauthSession;
}
@Override
protected void doLogout(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
super.doLogout(req, rsp);
oauthSession.get().logout();
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd.auth.oauth;
import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.inject.servlet.ServletModule;
/** Servlets and support related to OAuth authentication. */
public class OAuthModule extends ServletModule {
@Override
protected void configureServlets() {
filter("/login", "/login/*", "/oauth").through(OAuthWebFilter.class);
// This is needed to invalidate OAuth session during logout
serve("/logout").with(OAuthLogoutServlet.class);
DynamicMap.mapOf(binder(), OAuthServiceProvider.class);
}
}

View File

@ -0,0 +1,178 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd.auth.oauth;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthResult;
import com.google.inject.Inject;
import com.google.inject.servlet.SessionScoped;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SessionScoped
/* OAuth protocol implementation */
class OAuthSession {
private static final Logger log = LoggerFactory.getLogger(OAuthSession.class);
private static final SecureRandom randomState = newRandomGenerator();
private final String state;
private final DynamicItem<WebSession> webSession;
private final AccountManager accountManager;
private OAuthServiceProvider serviceProvider;
private OAuthToken token;
private OAuthUserInfo user;
private String redirectUrl;
@Inject
OAuthSession(DynamicItem<WebSession> webSession,
AccountManager accountManager) {
this.state = generateRandomState();
this.webSession = webSession;
this.accountManager = accountManager;
}
boolean isLoggedIn() {
return token != null && user != null;
}
boolean isOAuthFinal(HttpServletRequest request) {
return Strings.emptyToNull(request.getParameter("code")) != null;
}
boolean login(HttpServletRequest request, HttpServletResponse response,
OAuthServiceProvider oauth) throws IOException {
if (isLoggedIn()) {
return true;
}
log.debug("Login " + this);
if (isOAuthFinal(request)) {
if (!checkState(request)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return false;
}
log.debug("Login-Retrieve-User " + this);
token = oauth.getAccessToken(null,
new OAuthVerifier(request.getParameter("code")));
user = oauth.getUserInfo(token);
if (isLoggedIn()) {
log.debug("Login-SUCCESS " + this);
authenticateAndRedirect(response);
return true;
} else {
response.sendError(SC_UNAUTHORIZED);
return false;
}
} else {
log.debug("Login-PHASE1 " + this);
redirectUrl = request.getRequestURI();
response.sendRedirect(oauth.getAuthorizationUrl(null) +
"&state=" + state);
return false;
}
}
private void authenticateAndRedirect(HttpServletResponse rsp)
throws IOException {
com.google.gerrit.server.account.AuthRequest areq =
new com.google.gerrit.server.account.AuthRequest(user.getExternalId());
areq.setUserName(user.getUserName());
areq.setEmailAddress(user.getEmailAddress());
areq.setDisplayName(user.getDisplayName());
AuthResult arsp;
try {
arsp = accountManager.authenticate(areq);
} catch (AccountException e) {
log.error("Unable to authenticate user \"" + user + "\"", e);
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
webSession.get().login(arsp, true);
String suffix = redirectUrl.substring(
OAuthWebFilter.GERRIT_LOGIN.length() + 1);
suffix = URLDecoder.decode(suffix, StandardCharsets.UTF_8.name());
rsp.sendRedirect(suffix);
}
void logout() {
token = null;
user = null;
redirectUrl = null;
serviceProvider = null;
}
private boolean checkState(ServletRequest request) {
String s = Strings.nullToEmpty(request.getParameter("state"));
if (!s.equals(state)) {
log.error("Illegal request state '" + s + "' on OAuthProtocol " + this);
return false;
}
return true;
}
private static SecureRandom newRandomGenerator() {
try {
return SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(
"No SecureRandom available for GitHub authentication", e);
}
}
private static String generateRandomState() {
byte[] state = new byte[32];
randomState.nextBytes(state);
return Base64.encodeBase64URLSafeString(state);
}
@Override
public String toString() {
return "OAuthSession [token=" + token + ", user=" + user + "]";
}
public void setServiceProvider(OAuthServiceProvider provider) {
this.serviceProvider = provider;
}
public OAuthServiceProvider getServiceProvider() {
return serviceProvider;
}
}

View File

@ -0,0 +1,223 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd.auth.oauth;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.httpd.LoginUrlToken;
import com.google.gerrit.httpd.template.SiteHeaderFooter;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Singleton
/* OAuth web filter uses active OAuth session to perform OAuth requests */
class OAuthWebFilter implements Filter {
static final String GERRIT_LOGIN = "/login";
private final Provider<String> urlProvider;
private final Provider<CurrentUser> currentUserProvider;
private final Provider<OAuthSession> oauthSessionProvider;
private final DynamicMap<OAuthServiceProvider> oauthServiceProviders;
private final SiteHeaderFooter header;
private OAuthServiceProvider ssoProvider;
@Inject
OAuthWebFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider,
Provider<CurrentUser> currentUserProvider,
DynamicMap<OAuthServiceProvider> oauthServiceProviders,
Provider<OAuthSession> oauthSessionProvider,
SiteHeaderFooter header) {
this.urlProvider = urlProvider;
this.currentUserProvider = currentUserProvider;
this.oauthServiceProviders = oauthServiceProviders;
this.oauthSessionProvider = oauthSessionProvider;
this.header = header;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
pickSSOServiceProvider();
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession httpSession = ((HttpServletRequest) request).getSession(false);
if (currentUserProvider.get().isIdentifiedUser()) {
if (httpSession != null) {
httpSession.invalidate();
}
chain.doFilter(request, response);
return;
}
HttpServletResponse httpResponse = (HttpServletResponse) response;
String provider = httpRequest.getParameter("provider");
OAuthSession oauthSession = oauthSessionProvider.get();
OAuthServiceProvider service = ssoProvider == null
? oauthSession.getServiceProvider()
: ssoProvider;
if ((isGerritLogin(httpRequest)
|| oauthSession.isOAuthFinal(httpRequest))
&& !oauthSession.isLoggedIn()) {
if (service == null && Strings.isNullOrEmpty(provider)) {
selectProvider(httpRequest, httpResponse, null);
return;
} else {
if (service == null) {
service = findService(provider);
}
oauthSession.setServiceProvider(service);
oauthSession.login(httpRequest, httpResponse, service);
}
} else {
chain.doFilter(httpRequest, response);
}
}
private OAuthServiceProvider findService(String providerId)
throws ServletException {
Set<String> plugins = oauthServiceProviders.plugins();
for (String pluginName : plugins) {
Map<String, Provider<OAuthServiceProvider>> m =
oauthServiceProviders.byPlugin(pluginName);
for (Map.Entry<String, Provider<OAuthServiceProvider>> e
: m.entrySet()) {
if (providerId.equals(
String.format("%s_%s", pluginName, e.getKey()))) {
return e.getValue().get();
}
}
}
throw new ServletException("No provider found for: " + providerId);
}
private void selectProvider(HttpServletRequest req, HttpServletResponse res,
@Nullable String errorMessage)
throws IOException {
String self = req.getRequestURI();
String cancel = Objects.firstNonNull(
urlProvider != null ? urlProvider.get() : "/", "/");
cancel += LoginUrlToken.getToken(req);
Document doc = header.parse(OAuthWebFilter.class, "LoginForm.html");
HtmlDomUtil.find(doc, "hostName").setTextContent(req.getServerName());
HtmlDomUtil.find(doc, "login_form").setAttribute("action", self);
HtmlDomUtil.find(doc, "cancel_link").setAttribute("href", cancel);
Element emsg = HtmlDomUtil.find(doc, "error_message");
if (Strings.isNullOrEmpty(errorMessage)) {
emsg.getParentNode().removeChild(emsg);
} else {
emsg.setTextContent(errorMessage);
}
Element providers = HtmlDomUtil.find(doc, "providers");
Set<String> plugins = oauthServiceProviders.plugins();
for (String pluginName : plugins) {
Map<String, Provider<OAuthServiceProvider>> m =
oauthServiceProviders.byPlugin(pluginName);
for (Map.Entry<String, Provider<OAuthServiceProvider>> e
: m.entrySet()) {
addProvider(providers, pluginName, e.getKey(),
e.getValue().get().getName());
}
}
sendHtml(res, doc);
}
private static void addProvider(Element form, 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("?provider=%s_%s",
pluginName, id));
hyperlink.setTextContent(serviceName +
" (" + pluginName + " plugin)");
div.appendChild(hyperlink);
form.appendChild(div);
}
private static void sendHtml(HttpServletResponse res, Document doc)
throws IOException {
byte[] bin = HtmlDomUtil.toUTF8(doc);
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.setContentType("text/html");
res.setCharacterEncoding(StandardCharsets.UTF_8.name());
res.setContentLength(bin.length);
try (ServletOutputStream out = res.getOutputStream()) {
out.write(bin);
}
}
private void pickSSOServiceProvider()
throws ServletException {
SortedSet<String> plugins = oauthServiceProviders.plugins();
if (plugins.isEmpty()) {
throw new ServletException(
"OAuth service provider wasn't installed");
}
if (plugins.size() == 1) {
SortedMap<String, Provider<OAuthServiceProvider>> services =
oauthServiceProviders.byPlugin(Iterables.getOnlyElement(plugins));
if (services.size() == 1) {
ssoProvider = Iterables.getOnlyElement(services.values()).get();
}
}
}
private static boolean isGerritLogin(HttpServletRequest request) {
return request.getRequestURI().indexOf(GERRIT_LOGIN) >= 0;
}
}

File diff suppressed because one or more lines are too long

View File

@ -99,6 +99,7 @@ java_library(
'//gerrit-gwtexpui:server',
'//gerrit-httpd:httpd',
'//gerrit-lucene:lucene',
'//gerrit-oauth:oauth',
'//gerrit-openid:openid',
'//gerrit-reviewdb:server',
'//gerrit-server:server',

View File

@ -27,6 +27,7 @@ import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
import com.google.gerrit.httpd.RequestContextFilter;
import com.google.gerrit.httpd.WebModule;
import com.google.gerrit.httpd.WebSshGlueModule;
import com.google.gerrit.httpd.auth.oauth.OAuthModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.lifecycle.LifecycleManager;
@ -430,6 +431,8 @@ public class Daemon extends SiteProgram {
if (authConfig.getAuthType() == AuthType.OPENID ||
authConfig.getAuthType() == AuthType.OPENID_SSO) {
modules.add(new OpenIdModule());
} else if (authConfig.getAuthType() == AuthType.OAUTH) {
modules.add(new OAuthModule());
}
modules.add(sysInjector.getInstance(GetUserFilter.Module.class));

View File

@ -60,6 +60,7 @@ class InitAuth implements InitStep {
case DEVELOPMENT_BECOME_ANY_ACCOUNT:
case LDAP:
case LDAP_BIND:
case OAUTH:
case OPENID:
case OPENID_SSO:
break;
@ -94,6 +95,7 @@ class InitAuth implements InitStep {
case CUSTOM_EXTENSION:
case DEVELOPMENT_BECOME_ANY_ACCOUNT:
case HTTP:
case OAUTH:
case OPENID:
case OPENID_SSO:
break;

View File

@ -80,5 +80,8 @@ public enum AuthType {
CUSTOM_EXTENSION,
/** Development mode to enable becoming anyone you want. */
DEVELOPMENT_BECOME_ANY_ACCOUNT
DEVELOPMENT_BECOME_ANY_ACCOUNT,
/** Generic OAuth provider over HTTP. */
OAUTH
}

View File

@ -40,7 +40,8 @@ public class DefaultRealm implements Realm {
@Override
public boolean allowsEdit(final Account.FieldName field) {
if (authConfig.getAuthType() == AuthType.HTTP) {
if (authConfig.getAuthType() == AuthType.HTTP
|| authConfig.getAuthType() == AuthType.OAUTH) {
switch (field) {
case USER_NAME:
return false;

View File

@ -197,7 +197,7 @@ public class AuthConfig {
case LDAP_BIND:
case CLIENT_SSL_CERT_LDAP:
case CUSTOM_EXTENSION:
// Its safe to assume yes for an HTTP authentication type, as the
case OAUTH:
// only way in is through some external system that the admin trusts
//
return true;

View File

@ -8,6 +8,7 @@ java_library(
'//gerrit-extension-api:api',
'//gerrit-httpd:httpd',
'//gerrit-lucene:lucene',
'//gerrit-oauth:oauth',
'//gerrit-openid:openid',
'//gerrit-pgm:init-api',
'//gerrit-pgm:init-base',

View File

@ -19,6 +19,7 @@ import static com.google.inject.Stage.PRODUCTION;
import com.google.common.base.Splitter;
import com.google.gerrit.common.ChangeHookRunner;
import com.google.gerrit.httpd.auth.oauth.OAuthModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.lifecycle.LifecycleManager;
@ -343,6 +344,8 @@ public class WebAppInitializer extends GuiceServletContextListener
AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
if (authConfig.getAuthType() == AuthType.OPENID) {
modules.add(new OpenIdModule());
} else if (authConfig.getAuthType() == AuthType.OAUTH) {
modules.add(new OAuthModule());
}
return sysInjector.createChildInjector(modules);