Giter VIP home page Giter VIP logo

django-loginas's Introduction

django-loginas

About

"Login as user" for the Django admin.

PyPI version

loginas supports Python 3 only, as of version 0.4. If you're on 2, use 0.3.6.

Installing django-loginas

  • Add loginas to your Python path, or install using pip: pip install django-loginas

  • Add the loginas app to your INSTALLED_APPS:

# settings.py
INSTALLED_APPS = [... 'loginas', ...]
  • Add the loginas URL to your urls.py:
# urls.py
urlpatterns = [
    # from Django 3.2 on, make sure to add loginas urls before the admin site urls, i.e.:
    path('admin/', include('loginas.urls')),
    path('admin/', admin.site.urls),
]
  • If you're using a custom User model, you'll need to add the template to it so the button shows up:
# admin.py
class YourUserAdmin(ModelAdmin):
    change_form_template = 'loginas/change_form.html'

At this point, you should be good to go. Just visit the Django admin, navigate to a user and you should see the "Log in as user" button at the top right of the screen.

Configuring

At this point, the only users who will be able to log in as other users are those with the is_superuser permission. If you use custom User models, and haven't specified that permission, or if you want to change which users are authorized to log in as others, you can define the CAN_LOGIN_AS setting, like so:

# settings.py

# This will only allow admins to log in as other users:
CAN_LOGIN_AS = lambda request, target_user: request.user.is_superuser

# This will only allow admins to log in as other users, as long as
# those users are not admins themselves:
CAN_LOGIN_AS = lambda request, target_user: request.user.is_superuser and not target_user.is_superuser

# You can also define a string path to a module:
CAN_LOGIN_AS = "utils.helpers.custom_loginas"

By default, clicking "Login as user" will send the user to settings.LOGIN_REDIRECT_URL. You can override this behavior like so:

# settings.py

LOGINAS_REDIRECT_URL = '/loginas-redirect-url'

In order to automatically restore the original user upon log out, replace the default log out with a special log out that restores the original login session from a signed session.

# settings.py

from django.core.urlresolvers import reverse_lazy
LOGOUT_URL = reverse_lazy('loginas-logout')

Additionally, you can specify the redirect url for logout (the default is settings.LOGIN_REDIRECT_URL).

# settings.py

from django.core.urlresolvers import reverse_lazy
LOGINAS_LOGOUT_REDIRECT_URL = reverse_lazy('admin:index')

By default, clicking "Login as user" will not update user.last_login. You can override this behavior like so:

# settings.py

LOGINAS_UPDATE_LAST_LOGIN = True

By default, the login switch message will generate Django admin LogEntry messages using the User model's USERNAME_FIELD like f"User {impersonator_user.getattr(USERNAME_FIELD)} logged in as {impersonated_user.getattr(USERNAME_FIELD)}." You can override this behavior by passing in a different field name:

# settings.py

LOGINAS_USERNAME_FIELD = 'email'

Other implementation suggestions

Existing logout view?

If you already have a logout view, you can modify to login the original user again after having had a "login as" session. Here's an example:

class LogoutView(LogoutView):
    template_name = 'myapp/logged_out.html'

    @method_decorator(never_cache)
    def dispatch(self, request, *args, **kwargs):
        from loginas.utils import restore_original_login
        restore_original_login(request)
        return redirect('myapp:login')

Template awareness

You can add the context processor loginas.context_processors.impersonated_session_status in your settings.py file if you'd like to be able to access a variable is_impersonated_session in all your template contexts:

# settings.py

TEMPLATES = [
    {
        ...
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                ...
                'loginas.context_processors.impersonated_session_status',
            ],
        },
    },
]

Note that django-loginas won't let you log in as other superusers, to prevent privilege escalation from staff users to superusers. If you want to log in as a superuser, first demote them to a non-superuser, and then log in.

License

This software is distributed under the BSD license.

django-loginas's People

Contributors

artscoop avatar bashu avatar bbirand avatar benjaoming avatar cypreess avatar egoddard avatar esmail avatar fabiocaccamo avatar gadybadger avatar intellisense avatar jarcoal avatar jkbrzt avatar jleclanche avatar jmfederico avatar kane-c avatar knyghty avatar lggwettmann avatar manelclos avatar mavenium avatar mpasternak avatar paambaati avatar pacahon avatar palmitoto avatar peterjochum avatar rodolfotorres02 avatar skorokithakis avatar sobolevn avatar theunraveler avatar treyhunner avatar yamikep avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-loginas's Issues

Make a new release

The version on PyPI is outdated without Py3k support. Perhaps a good idea to make a new release and post this to PyPI?

Document "login as" usage with custom user models

First admin/auth/change_form.html should probably be renamed to loginas/change_form.html and a new admin/auth/user/change_form.html should be made that contains only:

{% extends "relatives/change_form.html" %}

There are two ways to add the login as button to your custom user model's admin page.

  1. The easiest way to add the login as button is to set the template on the model admin:

    class CustomUserAdmin(ModelAdmin):
        change_form_template = 'loginas/change_form.html'
    
  2. If you are already using a custom template you need to instead extend the loginas/change_form.html template in that template.

That's how I document a similar feature for django-relatives.

By the way you might checkout django-relatives if you get a chance. I just made it recently.

loginas for non staff user

Question:
Is it possible to grant "loginas" rights to somebody who is not staff member?
If yes, then how would this person access the loginas feature since this button is only present in the admin pages?
-> Is there a way such a button can be placed on any page?

Thank you
Davy

Invalid signature encountered

Hello,

in restore_original_login (utils), why to log an error when the original session expired ?
Can i made a PR to remove this error and and a parameter to choose the time delta value ?

Sincerely,

François

AdminSite.final_catch_all_view introduced in Django 3.2 might break admin loginas button

Hi there,

First and foremost, thank you for a wonderful django module. It has saved countless support and QA hours.

Python version: 3.7

Django version: 3.2.2

Loginas version: 0.3.9

Django 3.2 introduced final_catch_all_view on the AdminSite class. This was introduced to prevent a bad actor from enumerating admin pages.
https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#django.contrib.admin.AdminSite.final_catch_all_view

I think this change might have broken the admin loginas button implementation. If final_catch_all_view is set to True (the default), then clicking the loginas button results in a 404. If the setting is set to False, the button works as expected.

I have some custom admin urls in my project and the work around was modifying the def get_urls() method on AdminSite like so:

def get_urls(self):
    url_list = super().get_urls()

    # from django.urls import path
    custom_path_obj = path(...)
    url_list = [custom_path_obj] + url_list
    # the above line used to be: url_list.append(custom_path_obj)

    return url_list

This is done because the final_catch_all_view url is added to the END of the admin url list. By prepending the custom url, we ensure the django admin finds it before the catch all. Here is a conversation in the django developers google group with more details
https://groups.google.com/g/django-developers/c/vHZ8F8Nvq9M?pli=1

Although I think I understand how to fix this for my own admin pages, I'm unsure how to use this to fix the issue with loginas. We use loginas and would like to upgrade to 3.2, so I'm happy to take a pass at a pull request if someone else can provide some advice on how to implement.

Login as user is never disconnected

If i login as another user. When i log out, i retrieve my former user.
But then, when i logout again, i'm still connected.

The user session flag is never removed from session. Why ?

Sincerely,

F.P

permissions required to see the log in as button?

Hi,

What is actually required to display the "log in as" button?
I have a user that is in a group A that has the change permission on user but the button is not displayed. I tried to give all the permissions to this group and it did not change anything.
Only when I set the user as a super user, he can see the button.
Is it required to be a super user to see the "log in as" button?

Thanks

Feature Suggestion: Go back to being original user.

Nice job thanks!

You're already storing the original user.pk in the session, it would be great to be able to go back to being who you were without logging out. If there's no objection I'd be happy to add a view for that.

Then perhaps a context processor to put html for a button in the context when the user is being impersonated. It would be up to the application developer to decide where to place the button so that anyone will be able to see it, since that would vary for every application.

Add "reason" for logging in

For compliance reasons it might be nice to add a reason for logging in as a user, for example to add a link to the internal support ticket. Just a TextField that gets added to the django admin history would be great.

Get "Page Not Found"

While following the installation I kept getting "Page Not Found" and some errors like

Page not found (404)
Request Method:	GET
Request URL:	http://localhost:8000/admin/
Using the URLconf defined in config.urls, Django tried these URL patterns, in this order:

__debug__/
about/ [name='about']
admin/ ^login/user/(?P<user_id>.+)/$ [name='loginas-user-login']
admin/ ^logout/$ [name='loginas-logout']
users/
accounts/
...

Eventually I solved it by doing this in urls.py

urlpatterns = [
    path(settings.ADMIN_URL, admin.site.urls),
    path(settings.ADMIN_URL, include("loginas.urls")),
...

since I pull the admin url from config as prescribed by django-cookiecutter

Django admin is quoting models CharField id with special characters

Hi,

I found very esoteric bug. I am using custom id fields format for my models in django. It is CharField and values are in form of "usr_12345". Apparently for such specific id django admin assumes that some values can brake admins urls, so they are using special layer of custom quote/unquote from here:

https://github.com/django/django/blob/master/django/contrib/admin/utils.py#L63

Basically for such id usr_12345 django actually generate links like this: /admin/accounts/user/usr_5F12345/change/.

This affects also 3rd party tools as django-loginas link generated inside django admin is of this form:
`/admin/login/user/usr_5F12345/

Ths obviously creates 404 error as loginas cannot find user with quoted id usr_5F12345.

I am providing very easy fix for that, just unquoting the user_id value before processing.

PS. Quoting issue is completely transparent for integer based IDs.

Remove "Log in as user" button when editing a group

Hi,
There's a bug no one seems to have spotted.
When you edit a user group in the admin, the "Log in as user" button is displayed, which is useless. Worse, it tries to login as a user that has the same id as the currently edited group.

Login in a different domain

My Django web app has access to 2 different domains. Processing is done according to the user's country. When I click the button, I want to redirect a domain according to the user's country. I overrode login as a display method, but login to a different domain did not occur. When I click the button from domain a, I want to log in to domain b. How should we approach this?

Work with different frontend

Hello,

I use django as my backend and nuxt3 as my frontend. As soon as I login as another user through the admin dashboard, I get sent to the django urls. These are located at localhost:8000 for example. While my frontend is located at localhost:3000.

How can I solve this issue? It also doesn't look like my frontend know that it's logged in as another user.

loginas doesn't work if the authentication backend doesn't have `get_user` method

django-loginas does not work with django-rules as an Authentication Backend.

We use django-rules to specify custom authentication rules. As a result, we have this in our settings:

AUTHENTICATION_BACKENDS = (
    'rules.permissions.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

However, rules.permissions.ObjectPermissionBackend does not specify a get_user method (as seen here). Even though, the Django documentation says that each backend must implement a get_user method, apparently this is not enforced (!?). This was discussed in a django-rules issue here:
dfunckt/django-rules#46

The reason why I believe this is not enforced is because they made some changes in the behavior of force_login (https://code.djangoproject.com/ticket/27542#ticket).

A quick workaround would be to check if the authentication backend has a get_user method. If not, then continue.

Does this make sense?

Can't install loginas

Hello, i installed loginas with pip, then put in installed apps in settings, then i change my admin url, I remove admin.site.urls and I wrote include('loginas.urls'), but that doesn't work, could you help me to install it? Thanks

Update version support

Maybe it is time to extend the test config with new versions:

  • Django 1.10 has been out for a while, 1.11 is in alpha
  • Python has 3.5 and 3.6

How to use it with staff members with less permissions

I defined a staff member that can only view other users.
I can log in as that staff member to the admin page, and see the list of users.
When trying to use the login-as for a certain user, I'm getting:
You do not have permission to do that.

Is there any other permission I need to enable?
The use case might be customer support, needing to log in as a customer, but they should not have full access in the admin page.

Thanks

django.contrib.admin is required

It appears as though the loginas app has an undocumented dependency on the django.contrib.admin app in utils.py

Traceback (most recent call last):                                                                                                                 
[...]                                                                  
  File "/app/project/apps/accounts/urls.py", line 4, in <module>                                                                                   
    from loginas.views import user_login                                                                                                           
  File "/usr/local/lib/python3.7/site-packages/loginas/views.py", line 11, in <module>                                                             
    from .utils import login_as, restore_original_login                                                                                            
  File "/usr/local/lib/python3.7/site-packages/loginas/utils.py", line 7, in <module>                                                              
    from django.contrib.admin.models import CHANGE, LogEntry                                                                                       
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/models.py", line 39, in <module>                                               
    class LogEntry(models.Model):                                                                                                                  
  File "/usr/local/lib/python3.7/site-packages/django/db/models/base.py", line 111, in __new__                                                     
    "INSTALLED_APPS." % (module, name)                                                                                                             
RuntimeError: Model class django.contrib.admin.models.LogEntry doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

I believe this could be corrected by testing whether the admin is installed or not, and skipping trying to create the LogEntry if isn't installed. If that makes sense, I can submit a patch to make this change.

ImproperlyConfigured if target user is inactive

Hello,
The default implementation of django.contrib.auth.backends.ModelBackend.get_user prevents inactive users to logging in since Django 1.10 (see user_can_authenticate). I do believe this error should't be critical since you could fix it without updating project configuration and better showing warning / error message instead.
How about allow throwing custom exception in can_login_as with the reason why you can't log in as a target user and showing it to the authenticated user in this case?

Add group for users allowed to login as other users

I can imagine a situation in which a user that is not a superuser may need to authenticate as other users. I think it might be a good idea to check whether the user is a superuser or is in a particular group.

Thoughts on this?

Feature Idea: view decorator to prompt for login as a different user

Here's the use case:

  1. Staff user logs into admin site
  2. Staff user navigates to Post model's page
  3. Staff user clicks "View on Site"
  4. The post is private so a page is shown to admin user presenting a list of users to re-authenticate as
  5. Staff member selects a user to re-authenticate as and is logged in and redirected to the page

Error 500 due to template

In my project I have 500 error on group change page (ex. "/admin/auth/group/1/") due to template "/templates/loginas/change_form.html". I don't know why, workaround: not use pip, clone sources in project app directory, remove all templates and use this app.
I use Python 3.4, Django 1.7.

Django 1.5 syntax error

Error when used with Django 1.5 due to url syntax change (i.e. from {% url loginas-user-login object_id %}).

Please update, thanks!

Doesn't work with Django 3.0

Tried the latest django-loginas (master branch of this repo) and got this error:

  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/mmalysh/Development/mining-website/kryptex/config/urls.py", line 57, in <module>
    url(r'^adminium/', include('loginas.urls')),
  File "/Users/mmalysh/Development/mining-website/venv/lib/python3.6/site-packages/django/urls/conf.py", line 34, in include
    urlconf_module = import_module(urlconf_module)
  File "/Users/mmalysh/.pyenv/versions/3.6.9/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/mmalysh/Development/mining-website/venv/lib/python3.6/site-packages/loginas/urls.py", line 2, in <module>
    from loginas.views import user_login, user_logout
  File "/Users/mmalysh/Development/mining-website/venv/lib/python3.6/site-packages/loginas/views.py", line 5, in <module>
    from django.utils import six
ImportError: cannot import name 'six'

New release

Thanks for the incredibly useful tool! Would it be possible to release a new version? Thank you very much :) .

AttributeError When Using "is_admin" in "CAN_LOGIN_AS" via Readme

In the Readme file there is the following line:
CAN_LOGIN_AS = lambda request, target_user: request.user.is_admin and not target_user.is_admin

using Django 1.8, I received an AttributeError that is_admin is not a valid attribute ("'User' object has no attribute 'is_admin'"), so I changed it to is_superuser and it now works. Not sure if is_admin worked in older versions, but it seems to be broken in 1.8... otherwise, amazing package!

Send user_id with POST

Sorry for my English.

I'm trying to add "Login as" buttons to admin changelist and faced with the complexity associated with the fact that user_id passed via URL. If you will pass it as POST parameter, you will have the opportunity to use single form with multiple buttons like this:
<button form="loginas-form" name="user_id" value="{{ user.id }}">Login as {{ user.username }}</button>

Use POST instead of GET

Thanks for a very useful app!

I think the user switch should happen on a POST request. It's both a more appropriate HTTP method for this type of requests, but more importantly it will ensure that CSRF check is performed. As it is now, anybody can change the user for me just by knowing the URL and embedding an image to a page I visit.

Don't allow login as other staff users

For audit-ability, I don't want superusers to be able to authenticate as other staff users (especially not as other superusers).

One (or more) of these could work:

  1. Completely disallow logging in as other staff users
  2. Add a setting that when enabled will disallow logging in as other staff users
  3. Add a setting allowing certain user groups to be excluded from the loginas feature
  4. Add a setting restricting the loginas feature to certain user groups

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.