Skip to content

Commit

Permalink
Merge remote-tracking branch 'oauth/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
davido committed Aug 31, 2017
2 parents 880c4d2 + e497656 commit bcbc144
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 32 deletions.
17 changes: 3 additions & 14 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ workspace(name = "com_github_davido_gerrit_oauth_provider")
load("//:bazlets.bzl", "load_bazlets")

load_bazlets(
commit = "d1dd04380a2e41a32fca265c999936c35fcfae14",
commit = "b7514d03a7798905ff1513295b46620e57b8f386",
# local_path = "/home/<user>/projects/bazlets",
)

Expand All @@ -15,16 +15,5 @@ load(

gerrit_api()

load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", "maven_jar")

maven_jar(
name = "scribe",
artifact = "org.scribe:scribe:1.3.7",
sha1 = "583921bed46635d9f529ef5f14f7c9e83367bc6e",
)

maven_jar(
name = "commons_codec",
artifact = "commons-codec:commons-codec:1.4",
sha1 = "4216af16d38465bbab0f3dff8efa14204f7a399a",
)
load(":external_plugin_deps.bzl", "external_plugin_deps")
external_plugin_deps(omit_commons_codec = False)
11 changes: 9 additions & 2 deletions external_plugin_deps.bzl
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
load("//tools/bzl:maven_jar.bzl", "maven_jar")

def external_plugin_deps():
def external_plugin_deps(omit_commons_codec = True):
maven_jar(
name = "scribe",
artifact = "org.scribe:scribe:1.3.7",
sha1 = "583921bed46635d9f529ef5f14f7c9e83367bc6e",
)
)
if not omit_commons_codec:
maven_jar(
name = "commons_codec",
artifact = "commons-codec:commons-codec:1.4",
sha1 = "4216af16d38465bbab0f3dff8efa14204f7a399a",
)

68 changes: 67 additions & 1 deletion src/main/java/com/googlesource/gerrit/plugins/oauth/CasApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@

import org.scribe.builder.api.DefaultApi20;
import org.scribe.model.OAuthConfig;
import org.scribe.model.OAuthConstants;
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.scribe.utils.OAuthEncoder;

public class CasApi extends DefaultApi20 {
private static final String AUTHORIZE_URL = "%s/oauth2.0/authorize?client_id=%s&redirect_uri=%s";
private static final String AUTHORIZE_URL =
"%s/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s";

private final String rootUrl;

Expand All @@ -37,4 +45,62 @@ public String getAuthorizationUrl(OAuthConfig config) {
return String.format(
AUTHORIZE_URL, rootUrl, config.getApiKey(), OAuthEncoder.encode(config.getCallback()));
}

@Override
public Verb getAccessTokenVerb() {
return Verb.POST;
}

@Override
public OAuthService createService(OAuthConfig config) {
return new CasOAuthService(this, config);
}

private static final class CasOAuthService implements OAuthService {
private static final String VERSION = "2.0";
private static final String GRANT_TYPE = "grant_type";
private static final String GRANT_TYPE_VALUE = "authorization_code";

private final DefaultApi20 api;
private final OAuthConfig config;

private CasOAuthService(DefaultApi20 api, OAuthConfig config) {
this.config = config;
this.api = api;
}

@Override
public Token getAccessToken(Token token, Verifier verifier) {
OAuthRequest request =
new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_VALUE);
request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
request.addBodyParameter(OAuthConstants.CODE, verifier.getValue());
request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
Response response = request.send();
return api.getAccessTokenExtractor().extract(response.getBody());
}

@Override
public Token getRequestToken() {
throw new UnsupportedOperationException(
"Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there");
}

@Override
public String getVersion() {
return VERSION;
}

@Override
public void signRequest(Token token, OAuthRequest request) {
request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, token.getToken());
}

@Override
public String getAuthorizationUrl(Token token) {
return api.getAuthorizationUrl(config);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,28 +99,35 @@ public OAuthUserInfo getUserInfo(OAuthToken token) throws IOException {

JsonElement id = jsonObject.get("id");
if (id == null || id.isJsonNull()) {
throw new IOException(String.format("Response doesn't contain %s field", "id"));
throw new IOException(String.format("CAS response missing id: %s", response.getBody()));
}

JsonElement attrListJson = jsonObject.get("attributes");
if (attrListJson == null || !attrListJson.isJsonArray()) {
throw new IOException(String.format("Invalid JSON '%s': not a JSON Array", attrListJson));
if (attrListJson == null) {
throw new IOException(
String.format("CAS response missing attributes: %s", response.getBody()));
}

String email = null, name = null, login = null;
JsonArray attrJson = attrListJson.getAsJsonArray();
for (JsonElement elem : attrJson) {
if (elem == null || !elem.isJsonObject()) {
throw new IOException(String.format("Invalid JSON '%s': not a JSON Object", elem));

if (attrListJson != null && attrListJson.isJsonArray()) {
// It is possible for CAS to be configured to not return any attributes (email, name, login), in which case,
// CAS returns an empty JSON object "attributes":{}, rather than "null" or an empty JSON array "attributes": []

JsonArray attrJson = attrListJson.getAsJsonArray();
for (JsonElement elem : attrJson) {
if (elem == null || !elem.isJsonObject()) {
throw new IOException(String.format("Invalid JSON '%s': not a JSON Object", elem));
}
JsonObject obj = elem.getAsJsonObject();

String property = getStringElement(obj, "email");
if (property != null) email = property;
property = getStringElement(obj, "name");
if (property != null) name = property;
property = getStringElement(obj, "login");
if (property != null) login = property;
}
JsonObject obj = elem.getAsJsonObject();

String property = getStringElement(obj, "email");
if (property != null) email = property;
property = getStringElement(obj, "name");
if (property != null) name = property;
property = getStringElement(obj, "login");
if (property != null) login = property;
}

return new OAuthUserInfo(
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/Documentation/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ plugin.gerrit-oauth-provider-cas-oauth.root-url = "https://example.com/cas"

is required, since CAS is a self-hosted application.

Note that the CAS OAuth plugin only supports CAS V5 and higher.

The plugin expects CAS to make several attributes available to it:

| Name | Description | Required |
Expand Down
1 change: 1 addition & 0 deletions tools/bzl/maven_jar.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", "maven_jar")

0 comments on commit bcbc144

Please sign in to comment.