Add support for GitHub OAuth
diff --git a/BUCK b/BUCK
index a3df264..3216e67 100644
--- a/BUCK
+++ b/BUCK
@@ -2,12 +2,12 @@
define_license('scribe')
gerrit_plugin(
- name = 'gerrit-google-oauth-provider',
+ name = 'gerrit-oauth-provider',
srcs = glob(['src/main/java/**/*.java']),
resources = glob(['src/main/resources/**/*']),
manifest_entries = [
- 'Gerrit-PluginName: gerrit-google-oauth-provider',
- 'Gerrit-HttpModule: com.googlesource.gerrit.plugins.google.oauth.provider.HttpModule',
+ 'Gerrit-PluginName: gerrit-oauth-provider',
+ 'Gerrit-HttpModule: com.googlesource.gerrit.plugins.oauth.HttpModule',
],
provided_deps = ['//lib:gson'],
deps = [':scribe-oauth'],
@@ -15,7 +15,7 @@
java_library(
name = 'classpath',
- deps = [':gerrit-google-oauth-provider__plugin'],
+ deps = [':gerrit-oauth-provider__plugin'],
)
maven_jar(
diff --git a/README.md b/README.md
index cf060b4..53d11e8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,11 @@
-Google OAuth2 authentication provider for Gerrit Code Review
-============================================================
+OAuth2 authentication provider for Gerrit Code Review
+=====================================================
-This plugin depends on OAUTH extension point, exposed by this change: [1].
+This plugin depends on OAuth extension point, exposed by this change: [1].
+
+Currently the following OAuth providers are supported:
+
+* GitHub OAuth2
+* Google OAuth2
[1] https://gerrit-review.googlesource.com/65101
diff --git a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/HttpModule.java
deleted file mode 100644
index b3f2f0f..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/HttpModule.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.googlesource.gerrit.plugins.google.oauth.provider;
-
-import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
-import com.google.gerrit.httpd.plugins.HttpPluginModule;
-
-public class HttpModule extends HttpPluginModule {
- @Override
- protected void configureServlets() {
- bind(OAuthServiceProvider.class)
- .annotatedWith(Exports.named("google_oauth"))
- .to(GoogleOAuthService.class);
- }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHub2Api.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHub2Api.java
new file mode 100644
index 0000000..a452397
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHub2Api.java
@@ -0,0 +1,21 @@
+package com.googlesource.gerrit.plugins.oauth;
+
+import org.scribe.builder.api.DefaultApi20;
+import org.scribe.model.OAuthConfig;
+import org.scribe.utils.OAuthEncoder;
+
+public class GitHub2Api extends DefaultApi20 {
+ private static final String AUTHORIZE_URL =
+ "https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s";
+
+ @Override
+ public String getAccessTokenEndpoint() {
+ return "https://github.com/login/oauth/access_token";
+ }
+
+ @Override
+ public String getAuthorizationUrl(OAuthConfig config) {
+ return String.format(AUTHORIZE_URL, config.getApiKey(),
+ OAuthEncoder.encode(config.getCallback()));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/GoogleOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java
similarity index 72%
copy from src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/GoogleOAuthService.java
copy to src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java
index 576baf9..a7959ee 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/GoogleOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/GitHubOAuthService.java
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.google.oauth.provider;
+package com.googlesource.gerrit.plugins.oauth;
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.OutputFormat;
import com.google.gerrit.server.config.PluginConfig;
@@ -36,20 +37,24 @@
import java.io.IOException;
+import javax.servlet.http.HttpServletResponse;
+
@Singleton
-class GoogleOAuthService implements OAuthServiceProvider {
+class GitHubOAuthService implements OAuthServiceProvider {
+ static final String CONFIG_SUFFIX = "-github-oauth";
private static final String PROTECTED_RESOURCE_URL =
- "https://www.googleapis.com/oauth2/v2/userinfo?alt=json";
- private static final String SCOPE =
- "https://mail.google.com/ https://www.googleapis.com/auth/userinfo.email";
+ "https://api.github.com/user";
+
+ private static final String SCOPE = "user:email";
private final OAuthService service;
@Inject
- GoogleOAuthService(PluginConfigFactory cfgFactory,
+ GitHubOAuthService(PluginConfigFactory cfgFactory,
@PluginName String pluginName) {
- PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName);
+ PluginConfig cfg = cfgFactory.getFromGerritConfig(
+ pluginName + CONFIG_SUFFIX);
service = new ServiceBuilder()
- .provider(Google2Api.class)
+ .provider(GitHub2Api.class)
.apiKey(cfg.getString("client-id"))
.apiSecret(cfg.getString("client-secret"))
.callback(cfg.getString("callback"))
@@ -63,27 +68,30 @@
}
@Override
- public String getUsername(OAuthToken token) throws IOException {
+ 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() != HttpServletResponse.SC_OK) {
+ throw new IOException(String.format("Status %s (%s) for request %s",
+ response.getCode(), response.getBody(), request.getUrl()));
+ }
JsonElement userJson =
OutputFormat.JSON.newGson().fromJson(response.getBody(),
JsonElement.class);
if (userJson.isJsonObject()) {
JsonObject jsonObject = userJson.getAsJsonObject();
- JsonElement jsonElement = jsonObject.getAsJsonObject().get("name");
- if (jsonElement != null) {
- return jsonElement.getAsString();
- } else {
- throw new IOException(String.format(
- "Invalid JSON '%s': cannot find login field", userJson));
- }
+ JsonElement email = jsonObject.get("email");
+ JsonElement name = jsonObject.get("name");
+ JsonElement id = jsonObject.get("id");
+ return new OAuthUserInfo(id.getAsString(),
+ email.isJsonNull() ? null : email.getAsString(),
+ name.isJsonNull() ? null : name.getAsString());
} else {
- throw new IOException(String.format(
- "Invalid JSON '%s': not a JSON Object", userJson));
+ throw new IOException(String.format(
+ "Invalid JSON '%s': not a JSON Object", userJson));
}
}
@@ -117,6 +125,6 @@
@Override
public String getName() {
- return "Google OAuth2";
+ return "GitHub OAuth2";
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/Google2Api.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/Google2Api.java
similarity index 98%
rename from src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/Google2Api.java
rename to src/main/java/com/googlesource/gerrit/plugins/oauth/Google2Api.java
index d88cf83..7b4f4bf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/Google2Api.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/Google2Api.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.google.oauth.provider;
+package com.googlesource.gerrit.plugins.oauth;
import static org.scribe.utils.OAuthEncoder.encode;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/GoogleOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java
similarity index 75%
rename from src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/GoogleOAuthService.java
rename to src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java
index 576baf9..db24967 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/google/oauth/provider/GoogleOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/GoogleOAuthService.java
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.google.oauth.provider;
+package com.googlesource.gerrit.plugins.oauth;
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.OutputFormat;
import com.google.gerrit.server.config.PluginConfig;
@@ -36,18 +37,21 @@
import java.io.IOException;
+import javax.servlet.http.HttpServletResponse;
+
@Singleton
class GoogleOAuthService implements OAuthServiceProvider {
+ static final String CONFIG_SUFFIX = "-google-oauth";
private static final String PROTECTED_RESOURCE_URL =
- "https://www.googleapis.com/oauth2/v2/userinfo?alt=json";
- private static final String SCOPE =
- "https://mail.google.com/ https://www.googleapis.com/auth/userinfo.email";
+ "https://www.googleapis.com/plus/v1/people/me/openIdConnect";
+ private static final String SCOPE = "email profile";
private final OAuthService service;
@Inject
GoogleOAuthService(PluginConfigFactory cfgFactory,
@PluginName String pluginName) {
- PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName);
+ PluginConfig cfg = cfgFactory.getFromGerritConfig(
+ pluginName + CONFIG_SUFFIX);
service = new ServiceBuilder()
.provider(Google2Api.class)
.apiKey(cfg.getString("client-id"))
@@ -63,27 +67,30 @@
}
@Override
- public String getUsername(OAuthToken token) throws IOException {
+ 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() != HttpServletResponse.SC_OK) {
+ throw new IOException(String.format("Status %s (%s) for request %s",
+ response.getCode(), response.getBody(), request.getUrl()));
+ }
JsonElement userJson =
OutputFormat.JSON.newGson().fromJson(response.getBody(),
JsonElement.class);
if (userJson.isJsonObject()) {
JsonObject jsonObject = userJson.getAsJsonObject();
- JsonElement jsonElement = jsonObject.getAsJsonObject().get("name");
- if (jsonElement != null) {
- return jsonElement.getAsString();
- } else {
- throw new IOException(String.format(
- "Invalid JSON '%s': cannot find login field", userJson));
- }
+ JsonElement email = jsonObject.get("email");
+ JsonElement name = jsonObject.get("name");
+ JsonElement id = jsonObject.get("sub");
+ return new OAuthUserInfo(id.getAsString(),
+ email.isJsonNull() ? null : email.getAsString(),
+ name.isJsonNull() ? null : name.getAsString());
} else {
- throw new IOException(String.format(
- "Invalid JSON '%s': not a JSON Object", userJson));
+ throw new IOException(String.format(
+ "Invalid JSON '%s': not a JSON Object", userJson));
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
new file mode 100644
index 0000000..17bf3a3
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
@@ -0,0 +1,55 @@
+// 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.googlesource.gerrit.plugins.oauth;
+
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
+import com.google.gerrit.httpd.plugins.HttpPluginModule;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+
+public class HttpModule extends HttpPluginModule {
+
+ private final PluginConfigFactory cfgFactory;
+ private final String pluginName;
+
+ @Inject
+ HttpModule(PluginConfigFactory cfgFactory,
+ @PluginName String pluginName) {
+ this.cfgFactory = cfgFactory;
+ this.pluginName = pluginName;
+ }
+
+ @Override
+ protected void configureServlets() {
+ PluginConfig cfg = cfgFactory.getFromGerritConfig(
+ pluginName + GoogleOAuthService.CONFIG_SUFFIX);
+ if (cfg.getString("client-id") != null) {
+ bind(OAuthServiceProvider.class)
+ .annotatedWith(Exports.named(GoogleOAuthService.CONFIG_SUFFIX))
+ .to(GoogleOAuthService.class);
+ }
+
+ cfg = cfgFactory.getFromGerritConfig(
+ pluginName + GitHubOAuthService.CONFIG_SUFFIX);
+ if (cfg.getString("client-id") != null) {
+ bind(OAuthServiceProvider.class)
+ .annotatedWith(Exports.named(GitHubOAuthService.CONFIG_SUFFIX))
+ .to(GitHubOAuthService.class);
+ }
+ }
+}