diff --git a/README.md b/README.md
index 3ab8d10f..4034562d 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,29 @@ Check out the [docker section](./docker/README.)
Individual test result reports can be found in wisemapping-open-source/wise-webapp/target/failsafe-reports/index.html
Test coverage report of unit and integration test can be found in wisemapping-open-source/wise-webapp/target/site/jacoco and wisemapping-open-source/wise-webapp/target/site/jacoco-it folders. Coverage report is generated in the verify phase of [lifecicle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#introduction-to-the-build-lifecyclea) using [jacoco](https://www.jacoco.org/jacoco/trunk/doc/maven.html)
+
+## Google authorization
+
+You must configure the following wisemapping properties (app.properties) in order to get google authorization working
+ * `google.oauth2.callbackUrl`: url where google will redirect after user authentication, tipically {frontendBaseUrl}/c/registration-google. Also, this url must be defined in google app configuration
+ * `google.oauth2.clientId`: client id from google app
+ * `google.oauth2.clientSecret`: client secret from google app
+
+You must create a Google Application in [Google Cloud](https://console.cloud.google.com) and complete all the information required by Google. Here are the most important properties.
+
+Oauth consent screen
+ * Authorized domains: wisemapping domain (ex: wisemapping.com), and you can add domains of other environments if needed
+ * Permissions
+ * `https://www.googleapis.com/auth/userinfo.profile`
+ * `https://www.googleapis.com/auth/userinfo.email`
+ * Test users: emails for testing, those can be used before the application is validated by Google
+
+After that, in Credentials, you must create an `Oauth Client Id` credential
+ * Authorized JavaScript origins: list of authorized domains from which to redirect to Google. Ex: `https://wisemaping.com`, `https://wisemapping-testing.com:8080`
+ * Authorized redirect URIs: list of allowed urls to which google will redirect after authenticating . Ex: `https://wisemaping.com/c/registration-google`, `https://wisemapping-testing.com:8080/c/registration-google`
+
+After credential was created, Google will show you the clientId and clientSecret to configure your application. For productive applications, you must **publish** your application, this is a validation process with Google.
+
## Members
### Founders
diff --git a/config/database/hsql/create-schemas.sql b/config/database/hsql/create-schemas.sql
index 3e98ae79..50fe1d76 100644
--- a/config/database/hsql/create-schemas.sql
+++ b/config/database/hsql/create-schemas.sql
@@ -15,6 +15,9 @@ CREATE TABLE USER (
activation_date DATE,
allow_send_email CHAR(1) NOT NULL,
locale VARCHAR(5),
+ google_sync BOOLEAN,
+ sync_code VARCHAR(255),
+ google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
);
diff --git a/config/database/mysql/create-schemas.sql b/config/database/mysql/create-schemas.sql
index 974a14a4..e8d67e90 100644
--- a/config/database/mysql/create-schemas.sql
+++ b/config/database/mysql/create-schemas.sql
@@ -25,6 +25,9 @@ CREATE TABLE USER (
activation_date DATE,
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
locale VARCHAR(5),
+ google_sync BOOL,
+ sync_code VARCHAR(255),
+ google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
ON DELETE CASCADE
ON UPDATE NO ACTION
diff --git a/config/database/postgres/create-schemas.sql b/config/database/postgres/create-schemas.sql
index d9190c7c..c9ec9b20 100644
--- a/config/database/postgres/create-schemas.sql
+++ b/config/database/postgres/create-schemas.sql
@@ -15,6 +15,9 @@ CREATE TABLE "user" (
activation_date DATE,
allow_send_email TEXT NOT NULL DEFAULT 0,
locale VARCHAR(5),
+ google_sync BOOLEAN,
+ sync_code VARCHAR(255),
+ google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ON DELETE CASCADE ON UPDATE NO ACTION
);
diff --git a/distribution/mysql-init/0002create-schemas.sql b/distribution/mysql-init/0002create-schemas.sql
index 51d391ba..1fee2371 100644
--- a/distribution/mysql-init/0002create-schemas.sql
+++ b/distribution/mysql-init/0002create-schemas.sql
@@ -25,6 +25,9 @@ CREATE TABLE USER (
activation_date DATE,
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
locale VARCHAR(5),
+ google_sync BOOL,
+ sync_code VARCHAR(255),
+ google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
ON DELETE CASCADE
ON UPDATE NO ACTION
diff --git a/distribution/registration-google/update-db-mysql.sql b/distribution/registration-google/update-db-mysql.sql
new file mode 100644
index 00000000..ede72a63
--- /dev/null
+++ b/distribution/registration-google/update-db-mysql.sql
@@ -0,0 +1,4 @@
+ALTER TABLE USER
+ADD COLUMN `google_sync` TINYINT(1) NULL,
+ADD COLUMN `sync_code` VARCHAR(255) NULL,
+ADD COLUMN `google_token` VARCHAR(255) NULL;
diff --git a/distribution/registration-google/update-db-postgres.sql b/distribution/registration-google/update-db-postgres.sql
new file mode 100644
index 00000000..846add61
--- /dev/null
+++ b/distribution/registration-google/update-db-postgres.sql
@@ -0,0 +1,4 @@
+ALTER TABLE "user"
+ADD COLUMN `google_sync` BOOLEAN NULL,
+ADD COLUMN `sync_code` VARCHAR(255) NULL,
+ADD COLUMN `google_token` VARCHAR(255) NULL;
diff --git a/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java b/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java
index d331d2e6..e568f0ad 100644
--- a/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java
+++ b/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java
@@ -19,6 +19,7 @@
package com.wisemapping.dao;
import com.wisemapping.model.AccessAuditory;
+import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.Collaboration;
import com.wisemapping.model.Collaborator;
import com.wisemapping.model.User;
@@ -31,7 +32,6 @@ import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import org.springframework.security.crypto.password.PasswordEncoder;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -101,7 +101,11 @@ public class UserManagerImpl
@Override
public void createUser(User user) {
assert user != null : "Trying to store a null user";
- user.setPassword(passwordEncoder.encode(user.getPassword()));
+ if (!AuthenticationType.GOOGLE_OAUTH2.equals(user.getAuthenticationType())) {
+ user.setPassword(passwordEncoder.encode(user.getPassword()));
+ } else {
+ user.setPassword("");
+ }
getHibernateTemplate().saveOrUpdate(user);
}
diff --git a/wise-webapp/src/main/java/com/wisemapping/filter/CorsFilter.java b/wise-webapp/src/main/java/com/wisemapping/filter/CorsFilter.java
new file mode 100644
index 00000000..5cc60cdb
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/filter/CorsFilter.java
@@ -0,0 +1,79 @@
+/*
+* Copyright [2022] [wisemapping]
+*
+* Licensed under WiseMapping Public License, Version 1.0 (the "License").
+* It is basically the Apache License, Version 2.0 (the "License") plus the
+* "powered by wisemapping" text requirement on every single page;
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the license at
+*
+* http://www.wisemapping.org/license
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.wisemapping.filter;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * If your wisemapping customization throws cross domain errores in browser, you can configure this filter in webdefault.xml
+ * By default it will accept all domains, but you can restrict to the domain you need
+ *
+ *
+ * cross-origin
+ * com.wisemapping.filter.CorsFilter
+ *
+ * allowedOrigins
+ * *
+ *
+ *
+ * allowedMethods
+ * GET,POST,HEAD
+ *
+ *
+ * allowedHeaders
+ * X-Requested-With,Content-Type,Accept,Origin
+ *
+ *
+ *
+ * cross-origin
+ * /*
+ *
+ *
+ */
+public class CorsFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
+ throws IOException, ServletException {
+ if (servletResponse != null) {
+ // Authorize (allow) all domains to consume the content
+ ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", "*");
+ ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST");
+ }
+
+ chain.doFilter(servletRequest, servletResponse);
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java b/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java
index 4317910a..6e670a4e 100644
--- a/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java
+++ b/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java
@@ -54,6 +54,9 @@ public class RequestPropertiesInterceptor implements HandlerInterceptor {
@Value("${security.type}")
private String securityType;
+ @Value("${google.oauth2.url}")
+ private String googleOauth2Url;
+
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, Object object) throws Exception {
@@ -64,6 +67,8 @@ public class RequestPropertiesInterceptor implements HandlerInterceptor {
request.setAttribute("google.recaptcha2.enabled", recaptcha2Enabled);
request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey);
+ request.setAttribute("google.oauth2.url", googleOauth2Url);
+
request.setAttribute("site.homepage", siteHomepage);
request.setAttribute("site.static.js.url", siteStaticUrl);
diff --git a/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java b/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java
index 92b7725e..f1fd4b71 100644
--- a/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java
+++ b/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java
@@ -23,7 +23,8 @@ import org.jetbrains.annotations.NotNull;
public enum AuthenticationType {
DATABASE('D'),
LDAP('L'),
- OPENID('O');
+ GOOGLE_OAUTH2('G');
+
private final char schemaCode;
AuthenticationType(char schemaCode) {
diff --git a/wise-webapp/src/main/java/com/wisemapping/model/User.java b/wise-webapp/src/main/java/com/wisemapping/model/User.java
index 261471a8..4040e4f2 100644
--- a/wise-webapp/src/main/java/com/wisemapping/model/User.java
+++ b/wise-webapp/src/main/java/com/wisemapping/model/User.java
@@ -18,15 +18,12 @@
package com.wisemapping.model;
-import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Calendar;
-import java.util.HashSet;
-import java.util.Set;
@Entity
@Table(name = "USER")
@@ -39,21 +36,30 @@ public class User
private String lastname;
private String password;
private String locale;
-
+
@Column(name = "activation_code")
private long activationCode;
-
+
@Column(name = "activation_date")
private Calendar activationDate;
-
+
@Column(name = "allow_send_email")
private boolean allowSendEmail = false;
-
+
@Column(name = "authentication_type")
private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode();
-
+
@Column(name = "authenticator_uri")
private String authenticatorUri;
+
+ @Column(name = "google_sync")
+ private Boolean googleSync;
+
+ @Column(name = "sync_code")
+ private String syncCode;
+
+ @Column(name = "google_token")
+ private String googleToken;
public User() {
}
@@ -151,7 +157,35 @@ public class User
this.authenticatorUri = authenticatorUri;
}
- @Override
+ public void setAuthenticationTypeCode(Character authenticationTypeCode) {
+ this.authenticationTypeCode = authenticationTypeCode;
+ }
+
+ public Boolean getGoogleSync() {
+ return googleSync;
+ }
+
+ public void setGoogleSync(Boolean googleSync) {
+ this.googleSync = googleSync;
+ }
+
+ public String getSyncCode() {
+ return syncCode;
+ }
+
+ public void setSyncCode(String syncCode) {
+ this.syncCode = syncCode;
+ }
+
+ public String getGoogleToken() {
+ return googleToken;
+ }
+
+ public void setGoogleToken(String googleToken) {
+ this.googleToken = googleToken;
+ }
+
+ @Override
public String toString() {
return "User{" +
"firstname='" + firstname + '\'' +
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java b/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java
index 2601a934..bccece0d 100644
--- a/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java
@@ -19,7 +19,6 @@
package com.wisemapping.rest;
import com.wisemapping.exceptions.WiseMappingException;
-import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.Collaboration;
import com.wisemapping.model.Label;
import com.wisemapping.model.Mindmap;
@@ -54,10 +53,6 @@ public class AccountController extends BaseController {
@Autowired
private LabelService labelService;
- @Autowired
- private NotificationService notificationService;
-
-
@RequestMapping(method = RequestMethod.PUT, value = "account/password", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changePassword(@RequestBody String password) {
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/Oauth2Controller.java b/wise-webapp/src/main/java/com/wisemapping/rest/Oauth2Controller.java
new file mode 100644
index 00000000..a3381a3c
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/Oauth2Controller.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright [2022] [wisemapping]
+ *
+ * Licensed under WiseMapping Public License, Version 1.0 (the "License").
+ * It is basically the Apache License, Version 2.0 (the "License") plus the
+ * "powered by wisemapping" text requirement on every single page;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the license at
+ *
+ * http://www.wisemapping.org/license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.wisemapping.rest;
+
+import com.wisemapping.exceptions.WiseMappingException;
+import com.wisemapping.model.User;
+import com.wisemapping.rest.model.RestOath2CallbackResponse;
+import com.wisemapping.service.*;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+
+@Controller
+@CrossOrigin
+public class Oauth2Controller extends BaseController {
+ @Qualifier("userService")
+ @Autowired
+ private UserService userService;
+
+ @Qualifier("authenticationManager")
+ @Autowired
+ private AuthenticationManager authManager;
+
+ @Value("${google.recaptcha2.enabled}")
+ private Boolean recatchaEnabled;
+
+ @Value("${accounts.exclusion.domain:''}")
+ private String domainBanExclusion;
+
+ private void doLogin(HttpServletRequest request, String email) {
+ PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(email,null);
+ Authentication auth = authManager.authenticate(token);
+ SecurityContextHolder.getContext().setAuthentication(auth);
+ // update spring mvc session
+ HttpSession session = request.getSession(true);
+ session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
+ }
+
+ @RequestMapping(method = RequestMethod.POST, value = "/oauth2/googlecallback", produces = { "application/json" })
+ @ResponseStatus(value = HttpStatus.OK)
+ public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
+ User user = userService.createUserFromGoogle(code);
+ if (user.getGoogleSync() != null && user.getGoogleSync().booleanValue()) {
+ doLogin(request, user.getEmail());
+ }
+ RestOath2CallbackResponse response = new RestOath2CallbackResponse();
+ response.setEmail(user.getEmail());
+ response.setGoogleSync(user.getGoogleSync());
+ response.setSyncCode(user.getSyncCode());
+ return response;
+ }
+
+ @RequestMapping(method = RequestMethod.PUT, value = "/oauth2/confirmaccountsync", produces = { "application/json" })
+ @ResponseStatus(value = HttpStatus.OK)
+ public void confirmAccountSync(@NotNull @RequestParam String email, @NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
+ userService.confirmAccountSync(email, code);
+ doLogin(request, email);
+ }
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java b/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java
index 0b7a1ec1..234c6b09 100644
--- a/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java
@@ -22,6 +22,7 @@ import com.wisemapping.exceptions.EmailNotExistsException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.User;
+import com.wisemapping.rest.model.RestResetPasswordResponse;
import com.wisemapping.rest.model.RestUserRegistration;
import com.wisemapping.service.*;
import com.wisemapping.validator.Messages;
@@ -33,100 +34,110 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
+import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
import java.util.Arrays;
import java.util.List;
@Controller
@CrossOrigin
public class UserController extends BaseController {
- @Qualifier("userService")
- @Autowired
- private UserService userService;
+ @Qualifier("userService")
+ @Autowired
+ private UserService userService;
- @Autowired
- private RecaptchaService captchaService;
+ @Autowired
+ private RecaptchaService captchaService;
- @Value("${google.recaptcha2.enabled}")
- private Boolean recatchaEnabled;
+ @Qualifier("authenticationManager")
+ @Autowired
+ private AuthenticationManager authManager;
- @Value("${accounts.exclusion.domain:''}")
- private String domainBanExclusion;
+ @Value("${google.recaptcha2.enabled}")
+ private Boolean recatchaEnabled;
- private static final Logger logger = LogManager.getLogger();
- private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
+ @Value("${accounts.exclusion.domain:''}")
+ private String domainBanExclusion;
- @RequestMapping(method = RequestMethod.POST, value = "/users", produces = {"application/json"})
- @ResponseStatus(value = HttpStatus.CREATED)
- public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws WiseMappingException, BindException {
- logger.debug("Register new user:" + registration.getEmail());
+ private static final Logger logger = LogManager.getLogger();
+ private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
- // If tomcat is behind a reverse proxy, ip needs to be found in other header.
- String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER);
- if (remoteIp == null || remoteIp.isEmpty()) {
- remoteIp = request.getRemoteAddr();
- }
- logger.debug("Remote address" + remoteIp);
+ @RequestMapping(method = RequestMethod.POST, value = "/users", produces = { "application/json" })
+ @ResponseStatus(value = HttpStatus.CREATED)
+ public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request,
+ @NotNull HttpServletResponse response) throws WiseMappingException, BindException {
+ logger.debug("Register new user:" + registration.getEmail());
- verify(registration, remoteIp);
+ // If tomcat is behind a reverse proxy, ip needs to be found in other header.
+ String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER);
+ if (remoteIp == null || remoteIp.isEmpty()) {
+ remoteIp = request.getRemoteAddr();
+ }
+ logger.debug("Remote address" + remoteIp);
- final User user = new User();
- user.setEmail(registration.getEmail().trim());
- user.setFirstname(registration.getFirstname());
- user.setLastname(registration.getLastname());
- user.setPassword(registration.getPassword());
+ verify(registration, remoteIp);
- user.setAuthenticationType(AuthenticationType.DATABASE);
- userService.createUser(user, false, true);
- response.setHeader("Location", "/service/users/" + user.getId());
- }
+ final User user = new User();
+ user.setEmail(registration.getEmail().trim());
+ user.setFirstname(registration.getFirstname());
+ user.setLastname(registration.getLastname());
+ user.setPassword(registration.getPassword());
- @RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = {"application/json"})
- @ResponseStatus(value = HttpStatus.OK)
- public void resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException {
- try {
- userService.resetPassword(email);
- } catch (InvalidUserEmailException e) {
- throw new EmailNotExistsException(e);
- }
- }
+ user.setAuthenticationType(AuthenticationType.DATABASE);
+ userService.createUser(user, false, true);
+ response.setHeader("Location", "/service/users/" + user.getId());
+ }
- private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress) throws BindException {
+ @RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = { "application/json" })
+ @ResponseStatus(value = HttpStatus.OK)
+ public RestResetPasswordResponse resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException {
+ try {
+ return userService.resetPassword(email);
+ } catch (InvalidUserEmailException e) {
+ throw new EmailNotExistsException(e);
+ }
+ }
- final BindException errors = new RegistrationException(registration, "registration");
- final UserValidator validator = new UserValidator();
- validator.setUserService(userService);
- validator.validate(registration, errors);
+ private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress)
+ throws BindException {
- // If captcha is enabled, generate it ...
- if (recatchaEnabled) {
- final String recaptcha = registration.getRecaptcha();
- if (recaptcha != null) {
- final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha);
- if (reCaptchaResponse != null && !reCaptchaResponse.isEmpty()) {
- errors.rejectValue("recaptcha", reCaptchaResponse);
- }
- } else {
- errors.rejectValue("recaptcha", Messages.CAPTCHA_LOADING_ERROR);
- }
- } else {
- logger.warn("captchaEnabled is enabled.Recommend to enable it for production environments.");
- }
+ final BindException errors = new RegistrationException(registration, "registration");
+ final UserValidator validator = new UserValidator();
+ validator.setUserService(userService);
+ validator.validate(registration, errors);
- if (errors.hasErrors()) {
- throw errors;
- }
+ // If captcha is enabled, generate it ...
+ if (recatchaEnabled) {
+ final String recaptcha = registration.getRecaptcha();
+ if (recaptcha != null) {
+ final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha);
+ if (reCaptchaResponse != null && !reCaptchaResponse.isEmpty()) {
+ errors.rejectValue("recaptcha", reCaptchaResponse);
+ }
+ } else {
+ errors.rejectValue("recaptcha", Messages.CAPTCHA_LOADING_ERROR);
+ }
+ } else {
+ logger.warn("captchaEnabled is enabled.Recommend to enable it for production environments.");
+ }
- // Is excluded ?.
- final List excludedDomains = Arrays.asList(domainBanExclusion.split(","));
- final String emailDomain = registration.getEmail().split("@")[1];
- if (excludedDomains.contains(emailDomain)) {
- throw new IllegalArgumentException("Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error." + emailDomain);
- }
- }
+ if (errors.hasErrors()) {
+ throw errors;
+ }
+
+ // Is excluded ?.
+ final List excludedDomains = Arrays.asList(domainBanExclusion.split(","));
+ final String emailDomain = registration.getEmail().split("@")[1];
+ if (excludedDomains.contains(emailDomain)) {
+ throw new IllegalArgumentException(
+ "Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error."
+ + emailDomain);
+ }
+ }
}
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestOath2CallbackResponse.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestOath2CallbackResponse.java
new file mode 100644
index 00000000..f8b040f3
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestOath2CallbackResponse.java
@@ -0,0 +1,33 @@
+package com.wisemapping.rest.model;
+
+public class RestOath2CallbackResponse {
+
+ private String email;
+ private Boolean googleSync;
+ private String syncCode;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public Boolean getGoogleSync() {
+ return googleSync;
+ }
+
+ public void setGoogleSync(Boolean googleSync) {
+ this.googleSync = googleSync;
+ }
+
+ public String getSyncCode() {
+ return syncCode;
+ }
+
+ public void setSyncCode(String syncCode) {
+ this.syncCode = syncCode;
+ }
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordAction.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordAction.java
new file mode 100644
index 00000000..b5467616
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordAction.java
@@ -0,0 +1,7 @@
+package com.wisemapping.rest.model;
+
+public enum RestResetPasswordAction {
+
+ EMAIL_SENT, OAUTH2_USER
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordResponse.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordResponse.java
new file mode 100644
index 00000000..38e68cc1
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordResponse.java
@@ -0,0 +1,15 @@
+package com.wisemapping.rest.model;
+
+public class RestResetPasswordResponse {
+
+ RestResetPasswordAction action;
+
+ public RestResetPasswordAction getAction() {
+ return action;
+ }
+
+ public void setAction(RestResetPasswordAction action) {
+ this.action = action;
+ }
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java
index 949d3aea..30766566 100644
--- a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java
+++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.User;
import org.jetbrains.annotations.NotNull;
@@ -102,6 +103,10 @@ public class RestUser {
return this.user;
}
+ public AuthenticationType getAuthenticationType() {
+ return user.getAuthenticationType();
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof RestUser)) {
diff --git a/wise-webapp/src/main/java/com/wisemapping/security/GoogleAuthenticationProvider.java b/wise-webapp/src/main/java/com/wisemapping/security/GoogleAuthenticationProvider.java
new file mode 100644
index 00000000..89e94077
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/security/GoogleAuthenticationProvider.java
@@ -0,0 +1,62 @@
+package com.wisemapping.security;
+
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
+
+import com.wisemapping.model.User;
+
+public class GoogleAuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider {
+
+ private UserDetailsService userDetailsService;
+
+ public UserDetailsService getUserDetailsService() {
+ return userDetailsService;
+ }
+
+ public void setUserDetailsService(UserDetailsService userDetailsService) {
+ this.userDetailsService = userDetailsService;
+ }
+
+ /**
+ * Authenticate the given PreAuthenticatedAuthenticationToken.
+ *
+ * If the principal contained in the authentication object is null, the request will
+ * be ignored to allow other providers to authenticate it.
+ */
+ @Override
+ public Authentication authenticate(Authentication inputToken) throws AuthenticationException {
+ if (!supports(inputToken.getClass())) {
+ return null;
+ }
+ if (inputToken.getPrincipal() == null) {
+ throw new BadCredentialsException("No pre-authenticated principal found in request.");
+ }
+ UserDetails userDetails = userDetailsService.loadUserByUsername(inputToken.getName());
+ final User user = userDetails.getUser();
+
+ if (!user.isActive()) {
+ throw new BadCredentialsException("User has been disabled for login " + inputToken.getName());
+ }
+
+ PreAuthenticatedAuthenticationToken resultToken = new PreAuthenticatedAuthenticationToken(userDetails,
+ inputToken.getCredentials(), userDetails.getAuthorities());
+ resultToken.setDetails(userDetails);
+
+ userDetailsService.getUserService().auditLogin(user);
+
+ return resultToken;
+ }
+
+ /**
+ * Indicate that this provider only supports PreAuthenticatedAuthenticationToken
+ * (sub)classes.
+ */
+ @Override
+ public final boolean supports(Class> authentication) {
+ return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);
+ }
+
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java b/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java
index 4a32bf35..0d86c946 100644
--- a/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java
+++ b/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java
@@ -23,7 +23,6 @@ import com.wisemapping.model.User;
import com.wisemapping.service.UserService;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/UserService.java b/wise-webapp/src/main/java/com/wisemapping/service/UserService.java
index ef0cf222..6fbda735 100755
--- a/wise-webapp/src/main/java/com/wisemapping/service/UserService.java
+++ b/wise-webapp/src/main/java/com/wisemapping/service/UserService.java
@@ -20,6 +20,8 @@ package com.wisemapping.service;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.User;
+import com.wisemapping.rest.model.RestResetPasswordResponse;
+
import org.jetbrains.annotations.NotNull;
public interface UserService {
@@ -28,6 +30,10 @@ public interface UserService {
User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException;
+ User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException;
+
+ User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException;
+
void changePassword(@NotNull User user);
User getUserBy(String email);
@@ -36,7 +42,7 @@ public interface UserService {
void updateUser(User user);
- void resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException;
+ RestResetPasswordResponse resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException;
void removeUser(@NotNull User user);
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java b/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java
index 07866c1e..a3e78721 100755
--- a/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java
+++ b/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java
@@ -23,6 +23,10 @@ import com.wisemapping.exceptions.InvalidMindmapException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.*;
+import com.wisemapping.rest.model.RestResetPasswordAction;
+import com.wisemapping.rest.model.RestResetPasswordResponse;
+import com.wisemapping.service.google.GoogleAccountBasicData;
+import com.wisemapping.service.google.GoogleService;
import com.wisemapping.util.VelocityEngineUtils;
import com.wisemapping.util.VelocityEngineWrapper;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +43,7 @@ public class UserServiceImpl
private NotificationService notificationService;
private MessageSource messageSource;
private VelocityEngineWrapper velocityEngineWrapper;
-
+ private GoogleService googleService;
@Override
public void activateAccount(long code)
@@ -56,10 +60,15 @@ public class UserServiceImpl
}
@Override
- public void resetPassword(@NotNull String email)
+ public RestResetPasswordResponse resetPassword(@NotNull String email)
throws InvalidUserEmailException, InvalidAuthSchemaException {
final User user = userManager.getUserBy(email);
if (user != null) {
+ RestResetPasswordResponse response = new RestResetPasswordResponse();
+ if (user.getAuthenticationType().equals(AuthenticationType.GOOGLE_OAUTH2)) {
+ response.setAction(RestResetPasswordAction.OAUTH2_USER);
+ return response;
+ }
if (user.getAuthenticationType() != AuthenticationType.DATABASE) {
throw new InvalidAuthSchemaException("Could not change password for " + user.getAuthenticationType().getCode());
@@ -72,6 +81,9 @@ public class UserServiceImpl
// Send an email with the new temporal password ...
notificationService.resetPassword(user, password);
+
+ response.setAction(RestResetPasswordAction.EMAIL_SENT);
+ return response;
} else {
throw new InvalidUserEmailException("The email '" + email + "' does not exists.");
}
@@ -147,6 +159,55 @@ public class UserServiceImpl
return user;
}
+ @NotNull
+ public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
+ try {
+ GoogleAccountBasicData data = googleService.processCallback(callbackCode);
+ User existingUser = userManager.getUserBy(data.getEmail());
+ if (existingUser == null) {
+ User newUser = new User();
+ // new registrations from google starts synched
+ newUser.setGoogleSync(true);
+ newUser.setEmail(data.getEmail());
+ newUser.setFirstname(data.getName());
+ newUser.setLastname(data.getLastName());
+ newUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2);
+ newUser.setGoogleToken(data.getAccessToken());
+ existingUser = this.createUser(newUser, false, true);
+ } else {
+ // user exists and doesnt have confirmed account linking, I must wait for confirmation
+ if (existingUser.getGoogleSync() == null) {
+ existingUser.setGoogleSync(false);
+ existingUser.setSyncCode(callbackCode);
+ existingUser.setGoogleToken(data.getAccessToken());
+ userManager.updateUser(existingUser);
+ }
+
+ }
+ return existingUser;
+ } catch (Exception e) {
+ throw new WiseMappingException("Cant create user", e);
+ }
+ }
+
+ public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
+ User existingUser = userManager.getUserBy(email);
+ // additional security check
+ if (existingUser == null || !existingUser.getSyncCode().equals(code)) {
+ throw new WiseMappingException("User not found / incorrect code");
+ }
+ existingUser.setGoogleSync(true);
+ existingUser.setSyncCode(null);
+ // user will not be able to login again with usr/pwd schema
+ existingUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2);
+ existingUser.setPassword("");
+ userManager.updateUser(existingUser);
+
+ return existingUser;
+ }
+
+
+
public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
//To change body of created methods use File | Settings | File Templates.
final Locale locale = LocaleContextHolder.getLocale();
@@ -209,7 +270,11 @@ public class UserServiceImpl
this.velocityEngineWrapper = velocityEngineWrapper;
}
- @Override
+ public void setGoogleService(GoogleService googleService) {
+ this.googleService = googleService;
+ }
+
+ @Override
public User getCasUserBy(String uid) {
// TODO Auto-generated method stub
return null;
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleAccountBasicData.java b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleAccountBasicData.java
new file mode 100644
index 00000000..f8f43f76
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleAccountBasicData.java
@@ -0,0 +1,66 @@
+package com.wisemapping.service.google;
+
+public class GoogleAccountBasicData {
+
+ private String email;
+ private String accountId;
+ private String name;
+ private String lastName;
+ private String accessToken;
+ private String refreshToken;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ public void setRefreshToken(String refreshToken) {
+ this.refreshToken = refreshToken;
+ }
+
+ @Override
+ public String toString() {
+ return "GoogleAccountBasicData [email=" + email + ", accountId=" + accountId + ", name=" + name + ", lastName="
+ + lastName + ", accessToken=" + accessToken + ", refreshToken=" + refreshToken + "]";
+ }
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleService.java b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleService.java
new file mode 100644
index 00000000..2a4e8f6d
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleService.java
@@ -0,0 +1,106 @@
+package com.wisemapping.service.google;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.wisemapping.service.http.HttpInvoker;
+import com.wisemapping.service.http.HttpInvokerContentType;
+import com.wisemapping.service.http.HttpInvokerException;
+import com.wisemapping.service.http.HttpMethod;
+
+@Service
+public class GoogleService {
+ private HttpInvoker httpInvoker;
+ private String optinConfirmUrl;
+ private String accountBasicDataUrl;
+ private String clientId;
+ private String clientSecret;
+ private String callbackUrl;
+
+ public void setHttpInvoker(HttpInvoker httpInvoker) {
+ this.httpInvoker = httpInvoker;
+ }
+
+ public void setOptinConfirmUrl(String optinConfirmUrl) {
+ this.optinConfirmUrl = optinConfirmUrl;
+ }
+
+ public void setAccountBasicDataUrl(String accountBasicDataUrl) {
+ this.accountBasicDataUrl = accountBasicDataUrl;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public void setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ }
+
+ public void setCallbackUrl(String callbackUrl) {
+ this.callbackUrl = callbackUrl;
+ }
+
+ private String getNodeAsString(JsonNode node, String fieldName) {
+ return getNodeAsString(node, fieldName, null);
+ }
+
+ private String getNodeAsString(JsonNode node, String fieldName, String defaultValue) {
+ JsonNode subNode = node.get(fieldName);
+ return subNode != null ? subNode.asText() : defaultValue;
+ }
+
+ private Map getHeaders(String token) {
+ Map headers = new HashMap();
+ headers.put("Content-type", "application/json");
+ headers.put("Authorization", "Bearer " + token);
+ return headers;
+ }
+
+ private GoogleAccountBasicData getAccountBasicData(String token) throws HttpInvokerException {
+ JsonNode response = httpInvoker.invoke(accountBasicDataUrl, null, HttpMethod.GET, this.getHeaders(token), null,
+ null);
+ GoogleAccountBasicData data = new GoogleAccountBasicData();
+ data.setEmail(getNodeAsString(response, "email"));
+ data.setAccountId(getNodeAsString(response, "id"));
+ data.setName(getNodeAsString(response, "given_name", data.getEmail()));
+ data.setLastName(getNodeAsString(response, "family_name"));
+ return data;
+ }
+
+ private Map getOptinConfirmBody(String code) {
+ Map result = new HashMap();
+ result.put("client_id", clientId);
+ result.put("client_secret", clientSecret);
+ result.put("code", code);
+ result.put("redirect_uri", callbackUrl);
+ result.put("grant_type", "authorization_code");
+ return result;
+ }
+
+ public GoogleAccountBasicData processCallback(String code)
+ throws HttpInvokerException, JsonMappingException, JsonProcessingException {
+ Map body = this.getOptinConfirmBody(code);
+ JsonNode optinConfirmResponse = httpInvoker.invoke(
+ optinConfirmUrl,
+ HttpInvokerContentType.FORM_ENCODED,
+ HttpMethod.POST,
+ null,
+ null,
+ body);
+
+ String accessToken = getNodeAsString(optinConfirmResponse, "access_token");
+ String refreshToken = getNodeAsString(optinConfirmResponse, "refresh_token");
+
+ GoogleAccountBasicData data = this.getAccountBasicData(accessToken);
+ data.setAccessToken(accessToken);
+ data.setRefreshToken(refreshToken);
+ return data;
+ }
+
+}
\ No newline at end of file
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvoker.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvoker.java
new file mode 100644
index 00000000..be05d8a4
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvoker.java
@@ -0,0 +1,147 @@
+package com.wisemapping.service.http;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Service
+public class HttpInvoker {
+
+ protected static Logger logger = LogManager.getLogger(HttpInvoker.class);
+
+ private ObjectMapper mapper = new ObjectMapper();
+
+ public HttpInvoker() {
+ super();
+ }
+
+ public JsonNode invoke(
+ String url,
+ HttpInvokerContentType requestContentType,
+ HttpMethod method,
+ Map headers,
+ String jsonPayload,
+ Map formData)
+ throws HttpInvokerException {
+ String responseBody = null;
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("finalUrl: " + url);
+ logger.debug("method: " + method);
+ logger.debug("payload: " + jsonPayload);
+ logger.debug("header: " + headers);
+ }
+
+ CloseableHttpClient httpClient = HttpClients.createDefault();
+ HttpRequestBase httpRequst = null;
+
+ // build request
+ if (method.equals(HttpMethod.POST))
+ httpRequst = new HttpPost(url);
+ else if (method.equals(HttpMethod.PUT))
+ httpRequst = new HttpPut(url);
+ else if (method.equals(HttpMethod.GET))
+ httpRequst = new HttpGet(url);
+ else if (method.equals(HttpMethod.DELETE))
+ httpRequst = new HttpDelete(url);
+ else
+ throw new HttpInvokerException("Method " + method + " not suppoprted by http connector");
+
+ if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) {
+ HttpEntity entity = null;
+ if (requestContentType.equals(HttpInvokerContentType.JSON)) {
+ if (jsonPayload == null)
+ throw new HttpInvokerException("Json content is required");
+ entity = new StringEntity(jsonPayload, Charset.forName("UTF-8"));
+ ((HttpEntityEnclosingRequestBase) httpRequst).setEntity(entity);
+ }
+ if (requestContentType.equals(HttpInvokerContentType.FORM_ENCODED)) {
+ List nameValuePairs = new ArrayList();
+ Set keys = formData.keySet();
+ for (String key : keys) {
+ nameValuePairs.add(new BasicNameValuePair(key, formData.get(key).toString()));
+ }
+ entity = new UrlEncodedFormEntity(nameValuePairs);
+ ((HttpEntityEnclosingRequestBase) httpRequst).setEntity(entity);
+ }
+ if (entity == null)
+ throw new HttpInvokerException("Cant build entity to send");
+ }
+
+ if (headers != null) {
+ Set keys = headers.keySet();
+ for (String key : keys) {
+ httpRequst.setHeader(key, headers.get(key));
+ }
+ }
+
+ if (requestContentType != null)
+ httpRequst.setHeader("Content-Type", requestContentType.getHttpContentType());
+
+ // invoke
+ CloseableHttpResponse response = httpClient.execute(httpRequst);
+ // response process
+ JsonNode root = null;
+ responseBody = response.getEntity() != null && response.getEntity().getContent() != null
+ ? IOUtils.toString(response.getEntity().getContent(), (String) null)
+ : null;
+ if (responseBody != null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("response plain: " + responseBody);
+ }
+ try {
+ root = mapper.readTree(responseBody);
+ } catch (Exception e) {
+ int returnCode = response.getStatusLine().getStatusCode();
+ throw new HttpInvokerException("cant transform response to JSON. RQ: " + jsonPayload + ", RS: "
+ + responseBody + ", status: " + returnCode, e);
+ }
+ }
+
+ if (response.getStatusLine().getStatusCode() >= 400) {
+ logger.error("error response: " + responseBody);
+ throw new HttpInvokerException("error invoking " + url + ", response: " + responseBody + ", status: "
+ + response.getStatusLine().getStatusCode());
+ }
+
+ httpRequst.releaseConnection();
+ response.close();
+ httpClient.close();
+
+ return root;
+ } catch (HttpInvokerException e) {
+ throw e;
+ } catch (Exception e) {
+ logger.error("cant invoke service " + url);
+ logger.error("response: " + responseBody, e);
+ throw new HttpInvokerException("cant invoke service " + url, e);
+ }
+ }
+
+
+
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerContentType.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerContentType.java
new file mode 100644
index 00000000..e80da507
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerContentType.java
@@ -0,0 +1,18 @@
+package com.wisemapping.service.http;
+
+public enum HttpInvokerContentType {
+
+ JSON("application/json"),
+ FORM_ENCODED("application/x-www-form-urlencoded");
+
+ private String httpContentType;
+
+ private HttpInvokerContentType(String type) {
+ this.httpContentType = type;
+ }
+
+ public String getHttpContentType() {
+ return httpContentType;
+ }
+
+}
\ No newline at end of file
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerException.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerException.java
new file mode 100644
index 00000000..8ee37835
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerException.java
@@ -0,0 +1,13 @@
+package com.wisemapping.service.http;
+
+public class HttpInvokerException extends Exception {
+
+ public HttpInvokerException(String message) {
+ super(message);
+ }
+
+ public HttpInvokerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
\ No newline at end of file
diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpMethod.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpMethod.java
new file mode 100644
index 00000000..42553bfa
--- /dev/null
+++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpMethod.java
@@ -0,0 +1,5 @@
+package com.wisemapping.service.http;
+
+public enum HttpMethod {
+ POST, GET, DELETE, PUT
+}
diff --git a/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java b/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java
index 2db75cd5..e760c017 100644
--- a/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java
+++ b/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java
@@ -39,6 +39,11 @@ public class UsersController {
return new ModelAndView("forgot-password");
}
+ @RequestMapping(value = "registration-google", method = RequestMethod.GET)
+ public ModelAndView processGoogleCallback() {
+ return new ModelAndView("registration-google");
+ }
+
@RequestMapping(value = "registration", method = RequestMethod.GET)
public ModelAndView showRegistrationPage() {
return new ModelAndView("registration");
diff --git a/wise-webapp/src/main/webapp/WEB-INF/app.properties b/wise-webapp/src/main/webapp/WEB-INF/app.properties
index 3cbab059..d1fbc169 100755
--- a/wise-webapp/src/main/webapp/WEB-INF/app.properties
+++ b/wise-webapp/src/main/webapp/WEB-INF/app.properties
@@ -139,6 +139,17 @@ security.ldap.firstName.attribute=givenName
# Coma separated list of domains and emails ban
#accounts.exclusion.domain=
-
-
+# google will redirect to this url, this url must be configured in the google app
+# {baseurl}/c/registration-google
+google.oauth2.callbackUrl=https://wisemapping.com/c/registration-google
+# google app client id
+google.oauth2.clientId=
+# google app client secret
+google.oauth2.clientSecret=
+# google service for finish registration process, ie. exchange temporal code for user token
+google.oauth2.confirmUrl=https://oauth2.googleapis.com/token
+# google service for get user data (name, email, etc)
+google.oauth2.userinfoUrl=https://www.googleapis.com/oauth2/v3/userinfo
+# url for starting auth process with google
+google.oauth2.url=https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=${google.oauth2.callbackUrl}&prompt=consent&response_type=code&client_id=${google.oauth2.clientId}&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&access_type=offline&state=wisemapping&include_granted_scopes=true
diff --git a/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml b/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml
index 805880c2..0277589b 100644
--- a/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml
+++ b/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml
@@ -8,6 +8,7 @@
+
diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml
index 4d63d585..695f7b62 100644
--- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml
+++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml
@@ -11,7 +11,8 @@
-
+
+
@@ -19,4 +20,7 @@
+
+
+
\ No newline at end of file
diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml
index 5f23acc0..53b5b092 100644
--- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml
+++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml
@@ -3,12 +3,12 @@
-
+
@@ -34,6 +34,9 @@
+
+
+
@@ -47,6 +50,7 @@
+
diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml
index bedf0bdd..6d962e81 100755
--- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml
+++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml
@@ -18,12 +18,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wise-webapp/src/main/webapp/jsp/reactInclude.jsp b/wise-webapp/src/main/webapp/jsp/reactInclude.jsp
index afa2b5db..b8594eca 100644
--- a/wise-webapp/src/main/webapp/jsp/reactInclude.jsp
+++ b/wise-webapp/src/main/webapp/jsp/reactInclude.jsp
@@ -19,7 +19,8 @@
analyticsAccount: '${requestScope['google.analytics.account']}',
clientType: 'rest',
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},
- recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}'
+ recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}',
+ googleOauth2Url: '${requestScope['google.oauth2.url']}'
};