Grails RestfulController Overriding an resource update - database

So I'm having a little trouble when updating a resource in Grails. So here is my controller, in which I'm overriding some methods
#Override
protected saveResource(resource) {
if (request.post) {
permissionsService.addPermission(resource.id, springSecurityService.getCurrentUser(), 'Outing')
}
resource.save(flush: true)
}
#Override
protected updateResource(resource) {
if (permissionsService.isAdmin()) {
println 'doesnt print'
saveResource(resource)
} else if (permissionsService.canWrite('Outing', params.id, springSecurityService.getCurrentUser())) {
println 'doesnt print'
saveResource(resource)
} else {
println 'prints to console'
response.status = 404
}
}
This is what the top of the controller looks like
import grails.converters.JSON
import grails.rest.*
class OutingController extends RestfulController {
static responseFormats = ['json']
OutingController() {
super(Outing)
}
...............
}
My problem is that when I perform a PUT, for some reason the resource is still updated and saved. I can't figure out why because it never hits the saveResource or any other save function to my knowledge. I would like it to return with the status 404, but I could use some help with that. I'm basically adding permissions to the restful controller. Thanks for any help
Here is the RestfulController if you'd like to look. https://github.com/grails/grails-core/blob/master/grails-plugin-rest/src/main/groovy/grails/rest/RestfulController.groovy

Related

How can I do something before processing a request in Nancy 2.x?

I want to do some things in every request, no matter the Module or Route. How can I accomplish this in Nancy 2.x?
If found How to Intercept all Nancy requests and How do I capture all requests irrespective of verb or path, but they are only applicable for Nancy 1.x and the Documentation is out-of-date.
As you say documentation is not updated and most of the resources you can find online are for version 1.x.
How to solve it depends a little bit on what you want to do. If you are not messing with the response you can override ApplicationStartUp in the bootstrapper like this:
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
pipelines.BeforeRequest.AddItemToEndOfPipeline((ctx) =>
{
Console.Out.WriteLine("Hit");
return null;
});
base.ApplicationStartup(container, pipelines);
}
If you, on the other hand, need to meddle with the response and the headers you can do it in the constructor of your overridden NancyModule with your Get setup like this:
public InstrumentProgrammingNancyModule()
{
//// Enable CORS.
After.AddItemToEndOfPipeline((ctx) =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "GET");
});
Get("/" , _ =>
{
return somethingOrOther;
});
....
}
Both of these solutions work with Nancy 2.0.
You can try this :
public class NewBootstrapper : DefaultNancyBootstrapper
{
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
//Your code here
base.RequestStartup(container, pipelines, context);
}
}

Nativescript Angular ActivityIndicator

in my Nativescript Angular app i am using an ActivityIndicator, setup as i've seen in the Nativescript Angular docs (the GroceryList example):
<ActivityIndicator width="30" height="30" [busy]="refreshing" [visibility]="refreshing ? 'visible' : 'collapsed'" horizontalAlignment="center" verticalAlignment="center"></ActivityIndicator>
if the Component using it i have:
export class MyComponent {
public refreshing = false;
........
}
Then i fetch some data from my backend:
public onRefreshTap() {
console.log("onrefreshtap");
this.refreshing = true;
this.backend.getData(function (data) { //this.backend is my Service
this.refreshing = false;
})
}
The problem is that when i put this.refreshing to true, the ActivityIndicator correctly shows. But when bakend request completes (and so, i put this.refreshing=false) the ActivityIndicator does not hides... (and also it seems that its busy property is not updated, it stays in spinning state)..
What am i doing wrong ?
Thanks in advance
You could also try to access the refreshing property as it has been shown in the sample codes below. It could be a problem of accessing the property inside the callback method of your service.
public onRefreshTap() {
var that = this;
this.refreshing = true;
this.backend.getData(function (data) { //this.backend is my Service
that.refreshing = false;
})
}
or
public onRefreshTap() {
this.refreshing = true;
this.backend.getData((data) => {
that.refreshing = false;
})
}
It may be many things:
1) The change to false, on the Observable, is not being "seen" by the component.
------ The solution is run the code in a Zone (see https://angular.io/docs/ts/latest/api/core/index/NgZone-class.html )
2) The backend is returning an error (I don't see it dealing with that in the code).
------ The solution is put a function to deal with the error.
3) The callback is not being called. In your code, you're SENDING a function as a parameter to the backendService, so maybe the service is not executing it.
------ Try using a Promisses or Observables to deal with returned values (you'll have to Google about it, since I'm still learning them my explanation would be the worst). :)
Here's some code that might work:
my-component.html
<ActivityIndicator [busy]="isWorking" [visibility]="isWorking?'visible':'collapse'"></ActivityIndicator>
my-component.ts
import { Component, NgZone } from "#angular/core";
...
export class MyComponent {
isWorking:boolean = false;
constructor(private backendService: BackendService,
private _ngZone: NgZone)
{
this.isWorking = false;
}
public onRefreshTap() {
console.log("onrefreshtap");
this.isWorking = true;
this.backendService.getData()
.then(
// data is what your BackendService returned after some seconds
(data) => {
this._ngZone.run(
() => {
this.isWorking = false;
// I use to return null when some Server Error occured, but there are smarter ways to deal with that
if (!data || data == null || typeof(data)!=='undefined') return;
// here you deal with your data
}
)
}
);
}
}

Grails isn't responding with a 401 during ajax request and timed out session

I'm using grails along with spring security and angularjs. When a user session has expired and the user clicks an ajax action on the page, rather than respond with a 401, the application attempts to redirect to the login page which no response from the original ajax action.
I'm still using a traditional login page and some my application still has some traditional page links, so when a session has expired and a user clicks a page link, I would like to redirect to the login page.
If a user clicks on an ajax request, I would like to get a 401 response rather than the redirected html response so that I can do a redirect in my javascript.
I have the following config setting.
grails.plugin.springsecurity.providerNames = ['hriLoginClientAuthenticationProvider']
grails.plugin.springsecurity.useSecurityEventListener = true
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = '/login?error=1'
grails.plugin.springsecurity.auth.loginFormUrl = '/login'
grails.plugin.springsecurity.logout.postOnly = false
What do I need to do to get ajax request to not redirect to the login page?
I've run into a similar issue and have implemented a filter in the filter chain to detect AJAX requests and respond with a customized HTTP status (you can change it to 401 if you like).
Basically there are three parts to this. The first, is the filter. It's a servlet filter and examines the request as well as the state of the authentication in the session. Second, defining the filter as a bean within the application context in Resources.groovy. Finally, inserting it into the Spring Security filter chain, which I've done in Bootstrap.groovy.
I'll walk you through this now.
First the servlet filter (under src/java)
package com.xyz.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.ThrowableCauseExtractor;
import org.springframework.web.filter.GenericFilterBean;
public class AjaxTimeoutRedirectFilter extends GenericFilterBean {
// private static final Logger logger =
// LoggerFactory.getLogger(AjaxTimeoutRedirectFilter.class);
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private int customSessionExpiredErrorCode = 901;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
// logger.debug("Chain processed normally");
} catch (IOException ex) {
throw ex;
} catch (Exception ex) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class,
causeChain);
if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class,
causeChain);
}
if (ase != null) {
if (ase instanceof AuthenticationException) {
throw ase;
} else if (ase instanceof AccessDeniedException) {
if (authenticationTrustResolver
.isAnonymous(SecurityContextHolder.getContext()
.getAuthentication())) {
// logger.info("User session expired or not logged in yet");
String ajaxHeader = ((HttpServletRequest) request)
.getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(ajaxHeader)) {
// logger.info("Ajax call detected, send {} error code",
// this.customSessionExpiredErrorCode);
HttpServletResponse resp = (HttpServletResponse) response;
resp.sendError(this.customSessionExpiredErrorCode);
} else {
// logger.info("Redirect to login page");
throw ase;
}
} else {
throw ase;
}
}
}
}
}
private static final class DefaultThrowableAnalyzer extends
ThrowableAnalyzer {
/**
* #see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()
*/
protected void initExtractorMap() {
super.initExtractorMap();
registerExtractor(ServletException.class,
new ThrowableCauseExtractor() {
public Throwable extractCause(Throwable throwable) {
ThrowableAnalyzer.verifyThrowableHierarchy(
throwable, ServletException.class);
return ((ServletException) throwable)
.getRootCause();
}
});
}
}
public void setCustomSessionExpiredErrorCode(
int customSessionExpiredErrorCode) {
this.customSessionExpiredErrorCode = customSessionExpiredErrorCode;
}
}
Second, defining the filter as a bean in the application context in Resources.groovy
beans = {
ajaxTimeoutRedirectFilter(com.xyz.security.AjaxTimeoutRedirectFilter)
}
And finally, getting the filter into the Spring Security filter chain (I used BootStrap.groovy for this)
import grails.plugin.springsecurity.SecurityFilterPosition
import grails.plugin.springsecurity.SpringSecurityUtils
class BootStrap {
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter('ajaxTimeoutRedirectFilter', SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order + 10)
}
def destroy = {
}
}
Did you consider "locking a screen" when the user is idle on a client-side? Of course you should handle end of a session on server-side but in fact it seems even cleaner and more secure solution than waiting for an action from client side (especially if user has left and left on a screen some sensitive data).
Check out this ng-idle directive.

Http Methods in Angular JS

I've been looking for answers for the past days and still I haven't got any idea on how can I make my app work. I only have one controller for all the subtabs of my app. I had one http get method for one subtab under the main controller and I need another http get method for another subtab to also be under the main controller. How can I possibly do that?
You can use attribute routing.
[HttpGet]
[Route("method1/{id}")]
public HttpResponseMessage index(string id)
{
try
{
// your code
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.Forbidden,ex.Message);
}
}
[HttpGet]
[Route("method2/{Id}")]
public HttpResponseMessage GetData(string GetRecordsById)
{
try
{
//your code
}
catch (Exception)
{
throw;
}
}

Nancy testing GetModel<T> throws KeyNotFoundException

I'm trying to test that the model returned from my Nancy application is as expected. I have followed the docs here but whenever I call the GetModel<T> extension method it throws a KeyNotFoundException.
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
I know what the error means but I'm failing to see why it's being thrown.
Here's my module
public class SanityModule : NancyModule
{
public SanityModule()
{
Get["sanity-check"] = _ => Negotiate.WithModel(new SanityViewModel { Id = 1 })
.WithStatusCode(HttpStatusCode.OK);
}
}
my view model
public class SanityViewModel
{
public int Id { get; set; }
}
and here's my test
[TestFixture]
public class SanityModuleTests
{
[Test]
public void Sanity_Check()
{
// Arrange
var browser = new Browser(with =>
{
with.Module<SanityModule>();
with.ViewFactory<TestingViewFactory>();
});
// Act
var result = browser.Get("/sanity-check", with =>
{
with.HttpRequest();
with.Header("accept", "application/json");
});
var model = result.GetModel<SanityViewModel>();
// Asset
model.Id.ShouldBeEquivalentTo(1);
}
}
Debugging this test shows that the module is hit and completes just fine. Running the application shows that the response is as expected.
Can anyone shed some light on this?
Thanks to the lovely guys, albertjan and the.fringe.ninja, in the Nancy Jabbr room we've got an explanation as to what's going on here.
TL;DR It makes sense for this to not work but the error message should be more descriptive. There is a workaround below.
The issue here is that I am requesting the response as application/json whilst using TestingViewFactory.
Let's take a look at the implementation of GetModel<T>();
public static TType GetModel<TType>(this BrowserResponse response)
{
return (TType)response.Context.Items[TestingViewContextKeys.VIEWMODEL];
}
This is simply grabbing the view model from the NancyContext and casting it to your type. This is where the error is thrown, as there is no view model in NancyContext. This is because the view model is added to NancyContext in the RenderView method of TestingViewFactory.
public Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
// Intercept and store interesting stuff
viewLocationContext.Context.Items[TestingViewContextKeys.VIEWMODEL] = model;
viewLocationContext.Context.Items[TestingViewContextKeys.VIEWNAME] = viewName;
viewLocationContext.Context.Items[TestingViewContextKeys.MODULENAME] = viewLocationContext.ModuleName;
viewLocationContext.Context.Items[TestingViewContextKeys.MODULEPATH] = viewLocationContext.ModulePath;
return this.decoratedViewFactory.RenderView(viewName, model, viewLocationContext);
}
My test is requesting json so RenderView will not be called. This means you can only use GetModel<T> if you use a html request.
Workaround
My application is an api so I do not have any views so changing the line
with.Header("accept", "application/json");
to
with.Header("accept", "text/html");
will throw a ViewNotFoundException. To avoid this I need to implement my own IViewFactory. (this comes from the.fringe.ninja)
public class TestViewFactory : IViewFactory
{
#region IViewFactory Members
public Nancy.Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
viewLocationContext.Context.Items[Fixtures.SystemUnderTest.ViewModelKey] = model;
return new HtmlResponse();
}
#endregion
}
Then it is simply a case of updating
with.ViewFactory<TestingViewFactory>();
to
with.ViewFactory<TestViewFactory>();
Now GetModel<T> should work without needing a view.

Resources