I've used third party authentication backends, but your request seems a bit odd to me. I think I'm being caught up on the some of the specifics without enough context. Why can you not use a custom User that stores the backend that created it (or last used for authentication) and overrides set_password to delegate the password saving to the referenced authentication backend? You would need to also create your own subclass of ModelBackend that does the original User.set_password behavior. The only scenario that I can think of where your implementation would get a bit more complicated is if you wanted to support multiple active sessions using different authentication backends for the same user object.
Regards, Michael Manfre On Sat, Dec 13, 2014 at 4:28 AM, Roman Akopov <[email protected]> wrote: > > Tim, > > It's not about the benefit, it's about the possibility. The one simply > cannot use two external backends with support of password change because > each backend will have to provide own User model. I understand your point, > and I agree that some logic I suggest seems controversial, but current > implementation of plugable authentication backends is technically incomplete, > because password cannot be changed or reset, and ideologically broken, > because full functionality cannot be correctly added even with a lot of > code. Actually whole contrib.auth should be replaced to allow pluggable > authentication backends to provide password change or password reset > independently. > > Also, if you think it is good idea to force old behavior even if backend > supports extended functionality, what stops us from introducing two boolean > settings AUTHENTICATION_BACKEND_DELEGATE_PASSWORD_CHANGE and > AUTHENTICATION_BACKEND_DELEGATE_PASSWORD_RESET which will be False by > default? This will force old behavior while allowing to use extended > backends if required. > > I want it to be clear, I'm not asking for specific functionality, I'm > asking for extensibility. External authentication is complex subject by > itself and fighting with incomplete Django extensibility interface does not > make it simpler, but better extensibility interface does. > > Maybe, it will be wiser to discuss this feature request with people > actually using external authentication, not core developers, to let them > share their experience and maybe suggest better ways to implement this from > the users/app developers point of view. Maybe this feature is desired one, > but I'm just first who asked or maybe nobody actually needs it and I;m just > Django-pervert. I can talk about my end users only, and about theirs user > experience, you have no externally authenticated users at all. If we've > done with technical side, I mean it seems clear what should be changed at > source code level, and if only specific behavior logic is to be discussed, > maybe it is better to discuss this non-core developer question with wider > audience. What do you think? > > > > On Saturday, December 13, 2014 3:19:26 AM UTC+4, Tim Graham wrote: >> >> I haven't used external authentication backends for any projects, but I >> still think the concept of dynamically changing how the password change >> form/views work based on which backend you authenticated with is too much >> complexity. This scheme feels very brittle and I'm not sure that making >> this change in Django offers much benefit (in reduced code, for example) in >> the end. >> >> On Thursday, December 11, 2014 2:05:22 PM UTC-5, Roman Akopov wrote: >>> >>> I've researched a little more, and looks like there is >>> BACKEND_SESSION_KEY so it is possible to annotate user with backend on >>> subsequent requests. >>> https://github.com/django/django/blob/master/django/ >>> contrib/auth/__init__.py#L169 >>> Look like it should be a one line fix for this like >>> user.backend = backend >>> I think this will not break any existing code. >>> >>> At non-request situations for non-authenticated User model, i.e. >>> instantiated from database, or just created and saved, we can simply >>> fallback to old behavior. Actually, I think this is correct, because a user >>> can have valid credentials in various backends simultaneously. Imagine we >>> have three backends: Model, LDAP, SMTP. So if you authenticate with >>> credentials valid for SMTP backend, you can't change password because SMTP >>> does not support this at all. It should be something like methods throwing >>> NotImplementedError. If you authenticate with credentials valid for LDAP >>> backend you can change password and that change will be performed against >>> LDAP. If you authenticate with credentials valid for Model backend you can >>> change password in model database. This looks like consistent behavior, >>> since you change password you just used to authenticate, not some other >>> password you probably even do not know about. And if no custom backend is >>> registered or no backend information is available, we fallback to default >>> one. Of course there is a question, how to reset LDAP password from admin >>> interface. The answer will be "you cannot". This looks sane for me, because >>> end-users are happy using just one software for all their tasks and LDAP >>> administrator should use LDAP tools for administrative tasks anyway, and >>> password reset is just a small one, there will be permission management, >>> group membership and all of these tools should not be doubled in Django >>> admin. >>> >>> On Thursday, December 11, 2014 7:49:05 PM UTC+4, Tim Graham wrote: >>>> >>>> User is only annotated with backend when calling authenticate(). On >>>> subsequent requests, or in non-request situations like the Python shell, >>>> how will you know which backend to delegate to? >>>> >>>> How do existing LDAP backends deal with this problem? (or do they just >>>> ignore it?) >>>> >>>> On Thursday, December 11, 2014 4:40:09 AM UTC-5, Roman Akopov wrote: >>>>> >>>>> Hello, >>>>> >>>>> This is my fist post, so I'm not fully aware of any posting policies >>>>> you have, but at least I'll try to present my ideas in clear way. >>>>> >>>>> *Very brief description of what I suggest:* >>>>> >>>>> Optionally delegate password change and password reset to >>>>> authentication backend. >>>>> >>>>> *Motivation:* >>>>> >>>>> Django framework is very popular and widely used not only for public, >>>>> but for private projects too. To make it clear, by private I mean not only >>>>> personal, but mostly corporate intranet projects. >>>>> >>>>> One of the important part of any corporate project is some kind of >>>>> single sing on (SSO) or, at least, integration with external >>>>> authentication >>>>> backend. From my experience it may be any service able to validate >>>>> credentials. LDAP is most often used for authentication purposes, however, >>>>> it is not the only available choice. I had projects with authentication >>>>> delegated to custom HTTP web-services, SMTP, POP3 and IMAP servers, and so >>>>> on. Corporate intranet you have to deal with may be a zoo of unbelievably >>>>> non-standard software. Also, even if we’ll talk about LDAP, it may be a >>>>> few, more than one, LDAP servers you have to try authentication against, >>>>> like Active Directory and OpenLDAP. >>>>> >>>>> Current implementation of User model as well as standard forms, >>>>> delegates to backend authentication only, but not password reset or >>>>> change. >>>>> This forces to reimplementation of user model and/or reimplementation of >>>>> standard forms for very common tasks, high coupling and bad architecture. >>>>> Also, if left as is, behavior is very inconsistent, since user is >>>>> authenticated against one password database (LDAP), but changes or resets >>>>> password in another (relational model database). And while LDAP may be >>>>> administered externally by standard web tools, this is not a good option >>>>> too, since means inconsistent user experience. And custom HTTP services >>>>> cannot be administered by standard tools at all. >>>>> >>>>> *Implementation:* >>>>> >>>>> As far as I can see User model is already annotated with backend field >>>>> https://github.com/django/django/blob/master/django/ >>>>> contrib/auth/__init__.py#L75 >>>>> so it is possible, and looks simple, to delegate password change and >>>>> reset to backend, if backend support these operations, which means >>>>> AbstractBaseUser.set_password method >>>>> https://github.com/django/django/blob/master/django/ >>>>> contrib/auth/models.py#L226 >>>>> >>>>> should be refactored to two methods: reset_password and >>>>> change_password instead of one set_password, and these two method should >>>>> delegate operation to backend if supported. (set_password may be left as >>>>> alias of reset_password). Signatures will be >>>>> >>>>> def reset_password(self, new_password) >>>>> def change_password(self, old_password, new_password) >>>>> >>>>> Also authentication forms >>>>> https://github.com/django/django/blob/master/django/ >>>>> contrib/auth/models.py#L226 >>>>> >>>>> should be refactored to use these new reset_password and >>>>> change_password methods. There must be two methods, as change_password and >>>>> reset_password may not be both available, also change_password requires >>>>> old >>>>> password as backend may require it. Returning to LDAP, there may be >>>>> various >>>>> policies which should be respected, like not to change password too often, >>>>> but reset whenever you want, so we need two separate methods. >>>>> >>>>> *Backward compatibility:* >>>>> >>>>> As far as I can see change is backwards compatible. Authentication >>>>> backends not providing extra operations will behave old way without any >>>>> change. >>>>> >>>>> *Patch:* >>>>> >>>>> I wanted my design to be reviewed before I’ll try to provide any >>>>> patch. I'mm pretty sure I've missed something, so discussion is welcome. >>>>> Also, this will be my first code for Django project, so I'll probably >>>>> break >>>>> some rules and will need some help. >>>>> With best regards, >>>>> Roman >>>>> ... >>>> >>>> -- > You received this message because you are subscribed to the Google Groups > "Django developers (Contributions to Django itself)" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at http://groups.google.com/group/django-developers. > To view this discussion on the web visit > https://groups.google.com/d/msgid/django-developers/f73abdc9-be42-4c37-a188-171a2ea88525%40googlegroups.com > <https://groups.google.com/d/msgid/django-developers/f73abdc9-be42-4c37-a188-171a2ea88525%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/django-developers. To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/CAGdCwBvZZ0KrohREB4WM%3DDTbfKV4oPmE%2Bb3dGbDioqec%2BOei1Q%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.
