Merge branch 'stable-2.14' into stable-2.15 * stable-2.14: Align Eclipse compiler settings with core Gerrit's Upgrade bazlets to latest stable-2.14 Change-Id: I99a03c1f5564d16d0e5b3a6f7491bf28a015cfee
diff --git a/.gitignore b/.gitignore index b6af1fb..3681900 100644 --- a/.gitignore +++ b/.gitignore
@@ -8,3 +8,4 @@ /bazel-testlogs /eclipse-out /.idea +*.swp
diff --git a/README.md b/README.md index 9056d47..643de76 100644 --- a/README.md +++ b/README.md
@@ -7,6 +7,7 @@ With this plugin Gerrit can use OAuth2 protocol for authentication. Supported OAuth providers: +* [AirVantage](https://doc.airvantage.net/av/reference/cloud/API/#API-GeneralInformation-Authentication) * [Bitbucket](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html) * [CAS](https://www.apereo.org/projects/cas) * [CoreOS Dex](https://github.com/coreos/dex)
diff --git a/WORKSPACE b/WORKSPACE index ed7c61f..e8fdcb8 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -3,7 +3,7 @@ load("//:bazlets.bzl", "load_bazlets") load_bazlets( - commit = "10cd2e19ba04fcaaf7edef21b8fc407c699fb9d1", + commit = "9c84d2870bf8d3f51685a289a1fc9edc3bf9dc89", #local_path = "/home/<user>/projects/bazlets", )
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/AirVantageApi.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/AirVantageApi.java new file mode 100644 index 0000000..02f4bd5 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/AirVantageApi.java
@@ -0,0 +1,56 @@ +// Copyright (C) 2018 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.googlesource.gerrit.plugins.oauth; + +import static java.lang.String.format; + +import org.scribe.builder.api.DefaultApi20; +import org.scribe.extractors.AccessTokenExtractor; +import org.scribe.extractors.JsonTokenExtractor; +import org.scribe.model.OAuthConfig; +import org.scribe.model.Verb; +import org.scribe.oauth.OAuthService; + +public class AirVantageApi extends DefaultApi20 { + + private static final String AUTHORIZE_URL = + "https://eu.airvantage.net/api/oauth/authorize?client_id=%s&response_type=code"; + private static final String ACCESS_TOKEN_ENDPOINT = "https://eu.airvantage.net/api/oauth/token"; + + @Override + public String getAuthorizationUrl(OAuthConfig config) { + return format(AUTHORIZE_URL, config.getApiKey()); + } + + @Override + public String getAccessTokenEndpoint() { + return ACCESS_TOKEN_ENDPOINT; + } + + @Override + public Verb getAccessTokenVerb() { + return Verb.POST; + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() { + return new JsonTokenExtractor(); + } + + @Override + public OAuthService createService(OAuthConfig config) { + return new OAuth20ServiceImpl(this, config); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/AirVantageOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/AirVantageOAuthService.java new file mode 100644 index 0000000..25edf2e --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/AirVantageOAuthService.java
@@ -0,0 +1,127 @@ +// Copyright (C) 2018 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.googlesource.gerrit.plugins.oauth; + +import static com.google.gerrit.server.OutputFormat.JSON; +import static javax.servlet.http.HttpServletResponse.SC_OK; +import static org.slf4j.LoggerFactory.getLogger; + +import com.google.common.base.CharMatcher; +import com.google.gerrit.extensions.annotations.PluginName; +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.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.PluginConfig; +import com.google.gerrit.server.config.PluginConfigFactory; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import java.io.IOException; +import org.scribe.builder.ServiceBuilder; +import org.scribe.model.OAuthRequest; +import org.scribe.model.Response; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuthService; +import org.slf4j.Logger; + +@Singleton +public class AirVantageOAuthService implements OAuthServiceProvider { + private static final Logger log = getLogger(AirVantageOAuthService.class); + static final String CONFIG_SUFFIX = "-airvantage-oauth"; + private static final String AV_PROVIDER_PREFIX = "airvantage-oauth:"; + private static final String PROTECTED_RESOURCE_URL = + "https://eu.airvantage.net/api/v1/users/current"; + private final OAuthService service; + + @Inject + AirVantageOAuthService( + PluginConfigFactory cfgFactory, + @PluginName String pluginName, + @CanonicalWebUrl Provider<String> urlProvider) { + PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX); + String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/"; + + service = + new ServiceBuilder() + .provider(AirVantageApi.class) + .apiKey(cfg.getString(InitOAuth.CLIENT_ID)) + .apiSecret(cfg.getString(InitOAuth.CLIENT_SECRET)) + .callback(canonicalWebUrl + "oauth") + .build(); + } + + @Override + public OAuthUserInfo getUserInfo(OAuthToken token) throws IOException { + OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + Token t = new Token(token.getToken(), token.getSecret(), token.getRaw()); + service.signRequest(t, request); + Response response = request.send(); + if (response.getCode() != SC_OK) { + throw new IOException( + String.format( + "Status %s (%s) for request %s", + response.getCode(), response.getBody(), request.getUrl())); + } + JsonElement userJson = JSON.newGson().fromJson(response.getBody(), JsonElement.class); + if (log.isDebugEnabled()) { + log.debug("User info response: {}", response.getBody()); + } + if (userJson.isJsonObject()) { + JsonObject jsonObject = userJson.getAsJsonObject(); + JsonElement id = jsonObject.get("uid"); + if (id == null || id.isJsonNull()) { + throw new IOException("Response doesn't contain uid field"); + } + JsonElement email = jsonObject.get("email"); + JsonElement name = jsonObject.get("name"); + return new OAuthUserInfo( + AV_PROVIDER_PREFIX + id.getAsString(), + null, + email.getAsString(), + name.getAsString(), + id.getAsString()); + } + + throw new IOException(String.format("Invalid JSON '%s': not a JSON Object", userJson)); + } + + @Override + public OAuthToken getAccessToken(OAuthVerifier rv) { + Verifier vi = new Verifier(rv.getValue()); + Token to = service.getAccessToken(null, vi); + return new OAuthToken(to.getToken(), to.getSecret(), to.getRawResponse()); + } + + @Override + public String getAuthorizationUrl() { + return service.getAuthorizationUrl(null); + } + + @Override + public String getVersion() { + return service.getVersion(); + } + + @Override + public String getName() { + return "AirVantage OAuth2"; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/BitbucketOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/BitbucketOAuthService.java index e600067..55680f2 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/BitbucketOAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/BitbucketOAuthService.java
@@ -110,7 +110,7 @@ public OAuthToken getAccessToken(OAuthVerifier rv) { Verifier vi = new Verifier(rv.getValue()); Token to = service.getAccessToken(null, vi); - return new OAuthToken(to.getToken(), to.getSecret(), null); + return new OAuthToken(to.getToken(), to.getSecret(), to.getRawResponse()); } @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/DexOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/DexOAuthService.java index ae1ca98..55f373b 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/DexOAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/DexOAuthService.java
@@ -93,10 +93,10 @@ JsonElement emailElement = claimObject.get("email"); JsonElement nameElement = claimObject.get("name"); if (emailElement == null || emailElement.isJsonNull()) { - throw new IOException(String.format("Response doesn't contain email field")); + throw new IOException("Response doesn't contain email field"); } if (nameElement == null || nameElement.isJsonNull()) { - throw new IOException(String.format("Response doesn't contain name field")); + throw new IOException("Response doesn't contain name field"); } String email = emailElement.getAsString(); String name = nameElement.getAsString();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/FacebookOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/FacebookOAuthService.java index d3ebe8e..7461154 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/FacebookOAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/FacebookOAuthService.java
@@ -96,7 +96,7 @@ JsonObject jsonObject = userJson.getAsJsonObject(); JsonElement id = jsonObject.get("id"); if (id == null || id.isJsonNull()) { - throw new IOException(String.format("Response doesn't contain id field")); + throw new IOException("Response doesn't contain id field"); } JsonElement email = jsonObject.get("email"); JsonElement name = jsonObject.get("name");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java index 8a198d3..13f89cb 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java
@@ -91,7 +91,7 @@ JsonObject jsonObject = userJson.getAsJsonObject(); JsonElement id = jsonObject.get("id"); if (id == null || id.isJsonNull()) { - throw new IOException(String.format("Response doesn't contain id field")); + throw new IOException("Response doesn't contain id field"); } JsonElement email = jsonObject.get("email"); JsonElement name = jsonObject.get("name");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/GitLabOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitLabOAuthService.java index 2d6870a..792342e 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/GitLabOAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitLabOAuthService.java
@@ -106,7 +106,7 @@ public OAuthToken getAccessToken(OAuthVerifier rv) { Verifier vi = new Verifier(rv.getValue()); Token to = service.getAccessToken(null, vi); - return new OAuthToken(to.getToken(), to.getSecret(), null); + return new OAuthToken(to.getToken(), to.getSecret(), to.getRawResponse()); } @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java index e44843a..1199ea3 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java
@@ -115,7 +115,7 @@ JsonObject jsonObject = userJson.getAsJsonObject(); JsonElement id = jsonObject.get("id"); if (id == null || id.isJsonNull()) { - throw new IOException(String.format("Response doesn't contain id field")); + throw new IOException("Response doesn't contain id field"); } JsonElement email = jsonObject.get("email"); JsonElement name = jsonObject.get("name");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java index d51d918..c0e9cf1 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
@@ -98,5 +98,12 @@ .annotatedWith(Exports.named(Office365OAuthService.CONFIG_SUFFIX)) .to(Office365OAuthService.class); } + + cfg = cfgFactory.getFromGerritConfig(pluginName + AirVantageOAuthService.CONFIG_SUFFIX); + if (cfg.getString(InitOAuth.CLIENT_ID) != null) { + bind(OAuthServiceProvider.class) + .annotatedWith(Exports.named(AirVantageOAuthService.CONFIG_SUFFIX)) + .to(AirVantageOAuthService.class); + } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java index b8e54e4..842a0d7 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java
@@ -42,6 +42,7 @@ private final Section dexOAuthProviderSection; private final Section keycloakOAuthProviderSection; private final Section office365OAuthProviderSection; + private final Section airVantageOAuthProviderSection; @Inject InitOAuth(ConsoleUI ui, Section.Factory sections, @PluginName String pluginName) { @@ -64,6 +65,8 @@ sections.get(PLUGIN_SECTION, pluginName + KeycloakOAuthService.CONFIG_SUFFIX); this.office365OAuthProviderSection = sections.get(PLUGIN_SECTION, pluginName + Office365OAuthService.CONFIG_SUFFIX); + this.airVantageOAuthProviderSection = + sections.get(PLUGIN_SECTION, pluginName + AirVantageOAuthService.CONFIG_SUFFIX); } @Override @@ -131,6 +134,12 @@ if (configureOffice365OAuthProvider) { configureOAuth(office365OAuthProviderSection); } + + boolean configureAirVantageOAuthProvider = + ui.yesno(true, "Use AirVantage OAuth provider for Gerrit login ?"); + if (configureAirVantageOAuthProvider) { + configureOAuth(airVantageOAuthProviderSection); + } } private void configureOAuth(Section s) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuth20ServiceImpl.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuth20ServiceImpl.java index a9eaddb..ecd8f26 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuth20ServiceImpl.java
@@ -14,6 +14,8 @@ package com.googlesource.gerrit.plugins.oauth; +import static org.slf4j.LoggerFactory.getLogger; + import org.scribe.builder.api.DefaultApi20; import org.scribe.model.OAuthConfig; import org.scribe.model.OAuthConstants; @@ -22,9 +24,11 @@ import org.scribe.model.Token; import org.scribe.model.Verifier; import org.scribe.oauth.OAuthService; +import org.slf4j.Logger; /** TODO(gildur): remove when updating to newer scribe lib */ final class OAuth20ServiceImpl implements OAuthService { + private static final Logger log = getLogger(OAuth20ServiceImpl.class); private static final String VERSION = "2.0"; @@ -56,7 +60,13 @@ request.addBodyParameter(OAuthConstants.SCOPE, config.getScope()); } request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_VALUE); + if (log.isDebugEnabled()) { + log.debug("Access token request: {}", request); + } Response response = request.send(); + if (log.isDebugEnabled()) { + log.debug("Access token response: {}", response.getBody()); + } return api.getAccessTokenExtractor().extract(response.getBody()); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java index 360b650..e1ccd29 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java
@@ -97,7 +97,7 @@ JsonObject jsonObject = userJson.getAsJsonObject(); JsonElement id = jsonObject.get("id"); if (id == null || id.isJsonNull()) { - throw new IOException(String.format("Response doesn't contain id field")); + throw new IOException("Response doesn't contain id field"); } JsonElement email = jsonObject.get("mail"); JsonElement name = jsonObject.get("displayName");
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index c16d3ef..757ea1e 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -38,6 +38,10 @@ root-url = "<dex url>" client-id = "<client-id>" client-secret = "<client-secret>" + + [plugin "@PLUGIN@-airvantage-oauth"] + client-id = "<client-id>" + client-secret = "<client-secret>" ``` When one from the sections above is omitted, OAuth SSO is used. @@ -200,3 +204,9 @@ See [Using Dex](https://github.com/coreos/dex/blob/master/Documentation/using-dex.md) for an example. + +### AirVantage + +The client-id and client-secret for AirVantage OAuth can be obtained by registering +a Client application. +See [Getting Started](https://source.sierrawireless.com/airvantage/av/howto/cloud/gettingstarted_api).