DefaultAccountAdapter and DefaultSocialAccountAdapter? - django

I tried the following to specific different signup flows for users who sign up via social accounts (facebook) and those that sign up via traditional login.
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class NormalAdapter(DefaultSocialAccountAdapter):
def get_login_redirect_url(self, request):
if request.user.last_login == request.user.date_joined:
return 'survey/'
else:
return '/results/'
class CorporateAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
if request.user.last_login == request.user.date_joined:
return 'corporate/survey/'
else:
return 'corporate/results/'
But even if you log in with facebook, it calls DefaultAccountAdapter's get_login_redirect_url instead of DefaultSocialAccountAdapter's.

Point the all auth plugin to the correct adapter classes! Add the following settings to your django app:
# project/settings.py:
ACCOUNT_ADAPTER = 'python.path.to.your.CorporateAdapter'
SOCIALACCOUNT_ADAPTER ='python.path.to.your.NormalAdapter'
Then save and reload the app (if you are on apache, just restart apache)

Related

Apache superset Unable to login using custom security manager

I am customizing apache superset login using custom security manager, but unable to redirect the welcome page. My code for custom_security_manager.py is;
from flask import redirect, g, flash, request
from superset.security import SupersetSecurityManager
from flask_appbuilder.security.views import UserDBModelView,AuthDBView
from flask_appbuilder.security.views import expose
from flask_appbuilder.security.manager import BaseSecurityManager
from flask_login import login_user, logout_user
class CustomAuthDBView(AuthDBView):
#expose('/login/', methods=['GET', 'POST'])
def login(self):
token = request.args.get('token')
flash(token, 'warning')
if token == 'SOME_TOKEN_VALUE':
user = self.appbuilder.sm.find_user(username=request.args.get('username'))
flash(user, 'warning')
login_user(user, remember=False)
return redirect(self.appbuilder.get_url_for_index)
else:
flash('Unable to auto login', 'warning')
return super(CustomAuthDBView,self).login()
class CustomSecurityManager(SupersetSecurityManager):
authdbview = CustomAuthDBView
def __init__(self, appbuilder):
super(CustomSecurityManager, self).__init__(appbuilder)
I am using below link for login:
http://10.92.123.25:8088/login/?username=admin&token=SOME_TOKEN_VALUE
you have to add next redirect path in your URL.
Example:
http://10.92.123.25:8088/login/?username=admin&token=SOME_TOKEN_VALUE&next=/superset/dashboard/11?standalone=true

Django Update Middleware to replace decorator

I have the following Decorator which works fine when applied to different views with: #otp_required(login_url='login') on my site:
Decorator
from django.contrib.auth.decorators import user_passes_test
from django_otp import user_has_device
from django_otp.conf import settings
def otp_required(view=None, redirect_field_name='next', login_url=None, if_configured=False):
"""
Similar to :func:`~django.contrib.auth.decorators.login_required`, but
requires the user to be :term:`verified`. By default, this redirects users
to :setting:`OTP_LOGIN_URL`.
:param if_configured: If ``True``, an authenticated user with no confirmed
OTP devices will be allowed. Default is ``False``.
:type if_configured: bool
"""
if login_url is None:
login_url = settings.OTP_LOGIN_URL
def test(user):
return user.is_verified() or (if_configured and user.is_authenticated and not user_has_device(user))
decorator = user_passes_test(test, login_url=login_url, redirect_field_name=redirect_field_name)
return decorator if (view is None) else decorator(view)
However, I’d like to convert this into a Middleware as I want to avoid having to apply a decorator to every view on my site, but not managed to get working.
I tried to amend the following Middleware which I currently have in place which is just for authorised users and has been working but as per above decorator I want this Middleware extended to also have OTP required as well:
Middleware
from django.utils.deprecation import MiddlewareMixin
from django.urls import resolve, reverse
from django.http import HttpResponseRedirect
from wfi_workflow import settings
from django_otp import user_has_device
from django_otp.decorators import otp_required
from django_otp.middleware import is_verified
class LoginRequiredMiddleware(MiddlewareMixin):
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings by setting a tuple of routes to ignore
"""
##otp_required(login_url='login')
def process_request(self, request):
assert hasattr(request, 'user'), """
The Login Required middleware needs to be after AuthenticationMiddleware.
Also make sure to include the template context_processor:
'django.contrib.account.context_processors.account'."""
if not request.user.is_verified() and not request.path.startswith('/admin/') and not request.path.startswith('/account/' ):
current_route_name = resolve(request.path_info).url_name
if not current_route_name in settings.AUTH_EXEMPT_ROUTES:
return HttpResponseRedirect(reverse(settings.LOGIN_URL))
Help is much appreciated.
The fact that you return a HttpResponseRedirect will not work: Django's MiddlewareMixin will simply call the function to (optionally) alter the request, but it will never take the return into account.
What you can do is define middleware in a decorator-like structure, and return the HttpResponseRedirect in case the user should be authenticated with:
from django.urls import resolve, reverse
from django.http import HttpResponseRedirect
from wfi_workflow import settings
def OTPRequiredMiddleware(get_response):
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings by setting a tuple of routes to ignore
"""
def middleware(request):
from django_otp import user_has_device
if not user.is_verified() and not (if_configured and user.is_authenticated and not user_has_device(user)):
return HttpResponseRedirect(settings.OTP_LOGIN_URL)
return get_response(request)

Authenticating Django-Website with external Json Web Token

I have the following setup:
Apache Webserver running a Django Frontend Webpage
Application Server running a Django REST Framework
I now have to integrate the the Django Frontend into a 3rd party project which is written in java and angular. The authentication is completely handled by this 3rd party.
Users login over LDAP and create a JWT token.
Is it possible to simply receive the token in Django and authenticate the User after successfully decoding the token? And how would this work with the #login_required decorator when I have protected functions?
Is there some sort of project where I can orient on, or do I have to write all myself?
I use a built-in User model to store usernames. This enables me to login the user when authentication is successful and then use Django functionalities such as #login_requested as you'd normally use them.
Below is a sample code (without the code for REST authentication).
from django.contrib import messages
from django.contrib.auth import login
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.views.decorators.cache import never_cache
#never_cache
def user_login(request):
''' User login '''
if request.user.is_authenticated:
return HttpResponseRedirect(reverse('main:index'))
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# Your code for authentication here
# authenticate = ....
if authenticate:
# Get user // create one if it doesn't exist yet
user, created = User.objects.get_or_create(username=username)
# Login user - #login_required decorator can be used after user has been logged in
login(request, user)
next = request.POST.get('next', '/') if request.POST.get('next') else '/'
return HttpResponseRedirect(next)
else:
messages.warning(request, 'Authentication failed', extra_tags=forgot_password)
return HttpResponseRedirect(reverse('main:login'))
else:
return render(request, 'main/login.html', {})

how can I pass curent_user from flask-security to a pluggable view function?

I have a flask app that uses flask security for authentication. I want to use graphql with graphene to fetch data but I'm having trouble accessing the current_user proxy which is I've always used to resolve requests. graphene only provides a customized pluggable view which is understandable but it can't access the current_user within the context of the app and therefore current_user reverts back to AnonymousUser.
here is some sample code
from flask import Flask, render_template, redirect, request
from flask_security import Security, SQLAlchemySessionUserDatastore, login_required, current_user, login_user
from flask_graphql import GraphQLView
import graphene
from graphene_sqlalchemy import SQLAlchemyConnectionField
from .models import UserModel, RoleModel, Todo, TodoModel
from .pipeline import session
app = Flask(__name__, template_folder="../templates", static_folder="../static")
app.config.from_object('core.pipeline.configs.DevConfig')
user_datastore = SQLAlchemySessionUserDatastore(session, UserModel, RoleModel)
security = Security(app, user_datastore)
#app.route('/')
#login_required
def index(path):
user = current_user
return render_template('index.html')
Main issue in your code is
app.add_url_rule('/graphql', view_func=graphql_view())
Where graphql_view() run during code load without any flask request context.
Please, try this code
class GraphQLViewCurrentUser(GraphQLView):
def get_context(self, request):
context = super().get_context(request)
context.update({'current_user': current_user})
return context
app.add_url_rule(
'/graphql', view_func=GraphQLViewCurrentUser.as_view(
'graphql', schema=schema, context={}, graphiql=True))

How to secure Flask-Admin if my only user is going to be the Admin?

I have seen plenty of solutions online, however all of them addressed more complex apps which allow external users to create accounts. In my case the only user will be the admin. How do I secure the /admin routes created by Flask-Admin in an efficient way?
You can use Flask-Login for that. I usually add a route to the AdminIndexView class that handles the login if the user isn't logged in, yet. Otherwise the default admin page will be shown.
from flask import Flask
from flask_login import LoginManager
from flask_admin import Admin
app = Flask(__name__)
login_manager = LoginManager(app)
login_manager.session_protection = 'strong'
login_manager.login_view = 'admin.login'
admin = Admin(app, index_view=MyIndexView())
The definition of MyAdminView can look like this:
from flask_admin import AdminIndexView, expose, helpers
class FlaskyAdminIndexView(AdminIndexView):
#expose('/')
def index(self):
if not login.current_user.is_authenticated:
return redirect(url_for('.login'))
return super(MyAdminIndexView, self).index()
#expose('/login', methods=['GET', 'POST'])
def login(self):
form = LoginForm(request.form)
if helpers.validate_form_on_submit(form):
user = form.get_user()
if user is not None and user.verify_password(form.password.data):
login.login_user(user)
else:
flash('Invalid username or password.')
if login.current_user.is_authenticated:
return redirect(url_for('.index'))
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
#expose('/logout')
#login_required
def logout(self):
login.logout_user()
return redirect(url_for('.login'))
This integrates Flask-Login unobtrusively in the Flask-Admin interface. You will still need to implement the user and password verification like described in the Flask-Login documentation.
EDIT
To prevent unauthorized access to your admin routes create a ModelView class for each view and add a function is_accessible() with the following code:
def is_accessible(self):
if (not login.current_user.is_active or not
login.current_user.is_authenticated):
return False
return True