Code

Changes between Version 1 and Version 2 of ContribAuthImprovements


Ignore:
Timestamp:
03/22/12 00:01:45 (2 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