GAE, oauth2, and admin users - python

I currently use the "Google Accounts API" to allow users to login to my GAE app. So I use users.create_login_url and users.get_current_user and add an ndb.UserProperty to my own user entity so that I can retrieve data for that user.
I'm now in the process of switching to oauth2 (using authomatic).
I don't know how to handle admin users after the switch to oauth2. I currently use users.is_current_user_admin to detect an admin user, but that won't work if the admin logs in with oauth2.
I see two awkward solutions:
Keep using the Google Accounts API for admin users and have regular users login with oauth2.
Store a list of oauth2 credentials for admin users (hardwired in the code or in the datastore) so admin users will be recognized after login with oauth2.
Is there a better way or should I use one of the above, and if so, which one?

I'll describe how I ended up doing it in case it is helpful for others.
Below is my Login handler. If a user goes to "/login" then it displays login buttons. When a user clicks on a button, the page redirects to, e.g., "/login/google" to do OAuth2 processing.
If I want to login as admin, then I manually enter this URL "/login/gae".
class Login(webapp2.RequestHandler):
def get(self, provider=None):
# Show the login page and allow the user to select a provider
if not provider:
template = JINJA_ENVIRONMENT.get_template("login.html")
self.response.write(template.render())
# Only for admin login. Use app engine login.
elif provider == "gae":
self.redirect(users.create_login_url("/"))
# The user has selected a provider so we do oauth2 login.
else:
session = Webapp2Session(self, session=self.session)
result = authomatic.login(Webapp2Adapter(self),
provider,
session=session,
session_saver=session.save)
...
To allow admin to logout, I conditionally put an admin logout on my web pages by creating this template variable:
logout_url = users.create_logout_url("/") if users.is_current_user_admin() else None
and adding this to my page template:
{% if logout_url %}
<li>Admin Logout</li>
{% endif %}

Related

How to manage the separate session for same browser Admin & Frontend in django

I'm looking for a way to separate the session handling for the admin part of a site and the frontend.
A person should be able to log in to the admin (only if he has is_staff and/or is_superuser).
He should have to be able to login with another username into frontend.
So basically it's like two separate sessions for the admin and frontend.
Login/Logout and Permission-check functionality is not a problem, different sessions is the problem. Any ideas?
Thanks,
Probably you can't do that unless you have a separate authentication system which is not provided by django. Means, you can use django's auth system for admin users and a separate auth system which for normal users. IMHO, its not very ideal solution if you don't have a separate auth system.
Alternativly, you can write a new middleware for this. In that middleware, you can check if the user is authenticated with an admin user, and if he/she is, then log him/her out of the application and redirect to login page.
Here is an example:
from django.contrib.auth import logout
class RestrictAdminUserInFrontend(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path.find("/admin/") == -1 and request.user.is_authenticated and request.user.is_superuser:
logout(request)
return redirect('login-page')
response = self.get_response(request)
return response
And add that to MIDDLEWARE settings:
MIDDLEWARE = [
'other middlewares',
'path.to.RestrictAdminUserInFrontend'
]
So, in this approach your admin users will be forced to login using normal user's username and password. But admin site and frontend will use same session.

Django admin.site.urls only accessible to superuser (The admin login page only accessible to superuser)

What i want is to limit access to the django admin login page to only the superuser. Meaning if you are not the superuser, and try to access http://127.0.0.1:8000/admin - you should be redirected to 404 page , something like that.The means or the custom view to perform this authentication is the challenge. Please somebody assist me with a hint on how to do it?
urlpatterns = [
path('admin/', my_custom_function,name="check_if_superuser"),
# when somebody hits this url pattern , he/she should be taken to the
# function above for checking if superuser befor being redirected to
# django admin login page
]
and in my views.py i have the following function that does the authentication
def my_custom_function(request):
if request.user.is_superuser():
#... redirect to django admin login page
else:
# return render(404_page)
yeah something like that.
By default, django admin allows login for superuser or stuff user only. So, it is kind of safe to have a admin login panel. Also, if you want to restrict that login path, I think its best to put a firewall on that particular route. So that only whitelisted IPs can access it. You can use NGINX for this, and configuration should be something like this:
location /admin {
# block one workstation
deny 192.168.1.1;
# allow anyone in 192.168.1.0/24
allow 192.168.1.0/24;
# drop rest of the world
deny all;
}
This article could be helpful with the configuration.
I assume there might be a catch 22 in the described scenario.
To check user rights there should be a logged in user
If you put check only on available users - is_superuser and show 404:
logged in, non super_user will receive 404
not logged in visitor can go to admin page
If you add check whether user is logged in and if not show 404 as well:
no one can login from admin page, unless logged in somewhere else and got to admin afterwards
Both scenarios sound inconsistent to me. I think what you are trying to achieve is intended to be done by Django framework in a slightly different way.
There is has_permission() in AdminSite class in django.contrib.admin.sites which
Return True if the given HttpRequest has permission to view at least one page in the admin site
and by default returns request.user.is_active and request.user.is_staff
If you change it in your admin.py, only active superusers will be able to utilize admin:
from django.contrib import admin
def has_superuser_permission(request):
return request.user.is_active and request.user.is_superuser
# Only active superuser can access root admin site (default)
admin.site.has_permission = has_superuser_permission
And even logged in non-sups will be shown message about insufficient rights and prompted to re-login

Next query parameter doesn't work with django allauth for facebook login

I have implemented User account management into my application using Django all-auth. I have enabled login using username and password as well as with facebook connect.
The problem goes like this:
1) User visits a page http://example.com/page1/ and clicks login
2) He's taken to http://example.com/accounts/login?next=/page1/
3) When the user logs in using username and password, the user is redirected back to http://example.com/page1. But if the user logs in with facebook, he's taken to homepage.
How can I get desired behavior with Facebook login too?
You need to override the get_login_redirect_url method of django-allauth.
For this inherit the DefaultAccountAdapter class as
from allauth.account.adapter import DefaultAccountAdapter
class MyAccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
# get the next parameter from request object and return the url
And make changes on settings.py
ADAPTER = "APPNAME.FILENAME.MyAccountAdapter"
ACCOUNT_ADAPTER = "APPNAME.FILENAME.MyAccountAdapter"
This should work !
How are you generating the Facebook login link? Most likely you are not indicating the next parameter there. The allauth documentation gives this example:
Google
To get the proper next parameter you can access request.GET.next.

Logout with python-social-auth

I am dabbling a little with Python Django Social Auth using Twitter authentication.
I can login.
But, when I try to log out using django.contrib.auth.logout, it doesn't log out.
What's the way to logout?
Thanks.
Are you trying to log out just from the Django app or do you want to "forget" the Twitter access? Usually the twitter auth token is stored for simplified login the next time a user wants to connect to twitter, so the user doesn't have to "accept" the access again.
Django logout
If you just want to logout from the Django auth system, it should be enough to use the django.contrib.auth.views.logout view or to create a custom logout view.
Social auth disconnect
To completely unlink/disconnect a social account, you need to use the disconnect functions in social-auth. You can get the disconnect url using the following template tag:
{% url "socialauth_disconnect" "backend-name" %}
For more information, please refer to http://django-social-auth.readthedocs.org/en/v0.7.22/configuration.html#linking-in-your-templates.
Force approval prompt
Because you've already allowed your app access to the OAuth provider, the auth provider will remember that decision. There are usually two ways to force a confirmation of that access permission:
Revoke the access permission in the management console of your auth provider (e.g. disapprove twitter app access).
Set an extra OAuth argument that forces the approval prompt. I'm not sure if Twitter provides such a thing, but if you're using Google OAuth2 you can simply add {'approval_prompt': 'force'} to the GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS setting.
Do you have a logout view? You need to have a logout view.
Example:
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
This answer is outdated as django-social-auth is now python-social-auth
See newer Stack Overflow answer here.
Read the docs here
According to the documentation there is a difference between log out and disconnect. In short,
Disconnect - forget the user social account.
Log out - end the current user session and remove any related data (like cookies).
From the question, I assume you still want to allow the user to have the Twitter linked with the account. If you want to disconnect, check this answer.
To log the user out, you can have in your Django settings.py
LOGOUT_URL = "logout"
Then, in your urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
urlpatterns = [
path("logout/", auth_views.LogoutView.as_view(template_name="registration/logged_out.html"), name="logout"),
]
Then, to log the user out, you can just use in the template something like
Logout
Also, you'll have to create a the logged_out.html file in appname/templates/registration/ and include in it whatever you want the logged out user to see.

Log in to Django site through Facebook

I am using Django.
I used facebook-connect to put a Facebook login button on my page. This functionality works very well. Any user can click on the button and can log in to Facebook directly. I can also store Facebook user information, such as name and email address, in my site database.
But I would like users to be able to click on the Facebook login button and also log in to my site too. At the moment they can log in only to Facebook. How can I use the Facebook login button to also authenticate the user on my site?
Have a look at the widely used django-facebook app.
You can look to django docs:
other authentication sources
And write a custom backend:
class MyBackend(object):
def authenticate(self, username=None, password=None):
# Check the fb user is logged in, create User if needed and return a User by fbid.

Categories