Add password to Django Group model - django

Is it possible to password protect groups so that a user can only join a group if the user has a password for that group?
I know it's possible to add fields to the Group model with
Group.add_to_class('password', CharField(max_length=180, null=True, blank=True))
But how can I implement this in a secure way? I want the password to be properly hashed and stored. In essence, how can I add the User password field to Group as well?
EDIT:
The docstring for the Group class in the source.
Groups are a generic way of categorizing users to apply permissions,
or some other label, to those users. A user can belong to any number
of groups.
Beyond permissions, groups are a convenient way to categorize users to
apply some label, or extended functionality, to them.
All I'm using a group for here is a label, and I want to password protect that label for when users try to join.

After considering your responses in the comments, I believe your best bet will be to subclass Group and then implement an authentication procedure within it. Then you can use the functions make_password, check_password, and is_password_usable as documented here to manage the authentication process.

Related

Mongoid update association with existing documents, not creating a new one

I'm attempting to figure out how to elegantly update a document's associations with existing documents with Mongoid.
If I have Users and Groups, and want to assign a User to an existing group, how could I do this via update_attributes ?
I want to be able to do something like this:
user.attributes = { groups: [{"_id":"existing group id here"}]}
user.save
When I try to do the above, Mongoid attempts to INSERT a new group, thereby causing a ID duplicate error.
I have tried doing the same via nested attributes:
user.groups_attributes = [{"_id":"existing group id here"}]
user.save
And the same error occurs. Is there anyway I can do this WITHOUT having to manually query the group id and push it into the array? The reason I'm asking is because lets say i have a model with many associations.. i dont want to have to have blocks of code to update each association manually
Assigning an an existing User to an existing Group with update_attributes is very simple (assuming you already added the Group/User relation).
user.update_attributes(:group_id => 'existing group id here')

how to set the default permission name created in Django?

I'm using django 1.6
Now when I define a model, it will create three permissions record for it (can_create, can_update, can_delete).
I'm now adding other permissions on the models (which doesn't matter in this question), and want to make a view to let the user assign them all to users and groups.
Now the problem is:
I want to replace the default name displayed for the three default created permissions.
Is there any way to do this?
Yes there is possibility to create custom permission while creating the models/table in django. But this will create the extra custom permission, by default 3 permission will create i.e( add, change, delete). One can create custom permission by following thing.
class Task(models.Model):
...
class Meta:
permissions = (
("view_task", "Can see available tasks"),
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
)
The only thing this does is create those extra permissions when you run manage.py migrate (the function that creates permissions is connected to the post_migrate signal). Your code is in charge of checking the value of these permissions when a user is trying to access the functionality provided by the application (viewing tasks, changing the status of tasks, closing tasks.) Continuing the above example, the following checks if a user may view tasks:
user.has_perm('app.view_task')
One can see the django doc here django permission description
Based on this blog post and this Django ticket, I would say it is not possible and also not advisable to change these codenames (since they are used in the admin). It is however possible to change the human readable name (such as 'Can add permission')
I cannot add comment to the answers already there, hence adding a new answer.
I have been looking for a solution and could not find any. So I just make use of default permissions and use permissions to re-create them and use whatever name and codename you want. Django documentation here
class Foo(models.Model):
title = models.CharField(max_length=250)
class Meta:
default_permissions = ()
permissions = (
("add_foo", "Can add foo"),
("change_foo", "Can change foo"),
("delete_foo", "Can delete foo"),
("view_foo", "Can view foo"),
("list_foo", "Can list all foo")
)

URLs design for filtering Model's attributes in Django

Need you advice.
In my Django app I have a model for some Users. Users have some attributes: name, age, phone etc. This is not the API site. Just my intranet user's catalog.
I can get users using the following URLs:
/users/ # All users
/users/12 # User with ID 12
But how should I design URLs to get only the users which meet certain criteria? Is it a good practice to use query string for that purpose? Like this:
/users/ # All users
/users/?name=John # Filter by name
/users/?name=Peter&age=25 # Filter by name AND age
If the point is to display the same object list just filtered / sorted on a specific criteria (or set of) then yes, that's one of the purpose of query strings.

How to find user group and use of caching in django?

I am new to django/python and working my way through my webapp. I need assistance in solving one of my problems.
In my app, I am planning to assign each user (from auth_user) to one of the group ( from auth_group). Each group can have multiple users. I have entry in auth_group, auth_user and auth_user_groups. Here is my question:
At time of login I want to check that logging user belongs to which group?
I want to keep that group info in session/cache so all pages I can show information about that group only.
If you have any sample code will be great.
Giving support to the very well #trinchet's answer with an example of context_processor code.
Puts inside your webapp a new file called context_processors.py and writes this lines on it:
def user_groups(request):
"""
Add `groups` var to the context with all the
groups the logged in user has, so you can access
in your templates to this var as: {{ groups }}
"""
groups = None
if request.user.is_authenticated():
groups = user.groups
return {'groups': groups}
Finally on your settings.py add 'webbapp.context_processors.user_groups'to TEMPLATE_CONTEXT_PROCESSOR:
TEMPLATE_CONTEXT_PROCESSORS = (
'webbapp.context_processors.user_groups',
)
1) Be user an instance of auth.models.User, you can get all groups the user belong to, through user.groups. If you want to ask at time of login then you should do this in your login view.
2) You can use session or cache approaches to deal with, this is irrelevant, but once you have the group you need to render the pages having this value, i mean, you need to provide the group to the template rendering, to do this I suggest to you using a custom context processor.

Django: structuring a complex relationship intended for use with built-in admin site

I have a fairly complex relationship that I am trying to make work with the Django admin site. I have spent quite some time trying to get this right and it just seems like I am not getting the philosophy behind the Django models.
There is a list of Groups. Each Group has multiple departments. There are also Employees. Each Employee belongs to a single group, but some employees also belong to a single Department within a Group. (Some employees might belong to only a Group but no Department, but no Employee will belong only to a Department).
Here is a simplified version of what I currently have:
class Group:
name = models.CharField(max_length=128)
class Department
group = models.ForeignKey(Group)
class Employee
department = models.ForeignKey(Department)
group = models.ForeignKey(Group)
The problem with this is that the Department select box on the Employees page must display all Departments, because a group has not yet been set. I tried to rectify this by making an EmployeeInline for the GroupAdmin page, but it is not good to have 500+ employees on a non-paginated inline. I must be able to use the models.ModelAdmin page for Employees (unless there is a way to search, sort, collapse and perform actions on inlines).
If I make EmployeeInline an inline of DepartmentAdmin (instead of having a DepartmentInline in GroupAdmin), then things are even worse, because it is not possible to have an Employee that does not belong to a Group.
Given my description of the relationships, am I missing out on some part of the Django ORM that will allow me to structure this relationship the way it 'should be' instead of hacking around and trying to make things come together?
Thanks a lot.
It sounds like what you want is for the Department options to only be those that are ForeignKey'ed to Group? The standard answer is that the admin site is only for simple CRUD operations.
But doing what you're supposed to do is boring.
You could probably overcome this limitation with some ninja javascript and JSON.
So first of all, we need an API that can let us know which departments are available for each group.
def api_departments_from_group(request, group_id):
departments = Department.objects.filter(group__id=group_id)
return json(departments) # Note: serialize, however
Once the API is in place we can add some javascript to change the <option>'s on the department select...
$(function() {
// On page load...
if ($('#id_group')) {
// Trap when the group box is changed
$('#id_group').bind('blur', function() {
$.getJSON('/api/get-departments/' + $('#id_group').val() + '/', function(data) {
// Clear existing options
$('#id_department').children().remove();
// Parse JSON and turn into <option> tags
$.each(data, function(i, item) {
$('#id_department').append('<option>' + item.name + '</option>');
});
});
});
}
});
Save that to admin-ninja.js. Then you can include it on the admin model itself...
class EmployeeAdmin(models.ModelAdmin):
# ...
class Media:
js = ('/media/admin-ninja.js',)
Yeah, so I didn't test a drop of this, but you can get some ideas hopefully. Also, I didn't get fancy with anything, for example the javascript doesn't account for an option already already being selected (and then re-select it).