django management commands and user - django

i use the django custom management/commands quite a bit. i would like to provide access to my views via some of these scripts; however i'm in a quandary about how to do this for authenticated users. i'm not really using the middleware libraries, so all i need is access to the request.META['REMOTE_USER']; is there a recommended why i can fake this? eg,
def poll_view( request ):
user = None
if 'META' in request and 'REMOTE_USER' in request.META:
user = request.META['REMOTE_USER']
if not user == None:
do_something()
and in my management/command script i have:
class Command(BaseCommand):
def handle(self, *args, **kwargs):
req = ???
poll_view( req )

Perhaps you can refactor your view to extract just the functionality you need and put it in a helper module. That way, you can call this function in both the view and the management command.
Example:
-----------in a file named somewhere.py----------
def do_this():
#do these
pass
------------in your view-------------------------
from somewhere import do_this
def my_view(request):
user = request.META.get('REMOTE_USER', None)
if user:
do_this()
-----------in your management command----------
from somewhere import do_this
class my_command(...):
def handle(self, args*, kwargs**):
do_this()

Well the management commands are just functions, so instead of calling the view from the command, call the command from the view and pass the request as a parameter
views.py
from managment import Command
def some_view(request):
Command._private_function(request=request)
commands.py
class Command(...):
def _private_function(request=None):
if request:
// do action with request details (i.e. coming from a view)
else:
// do action without (i.e. coming from command line)
def handle(self,...):
_private_function()
This allows you to perform the main logic of your admin-command from either the command line, or from a view (i.e. the web). You need to be careful though and be sure that you actually want the person with access to the view (authenticated user) to perform whatever action it is

Related

How to write a Mixin for checking if the logged user has access to certain model instance

I want to write a Mixin (or use a third-party one) for checking if the logged user is the owner of certain object.
url(r'^api/mymodel/(?P<pk>\d)/?', CreateUpdateMyModel.as_view(), name='create_or_update')
class MyModel(models.Model):
owner = models.OneToOneField('auth.User')
class OwnerRequired(SingleObjectMixin):
# do this self.object = self.get_object() for getting the object
#
# some logic for checking if request.user == self.object.owner
# otherwise return something like Response(status=status.HTTP_403_FORBIDDEN)
Inheriting from SingleObjectMixin is important to me, since I want to be able to do something like:
class CreateUpdateMyModel(APIView, OwnerRequired):
model = MyModel
def post(self, request, *args, **kwargs):
# self.object should be available here
# so that write some code taking it into account
How OwnerRequired should look for fulfilling this?
I'm open to another alternatives, in fact, I've checked PermissionRequiredMixin from django-braces and I'd like to use it, but I'm not sure how to do it
permission_required = ?? # I can code a method for, but how can I pass the model instance and the request.user?
Is there another simple alternative?
Take a look at object level permissions. There's also a relevant example on that page in the examples sections - see the IsOwnerOrReadOnly example.
Also note that object level permissions are only run either:
You are using the GenericAPIView or a subclass of it, and calling get_object() to retrieve the instance.
You explicitly call self.check_object_permissions(request, instance within your view code.

Django: How can i get the logged user outside of view request?

I have a class method (outside of a view) who needs the logged user's information. How can i retrieve the logged in user without passing the request to the method? Unfortunately i can't find nowhere a nice solution and it's not logical just not exist such a way to do it, because Django should store somewhere the logged in user. Otherwise (i believe) it would be impossible to use #login_required decorator from django.contrib.auth.decorators. Right?
So if it's not possible why it's not? Why Django works like this if the only think i want is the logged in user and not all the informations inside request?
Thanks in advance.
About decorators, it is wrong. Actually decorators called with request argument.
I believe better way is that passing user or request object to class's method. But there are other ways to access request.
Here is the code that we use. You need to add this middleware to MIDDLEWARES. And import & calling get_request function.
Update July 2017: Tested with Python 3.6.1 and Django 1.10.7, based in the original code from this answer and in the Writing your own middleware documentation.
First create a new app, ie. startapp request_middleware.
Then add "request_middleware" to your INSTALLED_APPS in settings.py.
After that paste the code bellow in, ie. request_middleware.middleware.py.
Finally add "request_middleware.middleware.RequestMiddleware" to your MIDDLEWARE in settings.py (In my case I've placed it in between 'debug_toolbar.middleware.DebugToolbarMiddleware' and 'django.middleware.security.SecurityMiddleware' as far above the list as I could).
# request_middleware.middleware.py
from threading import current_thread
_REQUESTS = {}
class RequestNotFound(Exception):
def __init__(self, message):
self.message = message
def get_request():
thread = current_thread()
if thread not in _REQUESTS:
raise RequestNotFound('global request error')
else:
return _REQUESTS[thread]
class RequestMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def process_request(self, request):
_REQUESTS[current_thread()] = request
def __call__(self, request):
self.process_request(request)
response = self.get_response(request)
return response
After that simply do from request_middleware.middleware import get_request in order to use get_request in your code.

Having multiple decorators in Django and using only one

I am using django-tokenapi to allow for authentication of an Android project that is using Django as a backend. The project also has a web interface.
django-tokenapi uses the #token_required decorator to protect certain views. Django uses the #login_required decorator to protect certain views.
What I would like is to have only one view that is protected by #login_required OR #token_required so it can be used with either the webapp or Android app.
So, ideally, it would look like this:
#token_required
#login_required
def names_update(request):
....
....
However that does not work. Is there a better method of doing this? Or is the correct thing to have two views, one the webapp and one for Android, that are protected by the proper decorator, and then lead to the same method.
No there's no easy way, if possible at all, to write an generalized OR decorator according to you described.
However, you can write a new decorator that does exactly what you want:
from functools import wraps
from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
def token_or_login_required(view_func):
"""
Decorator which ensures the user is either logged in or
has provided a correct user and token pair.
"""
#csrf_exempt
#wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
user = request.REQUEST.get('user')
if user and user.is_authenticated:
return view_func(request, *args, **kwargs)
token = request.REQUEST.get('token')
if user and token:
user = authenticate(pk=user, token=token)
if user:
login(request, user)
return view_func(request, *args, **kwargs)
return HttpResponseForbidden()
return _wrapped_view
this decorate combines both token_required and login_required decorators, and will allow access to the view either if the user is logged in, or the token is valid.
You could try assigning a new view variable to the old view:
#token_required
def names_update_token(request):
...
#login_required
names_update_login = names_update_token
This should have the effect of creating a second view named names_update_login which is just a pointer to the first so that the code remains the same.
EDIT:
Another thought, and one I have used, is to create a more "generic" view and call it from each of the other views:
def update_token(request):
...
#token_required
def names_update_token(request):
update_token(request)
#login_required
def names_update_login(request):
update_token(request)
This gets around the issue you mentioned while still maintaining only a single copy of the actual code that implements the view.
It is possible to use more than one decorator on a single view. But in this case I think you should seperate the view and apply the decorators seperately. Otherwise token_required decorator will try to authenticate using token which won't be available on a request made using browser.

Persisting session variables across login

I want to hold information about a users preferences in a session variable. If the user chooses a preference while logged out and then later logs in, I want the preference to be maintained without needing to reselect it.
Django sessions maintain a session key in a cookie to track a users session. The way I understand it, this key is changed when a user logs in.
a) Does this mean all session variables are deleted on login or is there any sort of passover
b) In the case of not being able to save preferences across a login, is manually setting cookies the best way to proceed? I imagine a scenario like:
while logged out, maintain preferences in cookie
on login, copy preferences to session variable and write to db (via signal?)
on logout, update cookies with preferences (via signal?)
Update
I managed to get this functionality by saving preferences in the User's profile object, as well as in a cookie (these preferences are not sensitive in any way). When the user is logged in, their profile setting takes preference. When not logged in, the cookie preference is chosen
Upon login, Django calls session.flush() or session.cycle_key(), which makes sure nothing from the old session is kept. This is a security measure that protects you against session fixation vulnerabilities. So, when applying this solution, be aware what kind of variables you want to persist.
If you want to keep some state, you'll have to restore that after the login was issued.
The solution by Chase Seibert was a great start, it was very insecure due to threadsafety issues in that code. You can find an improved version here, which is safe to use:
from functools import wraps
class persist_session_vars(object):
"""
Some views, such as login and logout, will reset all session state.
(via a call to ``request.session.cycle_key()`` or ``session.flush()``).
That is a security measure to mitigate session fixation vulnerabilities.
By applying this decorator, some values are retained.
Be very aware what kind of variables you want to persist.
"""
def __init__(self, vars):
self.vars = vars
def __call__(self, view_func):
#wraps(view_func)
def inner(request, *args, **kwargs):
# Backup first
session_backup = {}
for var in self.vars:
try:
session_backup[var] = request.session[var]
except KeyError:
pass
# Call the original view
response = view_func(request, *args, **kwargs)
# Restore variables in the new session
for var, value in session_backup.items():
request.session[var] = value
return response
return inner
and now you can write:
from django.contrib.auth import views
#persist_session_vars(['some_field'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
And for class based views (django-allauth):
import allauth.account.views as auth_views
from django.utils.decorators import method_decorator
#method_decorator(persist_session_vars(['some_field']), name='dispatch')
class LoginView(auth_views.LoginView):
pass
and use that view in the url patterns:
import allauth.urls
from django.conf.urls import include, url
from . import views
urlpatterns = [
# Views that overlap the default:
url(r'^login/$', views.LoginView.as_view(), name='account_login'),
# default allauth urls
url(r'', include(allauth.urls)),
]
When you login/logout Django will flush all sessions if another user logs in (request.session.flush() in auth/init.py).
You're better of storing user settings in the database and add some middleware to get that data and store it in your request.
user data that persists sounds like it should live in something like a UserProfile model
I actually think your initial design made sense. If you want to save some session variables across the login/logout boundary, you can do something like this.
from functools import wraps
class persist_session_vars(object):
""" Some views, such as login and logout, will reset all session state.
However, we occasionally want to persist some of those session variables.
"""
session_backup = {}
def __init__(self, vars):
self.vars = vars
def __enter__(self):
for var in self.vars:
self.session_backup[var] = self.request.session.get(var)
def __exit__(self, exc_type, exc_value, traceback):
for var in self.vars:
self.request.session[var] = self.session_backup.get(var)
def __call__(self, test_func, *args, **kwargs):
#wraps(test_func)
def inner(*args, **kwargs):
if not args:
raise Exception('Must decorate a view, ie a function taking request as the first parameter')
self.request = args[0]
with self:
return test_func(*args, **kwargs)
return inner
You would throw this decorator on whatever view you're calling auth.login/logout from. If you're delegating to the built-in view for those, you can easily wrap them.
from django.contrib.auth import views
#persist_session_vars(['HTTP_REFERER'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
You can create a form Mixin that allows you to persist form values to the user's session (which doesn't require them being logged in). This is useful for things such as filter/sorting options on a public table-view report, where you want to keep their filter options persistent across refreshes.
View:
def list_states(request):
if request.method == 'GET':
form = StateListFilterForm().load_from_session(request.session)
elif request.method == 'POST':
form = StateListFilterForm(request.POST)
form.persist_to_session()
return render('forms/state_list.html', RequestContext(request, {'state_form': form})
Form:
class PersistableMixin:
def persist_to_session(form, session):
for key in form.fields.keys():
val = getattr(form, 'cleaned_data', form.data).get(key, None)
if val: # will not store empty str values
session[key] = val
return True
def load_from_session(form, session):
for key in form.fields.keys():
saved_val = session.get(key, '')
if saved_val: # will not load empty str values
form.fields[key].initial = saved_val
return form
class StateListFilterForm(forms.Form, PersistableMixin):
states = forms.MultipleChoiceField(required=False, choices=US_STATES)

Object Ownership in Django

I'm wondering how I might accomplish a simple 'object ownership' system with django models, such that, by default, only the owner of an object may edit it.
I am attempting to allow a 'Management' group to edit all objects on behalf of the object owners, and have at this point added a custom permission:
class Meta:
permissions = (
("manage_object", "Can manage objects"),
)
To establish 'ownership' I've toyed with the idea of adding a def to the model:
def owner(self):
return self.user
But then, how might I go further? I could implement the permissions in a view and display relevant UI with a template, i.e.:
if request.user is object.owner:
# ... do stuff
elseif request.user.has_perm.can_manage: # this line is probably not right
# ... do something else
... and then present different UI elements on a template level.
So, the question is:
what faults/benefits are there with this approach?
are there recommendations?
or, any other previously implement methods?
Best thanks!
My approach would be adding a method to the model:
class YourModelWithOwnership(models.model):
...
def user_can_manage_me(self, user):
return user == self.user or user.has_perm('your_app.manage_object')
I'd then call that method whenever a permission check is required, and take some action based on the outcome. So for a view that would be
from django.shortcuts import get_object_or_404
...
def view_func(request, item_id):
item = get_object_or_404(YourModelWithOwnership, id=item_id) # or whatever is needed to get the object
if not item.user_can_manage_me(request.user):
# user not allowed to manage
...
else:
...
Later I'd probably realize that that's still quite some boilerplate code to write in every view that needs that test, so I'd implement an exception that's thrown when a user can't manage an object...
class CannotManage(Exception):
pass
...and add another method to the model:
from django.db import models
from django.shortcuts import get_object_or_404
class YourModelWithOwnership(models.model):
...
#classmethod
def get_manageable_object_or_404(cls, user, *args, **kwds):
item = get_object_or_404(cls, *args, **kwds)
if not item.user_can_manage_me(user):
raise CannotManage
return item
Then, in the view functions, this can be used:
def view_func(request, item_id):
item = YourModelWithOwnership.get_manageable_object_or_404(request.user, id=item_id)
...
This will of course raise an exception when the user isn't the owner and does not have the proper permission. That exception can be handled in the process_exception() method of a custom middleware class so that there's a single handler for all instances where a user is not allowed to mess with the object.
A while back I wrote up the usual technique for doing this in the admin. You may want to read through that to see how the implementation works.
You can look into RowLevelPermissions branch. It hasn't been included even in 1.1 beta though, I guess it still needs some development.