Django send authenticated user to another django server with the same db - python

I know question sounds strange, I will explain it here.
I have two Django servers which share the same DB. One is a light front/back server and the order one takes the heavy computing part. They share the same database.
I am currently securing the web, and I have a couple of views in the light server requiring user login:
#login_required()
#permission_required('auth.can_upload', login_url='/accounts/login/')
This works nicely in the light server since the user is authenticated (request.user returns a valid user in the views).
The problem comes when I have to send the uploaded data to the other server since it is protected as I showed earlier, I do not know how to pass it the user that is already logged (user is valid since servers share the DB).
# send an upload request using requests
s = requests.Session()
r1 = s.get(upload_process_url)
csrf_token = r1.cookies['csrftoken']
a = s.post(upload_process_url, files=request.FILES,
data={'csrfmiddlewaretoken': csrf_token},
headers=dict(Referer=upload_process_url))
I cannot ask every time the user and password or save them. The thing is I want to pass the user that is already logged in a request.
The user was logged using the default django accounts/login page and authentication.
Any clues and what could I try? I think this problem cannot be as difficult as it looks to me. I just want to send an authenticated request. If I remove the decorators everything works nicely with this code
Thanks a lot

Have a look at REMOTE_USER authentication:
This document describes how to make use of external authentication sources (where the Web server sets the REMOTE_USER environment variable) in your Django applications. This type of authentication solution is typically seen on intranet sites, with single sign-on solutions such as IIS and Integrated Windows Authentication or Apache and mod_authnz_ldap, CAS, Cosign, WebAuth, mod_auth_sspi, etc.
Basically your "light" server does the authentication as it already does. When you are doing a request to your "heavy" server, you should set a Auth-User header containing the username of your user. Django will then automatically authenticates the corresponding user.
By default, Django will read an environment variable set by an authentication server. But we can make it work with a HTTP header instead:
# middlewares.py
from django.contrib.auth.middleware import RemoteUserMiddleware
class CustomHeaderMiddleware(RemoteUserMiddleware):
header = 'HTTP_AUTH_USER'
# settings.py
MIDDLEWARE = [
'...',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'my_project.middlewares.CustomHeaderMiddleware',
'...',
]
Then, you can do something like this then in your request (assuming you have your Django user at hand):
s = requests.Session()
r1 = s.get(upload_process_url)
a = s.post(
upload_process_url,
files=request.FILES,
headers={
'Auth-User': user.username,
},
)
Since you're not doing a request from a browser, you can avoid the CSRF protection by marking the called "heavy" view with #csrf_exempt decorator (as you found yourself).
Be careful though that your "heavy" server should not be accessible directly on the internet and always behind a proxy/VPN accessible only by your "light" server.

Related

Python Social Auth - How to know if social media connect is successful?

I have installed Python Social Auth. I use it to associate user site account with his social media accounts.
Facebook connect link is:
Connect
Redirection works but how to know if social media association is successful?
If an exception is catch, I can display get_messages() function. It's perfect!
But any return if it's successful.
I have tried to custom a pipeline but I have not access to request variable to set message like it: messages.success(request, "Successful message!')
You do have access to request in your custom pipeline:
The pipeline functions will get quite a lot of arguments, ranging from the backend in use, different model instances, server requests and provider responses.
You can read more about it here.
and you can get access to request object like this:
def custom_pipeline(strategy, *args, *kwargs):
request = strategy.request
and do whatever you wanted with messages.
There is an option
SOCIAL_AUTH_LOGIN_ERROR_URL = '/error/'
in python social auth settings which redirects to the url you mention in above setting.
This way you will know whether the connect is success or not.
Hope this is helpful.

Create Django token-based Authentication [duplicate]

I am trying to figure out the best way to implement token based authentication in my django app. An external, non-django application is setting a cookie, with a token, and I have a webservice that can retrieve user information based off of that token. If the user has the cookie set, they should not need to authenticate on my site and should be automatically logged in based on the info passed back by the web service. As I see it, there are a few different options to perform the actual check and I'm not sure which is best:
Write a custom decorator like the one in this snippet and use it instead of
login_required.
Call a custom authenticate method inside base_site through an ajax call. On every page, a check would be made and if the cookie exists and is valid, then the user would be automatically logged in.
Add some javascript to the LOGIN_REDIRECT_URL page that will check/validate the cookie in an ajax call, and automatically redirect back to the referrer if the cookie authenticated.
Is there an option I am missing? Ideally, there would be a way to build this into login_required, without having to write a custom decorator.
Before searching for code, be sure you read the documentation. http://docs.djangoproject.com/en/1.2/topics/auth/#other-authentication-sources
Also read the supplied Django source.
You want to create three things.
Middleware to capture the token. This is where most of the work happens. It checks for the token, authenticates it (by confirming it with the identity manager) and then logs in the user.
Authentication backend to find Users. This is a stub. All it does is create users as needed. Your identity manager has the details. You're just caching the current version of the user on Django's local DB.
Here's the middleware (edited).
from django.contrib.auth import authenticate, login
class CookieMiddleware( object ):
"""Authentication Middleware for OpenAM using a cookie with a token.
Backend will get user.
"""
def process_request(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured()
if "thecookiename" not in request.COOKIES:
return
token= request.COOKIES["thecookiename"]
# REST request to OpenAM server for user attributes.
token, attribute, role = identity_manager.get_attributes( token )
user = authenticate(remote_user=attribute['uid'][0])
request.user = user
login(request, user)
The identity_manager.get_attributes is a separate class we wrote to validate the token and get details on the user from the IM source. This, of course, has to be mocked for testing purposes.
Here's a backend (edited)
class Backend( RemoteUserBackend ):
def authenticate(**credentials):
"""We could authenticate the token by checking with OpenAM
Server. We don't do that here, instead we trust the middleware to do it.
"""
try:
user= User.objects.get(username=credentials['remote_user'])
except User.DoesNotExist:
user= User.objects.create(username=credentials['remote_user'] )
# Here is a good place to map roles to Django Group instances or other features.
return user
This does not materially change the decorators for authentication or authorization.
To make sure of this, we actually refresh the User and Group information from our
identity manager.
Note that the middleware runs for every single request. Sometimes, it's okay to pass the token to the backed authenticate method. If the token exists in the local user DB, the request can proceed without contacting the identity manager.
We, however, have complex rules and timeouts in the identity manager, so we have to examine every token to be sure it's valid. Once the middleware is sure the token is valid, we can then allow the backend to do any additional processing.
This isn't our live code (it's a little too complex to make a good example.)

Subclassing User to differentiate between an admin and an App with an API key?

This is my first API, so please forgive/correct me if I make any wrong claims.
I have an app that has an API. I would only like external apps (mobile clients etc.) with a valid API key to have access to it, and I think django-rest-framework's TokenAuthentication is the right fit for it. I would also like users to be able to log in with their username & password, and I'm looking to use OAuth2Authentication for that. But I don't want apps that use TokenAuthentication to have a "User" instance in the database (as they are not users in the traditional sense) so I'm thinking about doing something like this:
class Client(User):
pass
django-rest-framework says that request.user will be an instance of User. Will I bump into any problems if I use Client instead?
Is this the standard way of handling this situation? It just doesn't feel right to me (mainly because of question 1)
For mobile clients to have access to your API, using django-restframework's TokenAuthentication will work just fine. As you have already ascertained. You need to design a mechanism for handing out your tokens. If you are doing this dynamically then you will need to have your API request handle this.
Mobile Client: ( initial API request )
request /API/rabbit/1/
( no token)
Server: 401
test for the token ( fails )
'login please' via HTTP 401 response. ( or some other custom header or response information )
You can define your api on how to accomplish this, most folks use the http:
401 Unauthorized error code. I point this out because its clearly a design decision.
Mobile Client: ( request login)
prompts user for username and password, and makes a request too /login/ this could be a special mobileclient login like /mobile/login/ whose difference is that it hands back a token on a successful login.
Server: 200
verifies valid user and hands out token.
you could write this logic, or you could use 'rest_framework.authtoken.views.obtain_auth_token' which I recommend. See rest-frameworks
token authentication for details on this. Heed its warning about https.
Mobile Client: ( re-request API with token in http header)
receives token
now remakes initial request to /API/rabbit/1/ with its token in the header.
Server: 200
verifies valid token in the header and provides access to API. You will be writing this code.
Finally: You will need to design a strategy to 'age-out' your tokens, and or lock out users.
Also: make sure you add 'rest_framework.authtoken' to your INSTALLED_APPS, and make sure you call manage.py syncdb
Aside: you do not specifically have to use the TokenAuthentication ( request.user, request.auth) you can write your own code to peek in the header and see if the token is set. This is fairly easy to do with the python Cookie lib. You are still using-heavly the token management features of django-rest-framework. To be honest I think there documentation is a bit incomplete on configuring the 'TokenAuthentication' authentication back-ends.
Hope this helps!

Django request.user is empty

Using django, I am authenticating the user through Google. I get the initial request tokens & redirect the user to google for auth. After which google redirects the user back to my website (using the redirect_url I provide).
At this point the request.user.id is None so is request.user.username why is this happening? I need the user ID to enter the access_tokens (that google sends me) into the DB.
Under what conditions can request.user object in Django be empty?
UPDATE1: When I get redirected back from Google with the url pattern as http://mywebsite.com/lserv?s=goog control comes back to my django views function, but django gives me the request.user object user as Anonymous user with no username or id. why?
UPDATE2:
all this is running on python manage.py runserver for now...
UPDATE3: Anybody faced anythn similar to this? basically, out of no reason the user in request clears out automatically & I get assigned as Anonymous user. All this happens between url requests from the user (from browser). Why?
Django's auth mechanism has nothing to do with Google's or any other auth service. If you want to integrate third party auth service with your Django site, you should do it youself.
If you're using oauth2 library, it's README has a section named "Logging into Django w/ Twitter" may help you.
If you are using oauth api from google. To get the user you have to do something like this
from google.appengine.api import oauth
# ...
try:
# Get the db.User that represents the user on whose behalf the
# consumer is making this request.
user = oauth.get_current_user()
except oauth.OAuthRequestError, e:
# The request was not a valid OAuth request.
# ...

Django user impersonation by admin

I have a Django app. When logged in as an admin user, I want to be able to pass a secret parameter in the URL and have the whole site behave as if I were another user.
Let's say I have the URL /my-profile/ which shows the currently logged in user's profile. I want to be able to do something like /my-profile/?__user_id=123 and have the underlying view believe that I am actually the user with ID 123 (thus render that user's profile).
Why do I want that?
Simply because it's much easier to reproduce certain bugs that only appear in a single user's account.
My questions:
What would be the easiest way to implement something like this?
Is there any security concern I should have in mind when doing this? Note that I (obviously) only want to have this feature for admin users, and our admin users have full access to the source code, database, etc. anyway, so it's not really a "backdoor"; it just makes it easier to access a user's account.
I don't have enough reputation to edit or reply yet (I think), but I found that although ionaut's solution worked in simple cases, a more robust solution for me was to use a session variable. That way, even AJAX requests are served correctly without modifying the request URL to include a GET impersonation parameter.
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.session['impersonate_id'] = int(request.GET["__impersonate"])
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if request.user.is_superuser and 'impersonate_id' in request.session:
request.user = User.objects.get(id=request.session['impersonate_id'])
Usage:
log in: http://localhost/?__impersonate=[USERID]
log out (back to admin): http://localhost/?__unimpersonate=True
It looks like quite a few other people have had this problem and have written re-usable apps to do this and at least some are listed on the django packages page for user switching. The most active at time of writing appear to be:
django-hijack puts a "hijack" button in the user list in the admin, along with a bit at the top of page for while you've hijacked an account.
impostor means you can login with username "me as other" and your own password
django-impersonate sets up URLs to start impersonating a user, stop, search etc
I solved this with a simple middleware. It also handles redirects (that is, the GET parameter is preserved during a redirect). Here it is:
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.user = models.User.objects.get(id=int(request.GET["__impersonate"]))
def process_response(self, request, response):
if request.user.is_superuser and "__impersonate" in request.GET:
if isinstance(response, http.HttpResponseRedirect):
location = response["Location"]
if "?" in location:
location += "&"
else:
location += "?"
location += "__impersonate=%s" % request.GET["__impersonate"]
response["Location"] = location
return response
#Charles Offenbacher's answer is great for impersonating users who are not being authenticated via tokens. However, it will not work with clients side apps that use token authentication. To get user impersonation to work with apps using tokens, one has to directly set the HTTP_AUTHORIZATION header in the Impersonate Middleware. My answer basically plagiarizes Charles's answer and adds lines for manually setting said header.
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.session['impersonate_id'] = int(request.GET["__impersonate"])
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if request.user.is_superuser and 'impersonate_id' in request.session:
request.user = User.objects.get(id=request.session['impersonate_id'])
# retrieve user's token
token = Token.objects.get(user=request.user)
# manually set authorization header to user's token as it will be set to that of the admin's (assuming the admin has one, of course)
request.META['HTTP_AUTHORIZATION'] = 'Token {0}'.format(token.key)
i don't see how that is a security hole any more than using su - someuser as root on a a unix machine. root or an django-admin with root/admin access to the database can fake anything if he/she wants to. the risk is only in the django-admin account being cracked at which point the cracker could hide tracks by becoming another user and then faking actions as the user.
yes, it may be called a backdoor, but as ibz says, admins have access to the database anyways. being able to make changes to the database in that light is also a backdoor.
Set up so you have two different host names to the same server. If you are doing it locally, you can connect with 127.0.0.1, or localhost, for example. Your browser will see this as three different sites, and you can be logged in with different users. The same works for your site.
So in addition to www.mysite.com you can set up test.mysite.com, and log in with the user there. I often set up sites (with Plone) so I have both www.mysite.com and admin.mysite.com, and only allow access to the admin pages from there, meaning I can log in to the normal site with the username that has the problems.

Categories

Resources