diff --git a/pom.xml b/pom.xml index b91e7a81bcd524adf1666401a299b901b6b21886..bb5ef9dcffa29385c94435b7b9e3918b822558b4 100644 --- a/pom.xml +++ b/pom.xml @@ -1921,25 +1921,25 @@ <version>1.61</version> </dependency> <dependency> - <groupId>org.scribe</groupId> - <artifactId>scribe</artifactId> - <version>1.3.5</version> + <groupId>com.github.scribejava</groupId> + <artifactId>scribejava-apis</artifactId> + <version>6.5.1</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> - <version>0.10.5</version> + <version>0.10.6</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> - <version>0.10.5</version> + <version>0.10.6</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> - <version>0.10.5</version> + <version>0.10.6</version> <scope>runtime</scope> </dependency> <dependency> diff --git a/src/main/java/org/olat/core/util/WebappHelper.java b/src/main/java/org/olat/core/util/WebappHelper.java index 1277bf2d5f139090bf6bc6bef5a18acc7767ab70..3d7dd23dfaa04c41b35584f14b45fa9aa4f3f955 100644 --- a/src/main/java/org/olat/core/util/WebappHelper.java +++ b/src/main/java/org/olat/core/util/WebappHelper.java @@ -115,20 +115,15 @@ public class WebappHelper implements Initializable, Destroyable, ServletContextA } servletContextPath = servletContext.getContextPath(); - try { - InputStream meta = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF"); + try(InputStream meta = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF")) { if(meta != null) { - try { - Properties props = new Properties(); - props.load(meta); - changeSet = props.getProperty("Build-Change-Set"); - changeSetDate = props.getProperty("Build-Change-Set-Date"); - revisionNumber = props.getProperty("Build-Revision-Number"); - implementationVersion = props.getProperty("Implementation-Version"); - buildJdk = props.getProperty("Build-Jdk"); - } catch (IOException e) { - log.error("", e); - } + Properties props = new Properties(); + props.load(meta); + changeSet = props.getProperty("Build-Change-Set"); + changeSetDate = props.getProperty("Build-Change-Set-Date"); + revisionNumber = props.getProperty("Build-Revision-Number"); + implementationVersion = props.getProperty("Implementation-Version"); + buildJdk = props.getProperty("Build-Jdk"); } } catch (Exception e) { log.warn("MANIFEST.MF not found", e); diff --git a/src/main/java/org/olat/login/oauth/OAuthDispatcher.java b/src/main/java/org/olat/login/oauth/OAuthDispatcher.java index a77f99fa434dbc29397d6ca698a60d8da46e9178..a311835e823a56b8c2111c0d4a69b42ccab1252d 100644 --- a/src/main/java/org/olat/login/oauth/OAuthDispatcher.java +++ b/src/main/java/org/olat/login/oauth/OAuthDispatcher.java @@ -50,14 +50,19 @@ import org.olat.core.util.WebappHelper; import org.olat.login.oauth.model.OAuthRegistration; import org.olat.login.oauth.model.OAuthUser; import org.olat.login.oauth.spi.OpenIDVerifier; +import org.olat.login.oauth.spi.OpenIdConnectApi.OpenIdConnectService; +import org.olat.login.oauth.spi.OpenIdConnectFullConfigurableApi.OpenIdConnectFullConfigurableService; import org.olat.login.oauth.ui.JSRedirectWindowController; import org.olat.login.oauth.ui.OAuthAuthenticationController; import org.olat.user.UserManager; -import org.scribe.model.Token; -import org.scribe.model.Verifier; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; +import com.github.scribejava.core.model.OAuth1RequestToken; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuth10aService; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; + /** * Callback for OAuth 2 * @@ -106,11 +111,11 @@ public class OAuthDispatcher implements Dispatcher { return; } - try { - HttpSession sess = request.getSession(); + HttpSession sess = request.getSession(); + try(OAuthService service = (OAuthService)sess.getAttribute(OAuthConstants.OAUTH_SERVICE)) { + //OAuth 2.0 hasn't any request token Token requestToken = (Token)sess.getAttribute(OAuthConstants.REQUEST_TOKEN); - OAuthService service = (OAuthService)sess.getAttribute(OAuthConstants.OAUTH_SERVICE); OAuthSPI provider = (OAuthSPI)sess.getAttribute(OAuthConstants.OAUTH_SPI); Token accessToken; @@ -123,16 +128,29 @@ public class OAuthDispatcher implements Dispatcher { if(idToken == null) { redirectImplicitWorkflow(ureq); return; + } else if(service instanceof OpenIdConnectFullConfigurableService) { + OpenIDVerifier verifier = OpenIDVerifier.create(ureq, sess); + accessToken = ((OpenIdConnectFullConfigurableService)service).getAccessToken(verifier); + } else if(service instanceof OpenIdConnectService) { + OpenIDVerifier verifier = OpenIDVerifier.create(ureq, sess); + accessToken = ((OpenIdConnectService)service).getAccessToken(verifier); } else { - Verifier verifier = OpenIDVerifier.create(ureq, sess); - accessToken = service.getAccessToken(requestToken, verifier); + return; } - } else { + } else if(service instanceof OAuth10aService) { String requestVerifier = request.getParameter("oauth_verifier"); if(requestVerifier == null) {//OAuth 2.0 as a code requestVerifier = request.getParameter("code"); } - accessToken = service.getAccessToken(requestToken, new Verifier(requestVerifier)); + accessToken = ((OAuth10aService)service).getAccessToken((OAuth1RequestToken)requestToken, requestVerifier); + } else if(service instanceof OAuth20Service) { + String requestVerifier = request.getParameter("oauth_verifier"); + if(requestVerifier == null) {//OAuth 2.0 as a code + requestVerifier = request.getParameter("code"); + } + accessToken = ((OAuth20Service)service).getAccessToken(requestVerifier); + } else { + return; } OAuthUser infos = provider.getUser(service, accessToken); diff --git a/src/main/java/org/olat/login/oauth/OAuthLoginModule.java b/src/main/java/org/olat/login/oauth/OAuthLoginModule.java index a3d510d12b5dbd0b3f697ae02df1163fa561482c..99701c7d2356a053428641c57cf22eb68a1f9629 100644 --- a/src/main/java/org/olat/login/oauth/OAuthLoginModule.java +++ b/src/main/java/org/olat/login/oauth/OAuthLoginModule.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Set; import org.olat.core.configuration.AbstractSpringModule; +import org.olat.core.helpers.Settings; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.login.oauth.spi.OpenIdConnectFullConfigurableProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -180,16 +181,8 @@ public class OAuthLoginModule extends AbstractSpringModule { String endPoint = getStringPropertyValue("openIdConnectIF." + providerName + ".AuthorizationEndPoint", true); String displayName = getStringPropertyValue("openIdConnectIF." + providerName + ".DisplayName", true); - OpenIdConnectFullConfigurableProvider provider = new OpenIdConnectFullConfigurableProvider(); - provider.setRootEnabled(rootEnabled); - provider.setName(providerName); - provider.setDisplayName(displayName); - provider.setProviderName(providerName); - provider.setAppKey(apiKey); - provider.setAppSecret(apiSecret); - provider.setIssuer(issuer); - provider.setEndPoint(endPoint); - return provider; + return new OpenIdConnectFullConfigurableProvider(providerName, displayName, providerName, + apiKey, apiSecret, issuer, endPoint, rootEnabled, this); } public List<OAuthSPI> getAllSPIs() { @@ -268,6 +261,10 @@ public class OAuthLoginModule extends AbstractSpringModule { } return spi; } + + public String getCallbackUrl() { + return Settings.getServerContextPathURI() + OAuthConstants.CALLBACK_PATH; + } public boolean isAllowUserCreation() { return allowUserCreation; diff --git a/src/main/java/org/olat/login/oauth/OAuthResource.java b/src/main/java/org/olat/login/oauth/OAuthResource.java index 88bc2de744370ea5de0a781791c15a00935206f0..3646c7673e3a78146183eb60005a941ff0898fdd 100644 --- a/src/main/java/org/olat/login/oauth/OAuthResource.java +++ b/src/main/java/org/olat/login/oauth/OAuthResource.java @@ -22,17 +22,19 @@ package org.olat.login.oauth; import java.io.InputStream; import java.net.URL; import java.net.URLDecoder; +import java.util.UUID; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.olat.core.gui.media.MediaResource; -import org.olat.core.helpers.Settings; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; -import org.scribe.builder.ServiceBuilder; -import org.scribe.model.Token; -import org.scribe.oauth.OAuthService; + +import com.github.scribejava.core.model.OAuth1RequestToken; +import com.github.scribejava.core.oauth.OAuth10aService; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; /** * @@ -95,31 +97,22 @@ public class OAuthResource implements MediaResource { public static void redirect(OAuthSPI oauthProvider, HttpServletResponse httpResponse, HttpSession httpSession) { //Configure try { - ServiceBuilder builder= new ServiceBuilder(); - builder.provider(oauthProvider.getScribeProvider()) - .apiKey(oauthProvider.getAppKey()) - .apiSecret(oauthProvider.getAppSecret()); - String[] scopes = oauthProvider.getScopes(); - for(String scope:scopes) { - builder.scope(scope); - } - - String callbackUrl = Settings.getServerContextPathURI() + OAuthConstants.CALLBACK_PATH; - OAuthService service = builder - .callback(callbackUrl) - .build(); //Now build the call - + @SuppressWarnings("resource") // this need to be used afeter the redirect + OAuthService service = oauthProvider.getScribeProvider(); httpSession.setAttribute(OAuthConstants.OAUTH_SERVICE, service); httpSession.setAttribute(OAuthConstants.OAUTH_SPI, oauthProvider); - if("2.0".equals(service.getVersion())) { - String redirectUrl = service.getAuthorizationUrl(null); + if(service instanceof OAuth20Service) { + OAuth20Service oauthService = (OAuth20Service)service; + String state = UUID.randomUUID().toString().replace("-", ""); + String redirectUrl = oauthService.getAuthorizationUrl(state); saveStateAndNonce(httpSession, redirectUrl); httpResponse.sendRedirect(redirectUrl); - } else { - Token token = service.getRequestToken(); + } else if(service instanceof OAuth10aService) { + OAuth10aService oauthService = (OAuth10aService)service; + OAuth1RequestToken token = oauthService.getRequestToken(); httpSession.setAttribute(OAuthConstants.REQUEST_TOKEN, token); - String redirectUrl = service.getAuthorizationUrl(token); + String redirectUrl = oauthService.getAuthorizationUrl(token); httpResponse.sendRedirect(redirectUrl); } } catch (Exception e) { @@ -132,7 +125,7 @@ public class OAuthResource implements MediaResource { URL url = new URL(redirectUrl); final String[] pairs = url.getQuery().split("&"); for (String pair : pairs) { - final int idx = pair.indexOf("="); + final int idx = pair.indexOf('='); final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; diff --git a/src/main/java/org/olat/login/oauth/OAuthSPI.java b/src/main/java/org/olat/login/oauth/OAuthSPI.java index 6c8a86ce861fef1586866a5b0a2a97e33c875850..5711de84d0bde5c3aaa94d14d61249e5a222170c 100644 --- a/src/main/java/org/olat/login/oauth/OAuthSPI.java +++ b/src/main/java/org/olat/login/oauth/OAuthSPI.java @@ -19,11 +19,14 @@ */ package org.olat.login.oauth; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.olat.core.configuration.ConfigOnOff; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.model.Token; -import org.scribe.oauth.OAuthService; + +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuthService; /** * @@ -33,7 +36,7 @@ import org.scribe.oauth.OAuthService; */ public interface OAuthSPI extends ConfigOnOff { - public Api getScribeProvider(); + public OAuthService getScribeProvider(); public String getName(); @@ -47,16 +50,12 @@ public interface OAuthSPI extends ConfigOnOff { public String getIconCSS(); - public String getAppKey(); - - public String getAppSecret(); - - public String[] getScopes(); - public boolean isImplicitWorkflow(); - public OAuthUser getUser(OAuthService service, Token accessToken); - public String getIssuerIdentifier(); + + public OAuthUser getUser(OAuthService service, Token accessToken) + throws IOException, InterruptedException, ExecutionException; + } diff --git a/src/main/java/org/olat/login/oauth/spi/ADFSApi.java b/src/main/java/org/olat/login/oauth/spi/ADFSApi.java index 159a4e88361ce7423e83a97ba5830add96859e6e..0db05e901fb959f35a4bd42724e1891966264165 100644 --- a/src/main/java/org/olat/login/oauth/spi/ADFSApi.java +++ b/src/main/java/org/olat/login/oauth/spi/ADFSApi.java @@ -19,22 +19,24 @@ */ package org.olat.login.oauth.spi; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.olat.core.CoreSpringFactory; import org.olat.core.helpers.Settings; import org.olat.login.oauth.OAuthLoginModule; -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.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.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; -import org.scribe.utils.OAuthEncoder; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.utils.OAuthEncoder; /** * @@ -63,55 +65,54 @@ public class ADFSApi extends DefaultApi20 { //https://adfs.hamilton.ch/adfs/oauth2/authorize?response_type=code&client_id=25e53ef4-659e-11e4-b116-123b93f75cba&redirect_uri=https://kivik.frentix.com/olat/oauthcallback&resource=https://kivik.frentix.com/olat @Override - public String getAuthorizationUrl(OAuthConfig config) { + public String getAuthorizationBaseUrl() { OAuthLoginModule oauthModule = CoreSpringFactory.getImpl(OAuthLoginModule.class); String endpoint = oauthModule.getAdfsOAuth2Endpoint(); if(!endpoint.endsWith("/")) { endpoint += "/"; } - String authorizationUrl = endpoint + "authorize?response_type=code&client_id=%s&redirect_uri=%s&resource=%s"; - String resource = Settings.getServerContextPathURI(); - String url = String.format(authorizationUrl, config.getApiKey(), - OAuthEncoder.encode(config.getCallback()), - OAuthEncoder.encode(resource) - ); - return url; + + String authorizationUrl = endpoint + "authorize"; + String resource = OAuthEncoder.encode(Settings.getServerContextPathURI()); + return authorizationUrl + "?resource=" + resource; } - + @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return new JsonTokenExtractor(); + public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() { + return super.getAccessTokenExtractor(); } @Override - public OAuthService createService(OAuthConfig config) { - return new ADFSOAuth2Service(this, config); + public ADFSOAuth2Service createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new ADFSOAuth2Service(this, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, + httpClientConfig, httpClient); } - private class ADFSOAuth2Service extends OAuth20ServiceImpl { + private class ADFSOAuth2Service extends OAuth20Service { private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; private static final String GRANT_TYPE = "grant_type"; private final ADFSApi api; - private OAuthConfig config; - public ADFSOAuth2Service(ADFSApi api, OAuthConfig config) { - super(api, config); + public ADFSOAuth2Service(ADFSApi api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + super(api, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); this.api = api; - this.config = config; } @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { + public OAuth2AccessToken getAccessToken(String code) + throws InterruptedException, ExecutionException, IOException { OAuthRequest request = new OAuthRequest(Verb.POST, api.getAccessTokenEndpoint()); - 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()); + request.addBodyParameter(OAuthConstants.CLIENT_ID, getApiKey()); + request.addBodyParameter(OAuthConstants.CLIENT_SECRET, getApiSecret()); + request.addBodyParameter(OAuthConstants.CODE, code); + request.addBodyParameter(OAuthConstants.REDIRECT_URI, getCallback()); request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); - Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); + Response response = execute(request); + return api.getAccessTokenExtractor().extract(response); } } } diff --git a/src/main/java/org/olat/login/oauth/spi/ADFSProvider.java b/src/main/java/org/olat/login/oauth/spi/ADFSProvider.java index 5b688086bcf159c97f66f212eb297b3ec256850a..9561295bc624920e52653dbc3e2992b534d28085 100644 --- a/src/main/java/org/olat/login/oauth/spi/ADFSProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/ADFSProvider.java @@ -27,13 +27,15 @@ import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.model.Token; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 06.11.2014<br> @@ -75,8 +77,11 @@ public class ADFSProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new ADFSApi(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getAdfsApiKey()) + .apiSecret(oauthModule.getAdfsApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .build(new ADFSApi()); } @Override @@ -94,27 +99,11 @@ public class ADFSProvider implements OAuthSPI { return "o_icon o_icon_provider_adfs"; } - @Override - public String getAppKey() { - return oauthModule.getAdfsApiKey(); - } - - @Override - public String getAppSecret() { - return StringHelper.containsNonWhitespace(oauthModule.getAdfsApiSecret()) - ? oauthModule.getAdfsApiSecret() : "n/A"; - } - - @Override - public String[] getScopes() { - return new String[0]; - } - @Override public OAuthUser getUser(OAuthService service, Token accessToken) { OAuthUser user = new OAuthUser(); try { - JSONWebToken jwt = JSONWebToken.parse(accessToken); + JSONWebToken jwt = JSONWebToken.parse((OAuth2AccessToken)accessToken); JSONObject obj = jwt.getJsonPayload(); user.setId(getValue(obj, idAttributeName)); user.setFirstName(getValue(obj, firstNameAttributeName)); diff --git a/src/main/java/org/olat/login/oauth/spi/FacebookProvider.java b/src/main/java/org/olat/login/oauth/spi/FacebookProvider.java index c40760e15b5df89882e7f6baf338bed1149486cb..e46fa65c66cefaff2fbcf6685363b3e128f82b92 100644 --- a/src/main/java/org/olat/login/oauth/spi/FacebookProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/FacebookProvider.java @@ -19,6 +19,9 @@ */ package org.olat.login.oauth.spi; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.json.JSONException; import org.json.JSONObject; import org.olat.core.logging.OLog; @@ -27,18 +30,19 @@ import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.builder.api.FacebookApi; -import org.scribe.extractors.AccessTokenExtractor; -import org.scribe.extractors.JsonTokenExtractor; -import org.scribe.model.OAuthRequest; -import org.scribe.model.Response; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.github.scribejava.apis.FacebookApi; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 05.11.2014<br> @@ -69,13 +73,11 @@ public class FacebookProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new FacebookApi() { - @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return new JsonTokenExtractor(); - } - }; + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getFacebookApiKey()) + .apiSecret(oauthModule.getFacebookApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .build(FacebookApi.instance()); } @Override @@ -94,25 +96,12 @@ public class FacebookProvider implements OAuthSPI { } @Override - public String getAppKey() { - return oauthModule.getFacebookApiKey(); - } - - @Override - public String getAppSecret() { - return oauthModule.getFacebookApiSecret(); - } - - @Override - public String[] getScopes() { - return new String[0]; - } - - @Override - public OAuthUser getUser(OAuthService service, Token accessToken) { + public OAuthUser getUser(OAuthService service, Token accessToken) + throws InterruptedException, ExecutionException, IOException { + OAuth20Service oauthService = (OAuth20Service)service; OAuthRequest request = new OAuthRequest(Verb.GET, "https://graph.facebook.com/me"); - service.signRequest(accessToken, request); - Response oauthResponse = request.send(); + oauthService.signRequest((OAuth2AccessToken)accessToken, request); + Response oauthResponse = oauthService.execute(request); String body = oauthResponse.getBody(); return parseInfos(body); } diff --git a/src/main/java/org/olat/login/oauth/spi/Google2Api.java b/src/main/java/org/olat/login/oauth/spi/Google2Api.java deleted file mode 100644 index 97f3a95be7a430eb3b9193194c6195e6b184a9c4..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/login/oauth/spi/Google2Api.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * https://gist.github.com/yincrash/2465453 - */ -package org.olat.login.oauth.spi; - -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.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.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; -import org.scribe.utils.OAuthEncoder; - -/** - * Google OAuth2.0 - * Released under the same license as scribe (MIT License) - * @author yincrash - * - */ -public class Google2Api extends DefaultApi20 { - - private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%s&redirect_uri=%s"; - private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; - - @Override - public String getAccessTokenEndpoint() { - return "https://accounts.google.com/o/oauth2/token"; - } - - @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return new JsonTokenExtractor(); - } - - @Override - public String getAuthorizationUrl(OAuthConfig config) { - // Append scope if present - if (config.hasScope()) { - return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), - OAuthEncoder.encode(config.getCallback()), - OAuthEncoder.encode(config.getScope())); - } else { - return String.format(AUTHORIZE_URL, config.getApiKey(), - OAuthEncoder.encode(config.getCallback())); - } - } - - @Override - public Verb getAccessTokenVerb() { - return Verb.POST; - } - - @Override - public OAuthService createService(OAuthConfig config) { - return new GoogleOAuth2Service(this, config); - } - - private class GoogleOAuth2Service extends OAuth20ServiceImpl { - - private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; - private static final String GRANT_TYPE = "grant_type"; - private DefaultApi20 api; - private OAuthConfig config; - - public GoogleOAuth2Service(DefaultApi20 api, OAuthConfig config) { - super(api, config); - this.api = api; - this.config = config; - } - - @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { - OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - switch (api.getAccessTokenVerb()) { - case POST: - 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()); - request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); - break; - case GET: - default: - request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); - request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); - if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); - } - Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); - } - } - -} \ No newline at end of file diff --git a/src/main/java/org/olat/login/oauth/spi/Google2Provider.java b/src/main/java/org/olat/login/oauth/spi/Google2Provider.java index e9cd40b94969706bc8cd0ee649a877f841adb260..8e841d38de127fa1e3c53bc8e37e1fb19b79f741 100644 --- a/src/main/java/org/olat/login/oauth/spi/Google2Provider.java +++ b/src/main/java/org/olat/login/oauth/spi/Google2Provider.java @@ -19,6 +19,9 @@ */ package org.olat.login.oauth.spi; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.json.JSONException; import org.json.JSONObject; import org.olat.core.logging.OLog; @@ -27,15 +30,19 @@ import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.model.OAuthRequest; -import org.scribe.model.Response; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.github.scribejava.apis.GoogleApi20; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 05.11.2014<br> @@ -46,6 +53,8 @@ import org.springframework.stereotype.Service; public class Google2Provider implements OAuthSPI { private static final OLog log = Tracing.createLoggerFor(Google2Provider.class); + + private static final String SCOPES = "https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"; @Autowired private OAuthLoginModule oauthModule; @@ -66,8 +75,12 @@ public class Google2Provider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new Google2Api(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getGoogleApiKey()) + .apiSecret(oauthModule.getGoogleApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .defaultScope(SCOPES) + .build(GoogleApi20.instance()); } @Override @@ -86,29 +99,12 @@ public class Google2Provider implements OAuthSPI { } @Override - public String getAppKey() { - return oauthModule.getGoogleApiKey(); - } - - @Override - public String getAppSecret() { - return oauthModule.getGoogleApiSecret(); - } - - @Override - public String[] getScopes() { - return new String[] { - "https://www.googleapis.com/auth/plus.me", - "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/userinfo.profile" - }; - } - - @Override - public OAuthUser getUser(OAuthService service, Token accessToken) { + public OAuthUser getUser(OAuthService service, Token accessToken) + throws InterruptedException, ExecutionException, IOException { + OAuth20Service oauthService = (OAuth20Service)service; OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "https://www.googleapis.com/oauth2/v2/userinfo"); - service.signRequest(accessToken, oauthRequest); - Response oauthResponse = oauthRequest.send(); + oauthService.signRequest((OAuth2AccessToken)accessToken, oauthRequest); + Response oauthResponse = oauthService.execute(oauthRequest); String body = oauthResponse.getBody(); return parseInfos(body); } diff --git a/src/main/java/org/olat/login/oauth/spi/JSONWebToken.java b/src/main/java/org/olat/login/oauth/spi/JSONWebToken.java index a1668d669f1f389cb3601cae5fcc7bfdcb8e8f52..2ae7bb15d4af9332d29bd2b04a7f5d74f28d9622 100644 --- a/src/main/java/org/olat/login/oauth/spi/JSONWebToken.java +++ b/src/main/java/org/olat/login/oauth/spi/JSONWebToken.java @@ -22,7 +22,8 @@ package org.olat.login.oauth.spi; import org.json.JSONException; import org.json.JSONObject; import org.olat.core.util.StringHelper; -import org.scribe.model.Token; + +import com.github.scribejava.core.model.OAuth2AccessToken; /** * @@ -54,8 +55,9 @@ public class JSONWebToken { return jsonPayload; } - public static JSONWebToken parse(Token token) throws JSONException { - String accessToken = token.getToken(); + public static JSONWebToken parse(OAuth2AccessToken token) throws JSONException { + String accessToken= token.getAccessToken(); + int firstIndex = accessToken.indexOf('.'); int secondIndex = accessToken.indexOf('.', firstIndex + 1); diff --git a/src/main/java/org/olat/login/oauth/spi/LinkedInProvider.java b/src/main/java/org/olat/login/oauth/spi/LinkedInProvider.java index de8b3c29aa74abf6d91906cb12e67c0ecb5e43df..6b5b67c0e5b5308fbc4f0bdbbd7435fb98d233b1 100644 --- a/src/main/java/org/olat/login/oauth/spi/LinkedInProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/LinkedInProvider.java @@ -20,33 +20,28 @@ package org.olat.login.oauth.spi; import java.io.IOException; -import java.io.StringReader; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; +import java.util.concurrent.ExecutionException; +import org.json.JSONException; +import org.json.JSONObject; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.builder.api.LinkedInApi; -import org.scribe.model.OAuthRequest; -import org.scribe.model.Response; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; + +import com.github.scribejava.apis.LinkedInApi20; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; /** * @@ -59,6 +54,7 @@ public class LinkedInProvider implements OAuthSPI { private static final OLog log = Tracing.createLoggerFor(LinkedInProvider.class); + @Autowired private OAuthLoginModule oauthModule; @@ -93,74 +89,46 @@ public class LinkedInProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new LinkedInApi(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getLinkedInApiKey()) + .apiSecret(oauthModule.getLinkedInApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .defaultScope("r_basicprofile r_emailaddress") + .build(LinkedInApi20.instance()); } @Override - public String getAppKey() { - return oauthModule.getLinkedInApiKey(); - } - - @Override - public String getAppSecret() { - return oauthModule.getLinkedInApiSecret(); - } - - @Override - public String[] getScopes() { - return new String[]{ "r_basicprofile", "r_emailaddress" }; - } - - @Override - public OAuthUser getUser(OAuthService service, Token accessToken) { - OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address)"); - service.signRequest(accessToken, oauthRequest); - Response oauthResponse = oauthRequest.send(); + public OAuthUser getUser(OAuthService service, Token accessToken) + throws InterruptedException, ExecutionException, IOException { + OAuth20Service oauthService = (OAuth20Service)service; + OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address)"); + oauthRequest.addHeader("x-li-format", "json"); + oauthRequest.addHeader("Accept-Language", "en-GB"); + oauthService.signRequest((OAuth2AccessToken)accessToken, oauthRequest); + Response oauthResponse = oauthService.execute(oauthRequest); String body = oauthResponse.getBody(); return parseInfos(body); } public OAuthUser parseInfos(String body) { - OAuthUser infos = new OAuthUser(); + OAuthUser user = new OAuthUser(); + try { - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - Document doc = dBuilder.parse(new InputSource(new StringReader(body))); - - NodeList nodes = doc.getElementsByTagName("person"); - for (int i = 0; i < nodes.getLength(); i++) { - Element element = (Element)nodes.item(i); - for(Node node=element.getFirstChild(); node!=null; node=node.getNextSibling()) { - String localName = node.getNodeName(); - if("first-name".equals(localName)) { - infos.setFirstName(getCharacterDataFromElement(node)); - } else if("last-name".equals(localName)) { - infos.setLastName(getCharacterDataFromElement(node)); - } else if("email-address".equals(localName)) { - infos.setEmail(getCharacterDataFromElement(node)); - } else if("id".equals(localName)) { - infos.setId(getCharacterDataFromElement(node)); - } - } - } - } catch (ParserConfigurationException | SAXException | IOException e) { + JSONObject obj = new JSONObject(body); + user.setId(getValue(obj, "id")); + user.setFirstName(getValue(obj, "firstName")); + user.setLastName(getValue(obj, "lastName")); + user.setEmail(getValue(obj, "emailAddress")); + } catch (JSONException e) { log.error("", e); } - return infos; + + return user; } - public String getCharacterDataFromElement(Node parentNode) { - StringBuilder sb = new StringBuilder(); - for (Node node = parentNode.getFirstChild(); node != null; node = node.getNextSibling()) { - if (node.getNodeType() == Node.TEXT_NODE) { - String text = node.getNodeValue(); - if (StringHelper.containsNonWhitespace(text)) { - sb.append(text); - } - } - } - return sb.toString(); + private String getValue(JSONObject obj, String property) { + String value = obj.optString(property); + return StringHelper.containsNonWhitespace(value) ? value : null; } @Override diff --git a/src/main/java/org/olat/login/oauth/spi/OpenIDVerifier.java b/src/main/java/org/olat/login/oauth/spi/OpenIDVerifier.java index e7ae5bfbcf85fb5849831560d599d9199150db9d..bf9fdbd2b7fbfcf7a59b700249affd6b0f0405a6 100644 --- a/src/main/java/org/olat/login/oauth/spi/OpenIDVerifier.java +++ b/src/main/java/org/olat/login/oauth/spi/OpenIDVerifier.java @@ -23,7 +23,6 @@ import javax.servlet.http.HttpSession; import org.olat.core.gui.UserRequest; import org.olat.login.oauth.OAuthConstants; -import org.scribe.model.Verifier; /** * @@ -31,8 +30,9 @@ import org.scribe.model.Verifier; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class OpenIDVerifier extends Verifier { +public class OpenIDVerifier { + private final String state; private final String idToken; private final String accessToken; @@ -40,7 +40,7 @@ public class OpenIDVerifier extends Verifier { private final String sessionNonce; public OpenIDVerifier(String state, String idToken, String accessToken, String sessionState, String sessionNonce) { - super(state); + this.state = state; this.idToken = idToken; this.accessToken = accessToken; this.sessionState = sessionState; @@ -48,7 +48,7 @@ public class OpenIDVerifier extends Verifier { } public String getState() { - return getValue(); + return state; } public String getIdToken() { diff --git a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectApi.java b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectApi.java index bfb785d6e0f1365f62e22f8d58c80858d7742bf4..c1c976cf769dc705194abda335acbc799774facb 100644 --- a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectApi.java +++ b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectApi.java @@ -27,15 +27,15 @@ import org.olat.core.CoreSpringFactory; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.login.oauth.OAuthLoginModule; -import org.scribe.builder.api.DefaultApi20; -import org.scribe.extractors.AccessTokenExtractor; -import org.scribe.model.OAuthConfig; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.model.Verifier; -import org.scribe.oauth.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; -import org.scribe.utils.OAuthEncoder; + +import com.github.scribejava.apis.openid.OpenIdJsonTokenExtractor; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; /** * @@ -46,6 +46,12 @@ import org.scribe.utils.OAuthEncoder; public class OpenIdConnectApi extends DefaultApi20 { private static final OLog log = Tracing.createLoggerFor(OpenIdConnectApi.class); + + private final OpenIdConnectProvider provider; + + public OpenIdConnectApi(OpenIdConnectProvider provider) { + this.provider = provider; + } @Override public String getAccessTokenEndpoint() { @@ -53,22 +59,16 @@ public class OpenIdConnectApi extends DefaultApi20 { } @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return null; - } + public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() { + return OpenIdJsonTokenExtractor.instance(); + } @Override - public String getAuthorizationUrl(OAuthConfig config) { - OAuthLoginModule oauthModule = CoreSpringFactory.getImpl(OAuthLoginModule.class); - String url = oauthModule.getOpenIdConnectIFAuthorizationEndPoint(); + public String getAuthorizationBaseUrl() { + String url = provider.getEndPoint(); StringBuilder authorizeUrl = new StringBuilder(); authorizeUrl .append(url).append("?") - .append("response_type=").append(OAuthEncoder.encode("id_token token")) - .append("&client_id=").append(config.getApiKey()) - .append("&redirect_uri=").append(OAuthEncoder.encode(config.getCallback())) - .append("&scope=").append(OAuthEncoder.encode("openid email")) - .append("&state=").append(UUID.randomUUID().toString()) .append("&nonce=").append(UUID.randomUUID().toString()); return authorizeUrl.toString(); } @@ -79,36 +79,33 @@ public class OpenIdConnectApi extends DefaultApi20 { } @Override - public OAuthService createService(OAuthConfig config) { - return new OpenIdConnectService(this, config); + public OAuth20Service createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new OpenIdConnectService(this, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); } - public class OpenIdConnectService extends OAuth20ServiceImpl { + public class OpenIdConnectService extends OAuth20Service { - public OpenIdConnectService(DefaultApi20 api, OAuthConfig config) { - super(api, config); + public OpenIdConnectService(DefaultApi20 api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + super(api, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); } - @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { + public OAuth2AccessToken getAccessToken(OpenIDVerifier oVerifier) { OAuthLoginModule oauthModule = CoreSpringFactory.getImpl(OAuthLoginModule.class); try { - OpenIDVerifier oVerifier = (OpenIDVerifier)verifier; String idToken = oVerifier.getIdToken(); JSONObject idJson = JSONWebToken.parse(idToken).getJsonPayload(); JSONObject accessJson = JSONWebToken.parse(oVerifier.getAccessToken()).getJsonPayload(); boolean allOk = true; - if(!oauthModule.getOpenIdConnectIFIssuer().equals(idJson.get("iss"))) { - allOk &= false; - log.error("iss don't match issuer"); - } - if(!oauthModule.getOpenIdConnectIFIssuer().equals(accessJson.get("iss"))) { + if(!oauthModule.getOpenIdConnectIFIssuer().equals(idJson.get("iss")) + || !oauthModule.getOpenIdConnectIFIssuer().equals(accessJson.get("iss"))) { allOk &= false; log.error("iss don't match issuer"); } - if(!oauthModule.getOpenIdConnectIFApiKey().equals(idJson.get("aud"))) { + if(!getApiKey().equals(idJson.get("aud"))) { allOk &= false; log.error("aud don't match application key"); } @@ -122,7 +119,7 @@ public class OpenIdConnectApi extends DefaultApi20 { log.error("session nonce don't match verifier nonce"); } - return allOk ? new Token(idToken, oVerifier.getState()) : null; + return allOk ? new OAuth2AccessToken(idToken, oVerifier.getState()) : null; } catch (JSONException e) { log.error("", e); return null; diff --git a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableApi.java b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableApi.java index d95128d1d066ed9c0d274f365657f6f786488d42..2a2d1d493b58bb0fa09f3394da44f44509db5c5c 100644 --- a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableApi.java +++ b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableApi.java @@ -25,15 +25,15 @@ import org.json.JSONException; import org.json.JSONObject; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; -import org.scribe.builder.api.DefaultApi20; -import org.scribe.extractors.AccessTokenExtractor; -import org.scribe.model.OAuthConfig; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.model.Verifier; -import org.scribe.oauth.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; -import org.scribe.utils.OAuthEncoder; + +import com.github.scribejava.apis.openid.OpenIdJsonTokenExtractor; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; /** * @@ -42,7 +42,7 @@ import org.scribe.utils.OAuthEncoder; * */ public class OpenIdConnectFullConfigurableApi extends DefaultApi20 { - + private static final OLog log = Tracing.createLoggerFor(OpenIdConnectFullConfigurableApi.class); private final OpenIdConnectFullConfigurableProvider provider; @@ -55,23 +55,18 @@ public class OpenIdConnectFullConfigurableApi extends DefaultApi20 { public String getAccessTokenEndpoint() { return null; } - - @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return null; - } @Override - public String getAuthorizationUrl(OAuthConfig config) { + public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() { + return OpenIdJsonTokenExtractor.instance(); + } + + @Override + public String getAuthorizationBaseUrl() { String url = provider.getEndPoint(); StringBuilder authorizeUrl = new StringBuilder(); authorizeUrl .append(url).append("?") - .append("response_type=").append(OAuthEncoder.encode("id_token token")) - .append("&client_id=").append(config.getApiKey()) - .append("&redirect_uri=").append(OAuthEncoder.encode(config.getCallback())) - .append("&scope=").append(OAuthEncoder.encode("openid email")) - .append("&state=").append(UUID.randomUUID().toString()) .append("&nonce=").append(UUID.randomUUID().toString()); return authorizeUrl.toString(); } @@ -82,30 +77,27 @@ public class OpenIdConnectFullConfigurableApi extends DefaultApi20 { } @Override - public OAuthService createService(OAuthConfig config) { - return new OpenIdConnectFullConfigurableService(this, config); + public OpenIdConnectFullConfigurableService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new OpenIdConnectFullConfigurableService(this, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); } - public class OpenIdConnectFullConfigurableService extends OAuth20ServiceImpl { + public class OpenIdConnectFullConfigurableService extends OAuth20Service { - public OpenIdConnectFullConfigurableService(DefaultApi20 api, OAuthConfig config) { - super(api, config); + public OpenIdConnectFullConfigurableService(OpenIdConnectFullConfigurableApi api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + super(api, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); } - @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { + public OAuth2AccessToken getAccessToken(OpenIDVerifier oVerifier) { try { - OpenIDVerifier oVerifier = (OpenIDVerifier)verifier; String idToken = oVerifier.getIdToken(); - JSONObject idJson = JSONWebToken.parse(idToken).getJsonPayload(); + JSONObject idJson = JSONWebToken.parse(idToken).getJsonPayload(); JSONObject accessJson = JSONWebToken.parse(oVerifier.getAccessToken()).getJsonPayload(); boolean allOk = true; - if(!provider.getIssuer().equals(idJson.get("iss"))) { - allOk &= false; - log.error("iss don't match issuer"); - } - if(!provider.getIssuer().equals(accessJson.get("iss"))) { + if(!provider.getIssuer().equals(idJson.get("iss")) + || !provider.getIssuer().equals(accessJson.get("iss"))) { allOk &= false; log.error("iss don't match issuer"); } @@ -114,6 +106,7 @@ public class OpenIdConnectFullConfigurableApi extends DefaultApi20 { allOk &= false; log.error("aud don't match application key"); } + if(!oVerifier.getState().equals(oVerifier.getSessionState())) { allOk &= false; log.error("state doesn't match session state"); @@ -123,8 +116,7 @@ public class OpenIdConnectFullConfigurableApi extends DefaultApi20 { allOk &= false; log.error("session nonce don't match verifier nonce"); } - - return allOk ? new Token(idToken, oVerifier.getState()) : null; + return allOk ? new OAuth2AccessToken(idToken, oVerifier.getState()) : null; } catch (JSONException e) { log.error("", e); return null; diff --git a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableProvider.java b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableProvider.java index 71061e6d38220ea53d4891779ed6501a92e9554e..5844d25348b0fb6e59d6bb4fe7eeb11222fa15a2 100644 --- a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectFullConfigurableProvider.java @@ -25,11 +25,14 @@ import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthDisplayName; +import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.model.Token; -import org.scribe.oauth.OAuthService; + +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuthService; /** * @@ -41,15 +44,30 @@ public class OpenIdConnectFullConfigurableProvider implements OAuthSPI, OAuthDis private static final OLog log = Tracing.createLoggerFor(Google2Provider.class); - private String name; - private String displayName; - private String providerName; - private String appKey; - private String appSecret; - private String issuer; - private String endPoint; + private final String name; + private final String displayName; + private final String providerName; + private final String appKey; + private final String appSecret; + private final String issuer; + private final String endPoint; + + private final boolean rootEnabled; - private boolean rootEnabled; + private final OAuthLoginModule oauthModule; + + public OpenIdConnectFullConfigurableProvider(String name, String displayName, String providerName, + String appKey, String appSecret, String issuer, String endPoint, boolean rootEnabled, OAuthLoginModule oauthModule) { + this.name = name; + this.displayName = displayName; + this.providerName = providerName; + this.appKey = appKey; + this.appSecret = appSecret; + this.issuer = issuer; + this.endPoint = endPoint; + this.rootEnabled = rootEnabled; + this.oauthModule = oauthModule; + } @Override public boolean isEnabled() { @@ -61,18 +79,19 @@ public class OpenIdConnectFullConfigurableProvider implements OAuthSPI, OAuthDis return rootEnabled; } - public void setRootEnabled(boolean rootEnabled) { - this.rootEnabled = rootEnabled; - } - @Override public boolean isImplicitWorkflow() { return true; } @Override - public Api getScribeProvider() { - return new OpenIdConnectFullConfigurableApi(this); + public OAuthService getScribeProvider() { + return new ServiceBuilder(getAppKey()) + .apiSecret(getAppSecret()) + .callback(oauthModule.getCallbackUrl()) + .defaultScope("openid email") + .responseType("id_token token") + .build(new OpenIdConnectFullConfigurableApi(this)); } @Override @@ -80,76 +99,41 @@ public class OpenIdConnectFullConfigurableProvider implements OAuthSPI, OAuthDis return name; } - public void setName(String name) { - this.name = name; - } - @Override public String getDisplayName() { return displayName; } - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - @Override public String getProviderName() { return providerName; } - - public void setProviderName(String providerName) { - this.providerName = providerName; - } @Override public String getIconCSS() { return "o_icon o_icon_provider_" + name; } - @Override public String getAppKey() { return appKey; } - - public void setAppKey(String appKey) { - this.appKey = appKey; - } - @Override public String getAppSecret() { return appSecret; } - - public void setAppSecret(String appSecret) { - this.appSecret = appSecret; - } public String getIssuer() { return issuer; } - public void setIssuer(String issuer) { - this.issuer = issuer; - } - public String getEndPoint() { return endPoint; } - public void setEndPoint(String endPoint) { - this.endPoint = endPoint; - } - - @Override - public String[] getScopes() { - return new String[] { "openid", "email" }; - } - @Override public OAuthUser getUser(OAuthService service, Token accessToken) { try { - String idToken = accessToken.getToken(); + String idToken = ((OAuth2AccessToken)accessToken).getAccessToken(); JSONWebToken token = JSONWebToken.parse(idToken); return parseInfos(token.getPayload()); } catch (JSONException e) { diff --git a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectProvider.java b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectProvider.java index 3bd2ea380c8beec0a4a7b81f24ce75289a8808ac..0c534101fc47718a35c6035f260fa723335de624 100644 --- a/src/main/java/org/olat/login/oauth/spi/OpenIdConnectProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/OpenIdConnectProvider.java @@ -27,12 +27,14 @@ import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.model.Token; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 15.07.2016<br> @@ -63,8 +65,17 @@ public class OpenIdConnectProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new OpenIdConnectApi(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getOpenIdConnectIFApiKey()) + .apiSecret(oauthModule.getOpenIdConnectIFApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .defaultScope("openid email") + .responseType("id_token token") + .build(new OpenIdConnectApi(this)); + } + + public String getEndPoint() { + return oauthModule.getOpenIdConnectIFAuthorizationEndPoint(); } @Override @@ -82,25 +93,10 @@ public class OpenIdConnectProvider implements OAuthSPI { return "o_icon o_icon_provider_openid"; } - @Override - public String getAppKey() { - return oauthModule.getOpenIdConnectIFApiKey(); - } - - @Override - public String getAppSecret() { - return oauthModule.getOpenIdConnectIFApiSecret(); - } - - @Override - public String[] getScopes() { - return new String[] { "openid", "email" }; - } - @Override public OAuthUser getUser(OAuthService service, Token accessToken) { try { - String idToken = accessToken.getToken(); + String idToken = ((OAuth2AccessToken)accessToken).getAccessToken(); JSONWebToken token = JSONWebToken.parse(idToken); return parseInfos(token.getPayload()); } catch (JSONException e) { diff --git a/src/main/java/org/olat/login/oauth/spi/TequilaApi.java b/src/main/java/org/olat/login/oauth/spi/TequilaApi.java index c26528f3ae7c51f9c0a9051716a2faa90e9a83eb..ee9c1a60e6b8a1532a4b3824ca58622ed24210e3 100644 --- a/src/main/java/org/olat/login/oauth/spi/TequilaApi.java +++ b/src/main/java/org/olat/login/oauth/spi/TequilaApi.java @@ -19,25 +19,27 @@ */ package org.olat.login.oauth.spi; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.olat.core.CoreSpringFactory; -import org.olat.login.oauth.OAuthLoginModule; -import org.scribe.builder.api.DefaultApi20; -import org.scribe.extractors.AccessTokenExtractor; -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.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; -import org.scribe.utils.OAuthEncoder; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.utils.OAuthEncoder; /** * @@ -46,61 +48,69 @@ import org.scribe.utils.OAuthEncoder; * */ public class TequilaApi extends DefaultApi20 { - + + private static final OLog log = Tracing.createLoggerFor(TequilaApi.class); + + private final String endPoint; + + protected TequilaApi(String endPoint) { + this.endPoint = endPoint; + } + @Override public String getAccessTokenEndpoint() { - OAuthLoginModule oauthModule = CoreSpringFactory.getImpl(OAuthLoginModule.class); - String endpoint = oauthModule.getTequilaOAuth2Endpoint(); - if(!endpoint.endsWith("/")) { - endpoint += "/"; + String tokenEndPoint = endPoint; + if(!tokenEndPoint.endsWith("/")) { + tokenEndPoint += "/"; } - endpoint += "token"; - return endpoint; + tokenEndPoint += "token"; + return tokenEndPoint; } @Override - public String getAuthorizationUrl(OAuthConfig config) { - OAuthLoginModule oauthModule = CoreSpringFactory.getImpl(OAuthLoginModule.class); - String endpoint = oauthModule.getTequilaOAuth2Endpoint(); - if(!endpoint.endsWith("/")) { - endpoint += "/"; + public String getAuthorizationBaseUrl() { + String baseUrl = endPoint; + if(!baseUrl.endsWith("/")) { + baseUrl += "/"; } - String url = endpoint + "auth?response_type=code" + - "&client_id=" + urlEncode(config.getApiKey()) + - "&scope=" + config.getScope() + - "&redirect_uri=" + urlEncode(config.getCallback()); - return url; + return baseUrl + "auth"; } - + @Override - public AccessTokenExtractor getAccessTokenExtractor() { + public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() { return new TequilaBearerExtractor(); } @Override - public OAuthService createService(OAuthConfig config) { - return new TequilaAuth2Service(this, config); + public TequilaAuth2Service createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new TequilaAuth2Service(this, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); } - public static class TequilaBearerExtractor implements AccessTokenExtractor { + public static class TequilaBearerExtractor implements TokenExtractor<OAuth2AccessToken> { private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\""); @Override - public Token extract(String response) { - Matcher matcher = accessTokenPattern.matcher(response); - if(matcher.find()) { - return new Token(matcher.group(1), "", response); - } - if(response.contains("Bearer")) { - String t = "\"access_token\": \"Bearer "; - int index = response.indexOf(t); - if(index >= 0) { - int endIndex = response.indexOf("\"", index + t.length()); - String token = response.substring(index + t.length(), endIndex); - String decodedToken = OAuthEncoder.decode(token); - return new Token(decodedToken, "", response); + public OAuth2AccessToken extract(Response response) { + try { + String bodyResponse = response.getBody(); + Matcher matcher = accessTokenPattern.matcher(bodyResponse); + if(matcher.find()) { + return new OAuth2AccessToken(matcher.group(1), bodyResponse); + } + if(bodyResponse.contains("Bearer")) { + String t = "\"access_token\": \"Bearer "; + int index = bodyResponse.indexOf(t); + if(index >= 0) { + int endIndex = bodyResponse.indexOf('"', index + t.length()); + String token = bodyResponse.substring(index + t.length(), endIndex); + String decodedToken = OAuthEncoder.decode(token); + return new OAuth2AccessToken(decodedToken, bodyResponse); + } } + } catch (IOException e) { + log.error("", e); } return null; } @@ -114,37 +124,35 @@ public class TequilaApi extends DefaultApi20 { } } - private class TequilaAuth2Service extends OAuth20ServiceImpl { + private class TequilaAuth2Service extends OAuth20Service { private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; private static final String GRANT_TYPE = "grant_type"; private final TequilaApi api; - private OAuthConfig config; - public TequilaAuth2Service(TequilaApi api, OAuthConfig config) { - super(api, config); + public TequilaAuth2Service(TequilaApi api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + super(api, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); this.api = api; - this.config = config; } @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { + public OAuth2AccessToken getAccessToken(String code) throws IOException, InterruptedException, ExecutionException { OAuthRequest request = new OAuthRequest(Verb.POST, api.getAccessTokenEndpoint()); - 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()); - request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); - request.addBodyParameter(OAuthConstants.SCOPE, config.getScope()); - Response response = request.send(); - Token token = api.getAccessTokenExtractor().extract(response.getBody()); - return token; + request.addBodyParameter(OAuthConstants.CLIENT_ID, getApiKey()); + request.addBodyParameter(OAuthConstants.CLIENT_SECRET, getApiSecret()); + request.addBodyParameter(OAuthConstants.CODE, code); + request.addBodyParameter(OAuthConstants.REDIRECT_URI, getCallback()); + request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); + request.addBodyParameter(OAuthConstants.SCOPE, getDefaultScope()); + Response response = execute(request); + return api.getAccessTokenExtractor().extract(response); } @Override - public void signRequest(Token accessToken, OAuthRequest request) { - request.addHeader(OAuthConstants.HEADER, "Bearer " + urlEncode(accessToken.getToken())); + public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { + request.addHeader(OAuthConstants.HEADER, "Bearer " + urlEncode(accessToken.getAccessToken())); } } } diff --git a/src/main/java/org/olat/login/oauth/spi/TequilaProvider.java b/src/main/java/org/olat/login/oauth/spi/TequilaProvider.java index dfc20441af1b5f7a1e4e669853117f0b236601b4..9fc6f766b8f66c11c60b51b6dc542b1b457e4736 100644 --- a/src/main/java/org/olat/login/oauth/spi/TequilaProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/TequilaProvider.java @@ -19,6 +19,9 @@ */ package org.olat.login.oauth.spi; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.json.JSONException; import org.json.JSONObject; import org.olat.core.logging.OLog; @@ -27,15 +30,18 @@ import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.model.OAuthRequest; -import org.scribe.model.Response; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 30 oct. 2017<br> @@ -58,8 +64,12 @@ public class TequilaProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new TequilaApi(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getTequilaApiKey()) + .apiSecret(oauthModule.getTequilaApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .defaultScope(SCOPE) + .build(new TequilaApi(oauthModule.getTequilaOAuth2Endpoint())); } @Override @@ -82,37 +92,24 @@ public class TequilaProvider implements OAuthSPI { return "o_icon o_icon_provider_tequila"; } - @Override - public String getAppKey() { - return oauthModule.getTequilaApiKey(); - } - - @Override - public String getAppSecret() { - return oauthModule.getTequilaApiSecret(); - } - - @Override - public String[] getScopes() { - return new String[] { SCOPE }; - } - @Override public boolean isImplicitWorkflow() { return false; } @Override - public OAuthUser getUser(OAuthService service, Token accessToken) { + public OAuthUser getUser(OAuthService service, Token accessToken) + throws InterruptedException, ExecutionException, IOException { String endpoint = oauthModule.getTequilaOAuth2Endpoint(); if(!endpoint.endsWith("/")) { endpoint += "/"; } + OAuth20Service oauthService = (OAuth20Service)service; OAuthRequest request = new OAuthRequest(Verb.GET, endpoint + "userinfo"); - service.signRequest(accessToken, request); + oauthService.signRequest((OAuth2AccessToken)accessToken, request); request.addHeader("Accept", "application/json"); - Response oauthResponse = request.send(); + Response oauthResponse = oauthService.execute(request); String body = oauthResponse.getBody(); return parseResponse(body); } diff --git a/src/main/java/org/olat/login/oauth/spi/TwitterProvider.java b/src/main/java/org/olat/login/oauth/spi/TwitterProvider.java index f2ea5df104d823ef645b0592d562104ff9e4914a..f2a446f27fe558adb683dabd09caddc0eaa7b5ec 100644 --- a/src/main/java/org/olat/login/oauth/spi/TwitterProvider.java +++ b/src/main/java/org/olat/login/oauth/spi/TwitterProvider.java @@ -19,6 +19,9 @@ */ package org.olat.login.oauth.spi; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.json.JSONException; import org.json.JSONObject; import org.olat.core.logging.OLog; @@ -27,16 +30,19 @@ import org.olat.core.util.StringHelper; import org.olat.login.oauth.OAuthLoginModule; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; -import org.scribe.builder.api.Api; -import org.scribe.builder.api.TwitterApi; -import org.scribe.model.OAuthRequest; -import org.scribe.model.Response; -import org.scribe.model.Token; -import org.scribe.model.Verb; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.github.scribejava.apis.TwitterApi; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth1AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth10aService; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 05.11.2014<br> @@ -67,8 +73,11 @@ public class TwitterProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new TwitterApi.SSL(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(oauthModule.getTwitterApiKey()) + .apiSecret(oauthModule.getTwitterApiSecret()) + .callback(oauthModule.getCallbackUrl()) + .build(TwitterApi.instance()); } @Override @@ -87,26 +96,11 @@ public class TwitterProvider implements OAuthSPI { } @Override - public String getAppKey() { - return oauthModule.getTwitterApiKey(); - } - - @Override - public String getAppSecret() { - return oauthModule.getTwitterApiSecret(); - } - - @Override - public String[] getScopes() { - return new String[0]; - } - - @Override - public OAuthUser getUser(OAuthService service, Token accessToken) { - OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "https://api.twitter.com/1.1/account/verify_credentials.json"); - service.signRequest(accessToken, oauthRequest); - Response oauthResponse = oauthRequest.send(); - String body = oauthResponse.getBody(); + public OAuthUser getUser(OAuthService service, Token accessToken) throws IOException, InterruptedException, ExecutionException { + OAuthRequest request = new OAuthRequest(Verb.GET, "https://api.twitter.com/1.1/account/verify_credentials.json"); + ((OAuth10aService)service).signRequest((OAuth1AccessToken)accessToken, request); // the access token from step 4 + final Response response = service.execute(request); + String body = response.getBody(); return parseInfos(body); } diff --git a/src/main/java/org/olat/modules/gotomeeting/oauth/GetGoOAuthDispatcher.java b/src/main/java/org/olat/modules/gotomeeting/oauth/GetGoOAuthDispatcher.java index 8582092c046db67b588ecc0c247de94f1afc9af0..b4d6fbafc3769321c253bfc6e689cec941489963 100644 --- a/src/main/java/org/olat/modules/gotomeeting/oauth/GetGoOAuthDispatcher.java +++ b/src/main/java/org/olat/modules/gotomeeting/oauth/GetGoOAuthDispatcher.java @@ -42,11 +42,11 @@ import org.olat.login.oauth.OAuthConstants; import org.olat.modules.gotomeeting.GoToMeetingManager; import org.olat.modules.gotomeeting.manager.GoToJsonUtil; import org.olat.modules.gotomeeting.model.GoToOrganizerG2T; -import org.scribe.model.Token; -import org.scribe.model.Verifier; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuth20Service; + /** * * Initial date: 14 janv. 2019<br> @@ -77,17 +77,11 @@ public class GetGoOAuthDispatcher implements Dispatcher { return; } - try { - HttpSession sess = request.getSession(); - //OAuth 2.0 hasn't any request token - Token requestToken = (Token)sess.getAttribute(OAuthConstants.REQUEST_TOKEN); - OAuthService service = (OAuthService)sess.getAttribute(OAuthConstants.OAUTH_SERVICE); - - // request access token + HttpSession sess = request.getSession(); + try(OAuth20Service service = (OAuth20Service)sess.getAttribute(OAuthConstants.OAUTH_SERVICE)) { String code = request.getParameter("code"); - Token accessToken = service.getAccessToken(requestToken, new Verifier(code)); - String token = accessToken.getToken(); - + Token accessToken = service.getAccessToken(code); + String token = accessToken.getRawResponse(); boolean success = false; if(StringHelper.containsNonWhitespace(token)) { String body = accessToken.getRawResponse(); diff --git a/src/main/java/org/olat/modules/gotomeeting/oauth/GetToResource.java b/src/main/java/org/olat/modules/gotomeeting/oauth/GetToResource.java index 4a7d80cdeb428ca9c7fa8f95643623d3a0f32d88..eb5bacbf86c86e05a36156eb3130a32b006fdd96 100644 --- a/src/main/java/org/olat/modules/gotomeeting/oauth/GetToResource.java +++ b/src/main/java/org/olat/modules/gotomeeting/oauth/GetToResource.java @@ -22,18 +22,18 @@ package org.olat.modules.gotomeeting.oauth; import java.io.InputStream; import java.net.URL; import java.net.URLDecoder; +import java.util.UUID; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.olat.core.gui.media.MediaResource; -import org.olat.core.helpers.Settings; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.login.oauth.OAuthConstants; import org.olat.login.oauth.OAuthSPI; -import org.scribe.builder.ServiceBuilder; -import org.scribe.oauth.OAuthService; + +import com.github.scribejava.core.oauth.OAuth20Service; /** * @@ -96,27 +96,14 @@ public class GetToResource implements MediaResource { public static void redirect(OAuthSPI oauthProvider, HttpServletResponse httpResponse, HttpSession httpSession) { //Configure try { - ServiceBuilder builder= new ServiceBuilder(); - builder.provider(oauthProvider.getScribeProvider()) - .apiKey(oauthProvider.getAppKey()) - .apiSecret(oauthProvider.getAppSecret()); - String[] scopes = oauthProvider.getScopes(); - for(String scope:scopes) { - builder.scope(scope); - } - - String callbackUrl = Settings.getServerContextPathURI() + GoToApi.GETGO_CALLBACK; - OAuthService oauthService = builder - .callback(callbackUrl) - .build(); //Now build the call - - httpSession.setAttribute(OAuthConstants.OAUTH_SERVICE, oauthService); + @SuppressWarnings("resource") + OAuth20Service service = (OAuth20Service)oauthProvider.getScribeProvider(); + httpSession.setAttribute(OAuthConstants.OAUTH_SERVICE, service); httpSession.setAttribute(OAuthConstants.OAUTH_SPI, oauthProvider); - - String redirectUrl = oauthService.getAuthorizationUrl(null); + String state = UUID.randomUUID().toString().replace("-", ""); + String redirectUrl = service.getAuthorizationUrl(state); saveStateAndNonce(httpSession, redirectUrl); httpResponse.sendRedirect(redirectUrl); - } catch (Exception e) { log.error("", e); } diff --git a/src/main/java/org/olat/modules/gotomeeting/oauth/GoToApi.java b/src/main/java/org/olat/modules/gotomeeting/oauth/GoToApi.java index 32557de42d0a334a0a4674eed3c535b3b64719a3..2daebf788d7bbd0c2bff074796c5c8c133898b33 100644 --- a/src/main/java/org/olat/modules/gotomeeting/oauth/GoToApi.java +++ b/src/main/java/org/olat/modules/gotomeeting/oauth/GoToApi.java @@ -19,19 +19,20 @@ */ package org.olat.modules.gotomeeting.oauth; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + import org.olat.core.util.StringHelper; -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.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.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; /** * @@ -40,9 +41,9 @@ import org.scribe.oauth.OAuthService; * */ public class GoToApi extends DefaultApi20 { - + public static final String GETGO_CALLBACK = "/getgocallback"; - private static final String AUTHORIZE_URL = "https://api.getgo.com/oauth/v2/authorize?response_type=code&client_id=%s"; + private static final String AUTHORIZE_URL = "https://api.getgo.com/oauth/v2/authorize"; @Override public String getAccessTokenEndpoint() { @@ -50,8 +51,8 @@ public class GoToApi extends DefaultApi20 { } @Override - public String getAuthorizationUrl(OAuthConfig config) { - return String.format(AUTHORIZE_URL, config.getApiKey()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } @Override @@ -60,42 +61,38 @@ public class GoToApi extends DefaultApi20 { } @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return new JsonTokenExtractor(); - } - - @Override - public OAuthService createService(OAuthConfig config) { - return new GoToOAuth2Service(this, config); + public GoToOAuth2Service createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new GoToOAuth2Service(this, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); } - private class GoToOAuth2Service extends OAuth20ServiceImpl { + private class GoToOAuth2Service extends OAuth20Service { private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; private static final String GRANT_TYPE = "grant_type"; private GoToApi api; - private OAuthConfig config; - public GoToOAuth2Service(GoToApi api, OAuthConfig config) { - super(api, config); + public GoToOAuth2Service(GoToApi api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + super(api, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); this.api = api; - this.config = config; } @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { + public OAuth2AccessToken getAccessToken(String code) + throws InterruptedException, ExecutionException, IOException { OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); // header request.addHeader("Content-Type", "application/x-www-form-urlencoded"); request.addHeader("Accept", "application/json"); - String authVal = config.getApiKey() + ":" +config.getApiSecret(); + String authVal = getApiKey() + ":" + getApiSecret(); request.addHeader("Authorization", "Basic " + StringHelper.encodeBase64(authVal)); // body - request.addBodyParameter(OAuthConstants.CODE, verifier.getValue()); - request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); + request.addBodyParameter(OAuthConstants.CODE, code); + request.addBodyParameter(OAuthConstants.REDIRECT_URI, getCallback()); request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); - Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); + Response response = execute(request); + return api.getAccessTokenExtractor().extract(response); } } } diff --git a/src/main/java/org/olat/modules/gotomeeting/oauth/GoToProvider.java b/src/main/java/org/olat/modules/gotomeeting/oauth/GoToProvider.java index 7fada5a64cd9d316fb2bc27f877b15c5f8d9f0e9..b55d71e16c0f0c86eaa63cc3beec24260f3e8bc2 100644 --- a/src/main/java/org/olat/modules/gotomeeting/oauth/GoToProvider.java +++ b/src/main/java/org/olat/modules/gotomeeting/oauth/GoToProvider.java @@ -19,15 +19,17 @@ */ package org.olat.modules.gotomeeting.oauth; +import org.olat.core.helpers.Settings; import org.olat.login.oauth.OAuthSPI; import org.olat.login.oauth.model.OAuthUser; import org.olat.modules.gotomeeting.GoToMeetingModule; -import org.scribe.builder.api.Api; -import org.scribe.model.Token; -import org.scribe.oauth.OAuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuthService; + /** * * Initial date: 14 janv. 2019<br> @@ -46,8 +48,11 @@ public class GoToProvider implements OAuthSPI { } @Override - public Api getScribeProvider() { - return new GoToApi(); + public OAuthService getScribeProvider() { + return new ServiceBuilder(goToMeetingModule.getTrainingConsumerKey()) + .apiSecret(goToMeetingModule.getTrainingConsumerSecret()) + .callback(Settings.getServerContextPathURI() + GoToApi.GETGO_CALLBACK) + .build(new GoToApi()); } @Override @@ -70,20 +75,6 @@ public class GoToProvider implements OAuthSPI { return "o_icon o_gotomeeting_icon"; } - @Override - public String getAppKey() { - return goToMeetingModule.getTrainingConsumerKey(); - } - - @Override - public String getAppSecret() { - return goToMeetingModule.getTrainingConsumerSecret(); - } - - @Override - public String[] getScopes() { - return new String[0]; - } @Override public boolean isImplicitWorkflow() { diff --git a/src/test/java/org/olat/login/oauth/OAuthDispatcherTest.java b/src/test/java/org/olat/login/oauth/OAuthDispatcherTest.java index 187a9ec25b147e44b5415ae0d4e5015c824fef80..3a2a066c13d354b3927efe8ae83c2bc2eed18723 100644 --- a/src/test/java/org/olat/login/oauth/OAuthDispatcherTest.java +++ b/src/test/java/org/olat/login/oauth/OAuthDispatcherTest.java @@ -21,6 +21,7 @@ package org.olat.login.oauth; import java.io.IOException; import java.net.URL; +import java.util.HashMap; import org.apache.commons.io.IOUtils; import org.json.JSONException; @@ -30,14 +31,15 @@ import org.junit.Test; import org.olat.login.oauth.model.OAuthUser; import org.olat.login.oauth.spi.ADFSApi; import org.olat.login.oauth.spi.FacebookProvider; -import org.olat.login.oauth.spi.Google2Api; import org.olat.login.oauth.spi.Google2Provider; import org.olat.login.oauth.spi.JSONWebToken; import org.olat.login.oauth.spi.LinkedInProvider; import org.olat.login.oauth.spi.TequilaApi; import org.olat.login.oauth.spi.TequilaProvider; import org.olat.login.oauth.spi.TwitterProvider; -import org.scribe.model.Token; + +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.Response; /** * @@ -50,12 +52,13 @@ public class OAuthDispatcherTest { @Test public void parseEmail_linkedIn() { StringBuilder sb = new StringBuilder(); - sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>") - .append("<person>") - .append("<first-name>John</first-name>") - .append("<last-name>Smith</last-name>") - .append("<email-address>j.smith@openolat.com</email-address>") - .append("</person>"); + sb.append("{\n") + .append("\"emailAddress\": \"j.smith@openolat.com\",\n") + .append("\"firstName\": \"John\",\n") + .append("\"id\": \"saasdhgdhj\",") + .append("\"lastName\": \"Smith\"") + .append("}"); + OAuthUser infos = new LinkedInProvider().parseInfos(sb.toString()); Assert.assertNotNull(infos); @@ -90,16 +93,6 @@ public class OAuthDispatcherTest { Assert.assertEquals("fr", infos.getLang()); } - @Test - public void parseToken_google() throws IOException { - URL jsonUrl = OAuthDispatcherTest.class.getResource("token_google.json"); - String body = IOUtils.toString(jsonUrl, "UTF-8"); - - Token token = new Google2Api().getAccessTokenExtractor().extract(body); - Assert.assertNotNull(token); - Assert.assertEquals("ya29.GlunBoqIMXtDT81i_QwNg75qTJDvprP96EWP1wZx-DGu47o5OGXPIEkcbJWi-eDN8gfc0B1mVSVkZoKuwaHu6YBZgNuCRDp73unPOCAb4Zn7fVQc5mbMqWAIpLO1", token.getToken()); - } - @Test public void parseUserInfos_facebook() throws IOException { URL jsonUrl = OAuthDispatcherTest.class.getResource("me_facebook.json"); @@ -114,12 +107,14 @@ public class OAuthDispatcherTest { } @Test - public void parseADFSToken() throws JSONException { - String response = "{\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkhnbmtjOVBMd0E3ampHMjlWbndpQk43WnlaYyJ9.eyJhdWQiOiJodHRwczovL2tpdmlrLmZyZW50aXguY29tL29sYXQiLCJpc3MiOiJodHRwOi8vYWRmcy5oYW1pbHRvbi5jaC9hZGZzL3NlcnZpY2VzL3RydXN0IiwiaWF0IjoxNDE1MzQ3MDE0LCJleHAiOjE0MTUzNTA2MTQsIlNuIjoiT3Blbk9sYXQiLCJkaXNwbGF5TmFtZVByaW50YWJsZSI6IlRlc3R1c2VyIiwiU0FNQWNjb3VudE5hbWUiOiJ0ZXN0X29wZW5vbGF0IiwiYXV0aF90aW1lIjoiMjAxNC0xMS0wN1QwNzo1Njo1NC4zOTFaIiwiYXV0aG1ldGhvZCI6InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0IiwidmVyIjoiMS4wIiwiYXBwaWQiOiIyNWU1M2VmNC02NTllLTExZTQtYjExNi0xMjNiOTNmNzVjYmEifQ.l17qB7LWkODD66OuRbhjDEdKEQWrEfaeR7hBpbdN8XqGIOMS2sc2xQNYJH9Lh061XOJt9WPqrAW8sHSu2eaR1qpw8o6LcWksKvh0LJbCmVPqQggLDj8Q4kSFIzbs9YAQautTAvobdb_hsoGT9rhGN4SDIcpJA8Uq8JWwYDjWfDCpCVRHZPmyZiOmh-5rBT8SxSiV0QgFexhmbvLAZhaEmsZGzSaj2r39cyK0dlt7OuR_1KjQeB86ycOMP1PT1OAGWJc1lgGP12gDo-FkcK5mOY6mgC8za7OOwgTUkE4pbXwygi4nPBXHQVPku-bWtigLZWfTln4Ght3fqMIzJOQXag\",\"token_type\":\"bearer\",\"expires_in\":3600}"; - Token accessToken = new ADFSApi().getAccessTokenExtractor().extract(response); + public void parseADFSToken() throws JSONException, IOException { + String responseBody = "{\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkhnbmtjOVBMd0E3ampHMjlWbndpQk43WnlaYyJ9.eyJhdWQiOiJodHRwczovL2tpdmlrLmZyZW50aXguY29tL29sYXQiLCJpc3MiOiJodHRwOi8vYWRmcy5oYW1pbHRvbi5jaC9hZGZzL3NlcnZpY2VzL3RydXN0IiwiaWF0IjoxNDE1MzQ3MDE0LCJleHAiOjE0MTUzNTA2MTQsIlNuIjoiT3Blbk9sYXQiLCJkaXNwbGF5TmFtZVByaW50YWJsZSI6IlRlc3R1c2VyIiwiU0FNQWNjb3VudE5hbWUiOiJ0ZXN0X29wZW5vbGF0IiwiYXV0aF90aW1lIjoiMjAxNC0xMS0wN1QwNzo1Njo1NC4zOTFaIiwiYXV0aG1ldGhvZCI6InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0IiwidmVyIjoiMS4wIiwiYXBwaWQiOiIyNWU1M2VmNC02NTllLTExZTQtYjExNi0xMjNiOTNmNzVjYmEifQ.l17qB7LWkODD66OuRbhjDEdKEQWrEfaeR7hBpbdN8XqGIOMS2sc2xQNYJH9Lh061XOJt9WPqrAW8sHSu2eaR1qpw8o6LcWksKvh0LJbCmVPqQggLDj8Q4kSFIzbs9YAQautTAvobdb_hsoGT9rhGN4SDIcpJA8Uq8JWwYDjWfDCpCVRHZPmyZiOmh-5rBT8SxSiV0QgFexhmbvLAZhaEmsZGzSaj2r39cyK0dlt7OuR_1KjQeB86ycOMP1PT1OAGWJc1lgGP12gDo-FkcK5mOY6mgC8za7OOwgTUkE4pbXwygi4nPBXHQVPku-bWtigLZWfTln4Ght3fqMIzJOQXag\",\"token_type\":\"bearer\",\"expires_in\":3600}"; + Response response = new Response(200, "", new HashMap<>(), responseBody); + + OAuth2AccessToken accessToken = new ADFSApi().getAccessTokenExtractor().extract(response); Assert.assertNotNull(accessToken); - String token = accessToken.getToken(); + String token = accessToken.getAccessToken(); Assert.assertNotNull(token); //get JSON Web Token @@ -155,10 +150,11 @@ public class OAuthDispatcherTest { @Test public void extractTequilaBearerToken() { - String response = "\"access_token\": \"Bearer 880a11c9aaae0abf0f6a384c559110d8c7570456\", \"scope\": \"Tequila.profile\""; + String responseBody = "\"access_token\": \"Bearer 880a11c9aaae0abf0f6a384c559110d8c7570456\", \"scope\": \"Tequila.profile\""; + Response response = new Response(200, "", new HashMap<>(), responseBody); TequilaApi.TequilaBearerExtractor extractor = new TequilaApi.TequilaBearerExtractor(); - Token token = extractor.extract(response); - String accessToken = token.getToken(); + OAuth2AccessToken token = extractor.extract(response); + String accessToken = token.getAccessToken(); Assert.assertEquals("880a11c9aaae0abf0f6a384c559110d8c7570456", accessToken); } @@ -169,8 +165,6 @@ public class OAuthDispatcherTest { Assert.assertNotNull(infos); Assert.assertEquals("Service", infos.getFirstName()); Assert.assertEquals("Erecruiting_oAuth2", infos.getLastName()); - Assert.assertEquals("M02491", infos.getId()); - - + Assert.assertEquals("M02491", infos.getId()); } }