mirror of
https://bitbucket.org/wisemapping/wisemapping-open-source.git
synced 2024-11-15 03:07:55 +01:00
registration/login google
This commit is contained in:
parent
433cb5549a
commit
6a54723d1d
13
README.md
13
README.md
@ -58,6 +58,19 @@ 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
|
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)
|
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 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
|
||||||
|
|
||||||
|
Be sure your google app has the following scopes allowed:
|
||||||
|
* `https://www.googleapis.com/auth/userinfo.profile`
|
||||||
|
* `https://www.googleapis.com/auth/userinfo.email`
|
||||||
|
|
||||||
|
|
||||||
## Members
|
## Members
|
||||||
|
|
||||||
### Founders
|
### Founders
|
||||||
|
@ -15,6 +15,9 @@ CREATE TABLE USER (
|
|||||||
activation_date DATE,
|
activation_date DATE,
|
||||||
allow_send_email CHAR(1) NOT NULL,
|
allow_send_email CHAR(1) NOT NULL,
|
||||||
locale VARCHAR(5),
|
locale VARCHAR(5),
|
||||||
|
google_sync BOOLEAN,
|
||||||
|
sync_code VARCHAR(255),
|
||||||
|
google_token VARCHAR(255),
|
||||||
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
|
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ CREATE TABLE USER (
|
|||||||
activation_date DATE,
|
activation_date DATE,
|
||||||
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
|
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
|
||||||
locale VARCHAR(5),
|
locale VARCHAR(5),
|
||||||
|
google_sync BOOL,
|
||||||
|
sync_code VARCHAR(255),
|
||||||
|
google_token VARCHAR(255),
|
||||||
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
|
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
ON UPDATE NO ACTION
|
ON UPDATE NO ACTION
|
||||||
|
@ -15,6 +15,9 @@ CREATE TABLE "user" (
|
|||||||
activation_date DATE,
|
activation_date DATE,
|
||||||
allow_send_email TEXT NOT NULL DEFAULT 0,
|
allow_send_email TEXT NOT NULL DEFAULT 0,
|
||||||
locale VARCHAR(5),
|
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
|
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ON DELETE CASCADE ON UPDATE NO ACTION
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ CREATE TABLE USER (
|
|||||||
activation_date DATE,
|
activation_date DATE,
|
||||||
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
|
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
|
||||||
locale VARCHAR(5),
|
locale VARCHAR(5),
|
||||||
|
google_sync BOOL,
|
||||||
|
sync_code VARCHAR(255),
|
||||||
|
google_token VARCHAR(255),
|
||||||
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
|
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
ON UPDATE NO ACTION
|
ON UPDATE NO ACTION
|
||||||
|
4
distribution/registration-google/update-db-mysql.sql
Normal file
4
distribution/registration-google/update-db-mysql.sql
Normal file
@ -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;
|
4
distribution/registration-google/update-db-postgres.sql
Normal file
4
distribution/registration-google/update-db-postgres.sql
Normal file
@ -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;
|
@ -19,6 +19,7 @@
|
|||||||
package com.wisemapping.dao;
|
package com.wisemapping.dao;
|
||||||
|
|
||||||
import com.wisemapping.model.AccessAuditory;
|
import com.wisemapping.model.AccessAuditory;
|
||||||
|
import com.wisemapping.model.AuthenticationType;
|
||||||
import com.wisemapping.model.Collaboration;
|
import com.wisemapping.model.Collaboration;
|
||||||
import com.wisemapping.model.Collaborator;
|
import com.wisemapping.model.Collaborator;
|
||||||
import com.wisemapping.model.User;
|
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.orm.hibernate5.support.HibernateDaoSupport;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
@ -101,7 +101,10 @@ public class UserManagerImpl
|
|||||||
@Override
|
@Override
|
||||||
public void createUser(User user) {
|
public void createUser(User user) {
|
||||||
assert user != null : "Trying to store a null 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);
|
getHibernateTemplate().saveOrUpdate(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
}
|
@ -54,6 +54,9 @@ public class RequestPropertiesInterceptor implements HandlerInterceptor {
|
|||||||
@Value("${security.type}")
|
@Value("${security.type}")
|
||||||
private String securityType;
|
private String securityType;
|
||||||
|
|
||||||
|
@Value("${google.oauth2.url}")
|
||||||
|
private String googleOauth2Url;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, Object object) throws Exception {
|
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.enabled", recaptcha2Enabled);
|
||||||
request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey);
|
request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey);
|
||||||
|
|
||||||
|
request.setAttribute("google.oauth2.url", googleOauth2Url);
|
||||||
|
|
||||||
request.setAttribute("site.homepage", siteHomepage);
|
request.setAttribute("site.homepage", siteHomepage);
|
||||||
request.setAttribute("site.static.js.url", siteStaticUrl);
|
request.setAttribute("site.static.js.url", siteStaticUrl);
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public enum AuthenticationType {
|
public enum AuthenticationType {
|
||||||
DATABASE('D'),
|
DATABASE('D'),
|
||||||
LDAP('L'),
|
LDAP('L'),
|
||||||
OPENID('O');
|
GOOGLE_OAUTH2('G');
|
||||||
|
|
||||||
private final char schemaCode;
|
private final char schemaCode;
|
||||||
|
|
||||||
AuthenticationType(char schemaCode) {
|
AuthenticationType(char schemaCode) {
|
||||||
|
@ -18,15 +18,12 @@
|
|||||||
|
|
||||||
package com.wisemapping.model;
|
package com.wisemapping.model;
|
||||||
|
|
||||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "USER")
|
@Table(name = "USER")
|
||||||
@ -39,21 +36,30 @@ public class User
|
|||||||
private String lastname;
|
private String lastname;
|
||||||
private String password;
|
private String password;
|
||||||
private String locale;
|
private String locale;
|
||||||
|
|
||||||
@Column(name = "activation_code")
|
@Column(name = "activation_code")
|
||||||
private long activationCode;
|
private long activationCode;
|
||||||
|
|
||||||
@Column(name = "activation_date")
|
@Column(name = "activation_date")
|
||||||
private Calendar activationDate;
|
private Calendar activationDate;
|
||||||
|
|
||||||
@Column(name = "allow_send_email")
|
@Column(name = "allow_send_email")
|
||||||
private boolean allowSendEmail = false;
|
private boolean allowSendEmail = false;
|
||||||
|
|
||||||
@Column(name = "authentication_type")
|
@Column(name = "authentication_type")
|
||||||
private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode();
|
private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode();
|
||||||
|
|
||||||
@Column(name = "authenticator_uri")
|
@Column(name = "authenticator_uri")
|
||||||
private String authenticatorUri;
|
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() {
|
public User() {
|
||||||
}
|
}
|
||||||
@ -151,7 +157,35 @@ public class User
|
|||||||
this.authenticatorUri = authenticatorUri;
|
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() {
|
public String toString() {
|
||||||
return "User{" +
|
return "User{" +
|
||||||
"firstname='" + firstname + '\'' +
|
"firstname='" + firstname + '\'' +
|
||||||
|
@ -22,6 +22,7 @@ import com.wisemapping.exceptions.EmailNotExistsException;
|
|||||||
import com.wisemapping.exceptions.WiseMappingException;
|
import com.wisemapping.exceptions.WiseMappingException;
|
||||||
import com.wisemapping.model.AuthenticationType;
|
import com.wisemapping.model.AuthenticationType;
|
||||||
import com.wisemapping.model.User;
|
import com.wisemapping.model.User;
|
||||||
|
import com.wisemapping.rest.model.RestOath2CallbackResponse;
|
||||||
import com.wisemapping.rest.model.RestUserRegistration;
|
import com.wisemapping.rest.model.RestUserRegistration;
|
||||||
import com.wisemapping.service.*;
|
import com.wisemapping.service.*;
|
||||||
import com.wisemapping.validator.Messages;
|
import com.wisemapping.validator.Messages;
|
||||||
@ -33,100 +34,143 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.HttpStatus;
|
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.stereotype.Controller;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
public class UserController extends BaseController {
|
public class UserController extends BaseController {
|
||||||
@Qualifier("userService")
|
@Qualifier("userService")
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RecaptchaService captchaService;
|
private RecaptchaService captchaService;
|
||||||
|
|
||||||
@Value("${google.recaptcha2.enabled}")
|
@Qualifier("authenticationManager")
|
||||||
private Boolean recatchaEnabled;
|
@Autowired
|
||||||
|
private AuthenticationManager authManager;
|
||||||
|
|
||||||
@Value("${accounts.exclusion.domain:''}")
|
@Value("${google.recaptcha2.enabled}")
|
||||||
private String domainBanExclusion;
|
private Boolean recatchaEnabled;
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger();
|
@Value("${accounts.exclusion.domain:''}")
|
||||||
private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
|
private String domainBanExclusion;
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.POST, value = "/users", produces = {"application/json"})
|
private static final Logger logger = LogManager.getLogger();
|
||||||
@ResponseStatus(value = HttpStatus.CREATED)
|
private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
|
||||||
public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws WiseMappingException, BindException {
|
|
||||||
logger.debug("Register new user:" + registration.getEmail());
|
|
||||||
|
|
||||||
// If tomcat is behind a reverse proxy, ip needs to be found in other header.
|
@RequestMapping(method = RequestMethod.POST, value = "/users", produces = { "application/json" })
|
||||||
String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER);
|
@ResponseStatus(value = HttpStatus.CREATED)
|
||||||
if (remoteIp == null || remoteIp.isEmpty()) {
|
public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request,
|
||||||
remoteIp = request.getRemoteAddr();
|
@NotNull HttpServletResponse response) throws WiseMappingException, BindException {
|
||||||
}
|
logger.debug("Register new user:" + registration.getEmail());
|
||||||
logger.debug("Remote address" + remoteIp);
|
|
||||||
|
|
||||||
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();
|
verify(registration, remoteIp);
|
||||||
user.setEmail(registration.getEmail().trim());
|
|
||||||
user.setFirstname(registration.getFirstname());
|
|
||||||
user.setLastname(registration.getLastname());
|
|
||||||
user.setPassword(registration.getPassword());
|
|
||||||
|
|
||||||
user.setAuthenticationType(AuthenticationType.DATABASE);
|
final User user = new User();
|
||||||
userService.createUser(user, false, true);
|
user.setEmail(registration.getEmail().trim());
|
||||||
response.setHeader("Location", "/service/users/" + user.getId());
|
user.setFirstname(registration.getFirstname());
|
||||||
}
|
user.setLastname(registration.getLastname());
|
||||||
|
user.setPassword(registration.getPassword());
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = {"application/json"})
|
user.setAuthenticationType(AuthenticationType.DATABASE);
|
||||||
@ResponseStatus(value = HttpStatus.OK)
|
userService.createUser(user, false, true);
|
||||||
public void resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException {
|
response.setHeader("Location", "/service/users/" + user.getId());
|
||||||
try {
|
}
|
||||||
userService.resetPassword(email);
|
|
||||||
} catch (InvalidUserEmailException e) {
|
|
||||||
throw new EmailNotExistsException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress) throws BindException {
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
final BindException errors = new RegistrationException(registration, "registration");
|
@RequestMapping(method = RequestMethod.POST, value = "/users/googleCallback", produces = { "application/json" })
|
||||||
final UserValidator validator = new UserValidator();
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
validator.setUserService(userService);
|
public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
|
||||||
validator.validate(registration, errors);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// If captcha is enabled, generate it ...
|
@RequestMapping(method = RequestMethod.PUT, value = "/users/confirmAccountSync", produces = { "application/json" })
|
||||||
if (recatchaEnabled) {
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
final String recaptcha = registration.getRecaptcha();
|
public void confirmAccountSync(@NotNull @RequestParam String email, @NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
|
||||||
if (recaptcha != null) {
|
userService.confirmAccountSync(email, code);
|
||||||
final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha);
|
doLogin(request, email);
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.hasErrors()) {
|
@RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = { "application/json" })
|
||||||
throw errors;
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
}
|
public void resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException {
|
||||||
|
try {
|
||||||
|
userService.resetPassword(email);
|
||||||
|
} catch (InvalidUserEmailException e) {
|
||||||
|
throw new EmailNotExistsException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Is excluded ?.
|
private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress)
|
||||||
final List<String> excludedDomains = Arrays.asList(domainBanExclusion.split(","));
|
throws BindException {
|
||||||
final String emailDomain = registration.getEmail().split("@")[1];
|
|
||||||
if (excludedDomains.contains(emailDomain)) {
|
final BindException errors = new RegistrationException(registration, "registration");
|
||||||
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);
|
final UserValidator validator = new UserValidator();
|
||||||
}
|
validator.setUserService(userService);
|
||||||
}
|
validator.validate(registration, 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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.hasErrors()) {
|
||||||
|
throw errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is excluded ?.
|
||||||
|
final List<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -23,7 +23,6 @@ import com.wisemapping.model.User;
|
|||||||
import com.wisemapping.service.UserService;
|
import com.wisemapping.service.UserService;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ public interface UserService {
|
|||||||
|
|
||||||
User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException;
|
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);
|
void changePassword(@NotNull User user);
|
||||||
|
|
||||||
User getUserBy(String email);
|
User getUserBy(String email);
|
||||||
|
@ -23,6 +23,8 @@ import com.wisemapping.exceptions.InvalidMindmapException;
|
|||||||
import com.wisemapping.exceptions.WiseMappingException;
|
import com.wisemapping.exceptions.WiseMappingException;
|
||||||
import com.wisemapping.mail.NotificationService;
|
import com.wisemapping.mail.NotificationService;
|
||||||
import com.wisemapping.model.*;
|
import com.wisemapping.model.*;
|
||||||
|
import com.wisemapping.service.google.GoogleAccountBasicData;
|
||||||
|
import com.wisemapping.service.google.GoogleService;
|
||||||
import com.wisemapping.util.VelocityEngineUtils;
|
import com.wisemapping.util.VelocityEngineUtils;
|
||||||
import com.wisemapping.util.VelocityEngineWrapper;
|
import com.wisemapping.util.VelocityEngineWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -39,7 +41,7 @@ public class UserServiceImpl
|
|||||||
private NotificationService notificationService;
|
private NotificationService notificationService;
|
||||||
private MessageSource messageSource;
|
private MessageSource messageSource;
|
||||||
private VelocityEngineWrapper velocityEngineWrapper;
|
private VelocityEngineWrapper velocityEngineWrapper;
|
||||||
|
private GoogleService googleService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activateAccount(long code)
|
public void activateAccount(long code)
|
||||||
@ -147,6 +149,55 @@ public class UserServiceImpl
|
|||||||
return user;
|
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 {
|
public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
|
||||||
//To change body of created methods use File | Settings | File Templates.
|
//To change body of created methods use File | Settings | File Templates.
|
||||||
final Locale locale = LocaleContextHolder.getLocale();
|
final Locale locale = LocaleContextHolder.getLocale();
|
||||||
@ -209,7 +260,11 @@ public class UserServiceImpl
|
|||||||
this.velocityEngineWrapper = velocityEngineWrapper;
|
this.velocityEngineWrapper = velocityEngineWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setGoogleService(GoogleService googleService) {
|
||||||
|
this.googleService = googleService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public User getCasUserBy(String uid) {
|
public User getCasUserBy(String uid) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return null;
|
return null;
|
||||||
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<String, String> getHeaders(String token) {
|
||||||
|
Map<String, String> headers = new HashMap<String, String>();
|
||||||
|
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<String, String> getOptinConfirmBody(String code) {
|
||||||
|
Map<String, String> result = new HashMap<String, String>();
|
||||||
|
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<String, String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<String, String> headers,
|
||||||
|
String jsonPayload,
|
||||||
|
Map<String, String> 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<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
|
||||||
|
Set<String> 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<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.wisemapping.service.http;
|
||||||
|
|
||||||
|
public enum HttpMethod {
|
||||||
|
POST, GET, DELETE, PUT
|
||||||
|
}
|
@ -39,6 +39,11 @@ public class UsersController {
|
|||||||
return new ModelAndView("forgot-password");
|
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)
|
@RequestMapping(value = "registration", method = RequestMethod.GET)
|
||||||
public ModelAndView showRegistrationPage() {
|
public ModelAndView showRegistrationPage() {
|
||||||
return new ModelAndView("registration");
|
return new ModelAndView("registration");
|
||||||
|
@ -139,6 +139,17 @@ security.ldap.firstName.attribute=givenName
|
|||||||
# Coma separated list of domains and emails ban
|
# Coma separated list of domains and emails ban
|
||||||
#accounts.exclusion.domain=
|
#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
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<definition name="login" template="/jsp/reactInclude.jsp"/>
|
<definition name="login" template="/jsp/reactInclude.jsp"/>
|
||||||
<definition name="registration" template="/jsp/reactInclude.jsp"/>
|
<definition name="registration" template="/jsp/reactInclude.jsp"/>
|
||||||
|
<definition name="registration-google" template="/jsp/reactInclude.jsp"/>
|
||||||
<definition name="forgot-password" template="/jsp/reactInclude.jsp"/>
|
<definition name="forgot-password" template="/jsp/reactInclude.jsp"/>
|
||||||
<definition name="mindmapList" template="/jsp/reactInclude.jsp"/>
|
<definition name="mindmapList" template="/jsp/reactInclude.jsp"/>
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
<bean id="passwordEncoder" class="com.wisemapping.security.DefaultPasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
|
<bean id="passwordEncoder" class="com.wisemapping.security.DefaultPasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
|
||||||
|
|
||||||
<sec:authentication-manager alias="authenticationManager">
|
<sec:authentication-manager alias="authenticationManager">
|
||||||
<sec:authentication-provider ref="dbAuthenticationProvider"/>
|
<sec:authentication-provider ref="dbAuthenticationProvider" />
|
||||||
|
<sec:authentication-provider ref="googleAuthenticationProvider" />
|
||||||
<sec:authentication-provider user-service-ref="userDetailsService"/>
|
<sec:authentication-provider user-service-ref="userDetailsService"/>
|
||||||
</sec:authentication-manager>
|
</sec:authentication-manager>
|
||||||
|
|
||||||
@ -19,4 +20,7 @@
|
|||||||
<property name="userDetailsService" ref="userDetailsService"/>
|
<property name="userDetailsService" ref="userDetailsService"/>
|
||||||
<property name="encoder" ref="passwordEncoder"/>
|
<property name="encoder" ref="passwordEncoder"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
<bean id="googleAuthenticationProvider" class="com.wisemapping.security.GoogleAuthenticationProvider">
|
||||||
|
<property name="userDetailsService" ref="userDetailsService"/>
|
||||||
|
</bean>
|
||||||
</beans>
|
</beans>
|
@ -3,12 +3,12 @@
|
|||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
xmlns:sec="http://www.springframework.org/schema/security"
|
xmlns:sec="http://www.springframework.org/schema/security"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
http://www.springframework.org/schema/security
|
http://www.springframework.org/schema/security
|
||||||
http://www.springframework.org/schema/security/spring-security.xsd">
|
http://www.springframework.org/schema/security/spring-security.xsd">
|
||||||
|
|
||||||
<bean id="custom-firewall" class="org.springframework.security.web.firewall.StrictHttpFirewall">
|
<bean id="custom-firewall" class="org.springframework.security.web.firewall.StrictHttpFirewall">
|
||||||
<property name="allowSemicolon" value="true"/>
|
<property name="allowSemicolon" value="true"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
<sec:intercept-url pattern="/service/users/" method="POST" access="permitAll"/>
|
<sec:intercept-url pattern="/service/users/" method="POST" access="permitAll"/>
|
||||||
<sec:intercept-url pattern="/service/users/resetPassword" method="PUT" access="permitAll"/>
|
<sec:intercept-url pattern="/service/users/resetPassword" method="PUT" access="permitAll"/>
|
||||||
|
<sec:intercept-url pattern="/service/users/googleCallback" method="POST" access="permitAll"/>
|
||||||
|
<sec:intercept-url pattern="/service/users/confirmAccountSync" method="PUT" access="permitAll"/>
|
||||||
|
|
||||||
<sec:intercept-url pattern="/service/admin/users/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
|
<sec:intercept-url pattern="/service/admin/users/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
|
||||||
<sec:intercept-url pattern="/service/admin/database/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
|
<sec:intercept-url pattern="/service/admin/database/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
|
||||||
@ -47,6 +49,7 @@
|
|||||||
<sec:intercept-url pattern="/c/login" access="permitAll"/>
|
<sec:intercept-url pattern="/c/login" access="permitAll"/>
|
||||||
<sec:intercept-url pattern="/c/registration" access="hasRole('ANONYMOUS')"/>
|
<sec:intercept-url pattern="/c/registration" access="hasRole('ANONYMOUS')"/>
|
||||||
<sec:intercept-url pattern="/c/registration-success" access="hasRole('ANONYMOUS')"/>
|
<sec:intercept-url pattern="/c/registration-success" access="hasRole('ANONYMOUS')"/>
|
||||||
|
<sec:intercept-url pattern="/c/registration-google" access="permitAll"/>
|
||||||
<sec:intercept-url pattern="/c/forgot-password" access="hasRole('ANONYMOUS')"/>
|
<sec:intercept-url pattern="/c/forgot-password" access="hasRole('ANONYMOUS')"/>
|
||||||
<sec:intercept-url pattern="/c/forgot-password-success" access="hasRole('ANONYMOUS')"/>
|
<sec:intercept-url pattern="/c/forgot-password-success" access="hasRole('ANONYMOUS')"/>
|
||||||
|
|
||||||
|
@ -18,12 +18,25 @@
|
|||||||
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
|
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="httpInvoker" class="com.wisemapping.service.http.HttpInvoker">
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="googleService" class="com.wisemapping.service.google.GoogleService">
|
||||||
|
<property name="httpInvoker" ref="httpInvoker"/>
|
||||||
|
<property name="optinConfirmUrl" value="${google.oauth2.confirmUrl}"/>
|
||||||
|
<property name="accountBasicDataUrl" value="${google.oauth2.userinfoUrl}"/>
|
||||||
|
<property name="clientId" value="${google.oauth2.clientId}"/>
|
||||||
|
<property name="clientSecret" value="${google.oauth2.clientSecret}"/>
|
||||||
|
<property name="callbackUrl" value="${google.oauth2.callbackUrl}"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="userServiceTarget" class="com.wisemapping.service.UserServiceImpl">
|
<bean id="userServiceTarget" class="com.wisemapping.service.UserServiceImpl">
|
||||||
<property name="userManager" ref="userManager"/>
|
<property name="userManager" ref="userManager"/>
|
||||||
<property name="mindmapService" ref="mindMapServiceTarget"/>
|
<property name="mindmapService" ref="mindMapServiceTarget"/>
|
||||||
<property name="notificationService" ref="notificationService"/>
|
<property name="notificationService" ref="notificationService"/>
|
||||||
<property name="messageSource" ref="messageSource"/>
|
<property name="messageSource" ref="messageSource"/>
|
||||||
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
|
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
|
||||||
|
<property name="googleService" ref="googleService"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
|
<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
analyticsAccount: '${requestScope['google.analytics.account']}',
|
analyticsAccount: '${requestScope['google.analytics.account']}',
|
||||||
clientType: 'rest',
|
clientType: 'rest',
|
||||||
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},
|
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},
|
||||||
recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}'
|
recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}',
|
||||||
|
googleOauth2Url: '${requestScope['google.oauth2.url']}'
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user