is it possible to set a global branded error page for 404's a using dropwizard - grails

I find myself working a grails application that is being deployed as a fat jar built by a custom plugin that uses dropwizard to configure jetty.
It seems as though dropwizard doesn't allow facilitate the use of a plain old web.xml or jetty.xml and instead everything is set by java config at startup (i.e. using com.yammer.dropwizard.config.Environment).
Am I missing something here? Is there some way to map a 404 back to a URL or any kind of web page I can override so that a Jetty 404 isn't the default.
(yes I'm aware I could do something with the load balancer to redirect 404s)

I dont know how it is in grails, but this helps in java with dropwizard 0.7.1 run() method:
ResourceConfig jrConfig = environment.jersey().getResourceConfig();
environment.jersey().register(new RestErrorsHandler(jrConfig ));
Create this class for the mapping of exceptions -> give back an individual response!
#Provider
public class RestErrorsHandler implements ExceptionMapper<Exception> {
/**
* Deletes all ExpetionMappers.
*
* #param jrConfig
*/
public RestErrorsHandler(
ResourceConfig jrConfig)
{
// Remove all of Dropwizard's custom ExceptionMappers
Set<?> dwSingletons = jrConfig.getSingletons();
List<Object> singletonsToRemove = new ArrayList<Object>();
for (Object s : dwSingletons) {
// Remove all Exception mappers
if (s instanceof ExceptionMapper) {
singletonsToRemove.add(s);
}
}
for (Object s : singletonsToRemove) {
jrConfig.getSingletons().remove(s);
}
}
public Response toResponse(
Exception exception)
{
//Handle different exceptions in another way
if (exception.getClass().equals(JsonParseException.class)){
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
} else if(exception.getClass().equals(JsonParseException.class)){
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
} else if(exception.getClass().equals(Class.class)){
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
}
//genral problem -> output default
Response response = RestErrorsHandler.generalResponse(exception);
return response ;
}
public static Response generalResponse(Exception exception)
{
return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_PLAIN)
.entity("if you just want to give back a string, but could also be default html page or whatever").build();
}
}

Related

Spring Security Webflux/Reactive Exception Handling

I'm building app on spring webflux, and i'm stuck because spring security webflux (v.M5) did not behave like Spring 4 in term of exception handling.
I saw following post about how to customise spring security webflux:
Spring webflux custom authentication for API
If we throw exception let say in ServerSecurityContextRepository.load, Spring will update http header to 500 and nothing i can do to manipulate this exception.
However, any error thrown in controller can be handled using regular #ControllerAdvice, it just spring webflux security.
Is there anyway to handle exception in spring webflux security?
The solution I found is creating a component implementing ErrorWebExceptionHandler. The instances of ErrorWebExceptionHandler bean run before Spring Security filters. Here's a sample that I use:
#Slf4j
#Component
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
#Autowired
private DataBufferWriter bufferWriter;
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
AppError appError = ErrorCode.GENERIC.toAppError();
if (ex instanceof AppException) {
AppException ae = (AppException) ex;
status = ae.getStatusCode();
appError = new AppError(ae.getCode(), ae.getText());
log.debug(appError.toString());
} else {
log.error(ex.getMessage(), ex);
}
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
}
exchange.getResponse().setStatusCode(status);
return bufferWriter.write(exchange.getResponse(), appError);
}
}
If you're injecting the HttpHandler instead, then it's a bit different but the idea is the same.
UPDATE: For completeness, here's my DataBufferWriter object, which is a #Component:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#Slf4j
public class DataBufferWriter {
private final ObjectMapper objectMapper;
public <T> Mono<Void> write(ServerHttpResponse httpResponse, T object) {
return httpResponse
.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = httpResponse.bufferFactory();
try {
return bufferFactory.wrap(objectMapper.writeValueAsBytes(object));
} catch (Exception ex) {
log.warn("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
There is no need to register any bean and change default Spring behavior. Try more elegant solution instead:
We have:
The custom implementation of the ServerSecurityContextRepository
The method .load return Mono
public class HttpRequestHeaderSecurityContextRepository implements ServerSecurityContextRepository {
....
#Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
List<String> tokens = exchange.getRequest().getHeaders().get("X-Auth-Token");
String token = (tokens != null && !tokens.isEmpty()) ? tokens.get(0) : null;
Mono<Authentication> authMono = reactiveAuthenticationManager
.authenticate( new HttpRequestHeaderToken(token) );
return authMono
.map( auth -> (SecurityContext)new SecurityContextImpl(auth))
}
}
The problem is: if the authMono will contains an error instead of Authentication - spring will return the http response with 500 status (which means "an unknown internal error") instead of 401. Even the error is AuthenticationException or it's subclass - it makes no sense - Spring will return 500.
But it is clear for us: an AuthenticationException should produce the 401 error...
To solve the problem we have to help Spring how to convert an Exception into the HTTP response status code.
To make it we have can just use the appropriate Exception class: ResponseStatusException or just map an original exception to this one (for instance, by adding the onErrorMap() to the authMono object). See the final code:
public class HttpRequestHeaderSecurityContextRepository implements ServerSecurityContextRepository {
....
#Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
List<String> tokens = exchange.getRequest().getHeaders().get("X-Auth-Token");
String token = (tokens != null && !tokens.isEmpty()) ? tokens.get(0) : null;
Mono<Authentication> authMono = reactiveAuthenticationManager
.authenticate( new HttpRequestHeaderToken(token) );
return authMono
.map( auth -> (SecurityContext)new SecurityContextImpl(auth))
.onErrorMap(
er -> er instanceof AuthenticationException,
autEx -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, autEx.getMessage(), autEx)
)
;
)
}
}
I just went trough lots of documentation, having a similar problem.
My solution was using ResponseStatusException. AccessException of Spring-security seems to be understood.
.doOnError(
t -> AccessDeniedException.class.isAssignableFrom(t.getClass()),
t -> AUDIT.error("Error {} {}, tried to access {}", t.getMessage(), principal, exchange.getRequest().getURI())) // if an error happens in the stream, show its message
.onErrorMap(
SomeOtherException.class,
t -> { return new ResponseStatusException(HttpStatus.NOT_FOUND, "Collection not found");})
;
If this goes in the right direction for you, I can provide a bit better sample.

Making http requests from within a ApiController doesn't work

Here is a simplified version of the API controller I created.
public class SampleController : ApiController
{
[System.Web.Http.HttpGet]
public string Test(string url)
{
try
{
using (WebClient webClient = new WebClientEx())
{
return webClient.DownloadString(url);
}
}
catch (Exception ex)
{
return string.Empty;
}
}
Given a perfectly valid url this will throw a WebException that says "The remote name could not be resolved".
If I execute the same method within LinqPad, using the same url, it works. I've tried WebClient and WebRequest with the same results.
To be clear, this isn't a routing issue as I am able to hit and step through the code either way and the url involved is not part of this application.
I figured it out. I was initially thrown off because it appeared to work in a regular Controller but not in an ApiController. After I failed to reproduce my earlier "success", I finally figured out that I needed to use the system proxy.
webClient.Proxy = WebRequest.GetSystemWebProxy();

How to propagate spring security context to JMS?

I have a web application which sets a spring security context through a spring filter. Services are protected with spring annotations based on users roles. This works.
Asynchronous tasks are executed in JMS listeners (extend javax.jms.MessageListener). The setup of this listeners is done with Spring.
Messages are sent from the web application, at this time a user is authenticated. I need the same authentication in the JMS thread (user and roles) during message processing.
Today this is done by putting the spring authentication in the JMS ObjectMessage:
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
... put the auth object in jms message object
Then inside the JMS listener the authentication object is extracted and set in the context:
SecurityContext context = new SecurityContextImpl();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
This works most of the time. But when there is a delay before the processing of a message, message will never be processed. I couldn't determine yet the cause of these messages loss, but I'm not sure the way we propagate authentication is good, even if it works in custer when the message is processed in another server.
Is this the right way to propagate a spring authentication ?
Regards,
Mickaƫl
I did not find better solution, but this one works for me just fine.
By sending of JMS Message I'am storing Authentication as Header and respectively by receiving recreating Security Context. In order to store Authentication as Header you have to serialise it as Base64:
class AuthenticationSerializer {
static String serialize(Authentication authentication) {
byte[] bytes = SerializationUtils.serialize(authentication);
return DatatypeConverter.printBase64Binary(bytes);
}
static Authentication deserialize(String authentication) {
byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
return auth;
}
}
By sending just set Message header - you can create Decorator for Message Template, so that it will happen automatically. In you decorator just call such method:
private void attachAuthenticationContext(Message message){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String serialized = AuthenticationSerializer.serialize(auth);
message.setStringProperty("authcontext", serialized);
}
Receiving gets more complicated, but it can be also done automatically. Instead of applying #EnableJMS use following Configuration:
#Configuration
class JmsBootstrapConfiguration {
#Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public JmsListenerAnnotationBeanPostProcessor jmsListenerAnnotationProcessor() {
return new JmsListenerPostProcessor();
}
#Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
public JmsListenerEndpointRegistry defaultJmsListenerEndpointRegistry() {
return new JmsListenerEndpointRegistry();
}
}
class JmsListenerPostProcessor extends JmsListenerAnnotationBeanPostProcessor {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new ListenerEndpoint();
}
}
class ListenerEndpoint extends MethodJmsListenerEndpoint {
#Override
protected MessagingMessageListenerAdapter createMessageListenerInstance() {
return new ListenerAdapter();
}
}
class ListenerAdapter extends MessagingMessageListenerAdapter {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
propagateSecurityContext(jmsMessage);
super.onMessage(jmsMessage, session);
}
private void propagateSecurityContext(Message jmsMessage) throws JMSException {
String authStr = jmsMessage.getStringProperty("authcontext");
Authentication auth = AuthenticationSerializer.deserialize(authStr);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
I have implemented for myself a different solution, which seems easier for me.
Already I have a message converter, the standard JSON Jackson message converter, which I need to configure on the JMSTemplate and the listeners.
So I created a MessageConverter implementation which wraps around another message converter, and propagates the security context via the JMS message properties.
(In my case, the propagated context is a JWT token which I can extract from the current context and apply to the security context of the listening thread).
This way the entire responsibility for propagation of security context is elegantly implemented in a single class, and requires only a little bit of configuration.
Thanks great but I am handling this in easy way . put one util file and solved .
public class AuthenticationSerializerUtil {
public static final String AUTH_CONTEXT = "authContext";
public static String serialize(Authentication authentication) {
byte[] bytes = SerializationUtils.serialize(authentication);
return DatatypeConverter.printBase64Binary(bytes);
}
public static Authentication deserialize(String authentication) {
byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
return auth;
}
/**
* taking message and return string json from message & set current context
* #param message
* #return
*/
public static String jsonAndSetContext(Message message){
LongString authContext = (LongString)message.getMessageProperties().getHeaders().get(AUTH_CONTEXT);
Authentication auth = deserialize(authContext.toString());
SecurityContextHolder.getContext().setAuthentication(auth);
byte json[] = message.getBody();
return new String(json);
}
}

In OpenRasta, how should you handle codec errors or exceptions?

My scenario is this:
A client application executes a HTTP POST against an endpoint exposed by OpenRasta.
The body of the request contains an error that causes a problem in the codec - which is a custom implementation of OpenRasta.Codecs.IMediaTypeReader. This converts a JSON payload to the POCO expected by the handler.
The codec throws an exception that describes the error in a useful way. For example: Newtonsoft.Json.JsonReaderException: After parsing a value an unexpected character was encountered: ". Line 4, position 5.
The client application receives a HTTP 405 - MethodNotAllowed. The client doesn't see any of the exception details.
If the codec is modified to catch a JsonReaderException and return Missing.Value, similar to the Implementing a codec wiki, then the client receives a HTTP 500 - Internal Server Error. The body of the response also describes the following exception:
System.InvalidOperationException: The operation is not ready for invocation.
at OpenRasta.OperationModel.MethodBased.MethodBasedOperation.Invoke()
at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.<Invoke>b__0()
at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.Invoke()
at OpenRasta.OperationModel.OperationExecutor.Execute(IEnumerable`1 operations)
at OpenRasta.Pipeline.Contributors.OperationInvokerContributor.ExecuteOperations(ICommunicationContext context)
at OpenRasta.Pipeline.PipelineRunner.ExecuteContributor(ICommunicationContext context, ContributorCall call)
How should I modify my application so that:
The client receives a HTTP 400 Bad Request.
The client receives a string containing the details of the exception encountered in the codec.
Here is a minor variation of the answer above - this time with codec selection based on the operation result data.
Within IConfigurationSource:
using (OpenRastaConfiguration.Manual)
{
ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>();
ResourceSpace.Has.ResourcesOfType<ApplicationError>()
.WithoutUri
.TranscodedBy<ApplicationErrorCodec>();
// Or use a generic JSON serializer like this:
// .AsJsonDataContract();
// Other configuration here
}
Now ErrorCheckingContributor looks like this:
public class ErrorCheckingContributor : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner
.Notify(CheckRequestDecoding)
.After<KnownStages.IOperationResultInvocation>()
.And.Before<KnownStages.ICodecResponseSelection>();
}
private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
{
if (context.ServerErrors.Count == 0)
{
return PipelineContinuation.Continue;
}
Error err = context.ServerErrors[0];
// Get a suitable message (err.Message contains stack traces, so try to avoid that)
string msg = err.Title;
if (msg == null && err.Exception != null)
msg = err.Exception.Message;
if (msg == null)
msg = err.Message;
// Create instance of an error information resource which is specific for the application
// - This one is rather simple and only contains a copy of the message
ApplicationError error = new ApplicationError(msg);
// Set operation result to be "400 Bad Request" and remove errors
context.OperationResult = new OperationResult.BadRequest { ResponseResource = error };
context.ServerErrors.Clear();
// Render immediately without starting any handlers
return PipelineContinuation.RenderNow;
}
}
The class ApplicationError is:
public class ApplicationError
{
public string Message { get; set; }
public ApplicationError(string message)
{
Message = message;
}
}
At last we need a codec ApplicationErrorCodec for ApplicationError. This is not different from any other IMediaTypeWriter codec but depends a lot on your expected response media type. See https://github.com/openrasta/openrasta/wiki/Implementing-a-Codec for one example.
Having found this thread on Google Groups which contains all the answers, my current implementation looks something like this.
Within my implementation of IConfigurationSource:
using (OpenRastaConfiguration.Manual)
{
ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>();
// Other configuration here
}
Then ErrorCheckingContributor looks something like this:
public class ErrorCheckingContributor : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner
.Notify(CheckRequestDecoding)
.After<KnownStages.IOperationResultInvocation>()
.And.Before<KnownStages.ICodecResponseSelection>();
}
private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
{
if (context.ServerErrors.Count == 0)
{
return PipelineContinuation.Continue;
}
var first = context.ServerErrors[0];
if (first.Exception is Newtonsoft.Json.JsonReaderException)
{
context.Response.Entity.ContentType = MediaType.TextPlain;
context.Response.Entity.ContentLength = first.Exception.Message.Length;
using (var sw = new StreamWriter(context.Response.Entity.Stream))
{
sw.Write(first.Exception.Message);
}
}
return PipelineContinuation.Continue;
}
}
There's some things to be aware of with the above:
If a handler were to throw a JsonReaderException, it would also be processed here.
It doesn't check what media types the client accepts. This is different from exceptions thrown by Handlers that do go through codec selection.
Tried setting context.OperationResult to context.ServerErrors - but it doesn't go through the codec.

Single action class for multiple dynamic URIs throws exception when concurrent requesting

I've developed a web site using Struts2 as a controller and integrated it with Spring and Hibernate to do the business logic and DB stuff. The website's URIs are http://my.domian.com/URI; which {URI} is dynamically generated thorough the admin cms. The mapping of each uri to the servlet are done with help of Apache mod_rewrite, as follow:
RewriteCond %{HTTP_HOST} ^www\.domain\.com
RewriteRule ^([a-zA-Z0-9_-]+)$ /dynamic\.action?f=$1 [QSA,L]
(Before any further information, is this a good and suitable approach?)
The struts configuration is just a typically-academic one as:
<package name="Default" extends="struts-default" namespace="/">
...
<action name="dynamic" class="DynamicContentAction">
<result name="index">/content/web/dynamic/index.jsp</result>
</action>
</package>
DynamicContentAction is extending ActionSupport and implementing ServletRequestAware, ServletContextAware. I'm checking a couple of things (such as a current visiting language which is identified as a subdomain), looking up in the database that the requested uri is valid or not, generating that uri's content and setting a couple of runtime global variables (such as current visiting page id, layout config due to current visiting language ...) and put it on a Request object in this servlet.
Everything looks good and even works perfectly ok, unless too many dynamic pages being requested by a single user concurrently. "Too Many" in my case is at least 9-10 pages. In this case it throws exceptions, different ones! Sometimes the HttpServletRequest request is null, sometimes ServletContext servletContext is null, some other times these are ok, but the runtime variables are null which is used in business logic or db querying.
I've googled about it and found out that this action is being instantiated "Per Request". Isn't this so? If there is an action per request, what's wrong with this conflict or "nullability thing". Should I do some thread-like thing in that action, beyond the threading of struts?
I'd be so appreciated if you could help me out or point me a direction.
Here is simplified version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport implements ServletRequestAware, ServletContextAware {
private HttpServletRequest request;
private ServletContext servletContext;
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
}
else {
//Generating nice 404 error page content
}
request = null;
servletContext = null;
f = null;
return "index";
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
as I'm writing this post, I have came to the knowledge that this class is NOT thread-safe. Is it? So I've changed it a little bit as follow:
Here is newer version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport {
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
final HttpServletRequest request = ServletActionContext.getRequest();
final ServletContext servletContext = ServletActionContext.getServletContext();
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
f = null;
}
else {
//Generating nice 404 error page content
}
return "index";
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
and the Null thing problem is almost gone, but there is still conflict with the generated content. For example if user try to open:
http:// www.domain.com/A
http:// www.domain.com/B
http:// www.domain.com/C
http:// www.domain.com/D
http:// www.domain.com/E
simultaneously, all of the pages will be rendered in browser, but the content of A is shown in A and B, C is correct, and there is a very good chance that the content of D and E are incorrect too.

Resources