Difficulty having Django find database user - python

I'm writing a web app which has a page for admin tasks. One of the tasks is that the admin users must be able to edit other users details. Alas, I've fallen at quite a simple roadblock.
I've set up a very simple jQuery AJAX Get request, successfully transferring a string to the server and back. This is just background, but not the issue. The issue lies in retrieving other user's objects.
At the moment, with a username I know exists, this code which is accessed in views.py, produces a 500 Internal Server Error.
#login_required
def user_edit_getuser(request):
# Like before, get the request's context.
context = RequestContext(request)
inputname = request.GET['inputNameSend']
user_obj = User.objects.get(inputname)
return HttpResponse(inputname) #later will return a JSON String

get takes keyword arguments only: the key is the field to look up.
user_obj = User.objects.get(username=inputname)
Also, you should probably deal with the possibility that the GET request has no inputNameSend key.
For JS development, you can usually see the error page in the Chrome dev tools/Firebug console in the Network tab.

Related

Custom Authentication in Django Rest Framework

I have a django rest framework application with custom authentication scheme implemented. Now I want to allow external app call some methods of my application.
There's an endpoint for external app to login /external-app-login which implemented like this:
class ExternalAppLoginView(views.APIView):
def post(self, request):
if request.data.get('username') == EXTERNAL_APP_USER_NAME and request.data.get('password') == EXTERNAL_APP_PASSWORD:
user = models.User.objects.get(username=username)
login(request, user)
return http.HttpResponse(status=200)
return http.HttpResponse(status=401)
Now I want to add authentication. I implemented it like this:
class ExternalAppAuthentication(authentication.SessionAuthentication):
def authenticate(self, request):
return super().authenticate(request)
But authentication fails all the time. What is the correct way to do it? I want to store login/password of external app in variables in application, not in database.
The authentication fails, because it needs to return a registered user in your database to authenticate. However as the user info is all in variables instead of database, the issue arises.
There are more than one ways to overcome this issue. Firstly i would suggest you write the authentication code instead of using
return super().authenticate(request) as this would lead you to the real reason of the issue.
Also must give a read to this documentation link, it clears a lot of things regarding authentication.
https://www.django-rest-framework.org/api-guide/authentication/
Now after you have done all that, and you seek ways how to authenticate, then you can try either remote user authentication, or you can check for existing users in your variables and use anonymous user for authentication which resolves the issue.

Django - Changing the url to redirect from a middleware on the fly

I'm building a SSO login meant to be used from links send by emails.
Each link should auto-connect the user (SSO), and be clickable multiple times (they have a TTL, which depends on the email)
It works fine, but I'm concerned about the end-user sharing his url on social networks (basically copy/pasting the url, which contains the SSO token), allowing anyone following the link to be logged in automatically.
My first attempt was to try to remove the GET SSO_TOKEN parameter, from my SSOMiddleware, as follow:
if remove_token_middleware:
request.GET._mutable = True # GET is not mutable by default, we force it
# Remove the token from the url to avoid displaying it to the client (avoids sharing sso token when copy/pasting url)
del request.GET[SSO_TOKEN_PARAM]
request.GET._mutable = False # Restore default mutability
return login(request, user) if service.get("auto_auth") else None
Basically, my thought was that since the SSO_TOKEN is in the request.get object, removing it from it would eventually change the url where the user gets redirected
In my controller, here is how the user gets "redirected" (using render)
return render(request, 'campagne_emprunt/liste_offres_prets.html', locals())
When using render, there is no redirection, and the SSO token is still visible in the URL (in the browser address bar).
Is there a way to somehow tell Django to change the destination url, on the fly?
So in order to redirect, there is a builtin redirect function of Django that you use instead of rendering I think .. give it a shot,
from django.shortcuts import redirect
def redirect_view(request):
response = redirect('/redirect-success/')
return response
and for reference-
https://realpython.com/django-redirects/

Django Test : SESSION_KEY failing assertIn

I am using the Django Test client (django.test.Client) to run view tests. Upon attempting to use the Test Client on my index function that handles post requests for logins, it continually fails the test even though the authentication successfully occurs.
Heres my test:
def test_login(self):
response = self.client.post('/login/', {'username':'user', 'password':'pass'})
print response.content
self.assertIn(SESSION_KEY, self.client.session)
So the reason i know the login process successfully works is because response.content yields HTML data from another view that can only be access if request.user.is_authenticated() is true. In other words, they must be logged in for response.content to yield the "logged in page". So given this, i can tell that the function obviously works for its practical purpose of logging the user in, however, i've been scouring the docs for hours trying to figure out why i can't access SESSION_KEY from the client session. All my reading suggests that the django test client is in fact stateful in nature and should store the session.
Can someone shed some light on this?
Ok after much searching and asking around on #django, i made a working solution for Django 1.6.x
from django.contrib.auth import SESSION_KEY, get_user_model
from django.test import Client
def setUp(self):
self.client = Client()
def test_login_view(self):
user_pk = get_user_model()._default_manager.get(username__exact='test_username_here').pk
response = self.client.post('/login/', {'username':'test_username_here', 'password':'test_password_here'})
self.assertEqual(self.client.session[SESSION_KEY], user_pk)
The test_login_view function will be the one evaluating the view in my app that handles user logins from the template form. First, i grab user_pk which is the real primary key of the given user in the database. I used get_user_model() instead of User.objects.get() because the former allows you to reference regardless of whether the User model is modified or not. Of course you can use the latter as well. Second, i go ahead and send the post request using the test client just like a standard user's browser would. Finally, i discovered that self.client.session[SESSION_KEY] contains the primary key of the logged in user. (If the login was successful, otherwise, it will simply yield a KeyError)

Adding csrf_token to my post request from iPhone app

I have an iPhone application from which I would like to call a post service passing parameters in its request, doing so caused a server error 500.
I have read Django documentation here and I still haven't figure out how to get a csrf_token and how to add it to the AFNetworking AFHTTPRequestOperationManager POST method.
On the server side I've added django.middleware.csrf.CsrfViewMiddleware in the MIDDLEWARE_CLASSES section, but it doesn't seem to do the trick.
My view looks like this; I am not doing much, just hoping to pass.
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def foo(request):
c={}
c.update(csrf(request))
if request.method == 'POST':
return HttpResponseRedirect("Post received");
The Django CSRF Middleware uses cookies and forms and whatnot to send a code to the page, then make sure the correct page is the one sending information back. In both cases, you must do a GET request to the server, and if you have the middleware installed correctly, it will put the CSRF token into a cookie for you.
Check out the documentation for more info on this.
Now, I noticed you're using a library that uses NSURLConnection, so that should handle cookies for you. I got this bundle of code (untested) that lets you pull the cookie name that you specify in your settings file (again, check out the documentation link above) then put that in your POST.
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL: networkServerAddress];
for (NSHTTPCookie *cookie in cookies)
{
// get the right cookie
}
Of course, if you're only making POSTs and never GETs first, you don't have a CSRF token to send!
And that's why we have the #csrf_exempt tag. (Docs here) This is the way to go 99% of the time, since most apps you won't do a GET before you do a POST. (in webpages you have to do a GET first). Note that this is intended only when an app is sending only POSTs and there's no session to speak of. You really need to think about your own security when using this, and how you verify that a given app/user really is who they claim to be. And how you disable people from hitting this URL from a webbrowser.
TLDR: Probably use #csrf_exempt on the view, but be careful.

How do I redirect an user back to the page they were trying to access once they log in? (Django)

So currently I'm using #login_required to block certain pages from users and redirect them, telling them they need to log in. but what I can't understand is how do I "let them" go to the page they were trying to go to once they log in. Currently I'm just using a typical render_to_response('with a certain view') but what if i want that response to be anywhere where they were trying to access. How do i code that?
The #login_required will generally pass you back the redirect_field_name (default is "next") for example: /accounts/login/?next=/polls/3/. So in your login view after authenticating and logging in the user you can do something like
response = HttpResponseRedirect(next)
# Do whatever else you need to do here with the response object
return response
See the docs at https://docs.djangoproject.com/en/1.3/topics/auth/#the-login-required-decorator
You can pass a url parameter back to your login page and use that to direct the user once they complete the login successfully.
from the login requiered decorator docs it says:
By default, the path that the user should be redirected to upon
successful authentication is stored in a query string parameter called
"next".
and usually when the login is done it take to the "next" url
Here's what django.contrib.auth.views.login does:
If called via GET, it displays a login form that POSTs to the same
URL. More on this in a bit.
If called via POST, it tries to log the
user in. If login is successful, the view redirects to the URL
specified in next. If next isn't provided, it redirects to
settings.LOGIN_REDIRECT_URL (which defaults to /accounts/profile/). If
login isn't successful, it redisplays the login form.

Categories