Intercepting Grails GSP actions on either client or server side - grails

Grails 2.4.5 here. I am trying to implement the following UX behavior for my GSPs:
If a user has permission to click a button, then they may do so; however
If the user doesn't have permission to click a button, then when they click the button, a banner message (flash?) appears across the top of the screen with an rose/pinkish/red background stating 'You don't have permission to take this action'
To determine whether the user has the required permission, I have access to functionality from both the Groovy and GSP/taglib layers.
From the Groovy/controller layer:
SecurityUtils.hasPermission(String permission)
Ex: SecurityUtils.hasPermission('UPDATE_BUZZ')
From the GSP/taglib layer:
<sec:hasPermission permission="<permission name>">???</sec:hasPermission>
Ex: <sec:hasPermission permission="UPDATE_BUZZ">???</sec:hasPermission>
So, given those two available access checking mechanisms, and given the following controller:
class FizzController {
BuzzService BuzzService
def buzz() {
SomeData dataModel = buzzService.getModel(params)
render(view: 'buzz', model: [ dataModel: dataModel ])
}
}
...where buzz.gsp is:
<!-- Lots of HTML/GSP here -->
<g:submitButton name="update" value="Update" />
<!-- Lots more HTML/GSP down here -->
Given all that, my question is: How/where should I: (1) respond to the 'update' button's click handler, (2) perform the access check, and (3) render the error/banner/flash message? Code example (even pseudo-code) would be most awesome!

Here is what I would suggest:
Make sure your controller method is role based
Remove permission check in GSP, if the button should be visible to everyone
Create an AJAX call upon submission, if the response status is 403, display the banner.

If I were you, I'd probably try to use before-action filter in this case. In this filter I'd make a checking if current user has permissions to such an action (checking permissions should always be done in server-side, because of security reasons) and than:
if security check passes - just return true (controller will continue it's flow)
if security check fails - you can use default flash.message and return false (with propriate css style flasheed message could appears across the top of the screen with an rose/pinkish/red background)
Example code:
class UserFilters {
def filters = {
// ... other filters ...
permissionAllCheck(controller: '*', action: '*') {
before = {
doPermissionCheck(delegate)
}
}
}
private boolean doPermissionCheck(filters) {
if (! YourService.checkForPermissionForCurrentlyLoggedUser()) {
filters.flash.message = "You don't have permission to take this action"
return false
}
true
}
}
If you want to use filter only to specific controller/action, check applying section. Remember also, that you can use invert rule filter.
More info about different filterTypes.
You should also remember to add flash.message section to your layout (or selected views). You can always specify type of flash message and it's css style.

I am assuming by your question that you don't want a page refresh and perhaps not even an ajax call. Because if you did that then showing a banner is not difficult. You just want this to behave like JavaScript client-side validation (UX wise). If this assumption is wrong then don't read and use Aramiti's solution. Otherwise go ahead.
First solution
You can create a tag which takes a permission as input. Something like
<myTagLib:flashOnNoPermission permission="PERM" name="name" value="value">
</myTagLib:flashOnNoPermission>
This tag's definition can check the permission using sec:hasPermission. Then this tag can just render a template containing something like this
<hidden flash message>
<g:submitButton name="name" value="value" onclick="<unhide flash if no permission>"/>
Basically create a wrapper over grails button so that you can have flash messages alongwith buttons.
Problem
What if user's permissions are changed when user is on screen? Ajax takes care of that but this does not. Once screen is loaded then everything is fixed.
The banner is alongwith the button
Second solution
Add a common div at the top of your layout for displaying flash messages. Then create a tag similar to the above. Just don't add the flash message in the rendered template. Something like
<g:submitButton name="name" value="value" onclick="<unhide flash at top of layout if no permission>"/>
Problem
What if user's permissions are changed when he is on the screen?
Not sure why you need things onclick handler but if there is no reason just Aramiti's solution.

Best way would be to hide the button if the user has no permission to use it. That could be easily achieve through <sec:hasPermission permission="UPDATE_BUZZ">button</sec:hasPermission>
If you want button to be displayed even for user without permission, you could use the same hasPermission to add an extra class to the button. Now capture the clock event for that class using jQuery and show your message.

Related

Return to previous page in ASP.Net Core MVC

From my client detail page I have a button to edit the client record which redirects to an edit page. I have a "return to client detail" link on the edit page which I want to redirect the user back to the previous client detail page.
<a asp-controller="Client" asp-action="Detail" asp-route-id="#Model.ClientID">Return to client detail</a>
Currently this works as expected but takes extra time as it reloads the detail page from scratch (ie running all the various db queries again). Since the user is really just cancelling the edit without any changes to the state of the client I am wanting to return the user to the previous detail page without having to go through the controller action again.
Essentially I am wanting to simulate the browser back button (to improve responsiveness) but i'm not sure how to implement this or whether it's good practice to do so. Some guidance would be appreciated.
Thanks
For IActionResult you can use this code:
public IActionResult Test()
{
return Redirect(Request.Headers["Referer"].ToString());
}
U know what? I hate JS so i will write answer with backend side. The HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. So simply read that and pass to view (always remember about XSS and validation, user can easly spoof HTTP request)
In action controller
if(Request.Headers["Referer"] != null)
{
ViewData["Reffer"] = Request.Headers["Referer"].ToString();
}
In view (razor)
#if(!string.IsNullOrWhiteSpace(ViewData["Reffer"]))
{
Return to client detail
}
You can use
<a href='javascript:history.go(-1)'>Return to client detail</a>
or onclick
Return to client detail
It should be like this
<input type="button" onclick= "history.go(-1)" value="Return to client detail" />
One note of caution using Request.Headers["Referer"] - if someone refreshes the destination page for some reason, Request.Headers["Referer"] will be empty.
Using history.go(-1) gives the expected behavior despite page refresh.
I think that you need to get rid of the idea of passing through the controller. If you need to browse quickliest with asp net core code about href you can try this.
<a asp-area="" onclick="history.go(-1);">Return to client detail</a>
I would never rely on my button, thinking a user will prefer it to browser back button.
I would say the correct way to solve this problem is to store the page state somewhere, for example, save ViewModel in TempData or Session. Then, if exists, load from it, instead of running db queries. It's quick and reliable.
This Request.Headers["Referer"] will not work if the user refresh the page or the page is been loaded twice, which mean, clicking back will not take you out of the current page.

In asp.net MVC5 how do we execute controller code after a View's submit button is clicked?

VS2013, MVC5, VB
I currently have a page that successfully sends a POST to a payment processor when the user clicks the payment button. I would like to do some additional actions in code after the request to process the payment is made in that View. I don't know how to do that. It may be that I'm thinking of this completely wrong, but that's where I am right now.
I'm assuming I would return to an ActionResult Function to do the final things in code and then go to another View to POST to the payment processor. I guess I'm asking how to force a View to POST immediately when a Return View() is executed so the user isn't required to click a Payment button again. Ergo my question about POSTing from a controller, because all I understand how to do is POST from a View.
Adding to the original post: Suppose I want to execute Sub ClearCart() (in reality it could be any number of things) after the Payment Submit button is clicked. So that means I don't want to POST away to the payment processor's website just yet, I want to first run one more piece of code on my end, then POST to the payment processor. But in order to do that, I only understand how to get back to a controller in order to run Sub ClearCart(). So now after I've run Sub ClearCart(), I have to setup another page with another Payment Submit button, and this time the POST to the payment processor can proceed because I've run the Sub ClearCart(). But that means a user is clicking Pay twice. Once to get me back where I can run some code, and then again when it's time to go to the payment processor for real.
I could very easily be lacking some fundamental concept and therefore my question doesn't make a lot of sense, but that's why I titled this thread the way I did. If I return to a Controller to run some code, I wondered how I can effectively force the POST from Controller code so it's transparent to the user. Am I explaining any better?
I thought in MVC all code is run in the Controllers, and the Views are simply to set up the page. I don't really run server side code from the page, right? And once I hit Submit on a POST to another website, I'm gone until the user and/or return payment information comes back. If I wanted to do anything after the user commits to the payment, I don't know or understand how to do that.
Additional add to the original post: There is a moment in time when the user clicks the submit button. Once that's done, action transfers to the destination in the 'action' parameter and I have no opportunity to do anything until the user or the destination site responds back. If I want to run some code after the Submit Button is clicked, how do I do that? When I started this thread I didn't really know what code to put in this thread to explain my question, but as a result of the comments, I now think this is what I need to show from the View that the user commits from:
<form id="simForm" method='post' action='https://test.authorize.net/gateway/transact.dll'>
...all the stuff presented or included in the view for the user to make his/her decision
<input type='submit' value="Submit Payment" class="btn btn-warning" />
</form>
I want to do things after the user commits, i.e. clicks the Submit Button, but it's too late once they click. I was thinking I could have a button that says Submit Payment, but instead of POSTing to my 'action' link above, POST back to a follow-on Controller where I can execute the code I want to run after the user makes their decision and before they leave my site. But I don't understand how to (1) initiate the real POST to the payment processor from within that follow-on Controller, or (2) force that follow-on Controller's View to execute a POST to the payment processor immediately without requiring another user action.
Now I've taken everyone on this horrible journey through my thinking, it just occurred to me what I'm wanting to do in a controller is redirect from the controller in a way that is a POST with all my page inputs to an external URL. Is there such a method? The only redirect I'm familiar with is for redirecting within the site.
Thanks.
Best Regards,
Alan
One way to do this is to use ajax to get back to a Controller Action, execute the code, and then let ajax finish with the Submit. The following code seems to work fine:
The Controller:
Public Class MiscController
Inherits Controller
Public Function ActionsBeforeSubmit() As ActionResult
ClearCart()
Return New EmptyResult()
End Function
End Class
The View:
<form id="simForm" method='post' target="_blank" action='https://test.authorize.net/gateway/transact.dll'>
...PAGE CONTENT HERE
<input type='button' onclick="UserCommit()" value="Submit Payment" class="btn btn-warning" />
</form>
<script >
function UserCommit() {
$.ajax({
url: '#Url.Action("ActionsBeforeSubmit", "Misc")'
async: false,
})
$("#simForm").submit()
}
</script>
When the button is clicked, the script UserCommit() is run. Within that script ajax runs the Controller Action ActionsBeforeSubmit. Updated this post: async: false required to force Submit to wait until ActionsBeforeSubmit is done. (although some texts suggest to not use async: false) After the Action finishes, the script performs the Submit.
Best Regards,
Alan

Ruby- Change the layout on button release without changing the URL

I would like to have my Flash animation in fullscreen when I press a html button under the animation. I have a ../views/layout/viewer.rhtml (this one loads all menus). I have ./views/layout/fullscreen.rhtml (this one do not have menus, it has only 100% width and 100% height and the flash object). I would like to avoid Javascript method.
I have a show_controller:
layout "viewer"
def index
h = Histoire.find_by_label(params[:story_name])
# Checks if current_user has a subscription which allows the story whose ID is passed by param to be seen
if h.price != 0 && (current_user.nil? || current_user.abonnement.histoires.delete_if{|histoire| histoire.label != params[:story_name]}.length == 0)
flash[:notice] = "Sorry, your subscription doesn't allow you to access this story, please contact sales service"
redirect_to index_url
else
#histoire = Histoire.find_by_label(params[:story_name])
render :action => :show
end
end
I have a flash object:
My flash Animation need to have a fixed URL to run: /show/Story_Name, the current layout is viewer. When I press FullScreen button, I'd like Ruby to change the layout from "viewer" to "fullscreen".
Is there a way to change the layout from viewer to fullscreen when a button is pressed?
I'm beginner in this matter. Thanks a lot to the one who will help me.
Ruby code is executed on the server side, it won't do anything unless you reload the page to make another call to your controller, for example using a "layout_mode" parameter you may use in your view template.
I would use an unobtrusive javascript approach :
use a link to reload the page and change the layout_mode to fullscreen. This behavior allows user with Javascript disabled to access he fullscreen switch feature
use jQuery to modify this link so it changes the layout on click without reloading the page for the regular users
Side note : I would advice you to avoid naming your model objects in French, as Rails is strongly convention-oriented and makes an extensive use of english-inflections : Rails will consider stories as a collection and story as an individual member. It would work with "gourou" and "joujou" but won't with "hibou", "caillou", "chou"... ;)

Creating ajax-enabled subform with "Edit" button

I am looking for the best way to create ajax enabled subforms from items in a list with MVC 3. A static list of values should be generated, but with an "edit" link/button next to every item, to toggle inline edits.
I did follow the tutorial at this link
http://blog.janjonas.net/2011-07-24/asp_net-mvc_3-ajax-form-jquery-validate-supporting-unobtrusive-client-side-validation-and-server-side-validation [1]
However it is based on the form edit fields always being visible
I'd like to show a static list with field values, but let the user activate an edit field by clicking "edit" (e.g. button)
I did modify the example at [1] by creating a default partial view with a form with submit button only. When posting the data by ajax the edit form will show. It looks like it is working, (I only need to hide validation errors on the first POST - which does not send real data).
Update:
An even better solution would probably be to leave out all forms in the static view, just have a single css class button/link next to each item, and let jquery fetch the relevant view for the clicked item. I am not sure how to do that with MVC 3+jQuery though.
Another update:
I discovered Ajax.Actionlink, which did exactly what I wanted!
I found out how to do it, and it turned out to be real simple!
I created two partial views.
One for rendering each static item. I used used Ajax.ActionLink with InsertionMode "replace", and set the parent of the item as the target
The second for rendering the form. Here I used Ajax.Beginform with similar options.
On successfully saved data, I returned the static view, on failure, I returned the partial view with the ajax form again.
I'm happy I found a MVC-centric way to do it (although it is fun creating custom stuff with jQuery)
It sounds like you need an inline editing plugin for jQuery. I would try jEditable. I have not used it myself but appears to have extensive docs.
this entry might help: code + video + explanation ;)
http://ricardocovo.wordpress.com/2011/04/03/asp-mvc3-editing-records-with-jqueryui-dialogs-and-ajaxforms/
-covo

Rails: Prevent duplicate entry from simultaneous submission

We're having a problem with our app allowing people to sign up multiple times with the same account information (email, specifically).
Our user model validates the uniqueness of the email parameter, and we are also using some javascript to make sure that once the "sign up" button is clicked, it becomes unusable unless the sign up fails (theoretically ensuring only a single click).
It appears that the problem stems from users double-clicking the signup button before the javascript on the page finishes loading.
Is there a way from the Rails side that we can prevent this? Maybe something that creates a request stack, and then iterates through them? I ask because we can't be the only site that has this issue.
Thanks
Dumb question: Why don't you set the field in the database itself to unique?
If that is not possible, do what Steve Bourne suggested and use something like this:
var clicked = false;
$('#submit_button').click( function() {
$(this).preventDefault();
if(!clicked) {
clicked = true;
$('#submit_button').attr('disabled','disabled');
$('#form').submit();
}
Now, I didn't test that so setting clicked = true may be overkill ;)
Set the text field to Nothing right after the insert.
Not entirely convinced that's what's happening, but another option would be to only enable the submit button in jQuery's ready function, and start off with it hidden or disabled. Are you running a large amount of JS outside of the ready function?
If the problem is that users submit the form before your javascript is finished loading, why not make that impossible? (i.e. the submit button action doesn't submit the form, but your javascript submits on the click event?)
There are a few ways to then prevent dupes in JS; sounds like you've got that covered already.

Resources