Skip to content
Snippets Groups Projects
Commit 9e0d84c6 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

#3 Improved error messages

parent 271a2230
No related branches found
No related tags found
No related merge requests found
Showing
with 163 additions and 23 deletions
......@@ -41,8 +41,8 @@ export default {
mounted: function() {
var self = this;
document.addEventListener('apiError', function(event) {
self.$bvToast.toast(event.message, {
title: "Error",
self.$bvToast.toast(event.message.body, {
title: event.message.title,
variant: 'danger',
solid: true
});
......
......@@ -26,15 +26,11 @@ function apiRequest(url, options, showLoading = true) {
}
function dispatchApiErrorEvent(error) {
let message;
if (error.message) {
message = error.message;
} else {
message = 'Generic error';
}
let event = new CustomEvent('apiError');
event.message = message;
event.message = {
title: error.error || 'Error',
body: error.message || 'Unknown error'
};
document.dispatchEvent(event);
}
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>it.inaf.ia2</groupId>
......
......@@ -18,6 +18,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
......@@ -45,14 +47,20 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
// CORS are necessary only for development (API access from npm server)
if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
}
super.configure(http);
// avoid displaying the annoying BasicAuth browser popup when the
// session expires (this should happen mostly during development)
// [401 WWW-Authenticate is converted to 403]
http.exceptionHandling().defaultAuthenticationEntryPointFor(
new Http403ForbiddenEntryPoint(), new AntPathRequestMatcher("/keepAlive"));
http.csrf().disable();
}
......
......@@ -34,7 +34,7 @@ public class HomePageController {
private InvitedRegistrationManager invitedRegistrationManager;
@ResponseBody
@GetMapping(value = "/home", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@GetMapping(value = "/home", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<HomePageResponse> getMainPage(@Valid GroupsRequest request) {
HomePageResponse response = new HomePageResponse();
......@@ -56,7 +56,6 @@ public class HomePageController {
if (optReg.isPresent()) {
request.setAttribute("invited-registration", optReg.get());
return "/registration-completed";
//request.getRequestDispatcher("/registration-completed").forward(request, response);
}
return "index.html";
......
......@@ -2,9 +2,11 @@ package it.inaf.ia2.gms.controller;
import it.inaf.ia2.gms.authn.SessionData;
import it.inaf.ia2.gms.rap.RapClient;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
......@@ -20,13 +22,14 @@ public class KeepAliveController {
@Autowired
private RapClient rapClient;
@GetMapping("/keepAlive")
@GetMapping(value = "/keepAlive", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> keepAlive() {
LOG.trace("Keepalive called");
if (sessionData.getExpiresIn() < 60) {
rapClient.refreshToken();
LOG.trace("RAP token refreshed");
}
return ResponseEntity.noContent().build();
// empty JSON object response
return ResponseEntity.ok(new HashMap<>());
}
}
......@@ -4,7 +4,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
public class BadRequestException extends GmsException {
public BadRequestException(String message) {
super(message);
......
package it.inaf.ia2.gms.exception;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Scanner;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("${server.error.path:${error.path:/error}}")
public class ErrorController extends AbstractErrorController {
@Value("${support.contact.label}")
private String supportContactLabel;
@Value("${support.contact.email}")
private String supportContactEmail;
@Autowired
public ErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public void errorHtml(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> errors = super.getErrorAttributes(request, true);
HttpStatus status = getStatus(request);
String responseText;
if (status == HttpStatus.NOT_FOUND) {
responseText = getFileContent("404.html");
} else {
responseText = getFileContent("error.html")
.replace("#ERROR_TITLE#", (String) errors.get("error"))
.replace("#ERROR_MESSAGE#", (String) errors.get("message"))
.replace("#ADDITIONAL_MESSAGE#", getAdditionalMessage(status));
}
response.setContentType("text/html;charset=UTF-8");
response.getOutputStream().print(responseText);
}
private String getAdditionalMessage(HttpStatus status) {
if (status.is5xxServerError()) {
// unexpected error -> let users report the issue
return "<br/>If you need support please contact"
+ " <a href=\"mailto:" + supportContactEmail + "\">" + supportContactLabel + "</a>";
}
return "";
}
@RequestMapping(produces = MediaType.TEXT_PLAIN_VALUE)
public void errorText(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> errors = super.getErrorAttributes(request, true);
response.setContentType("text/plain;charset=UTF-8");
response.getOutputStream().print(errors.get("error") + ": " + errors.get("message"));
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, false);
return new ResponseEntity<>(body, status);
}
private String getFileContent(String templateFileName) throws IOException {
try (InputStream in = ErrorController.class.getClassLoader()
.getResourceAsStream("public/error/" + templateFileName)) {
Scanner s = new Scanner(in).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
}
@Override
public String getErrorPath() {
return null;
}
}
package it.inaf.ia2.gms.exception;
public abstract class GmsException extends RuntimeException {
public GmsException(String message) {
super(message);
}
}
......@@ -4,7 +4,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
public class NotFoundException extends GmsException {
public NotFoundException(String message) {
super(message);
......
......@@ -4,7 +4,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class UnauthorizedException extends RuntimeException {
public class UnauthorizedException extends GmsException {
public UnauthorizedException(String message) {
super(message);
......
......@@ -90,8 +90,10 @@ public class LoggingDAO {
private String getUser(HttpServletRequest request) {
if (request.getUserPrincipal() != null && request.getUserPrincipal() instanceof RapPrincipal) {
return request.getUserPrincipal().getName();
} else {
} else if (request.getSession(false) != null) {
return sessionData.getUserId();
} else {
return null;
}
}
}
......@@ -2,6 +2,7 @@ server.port=8082
server.servlet.context-path=/gms
spring.main.allow-bean-definition-overriding=true
server.error.whitelabel.enabled=false
security.oauth2.client.client-id=gms
security.oauth2.client.client-secret=gms-secret
......@@ -16,12 +17,13 @@ logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.jdbc=TRACE
logging.level.org.springframework.web=TRACE
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.url=jdbc:postgresql://localhost:5433/postgres
spring.datasource.username=gms
spring.datasource.password=gms
rap.ws-url=http://localhost/rap-ia2/ws
rap.ws.basic-auth=false
support.contact.label=IA2 team
support.contact.email=ia2@inaf.it
# For development only:
spring.profiles.active=dev
......
<!DOCTYPE html>
<html>
<head>
<title>Page Not Found</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
</head>
<body>
<div class="container mt-4">
<h1 class="mb-3 text-primary">Page Not Found</h1>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>#ERROR_TITLE#</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
</head>
<body>
<div class="container mt-4">
<h1 class="mb-3 text-danger">#ERROR_TITLE#</h1>
<p><strong>#ERROR_MESSAGE#</strong></p>
<p>#ADDITIONAL_MESSAGE#</p>
</div>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment