Here's a (fictive) structure (sorry, I know I don't have a strong imagination) which to resembles the structure of my application:
(Person, SuperPerson and NormalPerson are abstract):
I'm wondering if I can grant roles to each class (so all the instances of this class will get these rights) using spring security in Grails.
No, Spring Security grants roles at the instance, not class, level.
There are two ways to grant authorities (permissions):
Grant them directly to a principle (user)
Grant them to a group (role), and then add principles to the groups containing the authorities then need.
So in your case, you can take the role-based approach and have each class represented by a role. Then, when you create an instance of a class, add it to the appropriate role.
Example of using a service to grant authorities
class PersonService {
private Map authorityGroups = [
Person: ['Person'],
NormalPerson: ['Person', 'NormalPerson'],
Employee: ['Person', 'NormalPerson', 'Employee'],
...
]
Person create(Class klass, Map props) {
def person = klass.newInstance(props)
def groups = getGroupsForClass(klass)
person.save()
groups.each {
new PersonAuthorityGroup(authorityGroup: it, person: person).save()
}
}
private Collection<AuthorityGroup> getGroupsForClass(Class klass) {
authoritiyGroups[klass.simpleName].collect {
AuthorityGroup.findByName(it)
}
}
}
The service can then be used like this:
def a = personService.create(Person, [first: 'John', last: 'Galt'])
def b = personService.create(SuperPerson, [first: 'Jane', last: 'Smith'])
Related
I'm new to Django, I'm starting a Django app, I'm using the registration app and Django rest framework, this app will also serve as the backend for an iOS app, I already manage to create users from the iOS app and edit User Profiles from the browser, I need help to find an approach that lets the user edit his profile details once he is logged in. I tried setting a lookup_field like "user.username" to the UserProfileViewSet so I could access the UserProfile object and edit it, but that didn't work.
I also thought I could somehow return the user id after logging in and use this id to reference the userprofile I wanna edit but it does not seem practical at all. What do you think??
Other way I found to do this is by calling user.get_profile() but I don't know how I can make this work from the iOS app.
Here is my UserProfile Model, and serializers, any help would be great. Thanks
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
# Extra attribuets
pagetitle = models.TextField(null=False)
location = models.TextField(null=True)
website = models.TextField(null=True)
bio = models.TextField(null=True)
sex = models.TextField(null=True)
birthdate = models.DateField(null=True)
def __unicode__(self):
return "%s's profile" % self.user
def create_profile(sender, instance, created, **kwargs):
if created:
profile, created= UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_profile, sender=User)
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('username','email','password')
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer(many=False)
class Meta:
model = UserProfile
fields = ('user','bio')
I create the UserProfile using a signal like this
def create_profile(sender, instance, created, **kwargs):
if created:
profile, created= UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_profile, sender=User)
You didn't post your ViewSet code, so I have to guess. But your model should be set to User and your lookup_field should be set to username.
Something like:
class UserViewSet(ModelViewSet):
model = User
lookup_field = "username"
For everyone trying to get the profile for a user this is the way I managed to do it thanks to Kevin's help.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
depth = 1
fields = ('id','username','email','profile')
class CurrentUserView(APIView):
def get(self, request):
serializer = UserSerializer(request.user)
return Response(serializer.data)
Using a url like this
url(r'^api/current-user',CurrentUserView.as_view(),
name="current_user"),
You will get an answer like this
"id": 1,
"username": "username",
"email": "email#email.com",
"profile": {
"id": 1,
"user": 1,
"bio": "My Bio",
"sex": "M",
"birthdate": "1987-12-02"
}
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.
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])
I have a Spock test case, in which the setup block looks like this :
setup: "set the required objects"
def company = new Company(shortName:"infyyy",fullName:"infoysys",
region:"tamilnadu" ,email:"a#ac.com" ,telphone:34343433,fax:34343433).save(failOnError:true)
def project = new Project(name:"testing")
def userInstance = new User(username:username,password:password,
company:company,project:project,pt:["dummy"]).save(failOnError:true)
def tasksInstance = new Tasks(title:"testingwork",startDate:(new Date()-4),endDate:(new Date().clearTime()-6),description:"blah blah",project:project,completed:true,user:userInstance).save(failOnError:true)
And more over, the Tasks domain class looks like this :
class Tasks {
static belongsTo = [ user : User, project: Project ]
//other code
}
And User class is like this :
class User {
static hasMany = [ holidays : Holiday, tasks : Tasks, pt:String, project: Project ]
//other code
}
But when I run my test and my test fails(not with an error message, but it fails in the then block of my Spock test) and I find a error in it. My setup doesn't create any relationship between User and Tasks, which makes my test to fail.
The controller code, which I'm trying to test is :
def todaysTasks() {
def user = User.get(springSecurityService.principal.id)
def choice = params.managersProject
params.max = Math.min(params.max ? params.int('max') : 10,100)
def search = Tasks.createCriteria().list(max: params.max as Integer, offset: params.offset as Integer, order: params.order as String, sort : params.sort) {
and {
project {
like('name',"${choice}")
}
eq('endDate', new Date().clearTime())
}
}
println "todays task selected project is " + search
[tasksInstanceList : search, tasksInstanceTotal: search.getTotalCount() ]
}
The println in the above test prints 0. Why does this happen even though I'm making the endDate in my test less than today's date?
Thanks in advance.
As far as I know, GORM does not auto-populate relationships, by following a belongsTo relationship.
I always do the following.
def u=new User(...)
u.addToTasks(
title:"testingwork",
startDate:(new Date()-4),
endDate:...
)
u.save()
Note that I have not created a task object. I have passed the Map of values directly to addToX... this emphasizes that the added object belongs to User and should be instantiated and saved by GORM.
You cannot (and you shouldn't) test criteria queries in your unit tests. Criteria queires are not supported in grails (and spock) unit tests. Read this question for possible solutions.
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?