Filtering filtered data - grails

I'm new to grails and MVC so please bear with me.
I have some links on my GSP that do some static filtering. For instance, the example below returns only
those Request domain class instances with status Open. But I also want to be able to do some dynamic filtering on the same model (results in the code bellow).
Use case would be something like this: User sees all Request domain class instances in the table. He clicks on the link Open requests and gets only those Request instances that have status property with value Open. Than he sets dateFrom and dateTo using date picker control and clicks on the Filter button which calls the method/action that further filters data from the table. So it should return only those request that are opened and that are created within the specified period.
def openedRequests = {
def contact = Contact?.findByUser(springSecurityService.currentUser)
def productlines = contact.productlines()
def requestCriteria = Request.createCriteria()
def results = requestCriteria.list {
eq("status", "Open")
and {
'in'("productline",productlines)
}
}
render(view:'supportList', model:[requestInstanceList:results, requestInstanceTotal: results.totalCount])
}
EDIT
On my GSP I have few links that call controller actions which perform some domain class instances filtering. For example I have OpenedRequests, ClosedRequests, NewRequests. But I also have some textboxes, comboboxes, datePicker controls for additional filtering. I call the filterRequests action with a button.
def filterRequests = {
def contact = Contact?.findByUser(springSecurityService.currentUser)
def productlines = contact.productlines()
def requestCriteria = Request.createCriteria()
def results = requestCriteria.list {
if(params.fDateFrom && params.fDateTo){
def dateFrom = new SimpleDateFormat("dd.MM.yyyy").parse(params.fDateFrom_value)
def dateTo = new SimpleDateFormat("dd.MM.yyyy").parse(params.fDateTo_value)
between("dateCreated",dateFrom,dateTo)
}
if(params?.fStatus){
eq("status",params.fStatus)
}
if(params?.fCompany){
eq("company", params.fCompany)
}
and {'in'("productline",productlines)
}
if(params.sort != null && params.order != null){
order(params.sort, params.order)
}
}
render(view:'supportList', model:[requestInstanceList:results, requestInstanceTotal: results.totalCount])
}
I want to be able to filter Request instances with some of mentioned links and than if I set up some additional filters, for example dateFrom i dateTo with datePicker. I want those filters to be aware of previous filtering with link if there were any. What is the right way to do this?

You can use DetachedCriterias which where introduced with Grails 2.0.
A DetachedCriteria is independed from any session and can be reused easily:
def openRequests = new DetachedCriteria(Request).build {
eq("status", "Open")
and {
'in'("productline",productlines)
}
}
Then upon your next sub-filter request you can reuse the DetachedCriteria and perform a sub-query on it, like:
def results = openRequests.findByStartDateBetweenAndEndDateBetween(dateFrom, dateTo, dateFrom, dateTo)
Of course you have to remember somehow what the original query was (session, request param), to use the correct criteria as a basis for the sub-query.
(Disclaimer: I haven't yet tried detached criterias myself)

David suggested that I use Detached Criteria but I am using Grails 1.3.7 for my app. So, at the moment this isn't an option. I also thought of using database views and stored procedures but I wasn't sure how that will work with Grails (but that is something that I will definitely have to explore) and I wanted some results fast so I did something not very DRY. When I filter table with one of the mentioned links I save the name of the link/action in session and in filterRequest action (that does additional filtering) I check the session to see if there has been any previous 'link filtering' and if it were I apply those filters on the table with criteria, and after that I apply the filters that were manualy entered. I don't like it but that's all I came up with with my limited understanding of Grails. Below is my filterRequest action:
def filterRequests = {
def contact = Contact?.findByUser(springSecurityService.currentUser)
def productlines = contact.productlines()
def requestCriteria = Request.createCriteria()
def results = requestCriteria.list {
if(session.filter == "newRequests"){
and{
isNull("acceptedBy")
ne("status", "Closed")
}
}
if(session.filter == "openRequests"){
and{
ne("status",'Closed')
}
}
if(session.filter == "closedRequests"){
and{
eq("status", "Closed")
}
}
if(session.filter == "myRequests"){
and{
eq("acceptedBy", contact.realname)
}
}
if(params.fDateFrom && params.fDateTo){
def dateFrom = new SimpleDateFormat("dd.MM.yyyy").parse(params.fDateFrom_value)
def dateTo = new SimpleDateFormat("dd.MM.yyyy").parse(params.fDateTo_value)
and{
between("dateCreated",dateFrom,dateTo)
}
}
if(params?.fAcceptedBy){
and{
eq("acceptedBy", params.fAcceptedBy)
}
}
if(params?.fStartedBy){
and{
eq("startedBy", params.fStartedBy)
}
}
if(params?.fCompany){
and{
ilike("company", "%" + params.fCompany +"%")
}
}
and {'in'("productline",productlines)
}
if(params.sort != null && params.order != null){
order(params.sort, params.order)
}
}
}

Related

Jenkins Extensible Choice with user specific items based on users Roles

I have a situation where I would like to alter the contents of a choice parameter in a Jenkins parametrised build.
In my case I would like one project for deploying the application 'Deploy My App'. When building this project the user is presented with a choice parameter. I would like to alter the contents of this list depending on a user role. i.e. someone with the 'dev_deploy' role will be able to see the dev environments, someone with the 'test_deploy' role will be able to see the test environments etc.
I am currently using the Extensible Choice Parameter plugin and the Role-based Authorization Strategy plugin.
I know that I can write some groovey script to generate the list items for the choice.
def result = ["-------"]
def roles=??????
if(roles.get('dev_deploy') {
//Add dev environments
result.add('dev1')
....
}
if(roles.get('test_deploy') {
//Add test environments
result.add('test1')
....
}
return result
I just can't figure out who to get hold of the users roles?
Anyone know how I might do this, or have different solution to the problem?
Many thanks
OK, after a few more searches I came across the source (https://github.com/jenkinsci/role-strategy-plugin/tree/master/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy)
After further reading and a bit of playing around I came up with this...
import com.michelin.cio.hudson.plugins.rolestrategy.*
def result = ["-- Please Select --"]
def authStrategy = jenkins.model.Jenkins.instance.getAuthorizationStrategy()
if(authStrategy instanceof RoleBasedAuthorizationStrategy){
def currentUser = jenkins.model.Jenkins.instance.getAuthentication().getName();
def roleMap= authStrategy.roleMaps.get("globalRoles")
def sids= roleMap.getSidsForRole("Manage_Dev")
if(sids != null && sids.contains(currentUser)) {
result.add("dev1")
...
}
sids= roleMap.getSidsForRole("Manage_Test")
if(sids != null && sids.contains(currentUser)) {
result.add("tst1")
...
}
...
}
return result
Which works for me. Easy when you know how!
I came across code that helps me to overcome the same problem. I have refined the choice by using the Active Choice plugin and the Jenkins user role. Admins will have different drop-down options and a tester will have different drop-down options.
https://kinoshita.eti.br/2016/04/24/using-active-choices-with-role-strategy-plugin.html
import hudson.model.User
import hudson.model.Hudson
import hudson.security.AuthorizationStrategy
import hudson.security.Permission
import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy
import com.michelin.cio.hudson.plugins.rolestrategy.RoleMap
AuthorizationStrategy strategy = Hudson.getInstance().getAuthorizationStrategy();
jobs = []
user = User.current()
userId = user.getId()
if (strategy != null && strategy instanceof com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy) {
roleStrategy = (RoleBasedAuthorizationStrategy) strategy;
// not very straightforward to get the groups for a given user
roles = roleStrategy.getGrantedRoles("globalRoles")
for (entry in roles) {
role = entry.key
users = entry.value
if (role.getName().equals("tester")) {
if (userId in users) {
jobs = ["PROJECT_FOR_TESTERS1", "PROJECT_FOR_TESTERS2"]
break
}
} else if (role.getName().equals("admin")) {
if (userId in users) {
jobs = ["PROJECT_FOR_ADMINS1", "PROJECT_FOR_ADMINS2"]
break
}
}
}
}
return jobs
I had to make a slight adjustment to the accepted answer to get it to work.
See Cannot invoke method getSidsForRole() on null object
import com.michelin.cio.hudson.plugins.rolestrategy.*
def result = ['-- Please Select --']
def authStrategy = jenkins.model.Jenkins.instance.getAuthorizationStrategy()
if(authStrategy instanceof RoleBasedAuthorizationStrategy){
def currentUser = jenkins.model.Jenkins.instance.getAuthentication().getName();
def roleMap = authStrategy.getRoleMap(com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType.Global)
def sids = roleMap.getSidsForRole("develop")
if(sids != null && sids.contains(currentUser)) {
result.add('staging')
}
sids = roleMap.getSidsForRole("admin")
if(sids != null && sids.contains(currentUser)) {
result.add('prod')
}
}
return result

GRAILS: findALL() vs FindBy---(params.id)

Greeting everyone,
I am trying to pass a parameters from a URL to a findAll() method.
LINE3 I use findAll() to define mouse.
LINE2 def house will bring in the parameter DELAWARE when I go to the page: http://localhost:8080/TestApp/home/county/DELAWARE
House will only show one instance instead of a list.. is there anyway to pass the url instead of ["DELAWARE"]? (please see line 3) thanks :)
def county() {
def house = Home.findByCounty(params.id) //sends only user related address to view
def mouse = Home.findAll("from Home h where h.county= ?", ["DELAWARE"]);
if (!house) {
response.sendError(404)
} else {
[house:house, mouse:mouse ]
}
}
Working Code +1 #Danilo
def county() {
def house = Home.findAllByCounty (params.id) //sends only county specified thru URL e.g. http://localhost:8080/TestAPP/home/county/DELAWARE
if (!house) {
response.sendError(404)
} else {
[house:house ]
}
}
findBy* will return at most one row, if you want to get all rows use findAllBy*
In order to understand how the URL will be used by Grails you have to have a look at conf/UrlMappings.groovy. You may find something like this:
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
}
}
this means that when you call TestApp/home/county/DELAWARE what Grails is trying to do is use the home controller (HomeController), invoking the county method (def county(){...}) and passing DELAWARE as id.
This should work correctly if inside county method of the HomeController you have:
def filteredInstances = Home.findAllByCounty(params.id)

grails 2.4.4 issue inverting filters schema

In a new project based on grails 2.4.4 I am using filter schema with invert option. Few controllers and some actions from another controllers are excluded in filters by inverting the rule.
Filter will not be applied to Login, ForgotPassword ans ServerError Controllers and saveUser, verifyRegistration actions from different user controller. This filter schema doesn't work as expected.
When I am calling login api inside login controller, filter is getting executed and throws exception.
package com.project.filters
import grails.converters.JSON
class MyProjectAuthorizationFilters {
def userService
def grailsApplication
def filters = {
checkXAuthToken(controller:'login|forgotPassword|serverError', action:'saveUser|verifyRegistration', invert: true) {
before = {
try{
String tokenValue = request.getHeader('X-Auth-Token')
if(tokenValue == null && tokenValue == ""){
throw new MyCustomException(401, "Please provide X-Auth-Token in Header")
}
userService.getUserByAuthToken(tokenValue)
}catch (MyCustomException error) {
error.stackTrace = ""
response.setStatus(error.status)
render error as JSON
return false
}
}
}
}
}
I know we can also use controllerExclude, actionExclude, but did not know why this is breaking?
EDIT
I even tried using controllerExclude and actionExclude but it doesn't work as expected. Is this a weired behaviour or I am doing something wrong. Posting whole filter class code.
Thanks.

What is the best way to process a large list of Domain Objects?

What is the best way to process a large list of Domain Objects?
For example, I have 'Users' and 'Book' domains, and there is a permission READ on Book object.
When I'm adding a new Book, I'd like to set READ permission to all users for this Book.
At first was a code:
def users = Users.findAll{ ... }
users.each { addPermission(book, it, READ) }
I'm using Spring Security Core and ACL plugin.
But now, I think it is not best way to load 10000 Users Objects to memory.
I gonna use the SCROLL method with maxResults(???) from Criteria.
So my question is What the best way? How to determinate the best number of MaxResults?
For something like this, I would do a bulk update. ExecuteUpdate allows you to do such a thing and its much more performant. Look at this example and customize it to your need.
def updatedRecords = User.executeUpdate("update User set permission = 'READ' where somecriteriaOrNot ")
A more of Grails way to do this would be to use the batch processing. Try the example given below:
EDIT : Improved answer. Now, using pagination based batch processing.
def noOfObjectsTobeProcessedAtAtime=1000//Step or pagination size...
List offsetMaxMapList = (0..User.count()-1).step(noOfObjectsTobeProcessedAtAtime).collect{[max:noOfObjectsTobeProcessedAtAtime,offset:it]}
offsetMaxMapList.each{offsetMaxMap->
addPermissionToUserInBatch(params)
}
def addPermissionToUserInBatch(params){
def batch = []
def session
def users = Users.createCriteria().list(params){}
users.eachWithIndex { user, index ->
batch << user
if (batch.size() >= batchSize) {
User.withTransaction {
batch.each {User userObject ->
addPermission(book, userObject, READ)
}
}
batch.clear()
} else if (batch.size() < batchSize && (users.size() - index - 1) == 0) {
User.withTransaction {
batch.each {User userObject ->
addPermission(book, userObject, READ)
}
}
batch.clear()
}
session = sessionFactory.getCurrentSession()
session.clear()
}
}
Hope that helps!!!
Thank you all. I'd like to summarize. I hope ti will be a TEMPLATE to me.
def dc = new DetachedCriteria(Users).build{
//some conditions of criteria
}
def count = dc.count()
// Optional:
// dc = dc.build{
// projections { property('username') }
// }
def batchSize = 50 // Hibernate Doc recommends 10..50
0.step(count, batchSize){ offset->
dc.list(offset:offset, max:batchSize).each{
// doSmthWithTransaction(it)
}
//clear the first-level cache
//def hiberSession = sessionFactory.getCurrentSession()
//hiberSession.clear()
// or
Users.withSession { session -> session.clear() }
}
P.S. I don't use Transaction here since I use it on the doSmthWithTransaction method

How to restrict user from accessing a particular action of a controller?

def filters = {
loginCheck(controller:'*', action:'*') {
before = {
if(!session.user && !session.merchants)
{
redirect(action:'login')
return false
}
}}
That was my filter for login security. And below is interceptor in action for restricting user from search action. But both are not working. Can any one tell what the mistake is?
def beforeInterceptor = [action:this.&checkUser,Only:'search']
def scaffold = true
def checkUser()
{
if (session.user)
{
redirect(action:"search")
}
if(session.merchants)
{
redirect(action:"toIndex")
flash.message="You are not valid user for this action"
return false
}
}
There's a really nice shorthand that you can apply directly to actions (if you're not using filters):
#Secured(['ROLE_USER'])
def search = {
}
You can give every user ROLE_USER and simply require that role.
actually you filter should work as it is set for every controller and every action.
here is the grails example from the reference:
class SecurityFilters {
def filters = {
loginCheck(controller:'*', action:'*') {
before = {
if(!session.user && !actionName.equals('login')) {
redirect(action:'login')
return false
}
}
}
}
}
I worked with this one and it worked for me.
What I'm not sure is about the session.merchants in your code.
What is this ?
Did you follow this:
To create a filter create a class that
ends with the convention Filters in
the grails-app/conf directory.
Edit:
If you use spring security you don't need to add a filter or interceptor.
check the user guide: http://burtbeckwith.github.com/grails-spring-security-core/docs/manual/index.html
you can configure it with url mappings or annotations.
ex.
grails.plugins.springsecurity.controllerAnnotations.staticRules = [
'/js/admin/**': ['ROLE_ADMIN'],
'/someplugin/**': ['ROLE_ADMIN']
]
EDIT 2:
To get the logged in user use:
def authenticateService
...
def action{
def user = authenticateService.principal()
def username = user?.getUsername()
...

Resources