Restricting admin urls to already authenticated superusers in Django - django

I've searched around for some solutions to this, but they all focus on a single admin url. However I was wondering if there is a way to restrict ALL the admin views, not the accounts to already authenticated superusers.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^accounts/', include('accounts.urls'))
]
What I want is
urlpatterns = [
url(r'^admin/', is_superuser(admin.site.urls)),
url(r'^accounts/', include('accounts.urls'))
]
Or something like this that I can do in the view
#user_passes_test(lambda u: u.is_superuser, login_url='allauth.account.views.LoginView')
def superuser_only(request, template):
return render(request, template)
but still allows me to use admin.site.urls.
Is there a quick and elegant way to solve this? I want all users including the superuser to authenticate through accounts app.

You could create a middleware class that checks the request.path and the user and add it to the MIDDLEWARE var in your settings.
from django.http import Http404
class SuperUserMiddleware(object):
def process_request(self, request):
user = request.user
is_anonymous = user.is_anonymous()
if 'admin' in request.path
if not is_anonymous:
if not user.is_superuser:
raise Http404
else:
raise Http404

Related

Django Class-based Views - ListView if User Is Authenticated, FormView if User is Not, Same URL

When a visitor is logged out and visits my homepage, I want them to see a registration form. When the user logs in, they will be redirected back to the homepage, but it will show a listview-like view.
Is the best way to achieve this to subclass both ListView and FormView and override each method necessary to get the behavior I want? Or is there a better way? Checking if the user is authenticated in each method doesn't seem like the Django way to do this. Hoping there's a smart design pattern approach for doing this that I don't know about.
class HomepageView(ListView, FormView):
def get_template_names(self):
if self.request.user.is_authenticated:
return ['homepage/loggedin_index.html']
else:
return ['homepage/loggedout_index.html']
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated:
...
else:
...
def ... and so on
My must-have requirement is that the URL for both logged-in and logged-out users must resolve to the root URL since it's a homepage.
from django.urls import path
from .views import HomepageView
app_name = 'homepage'
urlpatterns = [
path('', HomepageView.as_view(), name='index'),
]
There is also a CreateListView that do what you want. And then you can change its form to the one you want.

django override auth login method

I want to override login function in __init__.py of django.contrib.auth
I did steps:
urls.py
url(r'^login/$', 'my_login'),
views.py
from django.contrib import auth
def login(request, user, backend=None):
# do some stuff
settings.py
INSTALLED_APPS = [
#'django.contrib.auth',
'my_auth_app'
]
But I feel it's the wrong approach.
Actually I want to get default contrib.auth with overidden login method
Done this approach.
urls.py
# separate my custom login from django default auth
path('accounts/login/', CustomLoginView.as_view(), name='login'),
path('accounts/', include('django.contrib.auth.urls')),
views.py
def custom_login(request, user, backend=None):
"""
modificated generic.auth login.
Send signal with extra parameter: previous [session_key]
"""
# get previous seesion_key for signal
prev_session_key = request.session.session_key
"""
original code
"""
# send extra argument prev_session_key
user_logged_in.send(sender=user.__class__, request=request, user=user, prev_session_key=prev_session_key)
# custom class-based view overriden on LoginView
class CustomLoginView(LoginView):
def form_valid(self, form):
"""Security check complete. Log the user in."""
# changed default login
custom_login(self.request, form.get_user())
return HttpResponseRedirect(self.get_success_url())
When I make custom_login based on default login(), I am afraid that it is no best approach, because i am copying a part of original code. Maybe it's better to use a decorator here?

Django - how to implement an example.com/username url system

I am trying to implement on my website a simple and friendly address system.
What i'm thinking about is when the user logged in, his username will be displayed in the address bar.
www.example.com/username1 (for home page profile)
www.example.com/username1/about/
www.example.com/username1/gallery/
www.example.com/username2 (for home page profile)
www.example.com/username2/about/
www.example.com/username2/gallery/
And additionally if anyone enter the address www.example.com/username1, will be shown the profile of user1.
I already implemented a register/Login system using Django-Allauth
mySite/urls.py
url(r'^accounts/', include('allauth.urls')),
home/urls.py
url(r'^$', views.index),
home/views.py
def index(request):
return render_to_response('home/index.html', context_instance=RequestContext(request))
I tried to follow some examples like Facing problem with user profile url scheme like example.com/username in django
But i dont have this thing working yet. I dont understand what to do :(
Please, give some advise.
Add the following url as the last item of the mySite/urls.py:
urlpatterns = patterns('',
...
url(r'^(?P<username>\w+)/', include('userapp.urls')),
)
Then the username parameter will be passed to the views of your userapp:
userapp/urls.py:
from userapp import views
urlpatterns = patterns('',
url(r'^$', views.profile, name='user_profile'),
url(r'^about/$', views.about, name='user_about'),
url(r'^gallery/$', views.gallery, name='user_gallery'),
)
userapp/views.py:
def profile(request, username):
user = get_object_or_404(User, username=username)
return render(request, 'userapp/profile.html', {'profile_user': user})
def about(request, username):
...
def gallery(request, username):
...
It's almost perfect, but i have a bug. Let me explain. When i login (my login system is: django-allauth) on the http://127.0.0.1:8000/accounts/login/ i return to http://127.0.0.1:8000 with an error 404.
I am not getting the http://127.0.0.1:8000/username/ with the template, when the login botton is clicked.
The instalation of django-allauth require adding to the settings.py
LOGIN_REDIRECT_URL = '/'
How can i redirect to http://127.0.0.1:8000/username/ and show the correct template?
1. Regarding
How can i redirect to
Based on the answer from https://stackoverflow.com/a/20143515/4992248
# settings.py:
ACCOUNT_ADAPTER = 'project.your_app.allauth.AccountAdapter'
# project/your_app/allauth.py:
from allauth.account.adapter import DefaultAccountAdapter
class AccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
return 'request.user.username' # probably also needs to add slash(s)
Would be better to use get_absolute_url, ie return 'request.user.get_absolute_url'. In this case you need to do:
# 1. Add `namespace` to `yoursite/urls.py`
urlpatterns = patterns('',
...
url(r'^(?P<username>\w+)/', include('userapp.urls', namespace='profiles_username')),
)
# 2. Add the code below to the Users class in models.py
def get_absolute_url(self):
# 'user_profile' is from the code shown by catavaran above
return reverse('profiles_username:user_profile', args=[self.username])
2. Regarding
show the correct template
catavaran wrote correct urls which leads to views.profile, so in view.py you need to write:
from django.shortcuts import render
from .models import UserProfile # import model, where username field exists
from .forms import UserProfileForm # import Users form
def profiles(request, username):
user = get_object_or_404(UserProfile, username=username)
return render(request, 'home/profiles.html', {'user_profile_form': user})
In template (i.e. profiles.html) you can show user's data via {{user_profile_form.as_p}}

Redirect if already logged in through Django urls?

Currently I use these patterns to log in and out
urlpatterns += patterns("",
(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
(r'^logout/$', 'django.contrib.auth.views.logout', {'template_name': 'logout.html'})
)
Inspite of having LOGIN_REDIRECT_URL = '/profile/' in my settings.py, Django does not send me to /profile/ if I want to access /login/ when I'm already logged in...
Can I somehow redirect in the URL patterns of the auth system? I'm reluctant to write a custom view for that.
I use something like this in my urls.py:
from django.contrib.auth.views import login
from django.contrib.auth.decorators import user_passes_test
login_forbidden = user_passes_test(lambda u: u.is_anonymous(), '/')
urlpatterns = patterns('',
url(r'^accounts/login/$', login_forbidden(login), name="login"),
How about riding the Django login view?
Then add this little piece of code in that view:
if request.user.is_authenticated():
# Redirect to profile
If you want to do something else in template itself for the register user:
{% if user.is_authenticated %}
I ended up writing a decorator for such task.
Please take note that I've made it quick & dirty.
from django.conf import settings
from django.shortcuts import redirect
def redirect_if_logged(f=None, redirect_to_url=None):
u"""
Decorator for views that checks that the user is already logged in, redirecting
to certain URL if so.
"""
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
redirect_url = redirect_to_url
if redirect_to_url is None:
# URL has 'next' param?
redirect_url_1 = request.GET.get('next')
# If not, redirect to referer
redirect_url_2 = request.META.get('HTTP_REFERER')
# If none, redirect to default redirect URL
redirect_url_3 = settings.LOGIN_REDIRECT_URL
redirect_url = redirect_url_1 or redirect_url_2 or redirect_url_3
return redirect(redirect_url, *args, **kwargs)
else:
return view_func(request, *args, **kwargs)
return _wrapped_view
if f is None:
return _decorator
else:
return _decorator(f)
Looking at the source code on Github, the default login view provided by django.contrib.auth will only use LOGIN_REDIRECT_URL if the login form is submitted via a POST request, and it has no next parameter. (The docs mention this too).
The login view doesn't actually do any checking of whether you're authenticated already or not - so an already-authenticated user hitting the page with a standard GET request will see the login form again - just the same as an unauthenticated user.
If you want different behaviour, I'd recommend writing your own login view.
On your projects urls.py file (url_patterns list), add redirect_authenticated_user=True as a parameter in login path like this:
path('admin/', admin.site.urls),
path('login/', auth_views.LoginView.as_view(
template_name='blog_app/login.html',
redirect_authenticated_user=True
), name='login'),
You could use this decorator
def login_excluded(redirect_to):
""" This decorator kicks authenticated users out of a view """
def _method_wrapper(view_method):
def _arguments_wrapper(self, request, *args, **kwargs):
if self.request.user.is_authenticated:
return redirect(redirect_to)
return view_method(request, *args, **kwargs)
return _arguments_wrapper
return _method_wrapper

django LOGIN_REDIRECT_URL with dynamic value

I'm trying to redirect a user to a url containing his username (like http://domain/username/), and trying to figure out how to do this. I'm using django.contrib.auth for my user management, so I've tried using LOGIN_REDIRECT_URL in the settings:
LOGIN_REDIRECT_URL = '/%s/' % request.user.username # <--- fail..
but it only seems to accept fixed strings, rather than something that'll be determined after the user is logged in. How can I still accomplish this?
A solution, is to redirect to a static route like '/userpage/' and have that redirect to the final dynamic page.
But I think the real solution is to make a new view that does what you really want.
from django.contrib.auth import authenticate, login
from django.http import HttpResponseRedirect
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
HttpResponseRedirect('/%s/'%username)
else:
# Return a 'disabled account' error message
else:
# Return an 'invalid login' error message.
http://docs.djangoproject.com/en/dev/topics/auth/#authentication-in-web-requests
for more information about rewriting the view. This is how the docs say to override this kind of thing.
With the class-based django.contrib.auth.views.LoginView, you can now simply override get_success_url:
urls.py:
url(r'^login$', MyLoginView.as_view(), name='login'),
url(r'^users/(?P<username>[a-zA-Z0-9]+)$', MyAccountView.as_view(), name='my_account'),
views.py
class MyLoginView(LoginView):
def get_success_url(self):
return reverse('my_account', args=[self.request.user.username])
Wrap the auth view in your own custom view and redirect to wherever you want if authentication succeeded.
from django.http import HttpResponseRedirect
from django.contrib import auth
from django.core.urlresolvers import reverse
def login(request):
template_response = auth.views.login(request)
if isinstance(template_response, HttpResponseRedirect) and template_response.url == '/accounts/profile/':
return HttpResponseRedirect(reverse('user', args=(request.user.username,)))
return template_response
Another alternative is to use the query param next to indicate where to redirect to after the login.
sign in
I use django-two-factor-auth login view which provides OTP features for login.
Hence I extend from two_factor's LoginView.
In main urls.py:
from two_factor.urls import urlpatterns as tf_urls
urlpatterns = [
# make sure login is before default two_factor (tf_urls) urls
# coz first url has higher preference
path('account/login/', MyLoginView.as_view(), name='login'),
path('', include(tf_urls)),
]
In views.py:
from two_factor.views.core import LoginView
from two_factor.utils import default_device
class MyLoginView(LoginView):
def get_success_url(self):
if self.is_otp_setup() is True:
return reverse('homepage:homepage')
# otp not setup. redirect to OTP setup page
else:
return reverse('two_factor:setup')
def is_otp_setup(self):
if self.request.user and \
self.request.user.is_authenticated and \
default_device(self.request.user):
return True
else:
return False