Improve OAuth error handling.

main
Paulo Gustavo Veiga 2023-04-01 13:33:15 -07:00
parent 7918bc20d0
commit 6acde03327
20 changed files with 333 additions and 289 deletions

View File

@ -13,8 +13,8 @@
</parent> </parent>
<properties> <properties>
<org.springframework.version>5.3.24</org.springframework.version> <org.springframework.version>5.3.26</org.springframework.version>
<org.springframework.addons>5.7.3</org.springframework.addons> <org.springframework.addons>5.7.7</org.springframework.addons>
<hibernate.version>5.6.12.Final</hibernate.version> <hibernate.version>5.6.12.Final</hibernate.version>
<hibernate-validator.version>6.0.21.Final</hibernate-validator.version> <hibernate-validator.version>6.0.21.Final</hibernate-validator.version>
<spring-security-taglibs.version>5.6.1</spring-security-taglibs.version> <spring-security-taglibs.version>5.6.1</spring-security-taglibs.version>
@ -36,7 +36,7 @@
<dependency> <dependency>
<groupId>org.testng</groupId> <groupId>org.testng</groupId>
<artifactId>testng</artifactId> <artifactId>testng</artifactId>
<version>6.9.8</version> <version>7.7.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -54,7 +54,7 @@
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<version>42.5.1</version> <version>42.5.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
@ -358,7 +358,7 @@
<configuration> <configuration>
<driver>com.mysql.jdbc.Driver</driver> <driver>com.mysql.jdbc.Driver</driver>
<username>root</username> <username>root</username>
<password></password> <password/>
<url>jdbc:mysql://127.0.0.1:3306/?useUnicode=true&amp;characterEncoding=UTF-8</url> <url>jdbc:mysql://127.0.0.1:3306/?useUnicode=true&amp;characterEncoding=UTF-8</url>
<autocommit>false</autocommit> <autocommit>false</autocommit>
<srcFiles> <srcFiles>
@ -556,11 +556,9 @@
<useTestClasspath>true</useTestClasspath> <useTestClasspath>true</useTestClasspath>
<useTestScope>true</useTestScope> <useTestScope>true</useTestScope>
<scanIntervalSeconds>0</scanIntervalSeconds> <scanIntervalSeconds>0</scanIntervalSeconds>
<daemon>true</daemon>
<waitForChild>false</waitForChild> <waitForChild>false</waitForChild>
<maxStartupLines>200</maxStartupLines> <maxStartupLines>200</maxStartupLines>
<jvmArgs>${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory} <jvmArgs>-Ddatabase.base.url=${project.build.directory} -Djetty.port=8080
-Djetty.port=8080
</jvmArgs> </jvmArgs>
</configuration> </configuration>
</execution> </execution>
@ -570,9 +568,6 @@
<goals> <goals>
<goal>stop</goal> <goal>stop</goal>
</goals> </goals>
<configuration>
<stopWait>1</stopWait>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>

View File

@ -1,38 +0,0 @@
/*
* 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.exceptions;
import org.jetbrains.annotations.NotNull;
public class MultipleSessionsOpenException
extends ClientException
{
private static final String MSG_KEY = "MINDMAP_OUTDATED_BY_YOU";
public MultipleSessionsOpenException(@NotNull String techInfo)
{
super(techInfo,Severity.INFO);
}
@NotNull
@Override
protected String getMsgBundleKey() {
return MSG_KEY;
}
}

View File

@ -0,0 +1,13 @@
package com.wisemapping.exceptions;
import com.wisemapping.service.google.http.HttpInvokerException;
import javax.validation.constraints.NotNull;
public class OAuthAuthenticationException extends WiseMappingException {
public OAuthAuthenticationException(@NotNull HttpInvokerException exception) {
super(exception.getMessage());
}
}

View File

@ -18,6 +18,7 @@
package com.wisemapping.rest; package com.wisemapping.rest;
import com.wisemapping.exceptions.ClientException; import com.wisemapping.exceptions.ClientException;
import com.wisemapping.exceptions.OAuthAuthenticationException;
import com.wisemapping.exceptions.Severity; import com.wisemapping.exceptions.Severity;
import com.wisemapping.mail.NotificationService; import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.User; import com.wisemapping.model.User;
@ -38,6 +39,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
import java.util.Locale; import java.util.Locale;
@ -95,6 +97,19 @@ public class BaseController {
return new RestErrors(ex.getMessage(messageSource, locale), ex.getSeverity(), ex.getTechInfo()); return new RestErrors(ex.getMessage(messageSource, locale), ex.getSeverity(), ex.getTechInfo());
} }
@ExceptionHandler(OAuthAuthenticationException.class)
@ResponseBody
public OAuthAuthenticationException handleOAuthErrors(@NotNull OAuthAuthenticationException ex, HttpServletResponse response) {
// @todo: Further research needed for this error. No clear why this happens.
// Caused by: com.wisemapping.service.http.HttpInvokerException: error invoking https://oauth2.googleapis.com/token, response: {
// "error": "invalid_grant",
// "error_description": "Bad Request"
//}, status: 400
//
response.setStatus(response.getStatus());
return ex;
}
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody @ResponseBody

View File

@ -40,7 +40,7 @@ import javax.servlet.http.HttpSession;
@Controller @Controller
@CrossOrigin @CrossOrigin
public class Oauth2Controller extends BaseController { public class OAuth2Controller extends BaseController {
@Qualifier("userService") @Qualifier("userService")
@Autowired @Autowired
private UserService userService; private UserService userService;
@ -68,7 +68,7 @@ public class Oauth2Controller extends BaseController {
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException { public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
User user = userService.createUserFromGoogle(code); User user = userService.createUserFromGoogle(code);
if (user.getGoogleSync() != null && user.getGoogleSync().booleanValue()) { if (user.getGoogleSync() != null && user.getGoogleSync()) {
doLogin(request, user.getEmail()); doLogin(request, user.getEmail());
} }
RestOath2CallbackResponse response = new RestOath2CallbackResponse(); RestOath2CallbackResponse response = new RestOath2CallbackResponse();

View File

@ -20,6 +20,7 @@ package com.wisemapping.service;
import com.wisemapping.dao.UserManager; import com.wisemapping.dao.UserManager;
import com.wisemapping.exceptions.InvalidMindmapException; import com.wisemapping.exceptions.InvalidMindmapException;
import com.wisemapping.exceptions.OAuthAuthenticationException;
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.*;
@ -27,6 +28,7 @@ import com.wisemapping.rest.model.RestResetPasswordAction;
import com.wisemapping.rest.model.RestResetPasswordResponse; import com.wisemapping.rest.model.RestResetPasswordResponse;
import com.wisemapping.service.google.GoogleAccountBasicData; import com.wisemapping.service.google.GoogleAccountBasicData;
import com.wisemapping.service.google.GoogleService; import com.wisemapping.service.google.GoogleService;
import com.wisemapping.service.google.http.HttpInvokerException;
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;
@ -43,7 +45,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; private GoogleService googleService;
@Override @Override
public void activateAccount(long code) public void activateAccount(long code)
@ -64,11 +66,11 @@ public class UserServiceImpl
throws InvalidUserEmailException, InvalidAuthSchemaException { throws InvalidUserEmailException, InvalidAuthSchemaException {
final User user = userManager.getUserBy(email); final User user = userManager.getUserBy(email);
if (user != null) { if (user != null) {
RestResetPasswordResponse response = new RestResetPasswordResponse(); RestResetPasswordResponse response = new RestResetPasswordResponse();
if (user.getAuthenticationType().equals(AuthenticationType.GOOGLE_OAUTH2)) { if (user.getAuthenticationType().equals(AuthenticationType.GOOGLE_OAUTH2)) {
response.setAction(RestResetPasswordAction.OAUTH2_USER); response.setAction(RestResetPasswordAction.OAUTH2_USER);
return response; return response;
} }
if (user.getAuthenticationType() != AuthenticationType.DATABASE) { if (user.getAuthenticationType() != AuthenticationType.DATABASE) {
throw new InvalidAuthSchemaException("Could not change password for " + user.getAuthenticationType().getCode()); throw new InvalidAuthSchemaException("Could not change password for " + user.getAuthenticationType().getCode());
@ -82,8 +84,8 @@ public class UserServiceImpl
// Send an email with the new temporal password ... // Send an email with the new temporal password ...
notificationService.resetPassword(user, password); notificationService.resetPassword(user, password);
response.setAction(RestResetPasswordAction.EMAIL_SENT); response.setAction(RestResetPasswordAction.EMAIL_SENT);
return response; return response;
} else { } else {
throw new InvalidUserEmailException("The email '" + email + "' does not exists."); throw new InvalidUserEmailException("The email '" + email + "' does not exists.");
} }
@ -161,51 +163,53 @@ public class UserServiceImpl
@NotNull @NotNull
public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException { public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
try { GoogleAccountBasicData data;
GoogleAccountBasicData data = googleService.processCallback(callbackCode); try {
User existingUser = userManager.getUserBy(data.getEmail()); data = googleService.processCallback(callbackCode);
if (existingUser == null) { } catch (HttpInvokerException e) {
User newUser = new User(); throw new OAuthAuthenticationException(e);
// new registrations from google starts synched }
newUser.setGoogleSync(true);
newUser.setEmail(data.getEmail()); User existingUser = userManager.getUserBy(data.getEmail());
newUser.setFirstname(data.getName()); if (existingUser == null) {
newUser.setLastname(data.getLastName()); User newUser = new User();
newUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2); // new registrations from google starts synched
newUser.setGoogleToken(data.getAccessToken()); newUser.setGoogleSync(true);
existingUser = this.createUser(newUser, false, true); newUser.setEmail(data.getEmail());
} else { newUser.setFirstname(data.getName());
// user exists and doesnt have confirmed account linking, I must wait for confirmation newUser.setLastname(data.getLastName());
if (existingUser.getGoogleSync() == null) { newUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2);
existingUser.setGoogleSync(false); newUser.setGoogleToken(data.getAccessToken());
existingUser.setSyncCode(callbackCode); existingUser = this.createUser(newUser, false, true);
existingUser.setGoogleToken(data.getAccessToken()); } else {
userManager.updateUser(existingUser); // user exists and doesn't 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;
}
return existingUser;
} catch (Exception e) {
throw new WiseMappingException("Cant create user", e);
}
} }
public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException { public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
User existingUser = userManager.getUserBy(email); User existingUser = userManager.getUserBy(email);
// additional security check // additional security check
if (existingUser == null || !existingUser.getSyncCode().equals(code)) { if (existingUser == null || !existingUser.getSyncCode().equals(code)) {
throw new WiseMappingException("User not found / incorrect code"); throw new WiseMappingException("User not found / incorrect code");
} }
existingUser.setGoogleSync(true); existingUser.setGoogleSync(true);
existingUser.setSyncCode(null); existingUser.setSyncCode(null);
// user will not be able to login again with usr/pwd schema // user will not be able to login again with usr/pwd schema
existingUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2); existingUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2);
existingUser.setPassword(""); existingUser.setPassword("");
userManager.updateUser(existingUser); userManager.updateUser(existingUser);
return existingUser;
}
return existingUser;
}
public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException { public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
@ -270,11 +274,11 @@ public class UserServiceImpl
this.velocityEngineWrapper = velocityEngineWrapper; this.velocityEngineWrapper = velocityEngineWrapper;
} }
public void setGoogleService(GoogleService googleService) { public void setGoogleService(GoogleService googleService) {
this.googleService = googleService; this.googleService = googleService;
} }
@Override @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;

View File

@ -1,17 +1,32 @@
/*
* 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.service.google; package com.wisemapping.service.google;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service; 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.fasterxml.jackson.databind.JsonNode;
import com.wisemapping.service.http.HttpInvoker; import com.wisemapping.service.google.http.HttpInvoker;
import com.wisemapping.service.http.HttpInvokerContentType; import com.wisemapping.service.google.http.HttpInvokerContentType;
import com.wisemapping.service.http.HttpInvokerException; import com.wisemapping.service.google.http.HttpInvokerException;
import com.wisemapping.service.http.HttpMethod;
@Service @Service
public class GoogleService { public class GoogleService {
@ -83,10 +98,10 @@ public class GoogleService {
return result; return result;
} }
public GoogleAccountBasicData processCallback(String code) public GoogleAccountBasicData processCallback(final String code)
throws HttpInvokerException, JsonMappingException, JsonProcessingException { throws HttpInvokerException {
Map<String, String> body = this.getOptinConfirmBody(code); Map<String, String> body = this.getOptinConfirmBody(code);
JsonNode optinConfirmResponse = httpInvoker.invoke( JsonNode optionConfirmResponse = httpInvoker.invoke(
optinConfirmUrl, optinConfirmUrl,
HttpInvokerContentType.FORM_ENCODED, HttpInvokerContentType.FORM_ENCODED,
HttpMethod.POST, HttpMethod.POST,
@ -94,8 +109,8 @@ public class GoogleService {
null, null,
body); body);
String accessToken = getNodeAsString(optinConfirmResponse, "access_token"); final String accessToken = getNodeAsString(optionConfirmResponse, "access_token");
String refreshToken = getNodeAsString(optinConfirmResponse, "refresh_token"); final String refreshToken = getNodeAsString(optionConfirmResponse, "refresh_token");
GoogleAccountBasicData data = this.getAccountBasicData(accessToken); GoogleAccountBasicData data = this.getAccountBasicData(accessToken);
data.setAccessToken(accessToken); data.setAccessToken(accessToken);

View File

@ -0,0 +1,167 @@
/*
* 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.service.google.http;
import java.nio.charset.StandardCharsets;
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.HttpStatus;
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.http.HttpMethod;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.validation.constraints.NotNull;
@Service
public class HttpInvoker {
protected static Logger logger = LogManager.getLogger(HttpInvoker.class);
private final ObjectMapper mapper = new ObjectMapper();
public HttpInvoker() {
super();
}
public JsonNode invoke(
@NotNull 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 httpRequest;
// build request
if (method.equals(HttpMethod.POST))
httpRequest = new HttpPost(url);
else if (method.equals(HttpMethod.PUT))
httpRequest = new HttpPut(url);
else if (method.equals(HttpMethod.GET))
httpRequest = new HttpGet(url);
else if (method.equals(HttpMethod.DELETE))
httpRequest = new HttpDelete(url);
else
throw new HttpInvokerException("Method " + method + " not supported 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, StandardCharsets.UTF_8);
((HttpEntityEnclosingRequestBase) httpRequest).setEntity(entity);
}
if (requestContentType.equals(HttpInvokerContentType.FORM_ENCODED)) {
List<NameValuePair> nameValuePairs = new ArrayList<>();
Set<String> keys = formData.keySet();
for (String key : keys) {
nameValuePairs.add(new BasicNameValuePair(key, formData.get(key).toString()));
}
entity = new UrlEncodedFormEntity(nameValuePairs);
((HttpEntityEnclosingRequestBase) httpRequest).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) {
httpRequest.setHeader(key, headers.get(key));
}
}
if (requestContentType != null)
httpRequest.setHeader("Content-Type", requestContentType.getHttpContentType());
// invoke
CloseableHttpResponse response = httpClient.execute(httpRequest);
// 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(), HttpStatus.SC_BAD_REQUEST);
}
httpRequest.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);
}
}
}

View File

@ -1,4 +1,4 @@
package com.wisemapping.service.http; package com.wisemapping.service.google.http;
public enum HttpInvokerContentType { public enum HttpInvokerContentType {
@ -7,7 +7,7 @@ public enum HttpInvokerContentType {
private String httpContentType; private String httpContentType;
private HttpInvokerContentType(String type) { HttpInvokerContentType(String type) {
this.httpContentType = type; this.httpContentType = type;
} }

View File

@ -0,0 +1,44 @@
/*
* 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.service.google.http;
import org.apache.http.HttpStatus;
public class HttpInvokerException extends Exception {
private final int statusCode;
public HttpInvokerException(String message) {
this(message, HttpStatus.SC_INTERNAL_SERVER_ERROR, null);
}
public HttpInvokerException(String message, int statusCode) {
this(message, statusCode, null);
}
public HttpInvokerException(String message, Throwable cause) {
this(message, HttpStatus.SC_INTERNAL_SERVER_ERROR, cause);
}
public HttpInvokerException(String message, int statusCode, Throwable cause) {
super(message, cause);
this.statusCode = statusCode;
}
public int getStatusCode() {
return statusCode;
}
}

View File

@ -1,147 +0,0 @@
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);
}
}
}

View File

@ -1,13 +0,0 @@
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);
}
}

View File

@ -1,5 +0,0 @@
package com.wisemapping.service.http;
public enum HttpMethod {
POST, GET, DELETE, PUT
}

View File

@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=Deine Zugriffsrechte auf diese Mindmap sind zur
MAP_CAN_NOT_BE_FOUND=Die Mindmap kann nicht gefunden werden. Sie muss gelöscht worden sein. MAP_CAN_NOT_BE_FOUND=Die Mindmap kann nicht gefunden werden. Sie muss gelöscht worden sein.
LABEL_CAN_NOT_BE_FOUND=Das Label kann nicht gefunden werden. Es muss gelöscht worden sein. LABEL_CAN_NOT_BE_FOUND=Das Label kann nicht gefunden werden. Es muss gelöscht worden sein.
MINDMAP_TIMESTAMP_OUTDATED=Es ist nicht möglich, deine Änderungen zu speichern, da deine Mindmap von {0} geändert wurde. Aktualisiere die Seite und versuche es erneut. MINDMAP_TIMESTAMP_OUTDATED=Es ist nicht möglich, deine Änderungen zu speichern, da deine Mindmap von {0} geändert wurde. Aktualisiere die Seite und versuche es erneut.
MINDMAP_OUTDATED_BY_YOU=Deine Änderungen können nicht gespeichert werden, da die Mindmap veraltet ist. Hast du mehrere Tabs geöffnet? Aktualisiere die Seite und versuche es erneut.
MINDMAP_LOCKED=Mindmap wird bearbeitet von {0} <{1}>. Die Mindmap wird im schreibgeschützten Modus geöffnet. MINDMAP_LOCKED=Mindmap wird bearbeitet von {0} <{1}>. Die Mindmap wird im schreibgeschützten Modus geöffnet.
MINDMAP_IS_LOCKED=Mindmap ist für die Bearbeitung gesperrt. MINDMAP_IS_LOCKED=Mindmap ist für die Bearbeitung gesperrt.
# Confirmed # Confirmed

View File

@ -42,7 +42,6 @@ ACCESS_HAS_BEEN_REVOKED=Your access permissions to this map has been revoked. Co
MAP_CAN_NOT_BE_FOUND=The map can not be found. It must have been deleted. MAP_CAN_NOT_BE_FOUND=The map can not be found. It must have been deleted.
LABEL_CAN_NOT_BE_FOUND=The label can not be found. It must have been deleted. LABEL_CAN_NOT_BE_FOUND=The label can not be found. It must have been deleted.
MINDMAP_TIMESTAMP_OUTDATED=It's not possible to save your changes because your mind map has been modified by ''{0}''. Refresh the page and try again. MINDMAP_TIMESTAMP_OUTDATED=It's not possible to save your changes because your mind map has been modified by ''{0}''. Refresh the page and try again.
MINDMAP_OUTDATED_BY_YOU=It's not possible to save your changes because map is out of date. Do you have multiple tabs opened ?. Refresh the page and try again.
MINDMAP_LOCKED=Map is being edited by {0} <{1}>. Map is opened in read only mode. MINDMAP_LOCKED=Map is being edited by {0} <{1}>. Map is opened in read only mode.
MINDMAP_IS_LOCKED=Min map is locked for edition. MINDMAP_IS_LOCKED=Min map is locked for edition.
# Confirmed # Confirmed

View File

@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED = Los permisos de acceso al mapa han sido revocados. No
MAP_CAN_NOT_BE_FOUND = No se puede encontrar el mapa. Debe haber sido borrado. MAP_CAN_NOT_BE_FOUND = No se puede encontrar el mapa. Debe haber sido borrado.
LABEL_CAN_NOT_BE_FOUND = No se puede encontrar la etiqueta. Debe haber sido borrado. LABEL_CAN_NOT_BE_FOUND = No se puede encontrar la etiqueta. Debe haber sido borrado.
MINDMAP_TIMESTAMP_OUTDATED = No es posible grabar sus cambios por que el mapa ha sido modificado por {0}''. Refresque la pagina y intentelo nuevamente. MINDMAP_TIMESTAMP_OUTDATED = No es posible grabar sus cambios por que el mapa ha sido modificado por {0}''. Refresque la pagina y intentelo nuevamente.
MINDMAP_OUTDATED_BY_YOU = No es posible guardar los cambios porque el mapa no está actualizado. ¿Tienes varias pestañas abiertas?. Actualice la página y vuelva a intentarlo.
MINDMAP_LOCKED = El mapa esta siendo editado por {0} <{1}>. Mapa sera abierto en modo lectura. MINDMAP_LOCKED = El mapa esta siendo editado por {0} <{1}>. Mapa sera abierto en modo lectura.
MINDMAP_IS_LOCKED = Mindmap está bloqueado para la edición. MINDMAP_IS_LOCKED = Mindmap está bloqueado para la edición.
# Confirmed # Confirmed

View File

@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=Vos autorisations d'accès à cette carte ont été rév
MAP_CAN_NOT_BE_FOUND=La carte est introuvable. Il a dû être supprimé. MAP_CAN_NOT_BE_FOUND=La carte est introuvable. Il a dû être supprimé.
LABEL_CAN_NOT_BE_FOUND=L'étiquette est introuvable. Il a dû être supprimé. LABEL_CAN_NOT_BE_FOUND=L'étiquette est introuvable. Il a dû être supprimé.
MINDMAP_TIMESTAMP_OUTDATED=Il n''est pas possible d''enregistrer vos modifications car votre mindmap a été modifiée par ''{0}''. Actualisez la page et réessayez. MINDMAP_TIMESTAMP_OUTDATED=Il n''est pas possible d''enregistrer vos modifications car votre mindmap a été modifiée par ''{0}''. Actualisez la page et réessayez.
MINDMAP_OUTDATED_BY_YOU=Il n'est pas possible d'enregistrer vos modifications car la carte n'est pas à jour. Avez-vous plusieurs onglets ouverts ?. Actualisez la page et réessayez.
MINDMAP_LOCKED=La carte est en cours de modification par {0} <{1}>. La carte est ouverte en mode lecture seule. MINDMAP_LOCKED=La carte est en cours de modification par {0} <{1}>. La carte est ouverte en mode lecture seule.
MINDMAP_IS_LOCKED=Mindmap est verrouillé pour l'édition. MINDMAP_IS_LOCKED=Mindmap est verrouillé pour l'édition.
# Confirmed # Confirmed

View File

@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=Ваш доступ к карте был отозван.
MAP_CAN_NOT_BE_FOUND=Карта не найдена. Вероятно, она удалена. MAP_CAN_NOT_BE_FOUND=Карта не найдена. Вероятно, она удалена.
LABEL_CAN_NOT_BE_FOUND=Метка не найдена. Вероятно, она удалена. LABEL_CAN_NOT_BE_FOUND=Метка не найдена. Вероятно, она удалена.
MINDMAP_TIMESTAMP_OUTDATED=Невозможно сохранить, карта была изменена ''{0}''. Обновите страницу и попробуйте еще раз. MINDMAP_TIMESTAMP_OUTDATED=Невозможно сохранить, карта была изменена ''{0}''. Обновите страницу и попробуйте еще раз.
MINDMAP_OUTDATED_BY_YOU=Невозможно сохранить - карта устарела. У вас открыто несколько вкладок браузера?. Обновите страницу и попробуйте еще раз.
MINDMAP_LOCKED=Карта редактируется {0} <{1}>. Карта открыта в режиме чтения. MINDMAP_LOCKED=Карта редактируется {0} <{1}>. Карта открыта в режиме чтения.
MINDMAP_IS_LOCKED=Карта доступна только для просмотра. MINDMAP_IS_LOCKED=Карта доступна только для просмотра.
# Confirmed # Confirmed

View File

@ -41,7 +41,6 @@ ACCESS_HAS_BEEN_REVOKED=您对该脑图的访问权限已被撤销。联系脑
MAP_CAN_NOT_BE_FOUND=找不到该脑图,应该是被删除了。 MAP_CAN_NOT_BE_FOUND=找不到该脑图,应该是被删除了。
LABEL_CAN_NOT_BE_FOUND=找不到该标签,应该是被删除了。 LABEL_CAN_NOT_BE_FOUND=找不到该标签,应该是被删除了。
MINDMAP_TIMESTAMP_OUTDATED=无法保存您的更改,因为您的思维导图已被''{0}''修改。刷新页面,然后重试。 MINDMAP_TIMESTAMP_OUTDATED=无法保存您的更改,因为您的思维导图已被''{0}''修改。刷新页面,然后重试。
MINDMAP_OUTDATED_BY_YOU=无法保存您的更改,因为脑图已经过期。您打开了多个页面吗?刷新页面,然后重试。
MINDMAP_LOCKED=脑图正在被{0}<{1}>编辑。脑图以只读模式打开。 MINDMAP_LOCKED=脑图正在被{0}<{1}>编辑。脑图以只读模式打开。
MINDMAP_IS_LOCKED=脑图被锁定编辑。 MINDMAP_IS_LOCKED=脑图被锁定编辑。
# Confirmed # Confirmed

View File

@ -18,7 +18,7 @@
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/> <property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
</bean> </bean>
<bean id="httpInvoker" class="com.wisemapping.service.http.HttpInvoker"> <bean id="httpInvoker" class="com.wisemapping.service.google.http.HttpInvoker">
</bean> </bean>
<bean id="googleService" class="com.wisemapping.service.google.GoogleService"> <bean id="googleService" class="com.wisemapping.service.google.GoogleService">