Skip to content

Commit

Permalink
feat: github oauth with embededOAuthAPI
Browse files Browse the repository at this point in the history
Signed-off-by: Pavol Baran <pbaran@redhat.com>
  • Loading branch information
xbaran4 committed Nov 24, 2021
1 parent 084c297 commit ac1ad86
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,6 @@ che.auth.access_denied_error_page=/error-oauth
# Reserved user names
che.auth.reserved_user_names=

# Configuration of GitHub OAuth client.
# You can setup GitHub OAuth to automate authentication to remote repositories.
# You need to first register this application with GitHub OAuth.
# GitHub OAuth client ID.
che.oauth.github.clientid=NULL

# GitHub OAuth client secret.
che.oauth.github.clientsecret=NULL

# GitHub OAuth authorization URI.
che.oauth.github.authuri= https://github.com/login/oauth/authorize

Expand Down
5 changes: 5 additions & 0 deletions wsmaster/che-core-api-auth-github/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-json</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -17,70 +17,33 @@
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.json.JsonHelper;
import org.eclipse.che.commons.json.JsonParseException;
import org.eclipse.che.security.oauth.shared.User;

/** OAuth authentication for github account. */
@Singleton
public class GitHubOAuthAuthenticator extends OAuthAuthenticator {
@Inject
public GitHubOAuthAuthenticator(
@Nullable @Named("che.oauth.github.clientid") String clientId,
@Nullable @Named("che.oauth.github.clientsecret") String clientSecret,
@Nullable @Named("che.oauth.github.redirecturis") String[] redirectUris,
@Nullable @Named("che.oauth.github.authuri") String authUri,
@Nullable @Named("che.oauth.github.tokenuri") String tokenUri)
String clientId, String clientSecret, String[] redirectUris, String authUri, String tokenUri)
throws IOException {
if (!isNullOrEmpty(clientId)
&& !isNullOrEmpty(clientSecret)
&& !isNullOrEmpty(authUri)
&& !isNullOrEmpty(tokenUri)
&& redirectUris != null
&& redirectUris.length != 0) {

configure(
clientId, clientSecret, redirectUris, authUri, tokenUri, new MemoryDataStoreFactory());
}
configure(
clientId, clientSecret, redirectUris, authUri, tokenUri, new MemoryDataStoreFactory());
}

@Override
public User getUser(OAuthToken accessToken) throws OAuthAuthenticationException {
GitHubUser user =
getJson(
"https://api.github.com/user?access_token=" + accessToken.getToken(), GitHubUser.class);

GithubEmail[] result =
getJson2(
"https://api.github.com/user/emails?access_token=" + accessToken.getToken(),
GithubEmail[].class,
null);
getJson("https://api.github.com/user", accessToken.getToken(), GitHubUser.class);
final String email = user.getEmail();

GithubEmail verifiedEmail = null;
for (GithubEmail email : result) {
if (email.isPrimary() && email.isVerified()) {
verifiedEmail = email;
break;
}
}
if (verifiedEmail == null
|| verifiedEmail.getEmail() == null
|| verifiedEmail.getEmail().isEmpty()) {
if (isNullOrEmpty(email)) {
throw new OAuthAuthenticationException(
"Sorry, we failed to find any verified emails associated with your GitHub account."
+ " Please, verify at least one email in your GitHub account and try to connect with GitHub again.");
}
user.setEmail(verifiedEmail.getEmail());
final String email = user.getEmail();
try {
new InternetAddress(email).validate();
} catch (AddressException e) {
Expand All @@ -89,32 +52,6 @@ public User getUser(OAuthToken accessToken) throws OAuthAuthenticationException
return user;
}

protected <O> O getJson2(String getUserUrl, Class<O> userClass, Type type)
throws OAuthAuthenticationException {
HttpURLConnection urlConnection = null;
InputStream urlInputStream = null;

try {
urlConnection = (HttpURLConnection) new URL(getUserUrl).openConnection();
urlConnection.setRequestProperty("Accept", "application/vnd.github.v3.html+json");
urlInputStream = urlConnection.getInputStream();
return JsonHelper.fromJson(urlInputStream, userClass, type);
} catch (JsonParseException | IOException e) {
throw new OAuthAuthenticationException(e.getMessage(), e);
} finally {
if (urlInputStream != null) {
try {
urlInputStream.close();
} catch (IOException ignored) {
}
}

if (urlConnection != null) {
urlConnection.disconnect();
}
}
}

@Override
public final String getOAuthProvider() {
return "github";
Expand All @@ -126,13 +63,14 @@ public OAuthToken getToken(String userId) throws IOException {
if (!(token == null || token.getToken() == null || token.getToken().isEmpty())) {
// Need to check if token which stored is valid for requests, then if valid - we returns it to
// caller
String tokenVerifyUrl = "https://api.github.com/?access_token=" + token.getToken();
String tokenVerifyUrl = "https://api.github.com/user";
HttpURLConnection http = null;
try {
http = (HttpURLConnection) new URL(tokenVerifyUrl).openConnection();
http.setInstanceFollowRedirects(false);
http.setRequestMethod("GET");
http.setRequestProperty("Accept", "application/json");
http.setRequestProperty("Authorization", "token " + token.getToken());

if (http.getResponseCode() == 401) {
return null;
Expand All @@ -147,38 +85,4 @@ public OAuthToken getToken(String userId) throws IOException {
}
return null;
}

/**
* information for each email address indicating if the address has been verified and if it’s the
* user’s primary email address for GitHub.
*/
public static class GithubEmail {
private boolean primary;
private boolean verified;
private String email;

public boolean isPrimary() {
return primary;
}

public void setPrimary(boolean primary) {
this.primary = primary;
}

public boolean isVerified() {
return verified;
}

public void setVerified(boolean verified) {
this.verified = verified;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.security.oauth;

import static com.google.common.base.Strings.isNullOrEmpty;

import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.security.oauth.shared.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class GitHubOAuthAuthenticatorProvider implements Provider<OAuthAuthenticator> {
private static final Logger LOG = LoggerFactory.getLogger(GitHubOAuthAuthenticatorProvider.class);
public static final String GITHUB_CLIENT_ID_PATH = "/che-conf/oauth/github/id";
public static final String GITHUB_CLIENT_SECRET_PATH = "/che-conf/oauth/github/secret";

private final OAuthAuthenticator authenticator;

@Inject
public GitHubOAuthAuthenticatorProvider(
@Nullable @Named("che.oauth.github.redirecturis") String[] redirectUris,
@Nullable @Named("che.oauth.github.authuri") String authUri,
@Nullable @Named("che.oauth.github.tokenuri") String tokenUri)
throws IOException {
authenticator =
getOAuthAuthenticator(
redirectUris, authUri, tokenUri, GITHUB_CLIENT_ID_PATH, GITHUB_CLIENT_SECRET_PATH);

}

@Override
public OAuthAuthenticator get() {
return authenticator;
}

@VisibleForTesting
OAuthAuthenticator getOAuthAuthenticator(
String[] redirectUris,
String authUri,
String tokenUri,
String clientIdPath,
String clientSecretPath)
throws IOException {

if (isNullOrEmpty(authUri)
|| isNullOrEmpty(tokenUri)
|| Objects.isNull(redirectUris)
|| redirectUris.length == 0) {
return new NoopOAuthAuthenticator();
}

String clientId, clientSecret;
try {
clientId = Files.readString(Path.of(clientIdPath));
clientSecret = Files.readString(Path.of(clientSecretPath));
} catch (IOException e) {
LOG.debug(
"Files with GitHub credentials cannot be accessed. NoopOAuthAuthenticator will be used.");
return new NoopOAuthAuthenticator();
}

if (isNullOrEmpty(clientId) || isNullOrEmpty(clientSecret)) {
LOG.debug(
"A file containing GitHub credentials is empty. NoopOAuthAuthenticator will be used.");
return new NoopOAuthAuthenticator();
}

return new GitHubOAuthAuthenticator(clientId, clientSecret, redirectUris, authUri, tokenUri);
}

static class NoopOAuthAuthenticator extends OAuthAuthenticator {
@Override
public User getUser(OAuthToken accessToken) throws OAuthAuthenticationException {
throw new OAuthAuthenticationException(
"The fallback noop authenticator cannot be used for GitHub authentication. Make sure OAuth is properly configured.");
}

@Override
public String getOAuthProvider() {
return "Noop";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ public class GithubModule extends AbstractModule {
protected void configure() {
Multibinder<OAuthAuthenticator> oAuthAuthenticators =
Multibinder.newSetBinder(binder(), OAuthAuthenticator.class);
oAuthAuthenticators.addBinding().to(GitHubOAuthAuthenticator.class);
oAuthAuthenticators.addBinding().toProvider(GitHubOAuthAuthenticatorProvider.class);
}
}
Loading

0 comments on commit ac1ad86

Please sign in to comment.