Changes between Version 1 and Version 2 of ContribAuthImprovements


Ignore:
Timestamp:
03/22/2012 02:01:45 AM (3 years ago)
Author:
russellm
Comment:

Edit to initial draft

Legend:

Unmodified
Added
Removed
Modified
  • ContribAuthImprovements

    v1 v2  
    1313However, there is also a use case for adding non-email, non-username identification marks to the base User object (e.g., an authentication identifier from another login system). This requires additional fields, and needing to perform a join to simply validate a user is wasteful.
    1414
    15 Therefore, there has been a long-lived request to "fix" auth.User, either by removing the base limitations of the model, or making the User model user-configurable in some way.
     15These use cases have driven a long-lived request (#3011) to "fix" auth.User, either by removing the base limitations of the model, or making the User model user-configurable in some way.
    1616
    17 Some django-developer discussions that cover this topic (there are many, over many years):
     17Some django-developer discussions that cover this topic (there have been many, over many years):
    1818
    1919 * http://groups.google.com/group/django-developers/browse_thread/thread/6dadb540ca5f7d9b
    2020 * http://groups.google.com/group/django-developers/browse_thread/thread/61c15301a89d88be
    2121
     22A large number of solutions have also been proposed over the years. Here is a summary of the most viable candidates:
     23
    2224== Solution 1: Superminimal update ==
    2325
    24 Don't make any significant contribution to `contrib.auth` -- just make 2 changes to he User model, phased in over several release cycles.
     26Don't make any significant contribution to `contrib.auth` -- just make 2 changes to he User model, phased in over multiple release cycles.
    2527
    26  * increase the length of the username field to 254 characters, so it can hold an email if necessary
    27  * Make the email field 254 characters long.
     28 1. Increase the length of the username field to 254 characters, so it can hold an email if necessary
     29 2. Make the email field 254 characters long.
    2830
    2931=== Implementation ===
     
    4143
    4244 * Doesn't actually fix the problem for any use case other than "email address as username"
    43  * Introduces a setting that immediately becomes deprecated (since it won't be needed once the migration is complete)
     45 * Introduces a setting that immediately becomes deprecated (since it won't be needed once the migration cycle is complete)
    4446 * Doesn't address the problem with any other usage of EmailField having a max_length of 75.
    4547 * Introduces a circular dependency between settings and models. When settings are loaded, INSTALLED_APPS is inspected, and each models file is loaded. If a models file contains a reference to settings, hilarity can ensue. This isn't a problem *most* of the time, but it can lead to some interesting side effects.
     
    4749== Solution 1a: Superminimal with forced migration ==
    4850
    49 As for Solution 1, but don't have the setting, and *require* the migration as part of the 1.5 upgrade path.
     51As for Solution 1, but don't have the setting -- *require* the database migration as part of the 1.5 upgrade path.
    5052
    5153=== Advantages ===
     
    5860
    5961 * Is a huge backwards incompatibility: Requires that every Django user upgrading read, understand, and act upon the instructions in the release notes.
    60  * Poor failure modes. If users fail to act on the release notes, their projects won't fail until the first user enters an email that is longer than 75 characters, or a username longer than 30 characters, at which point DatabaseErrors will be raised.
     62 * Poor failure modes. If a users fail to read and act on the instructions in the release notes, their projects won't fail until the first user enters an email that is longer than 75 characters, or a username longer than 30 characters, at which point DatabaseErrors will be raised.
    6163 
    62 == Solution 2: USER_MODEL setting ==
     64== Solution 2: AUTH_USER_MODEL setting ==
    6365
    64  * Allow users to specify a User model via a setting.
    65  * This is essentially the original #3011 proposal.
     66Allow users to specify a User model via a setting. This is essentially the original #3011 proposal.
     67
     68=== Implementation ===
     69
     70Introduce a AUTH_USER_MODEL setting; instead of assuming that auth.User is the user model, the auth app dynamically swaps in the model described in AUTH_USER_MODEL, and calls it User.
    6671
    6772=== Advantages ===
    6873
    6974 * Allows any user model, providing it adheres to some basic contract (defined by Django, probably using contrib.admin as a baseline use case)
     75 * Existing apps require no migration -- references to auth.User continue to work as-is.
    7076 * Mirrors current usage in contrib.comments
    7177
     
    8591 * Doesn't address the EmailField length problem for existing users. We could address this by having a User model (reflecting current field lengths) and a new SimpleUser (that reflects better defaults); then use global_settings and project template settings to define which User is the default for new vs existing projects.
    8692 * Doesn't solve the analogous problem for any other project. E.g., contrib.comments already has pluggable Comments models, and has invented a bespoke solution. Other projects will have similar needs; this solution doesn't address the duplication of code.
     93 * Has unpredictable failure modes if a third-party app assumes that User has a certain attribute or property which the project-provided User model doesn't support (or supports in a way different to the core auth.User model).
    8794
    8895=== Solution 3: Leverage App Refactor ===
    8996
    90  * Land Arthur Koziel's App Refactor patch from GSoC 2010. This introduces a number of benefits --reliable hooks for app startup, configurable app labels, predictable module loading, amongst others -- but the one that matters for the purposes of auth.User is that it allows Apps to be treated as items that need to be configured as a runtime activity. In this case, we need to be able to specify, at a project level, which model is your "User" model in the auth app.
     97Use Arthur Koziel's App Refactor patch from GSoC 2010 as a way to define a configurable auth app.
     98
     99=== Implementation ===
     100
     101 * Land the App Refactor patch. This introduces a number of benefits -- reliable hooks for app startup, configurable app labels, predictable module loading, amongst others -- but the one that matters for the purposes of auth.User is that it allows Apps to be treated as items that need to be configured as a runtime activity. In this case, we need to be able to specify, at a project level, which model is your "User" model in the auth app.
    91102
    92103 * Introduce the concept of a LazyForeignKey. LazyForeignKey is a normal foreign key, with all the usual foreign key behaviors; the only difference is that the model it links to isn't specified in the model -- it's a configuration item drawn from an application configuration. So, ForeignKey('auth.User') creates a foreign key to django.contrib.auth.User; LazyForeignKey('auth.User') asks the auth app for the model that is being used as the 'User' model, and creates a foreign key to that. This should just be a matter of slotting into the existing model reference resolution code, which is something that the app refactor cleans up.
     
    105116=== Problems ===
    106117
    107  * No transparent update path -- requires that third party apps be updated to be "pluggable app compatible". This means app authors need to make all ForeignKey(User) into LazyForeignKey('auth.User'), and modify any usage of forms/fields etc. This could be considered a benefit, however; Migrating User models is a nontrivial step, and it would
     118 * No transparent update path -- requires that third party apps be updated to be "pluggable auth compatible". This means app authors need to convert all ForeignKey(User) into LazyForeignKey('auth.User'), and modify any usage of forms/fields etc. This could be considered a benefit, however; Migrating User models is a nontrivial step, and it should probably involve some opt-in engineering.
    108119
    109120 * Doesn't address the immediate problem for EmailField. We could do the same User/SimpleUser conversion here; with the added benefit that we are also introducing apprefactor, so we can use the distinction between an "unconfigured" auth app and a "Django 1.5 AppRefactor Configured" auth app as the point for identifying whether User or SimpleUser is in use.
     121
     122== Solution 3a: Transparent LazyForeignKeys ==
     123
     124As for Solution 3, but don't include an explicit LazyForeignKey class. Instead, use the "pluggable" marker in the Meta class; If you define a ForeignKey to a 'pluggable' model, you're indicating that this foreign key reference might be changed later on.
     125
     126=== Advantages ===
     127
     128 * As for Solution 3, but provides a transparent migration path for apps -- no need to manually change ForeignKey(User). However, depending on your perspective, this might not actually be an advantage at all, because it removes the opt-in.
     129
     130=== Problems ===
     131
     132 * As for Solution 3.
    110133
    111134== Solution 4: contrib.newauth ==
     
    118141 * It exhibits the settings-models dependency problem
    119142 * It introduces a new feature -- the ability to have *multiple* user models -- that isn't an obvious improvement. Additional discussion is required before being adopted.
     143
     144=== Advantages ===
     145
     146 * Gives us a clean slate to examine authentication and authorisation issues.
    120147 
    121148=== Problems ===
    122149
    123150 * There's no pret-a-porter project ready as a candidate. Rebuilding contrib.auth is a big undertaking, and doesn't yet have a clear design (or even design requirements).
     151
     152== Universal problems ==
     153
     154Regardless of the final
     155
     156=== The User Contract ===
     157
     158In order for code to actually be able to adapt to swappable User models, there needs to be some common ground on what a "User" object can actually do. Django is in a position to enforce this by convention -- contrib.admin is a good baseline case.
     159
     160Consensus seems to be that the 'minimal contract' for User needs to be little more than that required basic identification -- you need to be able to authenticate using arbitrary credentials, and be able to service a request to print "Hello <user>"
     161
     162This 'minimal'
     163
     164=== Separation of authentication from authorisation ===
     165
     166contrib.admin requires
     167
     168=== Forms ===
     169
     170Any form interaction with User model needs to be able to adapt as the User model changes.
     171
     172 * Does ModelForm need to automatically resolve model references for models marked as pluggable?
     173 * Should Forms be configurable items for apps?
     174
     175=== Inheritance ===
     176
     177If the User model can change, then models inheriting from the User model need to be able to adapt to a variable base class.
     178
     179As a first iteration, it may be necessary to prohibit inheritance from a User model that is marked as pluggable.
    124180
    125181== Parallel concerns ==
     
    136192
    137193Discussion on django-developers suggests that complete consensus is unlikely; Currently awaiting BDFL mandate.
    138 
Back to Top