This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new c64509a  [OPENMEETINGS-2476] HttpURLConnection is replaced with 
HttpClient
c64509a is described below

commit c64509a16cd061dca8800db9bcdaf384bdbf7ee0
Author: Maxim Solodovnik <solomax...@gmail.com>
AuthorDate: Tue Oct 13 09:20:06 2020 +0700

    [OPENMEETINGS-2476] HttpURLConnection is replaced with HttpClient
---
 .../openmeetings/db/dao/user/IUserManager.java     |   3 -
 .../openmeetings/db/entity/server/OAuthServer.java |  17 +-
 .../openmeetings/web/admin/oauth/OAuthForm.java    |   2 +-
 .../apache/openmeetings/web/app/Application.java   |   3 +
 .../apache/openmeetings/web/app/UserManager.java   | 216 +++++++++++++++++++-
 .../openmeetings/web/pages/auth/SignInDialog.java  |   2 +-
 .../openmeetings/web/pages/auth/SignInPage.java    | 222 +--------------------
 .../web/app/TestUserManagerMocked.java             |  58 ++++--
 8 files changed, 279 insertions(+), 244 deletions(-)

diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/IUserManager.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/IUserManager.java
index 262b381..94d6aba 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/IUserManager.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/IUserManager.java
@@ -18,11 +18,9 @@
  */
 package org.apache.openmeetings.db.dao.user;
 
-import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.util.Locale;
 
-import org.apache.openmeetings.db.dto.user.OAuthUser;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.util.OmException;
 
@@ -35,7 +33,6 @@ public interface IUserManager {
        Object registerUser(User u, String password, String hash) throws 
OmException, NoSuchAlgorithmException;
 
        Long getLanguage(Locale loc);
-       User loginOAuth(OAuthUser user, long serverId) throws IOException, 
NoSuchAlgorithmException;
 
        boolean kickExternal(Long roomId, String externalType, String 
externalId);
        boolean kickUsersByRoomId(Long roomId);
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/server/OAuthServer.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/server/OAuthServer.java
index 24285f8..d223706 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/server/OAuthServer.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/server/OAuthServer.java
@@ -58,6 +58,14 @@ import org.apache.openmeetings.db.entity.HistoricalEntity;
 public class OAuthServer extends HistoricalEntity {
        private static final long serialVersionUID = 1L;
 
+       public enum RequestTokenMethod {
+               POST, GET
+       }
+
+       public enum RequestInfoMethod {
+               POST, GET, HEADER
+       }
+
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
@@ -116,7 +124,6 @@ public class OAuthServer extends HistoricalEntity {
        @MapKeyColumn(name = "name")
        @Column(name = "value")
        @CollectionTable(name = "oauth_mapping", joinColumns = @JoinColumn(name 
= "oauth_id"))
-       //FIXME TODO @XmlElement(name = "attrMapping", required = false)
        @XmlTransient
        private Map<String, String> mapping = new LinkedHashMap<>();
 
@@ -263,12 +270,4 @@ public class OAuthServer extends HistoricalEntity {
                                .append(", isDeleted()=").append(isDeleted())
                                .append("]").toString();
        }
-
-       public enum RequestTokenMethod {
-               POST, GET
-       }
-
-       public enum RequestInfoMethod {
-               POST, GET, HEADER
-       }
 }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
index 7ad488a..61ac9bb 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
@@ -18,9 +18,9 @@
  */
 package org.apache.openmeetings.web.admin.oauth;
 
+import static org.apache.openmeetings.web.app.UserManager.getRedirectUri;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static 
org.apache.openmeetings.web.common.confirmation.ConfirmationBehavior.newOkCancelDangerConfirm;
-import static org.apache.openmeetings.web.pages.auth.SignInPage.getRedirectUri;
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
index 460d840..87b2a85 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
@@ -176,6 +176,8 @@ public class Application extends 
AuthenticatedWebApplication implements IApplica
        @Autowired
        private UserDao userDao;
        @Autowired
+       private UserManager userManager;
+       @Autowired
        private ClientManager cm;
        @Autowired
        private WhiteboardManager wbManager;
@@ -340,6 +342,7 @@ public class Application extends 
AuthenticatedWebApplication implements IApplica
                        setExtProcessTtl(cfgDao.getInt(CONFIG_EXT_PROCESS_TTL, 
getExtProcessTtl()));
                        Version.logOMStarted();
                        recordingDao.resetProcessingStatus(); //we are starting 
so all processing recordings are now errors
+                       userManager.initHttpClient();
                        setInitComplete(true);
                } catch (Exception err) {
                        log.error("[appStart]", err);
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
index b2a5010..53265d5 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
@@ -18,29 +18,52 @@
  */
 package org.apache.openmeetings.web.app;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.dao.user.UserDao.getNewUserInstance;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 import static org.apache.openmeetings.util.OmException.UNKNOWN;
+import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_IGNORE_BAD_SSL;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getBaseUrl;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultGroup;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultLang;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.getMinLoginLength;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.isAllowRegisterFrontend;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.isSendVerificationEmail;
+import static org.apache.openmeetings.web.app.Application.urlForPage;
 
 import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.net.http.HttpResponse.BodyHandlers;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
 
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.dao.user.GroupDao;
 import org.apache.openmeetings.db.dao.user.IUserManager;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.dto.user.OAuthUser;
 import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.db.entity.server.OAuthServer;
+import org.apache.openmeetings.db.entity.server.OAuthServer.RequestInfoMethod;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Right;
 import org.apache.openmeetings.db.entity.user.User.Type;
@@ -49,15 +72,20 @@ import org.apache.openmeetings.service.mail.EmailManager;
 import org.apache.openmeetings.util.OmException;
 import org.apache.openmeetings.util.crypt.CryptProvider;
 import org.apache.openmeetings.util.crypt.ICrypt;
+import org.apache.openmeetings.web.pages.auth.SignInPage;
 import org.apache.wicket.IConverterLocator;
 import org.apache.wicket.core.util.lang.PropertyResolver;
 import org.apache.wicket.core.util.lang.PropertyResolverConverter;
+import org.apache.wicket.request.flow.RedirectToUrlException;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import com.github.openjson.JSONObject;
+
 /**
  *
  * @author swagner
@@ -68,6 +96,8 @@ public class UserManager implements IUserManager {
        private static final Logger log = 
LoggerFactory.getLogger(UserManager.class);
 
        @Autowired
+       private ConfigurationDao cfgDao;
+       @Autowired
        private GroupDao groupDao;
        @Autowired
        private UserDao userDao;
@@ -75,6 +105,7 @@ public class UserManager implements IUserManager {
        private EmailManager emailManager;
        @Autowired
        private IClientManager cm;
+       private HttpClient httpClient;
 
        private static boolean sendConfirmation() {
                String baseURL = getBaseUrl();
@@ -229,14 +260,63 @@ public class UserManager implements IUserManager {
                return LabelDao.getLanguage(loc, getDefaultLang());
        }
 
-       @Override
-       public User loginOAuth(OAuthUser user, long serverId) throws 
IOException, NoSuchAlgorithmException {
+       // ============= OAuth2 methods =============
+       public void initHttpClient() {
+               HttpClient.Builder builder = HttpClient.newBuilder()
+                               .version(HttpClient.Version.HTTP_1_1)
+                               .connectTimeout(Duration.ofSeconds(10));
+               final boolean ignoreBadSsl = 
cfgDao.getBool(CONFIG_IGNORE_BAD_SSL, false);
+               
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", 
String.valueOf(ignoreBadSsl));
+               if (ignoreBadSsl) {
+                       TrustManager[] trustAllCerts = new TrustManager[] {new 
X509TrustManager() {
+                               @Override
+                               public void 
checkClientTrusted(X509Certificate[] chain, String authType) throws 
CertificateException {
+                                       //no-op
+                               }
+
+                               @Override
+                               public void 
checkServerTrusted(X509Certificate[] chain, String authType) throws 
CertificateException {
+                                       //no-op
+                               }
+
+                               @Override
+                               public X509Certificate[] getAcceptedIssuers() {
+                                       return new X509Certificate[] {};
+                               }
+                       }};
+                       try {
+                               SSLContext sslContext = 
SSLContext.getInstance("SSL");
+                               sslContext.init(null, trustAllCerts, new 
SecureRandom());
+                               SSLParameters sslParams = new SSLParameters();
+                               
sslParams.setEndpointIdentificationAlgorithm("");
+                               builder.sslContext(sslContext)
+                                               .sslParameters(sslParams);
+                       } catch (Exception e) {
+                               log.error("[initHttpClient]", e);
+                       }
+               }
+               httpClient = builder.build();
+       }
+
+       public User loginOAuth(String code, OAuthServer server) throws 
IOException, NoSuchAlgorithmException, InterruptedException {
+               if (code == null) {
+                       showAuth(server);
+                       return null;
+               }
+               log.debug("OAuth response code={}", code);
+               AuthInfo authInfo = getToken(code, server);
+               if (authInfo == null) {
+                       return null;
+               }
+               log.debug("OAuthInfo={}", authInfo);
+               OAuthUser user = getAuthParams(authInfo, code, server);
+
                if (!userDao.validLogin(user.getLogin())) {
                        log.error("Invalid login, please check parameters");
                        return null;
                }
-               User u = userDao.getByLogin(user.getLogin(), Type.OAUTH, 
serverId);
-               if (!userDao.checkEmail(user.getEmail(), Type.OAUTH, serverId, 
u == null ? null : u.getId())) {
+               User u = userDao.getByLogin(user.getLogin(), Type.OAUTH, 
server.getId());
+               if (!userDao.checkEmail(user.getEmail(), Type.OAUTH, 
server.getId(), u == null ? null : u.getId())) {
                        log.error("Another user with the same email exists");
                        return null;
                }
@@ -246,7 +326,7 @@ public class UserManager implements IUserManager {
                        final User fUser = getNewUserInstance(null);
                        fUser.setType(Type.OAUTH);
                        fUser.getRights().remove(Right.LOGIN);
-                       fUser.setDomainId(serverId);
+                       fUser.setDomainId(server.getId());
                        fUser.addGroup(groupDao.get(getDefaultGroup()));
                        for (Map.Entry<String, String> entry : 
user.getUserData().entrySet()) {
                                final String expression = entry.getKey();
@@ -262,6 +342,103 @@ public class UserManager implements IUserManager {
                return u;
        }
 
+       private static Map<String, String> getInitParams(final OAuthServer s) {
+               Map<String, String> params = new HashMap<>();
+               params.put("{$client_id}", s.getClientId());
+               params.put("{$redirect_uri}", getRedirectUri(s));
+               return params;
+       }
+
+       public static void showAuth(final OAuthServer s) {
+               String authUrl = prepareUrl(s.getRequestKeyUrl(), 
getInitParams(s));
+               log.debug("redirectUrl={}", authUrl);
+               throw new RedirectToUrlException(authUrl);
+       }
+
+       private static String prepareUrl(String urlTemplate, Map<String, 
String> params) {
+               String result = urlTemplate;
+               for (Entry<String, String> e : params.entrySet()) {
+                       if (e.getValue() != null) {
+                               result = result.replace(e.getKey(), 
URLEncoder.encode(e.getValue(), UTF_8));
+                       }
+               }
+               return result;
+       }
+
+       public static String getRedirectUri(OAuthServer server) {
+               String result = "";
+               if (server.getId() != null) {
+                       String base = getBaseUrl();
+                       result = urlForPage(SignInPage.class, new 
PageParameters().add("oauthid", server.getId()), base);
+               }
+               return result;
+       }
+
+       private static Map<String, String> getParams(final OAuthServer s, 
String code, AuthInfo authInfo) {
+               Map<String, String> params = getInitParams(s);
+               params.put("{$client_id}", s.getClientId());
+               params.put("{$client_secret}", s.getClientSecret());
+               if (authInfo != null) {
+                       params.put("{$access_token}", authInfo.accessToken);
+                       params.put("{$user_id}", authInfo.userId);
+               }
+               if (code != null) {
+                       params.put("{$code}", code);
+               }
+               return params;
+       }
+
+       private static HttpRequest.Builder setNoCache(HttpRequest.Builder 
builder) {
+               return builder
+                               .header("Cache-Control", "no-cache, no-store, 
must-revalidate")
+                               .header("Pragma", "no-cache")
+                               .header("Expires", "0");
+       }
+
+       String doRequest(HttpRequest request) throws IOException, 
InterruptedException { // extracted as package private for testing
+               return httpClient.send(request, BodyHandlers.ofString()).body();
+       }
+
+       private AuthInfo getToken(String code, OAuthServer server) throws 
IOException, InterruptedException {
+               // build url params to request auth token
+               String requestTokenParams = 
prepareUrl(server.getRequestTokenAttributes(), getParams(server, code, null));
+               // request auth token
+               HttpRequest request = setNoCache(
+                               HttpRequest.newBuilder()
+                                       
.uri(URI.create(server.getRequestTokenUrl()))
+                                       .header("Content-Type", 
"application/x-www-form-urlencoded")
+                                       .header("charset", UTF_8.name())
+                                       
.method(server.getRequestTokenMethod().name(), 
BodyPublishers.ofString(requestTokenParams))
+                               ).build();
+
+               String resp = doRequest(request);
+               // parse json result
+               AuthInfo result = new AuthInfo(resp);
+               // access token must be specified
+               if (result.accessToken == null) {
+                       log.error("Response doesn't contain access_token 
field:\n {}", resp);
+                       return null;
+               }
+               return result;
+       }
+
+       private OAuthUser getAuthParams(AuthInfo authInfo, String code, 
OAuthServer server) throws IOException, InterruptedException {
+               // prepare url
+               String requestInfoUrl = prepareUrl(server.getRequestInfoUrl(), 
getParams(server, code, authInfo));
+               HttpRequest.Builder builder = 
setNoCache(HttpRequest.newBuilder().uri(URI.create(requestInfoUrl)));
+
+               if (server.getRequestInfoMethod() == RequestInfoMethod.HEADER) {
+                       builder.header("Authorization", "Bearer " + 
authInfo.accessToken);
+               } else {
+                       builder.method(server.getRequestInfoMethod().name(), 
BodyPublishers.noBody());
+               }
+
+               String json = doRequest(builder.build());
+               log.debug("User info={}", json);
+               // parse json result
+               return new OAuthUser(json, server);
+       }
+
        private class LanguageConverter extends PropertyResolverConverter {
                private static final long serialVersionUID = 1L;
                final String expression;
@@ -297,4 +474,33 @@ public class UserManager implements IUserManager {
                        return String.valueOf(object);
                }
        }
+
+       private static class AuthInfo {
+               final String accessToken;
+               final String refreshToken;
+               final String tokenType;
+               final String userId;
+               final long expiresIn;
+
+               AuthInfo(String jsonStr) {
+                       log.debug("AuthInfo={}", jsonStr);
+                       JSONObject json = new JSONObject(jsonStr);
+                       accessToken = json.optString("access_token");
+                       refreshToken = json.optString("refresh_token");
+                       tokenType = json.optString("token_type");
+                       userId = json.optString("user_id");
+                       expiresIn = json.optLong("expires_in");
+               }
+
+               @Override
+               public String toString() {
+                       return new StringBuilder()
+                               .append("AuthInfo 
[accessToken=").append(accessToken)
+                               .append(", refreshToken=").append(refreshToken)
+                               .append(", tokenType=").append(tokenType)
+                               .append(", userId=").append(userId)
+                               .append(", expiresIn=").append(expiresIn)
+                               .append("]").toString();
+               }
+       }
 }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
index 0bb98d2..fac217b 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInDialog.java
@@ -20,9 +20,9 @@ package org.apache.openmeetings.web.pages.auth;
 
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_LDAP_ID;
 import static 
org.apache.openmeetings.web.app.Application.getAuthenticationStrategy;
+import static org.apache.openmeetings.web.app.UserManager.showAuth;
 import static org.apache.openmeetings.web.pages.HashPage.APP;
 import static org.apache.openmeetings.web.pages.HashPage.APP_TYPE_NETWORK;
-import static org.apache.openmeetings.web.pages.auth.SignInPage.showAuth;
 
 import java.util.List;
 
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
index 0e794e6..b26cd30 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
@@ -18,43 +18,19 @@
  */
 package org.apache.openmeetings.web.pages.auth;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_IGNORE_BAD_SSL;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.getBaseUrl;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.isAllowRegisterFrontend;
-import static org.apache.openmeetings.web.app.Application.urlForPage;
 
-import java.io.DataOutputStream;
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLEncoder;
 import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
 
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-
-import org.apache.commons.io.IOUtils;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.server.OAuth2Dao;
-import org.apache.openmeetings.db.dao.user.IUserManager;
-import org.apache.openmeetings.db.dto.user.OAuthUser;
 import org.apache.openmeetings.db.entity.server.OAuthServer;
-import org.apache.openmeetings.db.entity.server.OAuthServer.RequestInfoMethod;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
 import org.apache.openmeetings.util.OmException;
 import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.UserManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.OmModalCloseButton;
 import org.apache.openmeetings.web.pages.BaseInitedPage;
@@ -67,7 +43,6 @@ import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.request.IRequestParameters;
 import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.flow.RedirectToUrlException;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.util.string.StringValue;
@@ -75,7 +50,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.github.openjson.JSONException;
-import com.github.openjson.JSONObject;
 
 import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import 
de.agilecoders.wicket.core.markup.html.bootstrap.dialog.TextContentModal;
@@ -137,7 +111,7 @@ public class SignInPage extends BaseInitedPage {
        @SpringBean
        private ConfigurationDao cfgDao;
        @SpringBean
-       private IUserManager userManager;
+       private UserManager userManager;
        @SpringBean
        private OAuth2Dao oauthDao;
 
@@ -162,20 +136,14 @@ public class SignInPage extends BaseInitedPage {
                                        return;
                                }
 
-                               if (!p.get("code").isNull()) { // got code
-                                       String code = p.get("code").toString();
-                                       log.debug("OAuth response code={}", 
code);
-                                       AuthInfo authInfo = getToken(code, 
server);
-                                       if (authInfo == null) {
-                                               return;
-                                       }
-                                       log.debug("OAuthInfo={}", authInfo);
-                                       OAuthUser user = 
getAuthParams(authInfo, code, server);
-                                       loginViaOAuth2(user, serverId);
-                               } else { // redirect to get code
-                                       showAuth(server);
+                               User u = 
userManager.loginOAuth(p.get("code").toOptionalString(), server);
+
+                               if (u != null && WebSession.get().signIn(u)) {
+                                       
setResponsePage(Application.get().getHomePage());
+                               } else {
+                                       log.error("Failed to login via 
OAuth2!");
                                }
-                       } catch 
(IOException|NoSuchAlgorithmException|JSONException e) {
+                       } catch 
(IOException|NoSuchAlgorithmException|InterruptedException|JSONException e) {
                                log.error("OAuth2 login error", e);
                        }
                }
@@ -240,176 +208,4 @@ public class SignInPage extends BaseInitedPage {
        protected void onParameterArrival(IRequestParameters params, 
AjaxRequestTarget arg1) {
                WebSession.get().setArea(getUrlFragment(params));
        }
-
-       // ============= OAuth2 methods =============
-       private static Map<String, String> getInitParams(final OAuthServer s) {
-               Map<String, String> params = new HashMap<>();
-               params.put("{$client_id}", s.getClientId());
-               params.put("{$redirect_uri}", getRedirectUri(s));
-               return params;
-       }
-
-       public static void showAuth(final OAuthServer s) {
-               String authUrl = prepareUrl(s.getRequestKeyUrl(), 
getInitParams(s));
-               log.debug("redirectUrl={}", authUrl);
-               throw new RedirectToUrlException(authUrl);
-       }
-
-       private static String prepareUrl(String urlTemplate, Map<String, 
String> params) {
-               String result = urlTemplate;
-               for (Entry<String, String> e : params.entrySet()) {
-                       if (e.getValue() != null) {
-                               try {
-                                       result = result.replace(e.getKey(), 
URLEncoder.encode(e.getValue(), UTF_8.name()));
-                               } catch (UnsupportedEncodingException err) {
-                                       log.error("Unexpected exception while 
encoding URI param {}", e, err);
-                               }
-                       }
-               }
-               return result;
-       }
-
-       public static String getRedirectUri(OAuthServer server) {
-               String result = "";
-               if (server.getId() != null) {
-                       String base = getBaseUrl();
-                       result = urlForPage(SignInPage.class, new 
PageParameters().add("oauthid", server.getId()), base);
-               }
-               return result;
-       }
-
-       private void prepareConnection(URLConnection inConnection) {
-               if (!(inConnection instanceof HttpsURLConnection)) {
-                       return;
-               }
-               if (!cfgDao.getBool(CONFIG_IGNORE_BAD_SSL, false)) {
-                       return;
-               }
-               TrustManager[] trustAllCerts = new TrustManager[] {new 
X509TrustManager() {
-                       @Override
-                       public void checkClientTrusted(X509Certificate[] arg0, 
String arg1) throws CertificateException {
-                               //no-op
-                       }
-
-                       @Override
-                       public void checkServerTrusted(X509Certificate[] arg0, 
String arg1) throws CertificateException {
-                               //no-op
-                       }
-
-                       @Override
-                       public X509Certificate[] getAcceptedIssuers() {
-                               return new X509Certificate[] {};
-                       }
-               }};
-               try {
-                       HttpsURLConnection connection = 
(HttpsURLConnection)inConnection;
-                       SSLContext sslContext = SSLContext.getInstance("SSL");
-                       sslContext.init(null, trustAllCerts, new 
java.security.SecureRandom());
-                       SSLSocketFactory sslSocketFactory = 
sslContext.getSocketFactory();
-                       connection.setSSLSocketFactory(sslSocketFactory);
-                       connection.setHostnameVerifier((arg0, arg1) -> true);
-               } catch (Exception e) {
-                       log.error("[prepareConnection]", e);
-               }
-       }
-
-       private static Map<String, String> getParams(final OAuthServer s, 
String code, AuthInfo authInfo) {
-               Map<String, String> params = getInitParams(s);
-               params.put("{$client_id}", s.getClientId());
-               params.put("{$client_secret}", s.getClientSecret());
-               if (authInfo != null) {
-                       params.put("{$access_token}", authInfo.accessToken);
-                       params.put("{$user_id}", authInfo.userId);
-               }
-               if (code != null) {
-                       params.put("{$code}", code);
-               }
-               return params;
-       }
-
-       private AuthInfo getToken(String code, OAuthServer server) throws 
IOException {
-               String requestTokenBaseUrl = server.getRequestTokenUrl();
-               // build url params to request auth token
-               String requestTokenParams = server.getRequestTokenAttributes();
-               requestTokenParams = prepareUrl(requestTokenParams, 
getParams(server, code, null));
-               // request auth token
-               HttpURLConnection connection = (HttpURLConnection) new 
URL(requestTokenBaseUrl).openConnection();
-               prepareConnection(connection);
-               
connection.setRequestMethod(server.getRequestTokenMethod().name());
-               connection.setRequestProperty("Content-Type", 
"application/x-www-form-urlencoded");
-               connection.setRequestProperty("charset", UTF_8.name());
-               connection.setRequestProperty("Content-Length", 
String.valueOf(requestTokenParams.length()));
-               connection.setDoInput(true);
-               connection.setDoOutput(true);
-               connection.setUseCaches(false);
-               DataOutputStream paramsOutputStream = new 
DataOutputStream(connection.getOutputStream());
-               paramsOutputStream.writeBytes(requestTokenParams);
-               paramsOutputStream.flush();
-               String sourceResponse = 
IOUtils.toString(connection.getInputStream(), UTF_8);
-               // parse json result
-               AuthInfo result = new AuthInfo(sourceResponse);
-               // access token must be specified
-               if (result.accessToken == null) {
-                       log.error("Response doesn't contain access_token 
field:\n {}", sourceResponse);
-                       return null;
-               }
-               return result;
-       }
-
-       private OAuthUser getAuthParams(AuthInfo authInfo, String code, 
OAuthServer server) throws IOException {
-               // prepare url
-               String requestInfoUrl = server.getRequestInfoUrl();
-               requestInfoUrl = prepareUrl(requestInfoUrl, getParams(server, 
code, authInfo));
-               // send request
-               HttpURLConnection connection = (HttpURLConnection) new 
URL(requestInfoUrl).openConnection();
-               if (server.getRequestInfoMethod() == RequestInfoMethod.HEADER) {
-                       connection.setRequestProperty("Authorization", 
String.format("Bearer %s", authInfo.accessToken));
-               } else {
-                       
connection.setRequestMethod(server.getRequestInfoMethod().name());
-               }
-               prepareConnection(connection);
-               String json = IOUtils.toString(connection.getInputStream(), 
UTF_8);
-               log.debug("User info={}", json);
-               // parse json result
-               return new OAuthUser(json, server);
-       }
-
-       private void loginViaOAuth2(OAuthUser user, long serverId) throws 
IOException, NoSuchAlgorithmException {
-               User u = userManager.loginOAuth(user, serverId);
-
-               if (u != null && WebSession.get().signIn(u)) {
-                       setResponsePage(Application.get().getHomePage());
-               } else {
-                       log.error("Failed to login via OAuth2!");
-               }
-       }
-
-       private static class AuthInfo {
-               final String accessToken;
-               final String refreshToken;
-               final String tokenType;
-               final String userId;
-               final long expiresIn;
-
-               AuthInfo(String jsonStr) {
-                       log.debug("AuthInfo={}", jsonStr);
-                       JSONObject json = new JSONObject(jsonStr);
-                       accessToken = json.optString("access_token");
-                       refreshToken = json.optString("refresh_token");
-                       tokenType = json.optString("token_type");
-                       userId = json.optString("user_id");
-                       expiresIn = json.optLong("expires_in");
-               }
-
-               @Override
-               public String toString() {
-                       return new StringBuilder()
-                               .append("AuthInfo 
[accessToken=").append(accessToken)
-                               .append(", refreshToken=").append(refreshToken)
-                               .append(", tokenType=").append(tokenType)
-                               .append(", userId=").append(userId)
-                               .append(", expiresIn=").append(expiresIn)
-                               .append("]").toString();
-               }
-       }
 }
diff --git 
a/openmeetings-web/src/test/java/org/apache/openmeetings/web/app/TestUserManagerMocked.java
 
b/openmeetings-web/src/test/java/org/apache/openmeetings/web/app/TestUserManagerMocked.java
index ce19e5b..b57c21e 100644
--- 
a/openmeetings-web/src/test/java/org/apache/openmeetings/web/app/TestUserManagerMocked.java
+++ 
b/openmeetings-web/src/test/java/org/apache/openmeetings/web/app/TestUserManagerMocked.java
@@ -18,6 +18,10 @@
  */
 package org.apache.openmeetings.web.app;
 
+import static org.apache.openmeetings.db.dto.user.OAuthUser.PARAM_EMAIL;
+import static org.apache.openmeetings.db.dto.user.OAuthUser.PARAM_FNAME;
+import static org.apache.openmeetings.db.dto.user.OAuthUser.PARAM_LNAME;
+import static org.apache.openmeetings.db.dto.user.OAuthUser.PARAM_LOGIN;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.setCryptClassName;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -25,16 +29,18 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.net.http.HttpRequest;
 import java.security.NoSuchAlgorithmException;
 
 import org.apache.openmeetings.db.dao.user.GroupDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
-import org.apache.openmeetings.db.dto.user.OAuthUser;
 import org.apache.openmeetings.db.entity.server.OAuthServer;
+import org.apache.openmeetings.db.entity.server.OAuthServer.RequestInfoMethod;
+import org.apache.openmeetings.db.entity.server.OAuthServer.RequestTokenMethod;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
 import org.apache.openmeetings.db.manager.IClientManager;
@@ -44,6 +50,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.stubbing.Answer;
@@ -59,25 +66,52 @@ class TestUserManagerMocked {
        @Mock
        private IClientManager cm;
        @InjectMocks
-       private UserManager userManager;
+       private UserManager userManager = Mockito.spy(new UserManager());
 
        @Test
-       void oauthTest() throws NoSuchAlgorithmException, IOException {
+       void oauthTest() throws NoSuchAlgorithmException, IOException, 
InterruptedException {
+               OAuthServer server = new OAuthServer()
+                               .setName("Google")
+                               
.setIconUrl("https://www.google.com/images/google_favicon_128.png";)
+                               .setEnabled(false)
+                               .setClientId("secret-client-id")
+                               .setClientSecret("secret-client-secret")
+                               
.setRequestKeyUrl("https://accounts.google.com/o/oauth2/auth?redirect_uri={$redirect_uri}&response_type=code&client_id={$client_id}";
+                                               + 
"&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile")
+                               
.setRequestTokenUrl("https://accounts.google.com/o/oauth2/token";)
+                               .setRequestTokenMethod(RequestTokenMethod.POST)
+                               
.setRequestTokenAttributes("code={$code}&client_id={$client_id}&client_secret={$client_secret}&redirect_uri={$redirect_uri}&grant_type=authorization_code")
+                               
.setRequestInfoUrl("https://www.googleapis.com/oauth2/v1/userinfo?access_token={$access_token}";)
+                               .setRequestInfoMethod(RequestInfoMethod.GET)
+                               .addMapping(PARAM_LOGIN, "preferred_username")
+                               .addMapping(PARAM_EMAIL, "email")
+                               .addMapping(PARAM_FNAME, "given_name")
+                               .addMapping(PARAM_LNAME, "family_name");
                
setCryptClassName(SCryptImplementation.class.getCanonicalName());
                doReturn(true).when(userDao).validLogin(anyString());
-               doReturn(true).when(userDao).checkEmail(anyString(), 
eq(Type.OAUTH), any(Long.class), nullable(Long.class));
-               when(userDao.update(any(User.class), nullable(String.class), 
any(Long.class))).then(new Answer<User>() {
+               
doReturn(true).when(userDao).checkEmail(eq("openmeeti...@pod.land"), 
eq(Type.OAUTH), nullable(Long.class), nullable(Long.class));
+               doAnswer(new Answer<User>() {
                        @Override
                        public User answer(InvocationOnMock invocation) throws 
Throwable {
                                Object[] args = invocation.getArguments();
                                return (User)args[0];
                        }
-               });
-               final String json = 
"{\"email\":\"openmeeti...@pod.land\",\"email_verified\":true,\"id\":78207,\"nationalcode_verified\":false,\"phone_number_verified\":false,\"preferred_username\":\"openmeetings\",\"sub\":\"78207\",\"user_metadata\":\"{\\\"mail.auto-forward\\\":true,\\\"email\\\":{\\\"auto_forward\\\":true}}\"}";
-               OAuthUser user = new OAuthUser(json, new OAuthServer()
-                               .addMapping(OAuthUser.PARAM_LOGIN, 
"preferred_username")
-                               .addMapping(OAuthUser.PARAM_EMAIL, "email"));
-               User u = userManager.loginOAuth(user, 1);
+               }).when(userDao).update(any(User.class), 
nullable(String.class), any(Long.class));
+               final String userJson = 
"{\"email\":\"openmeeti...@pod.land\",\"email_verified\":true,\"id\":78207,\"nationalcode_verified\":false,\"phone_number_verified\":false,\"preferred_username\":\"openmeetings\",\"sub\":\"78207\",\"user_metadata\":\"{\\\"mail.auto-forward\\\":true,\\\"email\\\":{\\\"auto_forward\\\":true}}\"}";
+               final String tokenJson = "{\"accessToken\": \"aaa\", 
\"refreshToken\": \"bbb\", \"tokenType\": \"cccc\", \"userId\": \"ddddd\", 
\"expiresIn\": \"eeeeee\"}";
+               doAnswer(new Answer<String>() {
+                       @Override
+                       public String answer(InvocationOnMock invocation) 
throws Throwable {
+                               Object[] args = invocation.getArguments();
+                               HttpRequest req = (HttpRequest)args[0];
+                               if 
(req.uri().getHost().equals("accounts.google.com")) {
+                                       return tokenJson;
+                               }
+                               return userJson;
+                       }
+               }).when(userManager).doRequest(any(HttpRequest.class));
+
+               User u = userManager.loginOAuth("bla-bla-bla", server);
                assertNotNull(u, "Valid user should be created");
                assertEquals("openmeetings", u.getLogin(), "User should have 
valid login");
                assertEquals("openmeeti...@pod.land", 
u.getAddress().getEmail(), "User should have valid email");

Reply via email to