Match one Sec role among many roles of SecUser grails - grails

I have admin user with following five roles[ROLE_ADMIN,ROLESWITCHUSER,ROLE_DOCTOR,ROLE_USER]
and some normal users with only one role i.e ROLE_USER ,now my question is how can i get only normal users from my secuser table i tried with somne iterations
def roleId=SecRole.findByAuthority("ROLE_USER")
userInstance = SecUserSecRole.findAllBySecRole(roleId).secUser
here i got userInstance with all users along with adminuser now i tried to elminate adminuser from my userInstance and saved it in selectUserMap but am getting result for sometime and sometimes its giving all users. I think the sort() function not sorting the userinstansce roles please help me
for(int i=0;i<userInstance.size();i++)
{
println( "am in loop "+i+userInstance[i].username+"roles"+userInstance[i].getAuthorities())
def x=(userInstance[i].getAuthorities().sort())
for(a in x )
{ //println(a.getAuthority())
if((a.getAuthority() == use))
abc=true
else
abc=false
if((a.getAuthority() == adm))
{
println("break")
break;
}
abc=(abc && (a.getAuthority() == use))
if(abc)
{
println("am in true if ")
selectUserMap.add(j,userInstance[i])
j=j+1
}
else
{
println("am in else")
}
}
}
println("==============all users "+selectUserMap)

One thing that would help is to use hierarchical roles - see section "14 Hierarchical Roles" at http://grails-plugins.github.com/grails-spring-security-core/docs/manual/ - and then you wouldn't grant ROLE_USER to anyone but "real" users. If you define your hierarchy like this:
grails.plugins.springsecurity.roleHierarchy = '''
ROLE_ADMIN > ROLE_USER
ROLE_DOCTOR > ROLE_USER
ROLE_ADMIN > ROLE_DOCTOR
'''
then you don't need to explicitly grant ROLE_USER in the database to either a doctor or an admin, but the role will be inferred as granted. Then your original query will work:
def userRole = SecRole.findByAuthority("ROLE_USER")
def usersWithUserRole = SecUserSecRole.findAllBySecRole(userRole).secUser
If you can't or don't want to do this, then you should use a proper database query. It's extremely and unnecessarily expensive to load every user and every user's roles from the database and filter them out in your application. Use this HQL query:
def userRole = SecRole.findByAuthority("ROLE_USER")
def users = SecUserSecRole.executeQuery(
'select u from SecUser u where ' +
'(select count(ur.user) from SecUserSecRole ur where ur.user=u)=1 and ' +
'(:r in (select ur.role from SecUserSecRole ur where ur.user=u))',
[r: userRole])

Oh, you've choosen really complicated way, can understand your algorhitm :( Maybe this is enough:
List selectUserList = userInstance.findAll {
List roles = it.authorities*.authority
return roles.contains('ROLE_USER') && !roles.contains('ROLE_ADMIN')
}
I guess you can build selectUserMap from this selectUserList list (can't understand where you get j)
PS maybe it's better to rename userInstance to userInstances, because it's a list actually, not one instance?

Related

Ruby passing parameters

What is the proper way to pass the parameters to a function?
For example:
def self.find_by_example(username, email)
user = User.find_by_username(username) || User.find_by_email(email)
end
I would like to find the user by his username or email but if a create a function passing the 2 parameters Rails shows
(wrong number of arguments (given 1, expected 2))
When I call User.find_by_example('example')
I still don't get it, the parameters passed in must not be the attribute?
and why does it say "given 1"?
You must be calling the function like `User.find_by_example("name to find") and the function expects two arguments (name and email). You could define the function as:
def self.find_by_example(term)
user = User.find_by_username(term) || User.find_by_email(term)
end
And call it User.find_by_example("Name to find") or User.find_by_example("email#to_find.com")
This does not work ok if you have users with a username like an email. And it is not much efficient if you wish to search by other fields. SO you could also:
def self.find_by_example(terms)
if terms[:email]
user = User.find_by_email(terms[:email])
elsif terms[:username]
user = User.find_by_username(terms[:username])
elsif terms[:id]
user = User.find_by_id(terms[:id])
elsif terms[:document]
user = User.find_by_document(terms[:document])
end
end
And call the method User.find_by_example(:email => "email#example.com"). This is similar to the find_by method that Active Record already provides (but allows many arguments), so no need to implement it.
The proposed and accepted answer is not really the equivalent of the code asked in the question. It is accepted, so one might assume that it guessed the OP intent correctly. But I think it can be useful for (especially junior) programmers to think about the problem more deeply.
Think of what method should do
(not just if it immediately gives you result you wish to see, there can be surprises in the edge cases)
The original code
def self.find_by_example(username, email)
user = User.find_by_username(username) || User.find_by_email(email)
end
Could be used this way x.find_by_example(nil, 'test#example.com').
If we assume there can't be users with NULL username (which IMO is a reasonable assumption), the call would result in finding an user strictly by email.
The proposed solution does not give you this possibility:
def self.find_by_example(term)
user = User.find_by_username(term) || User.find_by_email(term)
end
x.find_by_example('test#example.com') would return user with such username if exists, and (possibly other) user with such e-mail otherwise.
In other words - you have less control which field is used to find a user (which can be correct, if that's really what you need)
So it depends on the OP intent.
If one want to retain how the original method works, but improve the interface, it could be done like this:
def self.find_by_example2(username: nil, email: nil)
user = User.find_by_username(username) || User.find_by_email(email)
end
And calling x.find_by_example2(email: 'test#example.com') is equivalent to x.find_by_example(nil, 'test#example.com') but looks better.
Bonus: Performance implications
The proposed solution
def self.find_by_example(term)
user = User.find_by_username(term) || User.find_by_email(term)
end
makes second query when the user is not find by username. You can improve it as well if you wish to employ some sql magic:
def self.find_by_example(term)
user = User.where("username = ? OR (username IS NULL and email = ?)", term, term).first
end
There's another possibility (though not 100% equivalent to the accepted solution):
def self.find_by_example(term)
user = User.where("username = ? OR email = ?", term, term).first
end
(I'll leave as an exercise the answer how those are different, to keep this post short...ish)
Bonus 2: flexibility
This
def self.find_by_example(terms)
if terms[:email]
user = User.find_by_email(terms[:email])
elsif terms[:username]
user = User.find_by_username(terms[:username])
elsif terms[:id]
user = User.find_by_id(terms[:id])
elsif terms[:document]
user = User.find_by_document(terms[:document])
end
end
is a waste of your time, because rails gives you already better interface to do this.
Instead of calling
x.find_by_example(document: 'foo')
you could just do
User.find_by(document: 'foo')
There's really no need to implement it that way, it's basically crippled version of ActiveRecord interface, that you have to maintain as you add new fields to User model.

Dynamically excluding fields in inline django admin

I want to exclude some fields in my inline based on my request user.
I know somehow I can handle this with methods like 'get_formsets', 'add_view', 'change_view', but I'm not sure what the syntax is.
Any suggestions?
I achieved what I needed with the next code in my inline class:
def get_formset(self, request, obj=None, **kwargs):
if request.user.groups.all().count() > 0:
if request.user.groups.all()[0].name == 'User Group Name':
kwargs['exclude'] = ['field_to_exclude',]
return super(MyInline, self).get_formset(request, obj, **kwargs)
The answer to this question gave me the hints: different fields for add and change pages in admin
There's also the get_exclude hook:
class FoodInline(TabularInline):
model = Food
def get_exclude(self, request, obj=None):
group = request.user.groups.first()
if group and group.name == 'User Group Name':
return ['field_to_exclude', ]
return self.exclude

how to do a grails findAllBy with one to many relationship

I have a one to many relationship between User and Task and want to get all of the tasks for that user, but my query isn't returning any results. Here's what I have:
def getByStatus(String findBy) {
// either get by Open/Closed or by All.
def sortPref = [sort: "deadline", order: "asc"]
def u = User.get(session.user.id) // session.user is a user domain object
if (findBy != "All")
tasks = Task.findAllByUserAndStatus(u, findBy, sortPref)
else
tasks = Task.findAllByUser(u, sortPref)
}
I also tried searching by .findByUserIdAndStatus, but got an error saying that there was no UserId property on task. If I remove the user part, then I get all of the tasks.
Assuming there is no issue with lazy loading (assuming # of tasks per user is not considerably high), you can directly get required tasks based on user (from 1:M relationship) as:
def getByStatus(String findBy) {
// either get by Open/Closed or by All.
def u = User.get(session.user.id) // session.user is a user domain object
def tasks = findBy == 'All' ? u.tasks.sort{it.deadline} :
u.tasks.findAll{it.status == findBy}.sort{it.deadline}
}
If interested, you can have the sort set in the mapping of the domain class.

Grails - find all by presence of child obj in parent collection

I have 2 domain classes: Project and User.
Project hasMany on User via a SortedSet called allowedUsers. User does not belong to Project.
I want to find all Projects that a particular user is allowed to see. So trying syntax like:
Project.findAll{ it.allowedUsers.contains( userA ) }
Project.findAll{ userA in it.allowedUsers }
These dont work. And the find notation doesn't appear to support something like a ThatContains operator.
How can I achieve my aim?
Criteria should work, take a look at the "querying associations" section in http://grails.org/doc/latest/guide/GORM.html#criteria. Can you try the following:
def c = Project.createCriteria()
def results = c.list {
allowedUsers{
eq('id', userA.id)
}
}
You can also try where queries
def query = Project.where{
allowedUsers{id == userA.id}
}
def results = query.list()
or HQL
def query = """
select p from Project as p
inner join p.allowedUsers as user
where user.id = :user
"""
def results = Project.executeQuery(query, [user: userA.id])

How to query many fields, allowing for NULL

I have a Rails site that logs simple actions such as when people upvote and downvote information. For every new action, an EventLog is created.
What if the user changes his or her mind? I have an after_create callback that looks for complementary actions and deletes both if it finds a recent pair. For clarity, I mean that if a person upvotes something and soon cancels, both event_logs are deleted. What follows is my callback.
# Find duplicate events by searching nearly all the fields in the EventLog table
#duplicates = EventLog.where("user_id = ? AND event = ? AND project_id = ? AND ..., ).order("created_at DESC")
if #duplicates.size > 1
#duplicates.limit(2).destroy_all
end
The above code doesn't quite work because if any of the fields happen to be nil, the query returns [].
How can I write this code so it can handle null values, and/or is there a better way of doing this altogether?
If I understood this correctly,
some of the fields can be nil, and you want to find activity logs that have same user_id, same project_id or project id can be nil.
So I guess this query should work for you.
ActivityLog.where(user_id: <some_id> AND activity: <complementary_id> AND :project_id.in => [<some_project_id>, nil] ....)
This way you would get the complementary event logs where user_id is same and project id may or may not be present
class ActivityLog
QUERY_HASH = Proc.new{ {user_id: self.user_id,
activity: complementary_id(self.id),
and so on....
} }
How about:
# event_log.rb
def duplicate_attr_map
{
:user_id,
:project_id
}
end
def duplicates
attribs = duplicate_attr_map.reject_if(&:blank?)
query = attribs.map { |attr| "#{attr} = ?" }.join(' AND ')
values = attribs.map { |attr| self.send(attr) }
EventLog.where(query, *values).order("created_at DESC")
end
def delete_duplicates(n)
duplicates.limit(n).delete_all if duplicates.size > 1
end
# usage:
# EventLog.find(1).delete_duplicates(2)
not tested, could be improved

Resources