I'm having trouble figuring out how to add custom permissions to nav items, such that certain items will show up when the user is logged in / logged out, or other parameters are met (such as the user is part of a certain organization).
Any help or examples would be greatly appreciated.
With flask_security package you are able to set roles to users and check them in flask_admin views as follows:
from http import HTTPStatus
from flask_admin.contrib.sqla import ModelView
from flask_security import current_user, url_for_security
from flask import abort, redirect, request
class AdminSecurityMixin:
allowed_roles = []
def is_accessible(self):
return current_user.is_active and current_user.is_authenticated and \
(current_user.has_role('admin') or any(current_user.has_role(r) for r in self.allowed_roles))
def _handle_view(self, name, **kwargs):
if not self.is_accessible():
# if user is logged in, but can't view the admin, reject access
if current_user.is_authenticated:
abort(HTTPStatus.FORBIDDEN)
# otherwise redirect to the admin login view
# the next parameter is so we can redirect them after they'ved
# logged in to where they wanted to go originally
return redirect(url_for_security('login', next=request.url))
return None
class SecuredModelView(AdminSecurityMixin, ModelView):
allowed_roles = ['custom_role']
Now model registered with SecuredModelView will be accessible in admin panel only to users who are logged in and have assigned custom_role or admin role.
Roles can be created and added to existing users with following commands:
$ flask roles create custom_role
$ flask roles add <your user email> custom_role
I guess you already have a database with at least a user table.
I think you just want to design templates.
I invite you to follow this Flask tutorial about templates. It will introduce you Jinja, a templating language.
Related
I am trying to create a Online exam portal and I have created one admin portal for that site. I have 2 types of users in my site "Students" and "Teachers". How can I restrict students from accessing the admin portal and other views of admin panel?
In views.py in function related to your template, you can use the decorator permission_required as follows
from django.contrib.auth.decorators import permission_required
#permission_required('foo.access_my_view')
def my_view(request):
...
You have detailed instructions on how to add and modify the permissions here
To give a user the ability to login to the Django Admin, we set their staff flag.
Is there a way to make a "staff" Group where everyone put into it gains access to the admin page without manually setting staff status?
You can override the Django Admin site (follow the doc, it's pretty amazing).
During the time, create a custom admin site class and override it's has_permission(...) method
from django.contrib import admin
class MyCustomAdminSite(admin.AdminSite):
def has_permission(self, request):
has_perm = super().has_permission(request)
if has_perm:
return True
return request.user.groups.filter(name="YourGroupName").exists()
I have a view in my django backend which I want only admin to see.
#api_view(('GET',))
def get_analytics(request):
# Number of users who dogged in once to our system today
login_count= User.objects.filter(last_login__startswith=timezone.now().date()).count()
data = {}
data['login_count'] = login_count
return JSONResponse(data)
It's a function based view. How can I make it visible only to admin? it's corresponding url I hit from my angular app to get and render the data.
You can also use the below decorator
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
You can add IsAdminUser permission class to your view to restrict access to only admin users. Only users with admin permissions will be granted access then. Others will be denied access.
#api_view(('GET',))
#permission_classes((IsAdminUser, )) # add the permission class
def get_analytics(request):
...
return JSONResponse(data)
When the permissions fail, then either an exceptions.PermissionDenied or exceptions.NotAuthenticated exception will be raised depending on some conditions.
I am a newbie at Django. Using django-allauth I have set up single click sign in. I obtained my domain credentials ( client_id and secret_key) from google api console. But the problem is django-allauth is letting me login from any google account while I want the email addresses to be restricted to my domain ( #example.com instead of #gmail.com)
django-social-auth has the white listed domains parameter for this, how do I include this information in allauth?
I found django-allauth much easier to set up after spending hours on django-social-auth
Any help would be much appreciated.
Answering my own question-
What you want to do is stall the login after a user has been authenticated by a social account provider and before they can proceed to their profile page. You can do this with the
pre_social_login method of the DefaultSocialAccountAdapter class in allauth/socialaccount/adaptor.py
Invoked just after a user successfully authenticates via a
social provider, but before the login is actually processed
(and before the pre_social_login signal is emitted).
You can use this hook to intervene, e.g. abort the login by
raising an ImmediateHttpResponse
Why both an adapter hook and the signal? Intervening in
e.g. the flow from within a signal handler is bad -- multiple
handlers may be active and are executed in undetermined order.
Do something like
from allauth.socialaccount.adaptor import DefaultSocialAccountAdapter
class MySocialAccount(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
u = sociallogin.account.user
if not u.email.split('#')[1] == "example.com"
raise ImmediateHttpResponse(render_to_response('error.html'))
This is not an exact implementation but something like this works.
Here's an alternate solution:
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class CustomAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
return False # No email/password signups allowed
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def is_open_for_signup(self, request, sociallogin):
u = sociallogin.user
# Optionally, set as staff now as well.
# This is useful if you are using this for the Django Admin login.
# Be careful with the staff setting, as some providers don't verify
# email address, so that could be considered a security flaw.
#u.is_staff = u.email.split('#')[1] == "customdomain.com"
return u.email.split('#')[1] == "customdomain.com"
This code can live anywhere, but assuming it's in mysite/adapters.py, you'll also need the following in your settings.py:
ACCOUNT_ADAPTER = 'mysite.adapters.CustomAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'mysite.adapters.CustomSocialAccountAdapter'
You could do something in the line of overriding allauth's allauth.socialaccount.forms.SignupForm and checking the domain during the signup process.
Discalmer: this is all written without testing, but something in the line of that should work.
# settings.py
# not necesarry, but it would be a smart way to go instead of hardcoding it
ALLOWED_DOMAIN = 'example.com'
.
# forms.py
from django.conf import settings
from allauth.socialaccount.forms import SignupForm
class MySignupForm(SignupForm):
def clean_email(self):
data = self.cleaned_data['email']
if data.split('#')[1].lower() == settings.ALLOWED_DOMAIN:
raise forms.ValidationError(_(u'domena!'))
return data
in your urls override allauth defaults (put this before the include of django-allauth)
# urls.py
from allauth.socialaccount.views import SignupView
from .forms import MySignupForm
urlpatterns = patterns('',
# ...
url(r"^social/signup/$", SignupView.as_view(form_class=MySignupForm), name="account_signup"),
# ...
)
I'm not sure for the "^social/signup/$", recheck that.
I have several admin instances running on a site - one for each country, that the site supports.
However, if a user logs into one admin, they are automatically able to access other instances.
I need to make the auth code aware of which admin the user has logged into and prevent access to other admin systems.
Any ideas how this can be done?
You can use middleware to check for user permissions to access certain areas of admin site. Checkout this snippet. (You might want to know more about handling custom permissions in Django.)
If you need something more universal, you can use the code example below. The idea is simple: it uses custom functions to find out about user permissions and to give an appropriate response:
#coding: utf-8
# Note that RESTRICTED_URLS tuple takes three parameters: url regex, function to check
# whether user has certain permission, and a function to redirect the user to a certain
# page if he doesn't have sufficient rights.
import re
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponseRedirect
from django.contrib import messages
from backend.models import Professional
from django.contrib.auth.decorators import permission_required
def calculate_forbidden_response(request, view_func,view_args,view_kwargs):
if not request.user.is_authenticated():
return permission_required('')(view_func)(request,*view_args,**view_kwargs)
elif request.user.has_perm('backend.p_add_professional'):
messages.error(request, _('You need permission Spam to enter this cabinet.'))
return HttpResponseRedirect('/some_help_page_about_permissions.html')
def check_professional_permission(request):
return request.user.has_perm('backend.p_access_professional_cabinet')
RESTRICTED_URLS = (
(r'/professional/(.*)$', check_professional_permission, calculate_forbidden_response),
)
RESTRICTED_URLS_EXCEPTIONS = ()
class CheckPermissionMiddleware(object):
def __init__(self):
self.restricted = tuple([(re.compile(url[0]), url[1], url[2]) for url in RESTRICTED_URLS])
self.exceptions = tuple([re.compile(url) for url in RESTRICTED_URLS_EXCEPTIONS])
def process_view(self,request,view_func,view_args,view_kwargs):
if request.user.is_superuser:
return None
for path in self.exceptions:
if path.match(request.path): return None
for rule in self.restricted:
url, permission = rule[0], rule[1]
calculated_response = rule[2]
if url.match(request.path):
if not permission(request):
return calculated_response(request, view_func,view_args,view_kwargs)
else:
return None
return None