Changeset 2572

Show
Ignore:
Timestamp:
03/11/10 16:25:23 (5 months ago)
Author:
JensDiemer
Message:

reimplement JS-SHA-Login. Use jQuery. Do everything in one step. fixed: ticket:270 ticket:273

Location:
branches/0.9/pylucid_project
Files:
4 added
14 removed
9 modified

Legend:

Unmodified
Added
Removed
  • branches/0.9/pylucid_project/apps/pylucid/models/log.py

    r2494 r2572  
    5959        return queryset 
    6060 
    61     def request_limit(self, request, min_pause, ban_limit, app_label): 
     61    def request_limit(self, request, min_pause, ban_limit, app_label, no_page_msg=False): 
    6262        """ 
    6363        Monitor request min_pause and ban_limit. 
     
    8484        if last_actions > 0: 
    8585            msg = _("Request too fast!") 
     86            debug_msg = " (%s in the last %ssec. IP is blocked by %s overruns.)" % ( 
     87                last_actions, min_pause, ban_limit 
     88            ) 
    8689            if settings.DEBUG: 
    87                 msg += _(" Please wait min. %ssec.") % min_pause 
    88             request.page_msg.error(msg) 
     90                msg += debug_msg 
     91            if no_page_msg == False: 
     92                request.page_msg.error(msg) 
    8993            LogEntry.objects.log_action( 
    90                 app_label=app_label, action="request aborted", message="(%s in the last %ssec.)" % (last_actions, min_pause), 
     94                app_label=app_label, action="request aborted", message=debug_msg, 
    9195            ) 
    92             raise self.model.RequestTooFast() 
     96            raise self.model.RequestTooFast(msg) 
    9397 
    9498    def log_action(self, app_label, action, request=None, message=None, long_message=None, data=None): 
  • branches/0.9/pylucid_project/apps/pylucid_admin/admin_site.py

    r2482 r2572  
    44from django.contrib import admin 
    55from django.http import HttpResponseRedirect 
    6  
    7 from pylucid_project.apps.pylucid.models import PageTree, UserProfile 
    8  
    96 
    107 
     
    1714            return super(PyLucidAdminSite, self).logout(request) 
    1815 
     16        # Use the PyLucid own logout view, so the user it back on his cms page 
    1917        url = "/?" + settings.PYLUCID.AUTH_LOGOUT_GET_VIEW 
    2018        return HttpResponseRedirect(url) 
    2119 
    22     def login(self, request): 
    23         """ 
    24         Redirect to PyLucid's own login view. 
    25         In debug mode: 
    26             -user can suppress the redirect by adding any GET parameter. (e.g: domain.tld/admin/?foobar) 
    27             -Don't redirect if PyLucid own login view can't work. 
    28              
    29         TODO: prevent redirect loops 
    30         """ 
    31         if PageTree.on_site.all().count() == 0 or UserProfile.on_site.all().count() == 0 or \ 
    32                                                                         (request.GET and settings.DEBUG): 
    33             return super(PyLucidAdminSite, self).login(request) 
    34  
    35         url = settings.PYLUCID.AUTH_NEXT_URL % {"path": "/", "next_url": request.get_full_path()} 
    36         return HttpResponseRedirect(url) 
    3720 
    3821pylucid_admin_site = PyLucidAdminSite() 
    3922 
     23 
    4024# Use all django contrib model admins in our own admin site ;) 
    4125pylucid_admin_site._registry = admin.site._registry 
  • branches/0.9/pylucid_project/media/PyLucid/pylucid_js_tools.js

    r2524 r2572  
    4545                 
    4646        log("redirect work-a-round: replace the complete page"); 
     47                log(data); 
    4748        log("</body> index:" + data.indexOf("</body>")); 
    4849        replace_complete_page(data) 
     
    6970    if (!response_text) { 
    7071        response_text = "<h1>Ajax response error without any response text.</h1>"; 
     72                response_text += "<p>textStatus:" + textStatus + "</p>" 
     73                response_text += "<p>errorThrown:" + errorThrown + "</p>" 
     74                replace_page_content(response_text, textStatus); 
     75                return 
    7176    } 
    7277    replace_complete_page(response_text); 
  • branches/0.9/pylucid_project/media/PyLucid/shared_sha_tools.js

    r2002 r2572  
    33 */ 
    44 
    5 check_ok = false; 
    6 debug_msg = false; 
    7  
    8 // length of a SHA1 hexdigest string: 
    9 HASH_LEN = 40; 
    10 // length of the salt and challenge value string: 
    11 SALT_LEN = 5; 
    12  
    13  
    14 if (!document.getElementById) { 
    15     alert("Error: Your Browser is not supported!"); 
    16 } 
    17  
    18 if (navigator.cookieEnabled) { 
    19     if (navigator.cookieEnabled != true) { 
    20         alert("You must enable Cookies in your Browser!"); 
    21     } 
    22 } 
    23  
    24 /* ___________________________________________________________________________ 
    25  *  needfull generic functions: 
    26  */ 
    27  
    28 function get_value(object_id) { 
    29     // get a form value 
    30     try { 
    31         return document.getElementById(object_id).value; 
    32     } catch (e) { 
    33         alert("get_value('"+object_id+"') error! " + e); 
    34     } 
    35 } 
    36  
    37 function set_value(object_id, value) { 
    38     // set a form value 
    39     try { 
    40         obj = document.getElementById(object_id).value = value; 
    41     } catch (e) { 
    42         alert("set_value('"+object_id+"', '"+value+"') error! " + e); 
    43     } 
    44 } 
    45  
    46 function set_focus(object_id) { 
    47     try { 
    48         debug("set focus on id:" + object_id); 
    49         document.getElementById(object_id).focus(); 
    50     } catch (e) { 
    51         alert("set_focus('"+object_id+"') error:" + e); 
    52     } 
    53 } 
    54  
    55 function hide_by_id(object_id) { 
    56     try { 
    57         debug("hide_by_id: "+object_id); 
    58         obj = document.getElementById(object_id); 
    59         obj.style.display = 'none'; 
    60     } catch (e) { 
    61         alert("hide_by_id('"+object_id+"') error:" + e); 
    62     } 
    63 } 
    64 function unhide_by_id(object_id) { 
    65     try { 
    66         debug("unhide_by_id: "+object_id); 
    67         obj = document.getElementById(object_id); 
    68         obj.style.display = 'block'; 
    69     } catch (e) { 
    70         alert("unhide_by_id('"+object_id+"') error:" + e); 
    71     } 
    72 } 
    73  
    74 function change_color(object_id, color_name) { 
    75     try { 
    76         debug("change_color: "+object_id); 
    77         obj = document.getElementById(object_id); 
    78         obj.style.backgroundColor = color_name; 
    79     } catch (e) { 
    80         alert("change_color('"+object_id+"', '"+color_name+"') error:" + e); 
    81     } 
    82 } 
    83  
    84 /* ___________________________________________________________________________ 
    85  *  special functions: 
    86  */ 
    87  
    88 function check_ascii_only(data) { 
     5function is_only_ascii(data) { 
     6    // Check if the given string contains only ASCII characters 
    897    for (var i = 1; i <= data.length; i++) { 
    90        unicode_charcode = data.charCodeAt(i); 
    91        if (unicode_charcode > 127) { 
    92             alert("Only ASCII letters are allowed!"); 
     8       if (data.charCodeAt(i) > 127) { 
    939            return false; 
    9410       } 
    9511    } 
    9612    return true; 
    97  } 
     13} 
    9814 
    99 function get_plaintext_pass(object_id) { 
     15function sha_hexdigest(txt) { 
     16    // build the SHA hexdigest from the given string. Return false is anything is wrong. 
    10017    try { 
    101         in_pass = get_value(object_id); 
    102         debug("in_pass:" + in_pass); 
    103         if (in_pass.length<8) { 
    104             alert("Password min len 8! - current len:" + in_pass.length); 
    105             set_focus(object_id) 
     18        log("sha_hexdigest(" + txt + "):"); 
     19        SHA_hexdigest = hex_sha1(txt); // from: sha.js 
     20        len = SHA_hexdigest.length; 
     21        if (len != HASH_LEN) { 
     22            page_msg_error("sha_hexdigest() error! wrong length:" + len); 
    10623            return false; 
    10724        } 
    108  
    109         if (check_ascii_only(in_pass) == false) { 
    110             set_focus(object_id) 
    111             return false; 
    112         } 
    113         return in_pass; 
     25        log(SHA_hexdigest); 
     26        return SHA_hexdigest; 
    11427    } catch (e) { 
    115         alert("get_plaintext_pass() error:" + e); 
     28        page_msg_error("sha_hexdigest() error:" + e); 
    11629        return false; 
    11730    } 
    11831} 
    11932 
    120 function make_SHA(txt) { 
    121     try { 
    122         debug("make_SHA(" + txt + "):"); 
    123         SHA_hexdigest = hex_sha1(txt); // from: sha.js 
    124         len = SHA_hexdigest.length; 
    125         if (len != HASH_LEN) { 
    126             alert("make_SHA() error! wrong length:" + len); 
    127             return false; 
    128         } 
    129         debug(SHA_hexdigest); 
    130         return SHA_hexdigest; 
    131     } catch (e) { 
    132         alert("make_SHA() error:" + e); 
    133         return false; 
    134     } 
     33function _page_msg(msg){ 
     34    $("#js_page_msg").html(msg).css("display", "block").slideDown(); 
    13535} 
    136  
    137  
    138 /* ___________________________________________________________________________ 
    139  *  debugging: 
    140  */ 
    141  
    142 function init_debug() { 
    143     /* Create a debug window, if it's not exists */ 
    144     try { 
    145         if (debug_msg != true) { return; } 
    146         try { 
    147             if (debug_window) { 
    148                 return; 
    149             } 
    150         } catch (e) {} 
    151         debug_window = window.open("", "Debug", "dependent=yes, resizable=yes, scrollbars=yes, width=350, height=400, top=1, left=" + window.outerWidth); 
    152  
    153         debug_win = debug_window.document; 
    154         debug_win.writeln("<style>* { font-size: 0.85em; }</style>"); 
    155  
    156         var now = new Date(); 
    157         now = now.toLocaleString(); 
    158  
    159         debug_win.writeln("<h1>JS Debug - "+now+":</h1>"); 
    160         debug_win.writeln("---[DEBUG START]---"); 
    161         debug_win.writeln("cookie:" + document.cookie +"<br />"); 
    162     } catch (e) { 
    163         alert("init_debug() error:" + e); 
    164     } 
     36function page_msg_error(msg) { 
     37    $("#js_page_msg").removeClass("page_msg_info page_msg_success").addClass("page_msg_error"); 
     38    _page_msg(msg); 
    16539} 
    166  
    167  
    168 function debug(msg) { 
    169     try { 
    170         if (debug_msg != true) { return; } 
    171         init_debug(); 
    172         debug_win.writeln(msg + "<br />"); 
    173         debug_window.focus(); 
    174         // scroll to the last lines 
    175         debug_window.scrollBy(0, 1000); 
    176     } catch (e) { 
    177         alert("debug('"+msg+"') error:" + e); 
    178     } 
     40function page_msg_success(msg) { 
     41    $("#js_page_msg").removeClass("page_msg_info page_msg_success").addClass("page_msg_success"); 
     42    _page_msg(msg);     
    17943} 
    180  
    181  
    182 function debug_confirm() { 
    183     try { 
    184        if (debug_msg != true) { return; } 
    185        debug_window.focus(); 
    186        debug_win.writeln("---[DEBUG END]---"); 
    187        alert("OK for closing the debug window."); 
    188        debug_window.close(); 
    189        debug_window = false; 
    190     } catch (e) { 
    191         alert("debug_confirm() error:" + e); 
    192     } 
     44function page_msg_info(msg) { 
     45    $("#js_page_msg").removeClass("page_msg_success page_msg_error").addClass("page_msg_info"); 
     46    _page_msg(msg);     
    19347} 
  • branches/0.9/pylucid_project/pylucid_plugins/auth/forms.py

    r2507 r2572  
    88 
    99from pylucid_project.utils import crypt 
    10  
    11  
    12  
    13 def get_newforms_data(key_name, cleaned_data): 
    14     if not key_name in cleaned_data: 
    15         raise forms.ValidationError(u"No '%s' data in the form." % key_name) 
    16     return cleaned_data[key_name] 
    17  
    18  
    19 def validate_sha1(key_name, cleaned_data): 
    20     """ 
    21     A universal routine to validate a SHA1 hexdigest for newforms. 
    22     """ 
    23     sha_value = get_newforms_data(key_name, cleaned_data) 
    24  
    25     if crypt.validate_sha_value(sha_value) == True: 
    26         return sha_value 
    27     else: 
    28         raise forms.ValidationError(u"Wrong '%s' data." % key_name) 
    29  
    30  
    31 class SHA_LoginForm(forms.Form): 
     10from pylucid_project.apps.pylucid.models import UserProfile 
     11 
     12class WrongUserError(Exception): 
     13    pass 
     14 
     15class UsernameForm(forms.Form): 
     16    username = forms.CharField(max_length=_('30'), label=_('Username'), 
     17        help_text=_('Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).') 
     18    ) 
     19 
     20    def get_user(self): 
     21        username = self.cleaned_data["username"] 
     22        try: 
     23            user = User.objects.get(username=username) 
     24        except User.DoesNotExist, e: 
     25            raise WrongUserError("User %r doesn't exists!" % username) 
     26 
     27        if not user.is_active: 
     28            raise WrongUserError("User %r is not active!" % user) 
     29 
     30        return user 
     31 
     32    def get_user_profile(self, user=None): 
     33        if user is None: 
     34            user = self.get_user() 
     35        try: 
     36            return user.get_profile() 
     37        except UserProfile.DoesNotExist, err: 
     38            raise WrongUserError("Can't get user profile: %r" % err) 
     39 
     40    def get_user_and_profile(self): 
     41        user = self.get_user() 
     42        user_profile = self.get_user_profile(user) 
     43        return user, user_profile 
     44 
     45 
     46class ShaLoginForm(UsernameForm): 
    3247    """ 
    3348    Form for the SHA1-JavaScript-Login. 
    3449    """ 
    35     sha_a2 = forms.CharField( 
    36         min_length=crypt.HASH_LEN, max_length=crypt.HASH_LEN 
    37     ) 
    38     sha_b = forms.CharField( 
    39         min_length=crypt.HASH_LEN / 2, max_length=crypt.HASH_LEN / 2 
    40     ) 
    41  
    42     #__________________________________________________________________________ 
    43     # Validate the SHA1 hexdigest values: 
     50    sha_a2 = forms.CharField(min_length=crypt.HASH_LEN, max_length=crypt.HASH_LEN) 
     51    sha_b = forms.CharField(min_length=crypt.HASH_LEN / 2, max_length=crypt.HASH_LEN / 2) 
    4452 
    4553    def clean_sha_a2(self): 
    46         return validate_sha1("sha_a2", self.cleaned_data) 
     54        sha_a2 = self.cleaned_data["sha_a2"] 
     55        if crypt.validate_sha_value(sha_a2) != True: 
     56            raise forms.ValidationError(u"sha_a2 is not valid SHA value.") 
     57 
     58        return sha_a2 
    4759 
    4860    def clean_sha_b(self): 
     
    5163        some characers to use the rypt.validate_sha_value() method. 
    5264        """ 
    53         sha_value = get_newforms_data("sha_b", self.cleaned_data) 
     65        sha_b = self.cleaned_data["sha_b"] 
    5466 
    5567        # Fill with null, to match the full SHA1 hexdigest length. 
    5668        fill_len = crypt.HASH_LEN - (crypt.HASH_LEN / 2) 
    57         temp_value = ("0" * fill_len) + sha_value 
    58  
    59         if crypt.validate_sha_value(temp_value) == True: 
    60             return sha_value 
    61         else: 
    62             raise forms.ValidationError(u"Wrong sha_b data.") 
    63  
    64  
    65 class NewPasswordForm(forms.Form): 
    66     username = forms.CharField( 
    67         help_text="(required)", min_length=3, max_length=30 
    68     ) 
    69  
    70     # Should normaly never be send back! 
    71     raw_password = forms.CharField( 
    72         help_text="(required)", required=False, widget=forms.PasswordInput() 
    73     ) 
    74  
    75     sha_1 = forms.CharField( 
    76         label="SHA1 for django", 
    77         help_text="(automatic generated with JavaScript.)", 
    78         widget=forms.TextInput(attrs={"readonly":"readonly", "size":"40"}), 
    79         min_length=crypt.HASH_LEN, max_length=crypt.HASH_LEN 
    80     ) 
    81     sha_2 = forms.CharField( 
    82         label="SHA1 for PyLucid", 
    83         help_text="(automatic generated with JavaScript.)", 
    84         widget=forms.TextInput(attrs={"readonly":"readonly", "size":"40"}), 
    85         min_length=crypt.HASH_LEN, max_length=crypt.HASH_LEN 
    86     ) 
    87  
    88     #__________________________________________________________________________ 
    89     # Validate the SHA1 hexdigest values: 
    90  
    91     def clean_sha_1(self): 
    92         return validate_sha1("sha_1", self.cleaned_data) 
    93  
    94     def clean_sha_2(self): 
    95         return validate_sha1("sha_2", self.cleaned_data) 
    96  
    97  
    98 #______________________________________________________________________________ 
    99 # FORMS 
    100  
    101 #class BaseModelForm(forms.ModelForm): 
    102 #    """ 
    103 #    A model form witch don't validate unique fields. 
    104 # 
    105 #    This ModelForm is only for generating the forms and not for create/update 
    106 #    any database data. So a field unique Test would like generate Errors like: 
    107 #        User with this Username already exists. 
    108 # 
    109 #    see also: 
    110 #    http://www.jensdiemer.de/_command/118/blog/detail/30/ (de) 
    111 #    http://www.python-forum.de/topic-16000.html (de) 
    112 #    """ 
    113 #    def __init__(self, *args, **kwargs): 
    114 #        """ Change field meta in a DRY way """ 
    115 #        super(BaseModelForm, self).__init__(*args, **kwargs) 
    116 # 
    117 #        self.model.full_validate = self._skip 
    118 # 
    119 #    def _skip(self, *args, **kwargs): 
    120 #        pass 
    121 # 
    122 #    def validate_unique(self): 
    123 #        pass 
    124  
    125 class UsernameForm(forms.Form): 
    126     """ 
    127     form for input the username, used in auth.login() 
    128      
    129     FIXME: This is not DRY. 
    130     """ 
    131     username = forms.CharField(max_length=_('30'), label=_('Username'), 
    132         help_text=_('Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).') 
    133     ) 
    134  
    135     def is_valid(self): 
    136         """ do a secont validation: try to get the user from database and 
    137         check if he is active """ 
    138         is_valid = super(UsernameForm, self).is_valid() 
    139         if not is_valid: 
    140             return False 
    141  
    142         username = self.cleaned_data["username"] 
    143         try: 
    144             self.user = User.objects.get(username=username) 
    145         except User.DoesNotExist, e: 
    146             self._errors["username"] = ("User doesn't exist!",) 
    147             return False 
    148  
    149         return True 
    150  
    151 #    class Meta: 
    152 #        model = User 
    153 #        fields = ("username",) 
    154  
    155  
    156 class PasswordForm(forms.Form): 
    157     """ 
    158     form for input the username, used in auth._sha_login() 
    159      
    160     FIXME: This is not DRY. 
    161     """ 
    162     password = forms.CharField(max_length=_('128'), label=_('Password'), widget=forms.PasswordInput() 
    163     ) 
    164  
    165 #    def __init__(self, *args, **kwargs): 
    166 #        """ Change field meta in a DRY way """ 
    167 #        super(PasswordForm, self).__init__(*args, **kwargs) 
    168 # 
    169 #        self.fields['password'].widget = forms.PasswordInput() 
    170 #        self.fields['password'].help_text = "" 
    171  
    172     def is_valid(self, username): 
    173         is_valid = super(PasswordForm, self).is_valid() 
    174         if not is_valid: 
    175             return False 
    176  
    177         password = self.cleaned_data["password"] 
    178         self.user = auth.authenticate(username=username, password=password) 
    179         if not self.user: 
    180             self._errors["password"] = ("Wrong password!",) 
    181             return False 
    182  
    183         return True 
    184  
    185 #    class Meta: 
    186 #        model = User 
    187 #        fields = ("password",) 
    188  
    189  
    190 class ResetForm(forms.Form): 
    191     """ 
    192     from for input username and email, used in auth.pass_reset() 
    193     """ 
    194     username = forms.CharField(max_length=_('30'), label=_('Username'), 
    195         help_text=_('Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).') 
    196     ) 
    197     email = forms.EmailField(max_length=_('75'), label=_('E-mail address')) 
    198  
    199 #    def __init__(self, *args, **kwargs): 
    200 #        super(ResetForm, self).__init__(*args, **kwargs) 
    201 #        # User.email is normaly a not required field, here it's required! 
    202 #        self.fields['email'].required = True 
    203  
    204 #    class Meta: 
    205 #        model = User 
    206 #        fields = ("username", "email") 
     69        temp_value = ("0" * fill_len) + sha_b 
     70 
     71        if crypt.validate_sha_value(temp_value) != True: 
     72            raise forms.ValidationError(u"sha_b is not a valid SHA value.") 
     73 
     74        return sha_b 
     75 
     76# 
     77#class NewPasswordForm(forms.Form): 
     78#    username = forms.CharField( 
     79#        help_text="(required)", min_length=3, max_length=30 
     80#    ) 
     81# 
     82#    # Should normaly never be send back! 
     83#    raw_password = forms.CharField( 
     84#        help_text="(required)", required=False, widget=forms.PasswordInput() 
     85#    ) 
     86# 
     87#    sha_1 = forms.CharField( 
     88#        label="SHA1 for django", 
     89#        help_text="(automatic generated with JavaScript.)", 
     90#        widget=forms.TextInput(attrs={"readonly":"readonly", "size":"40"}), 
     91#        min_length=crypt.HASH_LEN, max_length=crypt.HASH_LEN 
     92#    ) 
     93#    sha_2 = forms.CharField( 
     94#        label="SHA1 for PyLucid", 
     95#        help_text="(automatic generated with JavaScript.)", 
     96#        widget=forms.TextInput(attrs={"readonly":"readonly", "size":"40"}), 
     97#        min_length=crypt.HASH_LEN, max_length=crypt.HASH_LEN 
     98#    ) 
     99# 
     100#    #__________________________________________________________________________ 
     101#    # Validate the SHA1 hexdigest values: 
     102# 
     103#    def clean_sha_1(self): 
     104#        return validate_sha1("sha_1", self.cleaned_data) 
     105# 
     106#    def clean_sha_2(self): 
     107#        return validate_sha1("sha_2", self.cleaned_data) 
     108# 
     109# 
     110##______________________________________________________________________________ 
     111## FORMS 
     112# 
     113##class BaseModelForm(forms.ModelForm): 
     114##    """ 
     115##    A model form witch don't validate unique fields. 
     116## 
     117##    This ModelForm is only for generating the forms and not for create/update 
     118##    any database data. So a field unique Test would like generate Errors like: 
     119##        User with this Username already exists. 
     120## 
     121##    see also: 
     122##    http://www.jensdiemer.de/_command/118/blog/detail/30/ (de) 
     123##    http://www.python-forum.de/topic-16000.html (de) 
     124##    """ 
     125##    def __init__(self, *args, **kwargs): 
     126##        """ Change field meta in a DRY way """ 
     127##        super(BaseModelForm, self).__init__(*args, **kwargs) 
     128## 
     129##        self.model.full_validate = self._skip 
     130## 
     131##    def _skip(self, *args, **kwargs): 
     132##        pass 
     133## 
     134##    def validate_unique(self): 
     135##        pass 
     136# 
     137#class UsernameForm(forms.Form): 
     138#    """ 
     139#    form for input the username, used in auth.login() 
     140#     
     141#    FIXME: This is not DRY. 
     142#    """ 
     143# 
     144# 
     145##    class Meta: 
     146##        model = User 
     147##        fields = ("username",) 
     148# 
     149# 
     150#class PasswordForm(forms.Form): 
     151#    """ 
     152#    form for input the username, used in auth._sha_login() 
     153#     
     154#    FIXME: This is not DRY. 
     155#    """ 
     156#    password = forms.CharField(max_length=_('128'), label=_('Password'), widget=forms.PasswordInput() 
     157#    ) 
     158# 
     159##    def __init__(self, *args, **kwargs): 
     160##        """ Change field meta in a DRY way """ 
     161##        super(PasswordForm, self).__init__(*args, **kwargs) 
     162## 
     163##        self.fields['password'].widget = forms.PasswordInput() 
     164##        self.fields['password'].help_text = "" 
     165# 
     166#    def is_valid(self, username): 
     167#        is_valid = super(PasswordForm, self).is_valid() 
     168#        if not is_valid: 
     169#            return False 
     170# 
     171#        password = self.cleaned_data["password"] 
     172#        self.user = auth.authenticate(username=username, password=password) 
     173#        if not self.user: 
     174#            self._errors["password"] = ("Wrong password!",) 
     175#            return False 
     176# 
     177#        return True 
     178# 
     179##    class Meta: 
     180##        model = User 
     181##        fields = ("password",) 
     182# 
     183# 
     184#class ResetForm(forms.Form): 
     185#    """ 
     186#    from for input username and email, used in auth.pass_reset() 
     187#    """ 
     188#    username = forms.CharField(max_length=_('30'), label=_('Username'), 
     189#        help_text=_('Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).') 
     190#    ) 
     191#    email = forms.EmailField(max_length=_('75'), label=_('E-mail address')) 
     192# 
     193##    def __init__(self, *args, **kwargs): 
     194##        super(ResetForm, self).__init__(*args, **kwargs) 
     195##        # User.email is normaly a not required field, here it's required! 
     196##        self.fields['email'].required = True 
     197# 
     198##    class Meta: 
     199##        model = User 
     200##        fields = ("username", "email") 
  • branches/0.9/pylucid_project/pylucid_plugins/auth/preference_forms.py

    r2492 r2572  
    1010    ban_limit = forms.IntegerField( 
    1111        help_text=_("Numbers login log messages after IP would be banned."), 
    12         initial=5, min_value=1, max_value=20 
     12        initial=6, min_value=1, max_value=20 
    1313    ) 
    1414    min_pause = forms.IntegerField( 
    1515        help_text=_("Minimum pause in seconds between two login log messages from the same user. (Used 'REMOTE_ADDR')"), 
    16         initial=30, min_value=1, max_value=600 
     16        initial=15, min_value=1, max_value=600 
    1717    ) 
    1818 
  • branches/0.9/pylucid_project/pylucid_plugins/auth/views.py

    r2494 r2572  
    2121from django.template import RequestContext 
    2222from django.contrib.sites.models import Site 
    23 from django.http import HttpResponseRedirect 
     23from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest 
    2424from django.utils.translation import ugettext as _ 
    2525from django.template.loader import render_to_string 
     
    2727 
    2828 
    29 from pylucid.shortcuts import render_pylucid_response 
    30 from pylucid.models import LogEntry, BanEntry, UserProfile 
     29from pylucid_project.apps.pylucid.shortcuts import render_pylucid_response 
     30from pylucid_project.apps.pylucid.models import LogEntry, BanEntry, UserProfile 
    3131 
    3232from pylucid_project.utils import crypt 
    3333 
    34 from pylucid_plugins.auth.forms import UsernameForm, PasswordForm, SHA_LoginForm 
     34from pylucid_project.pylucid_plugins.auth.forms import WrongUserError, UsernameForm, ShaLoginForm 
    3535from pylucid_plugins.auth.preference_forms import AuthPreferencesForm 
    3636 
     
    4747 
    4848 
    49 # For the tag list from page_admin plugin: 
    50 LUCIDTAG_EXAMPLE = """{% lucidTag admin_menu %}""" 
     49def _get_challenge(request): 
     50    """ create a new challenge, add it to session and return it""" 
     51    # Create a new random salt value for the password challenge: 
     52    challenge = crypt.get_new_salt() 
     53 
     54    # For later comparing with form data 
     55    request.session["challenge"] = challenge 
     56 
     57    return challenge 
     58 
     59 
     60def _bad_request(debug_msg): 
     61    """ 
     62    Create a new LogEntry and return a HttpResponseBadRequest 
     63    """ 
     64    LogEntry.objects.log_action( 
     65        app_label="pylucid_plugin.auth", action="login error", message=debug_msg, 
     66    ) 
     67    if settings.DEBUG: 
     68        msg = debug_msg 
     69    else: 
     70        msg = "" 
     71    return HttpResponseBadRequest(msg) 
     72 
     73 
     74def _is_post_ajax_request(request): 
     75    if not request.is_ajax(): 
     76        debug_msg = "request is not a ajax request" 
     77        return _bad_request(debug_msg) 
     78 
     79    if request.method != 'POST': 
     80        debug_msg = "request method %r wrong, only POST allowed" % request.method 
     81        return _bad_request(debug_msg) 
    5182 
    5283 
     
    5788    """ 
    5889    if request.user.is_authenticated(): 
    59         # admin_logout reverse is still broken in django, see: 
    60         # http://code.djangoproject.com/ticket/11080 
    61         # http://code.djangoproject.com/attachment/ticket/10061 
    62         #url = reverse("admin:logout") 
    63         #url = reverse("admin:index") + "logout/" # TODO: Update this if django is bugfixed 
    6490        template_name = "auth/logout_link.html" 
    65         url = "?auth=logout" 
     91        if hasattr(request.PYLUCID, "pagetree"): 
     92            # We are on a normal cms page -> Dont's change the url 
     93            url = "" 
     94        else: 
     95            # We are in the django admin panel -> Go to root page 
     96            url = "/" 
     97        url += "?auth=logout" 
    6698    else: 
    6799        template_name = "auth/login_link.html" 
     
    71103 
    72104 
     105def _wrong_login(request, debug_msg, user=None): 
     106    """ username or password is wrong. """ 
     107    if settings.DEBUG: 
     108        error_msg = debug_msg 
     109    else: 
     110        error_msg = _("Wrong username/password.") 
     111 
     112    # Protection against DOS attacks. 
     113    pref_form = AuthPreferencesForm() 
     114    preferences = pref_form.get_preferences() 
     115    min_pause = preferences["min_pause"] 
     116    ban_limit = preferences["ban_limit"] 
     117    try: 
     118        LogEntry.objects.request_limit( 
     119            request, min_pause, ban_limit, app_label="pylucid_plugin.auth", no_page_msg=True 
     120        ) 
     121    except LogEntry.RequestTooFast, err: 
     122        # min_pause is not observed 
     123        error_msg = err 
     124 
     125    # Log this error (Important: must be logged after LogEntry.objects.request_limit() stuff! 
     126    if user is not None: 
     127        data = {"user_username": user.username} 
     128    else: 
     129        data = None 
     130    LogEntry.objects.log_action( 
     131        app_label="pylucid_plugin.auth", action="login", message=debug_msg, data=data 
     132    ) 
     133 
     134    # create a new challenge and add it to session 
     135    challenge = _get_challenge(request) 
     136 
     137    response = "%s;%s" % (challenge, error_msg) 
     138    return HttpResponse(response, content_type="text/plain") 
     139 
     140 
     141def _sha_auth(request): 
     142    """ 
     143    login the user with username and sha values. 
     144    """ 
     145    response = _is_post_ajax_request(request) 
     146    if response is not None: # It's not a Ajax POST request 
     147        return response # Return HttpResponseBadRequest 
     148 
     149    form = ShaLoginForm(request.POST) 
     150    if not form.is_valid(): 
     151        debug_msg = "ShaLoginForm is not valid: %r" % form.errors 
     152        return _bad_request(debug_msg) 
     153 
     154    try: 
     155        challenge = request.session.pop("challenge") 
     156    except KeyError, err: 
     157        debug_msg = "Can't get 'challenge' from session: %s" % err 
     158        return _bad_request(debug_msg) 
     159 
     160    try: 
     161        user1, user_profile = form.get_user_and_profile() 
     162    except WrongUserError, err: 
     163        debug_msg = "Can't get user and user profile: %s" % err 
     164        return _wrong_login(request, debug_msg) 
     165 
     166    sha_checksum = user_profile.sha_login_checksum 
     167    sha_a2 = form.cleaned_data["sha_a2"] 
     168    sha_b = form.cleaned_data["sha_b"] 
     169 
     170    # authenticate with: 
     171    # pylucid.system.auth_backends.SiteSHALoginAuthBackend 
     172    user2 = auth.authenticate( 
     173        user=user1, challenge=challenge, 
     174        sha_a2=sha_a2, sha_b=sha_b, 
     175        sha_checksum=sha_checksum 
     176    ) 
     177    if user2 is None: 
     178        debug_msg = "auth.authenticate() failed. (must be a wrong password)" 
     179        return _wrong_login(request, debug_msg, user1) 
     180    else: 
     181        # everything is ok -> log the user in and display "last login" page message 
     182        last_login = user2.last_login 
     183        auth.login(request, user2) 
     184        message = render_to_string('auth/login_info.html', {"last_login":last_login}) 
     185        request.page_msg.successful(message) 
     186        return HttpResponse("OK", content_type="text/plain") 
     187 
     188 
     189def _get_salt(request): 
     190    """ 
     191    return the user password salt. 
     192    If the user doesn't exist or is not active, return a pseudo salt. 
     193    """ 
     194    response = _is_post_ajax_request(request) 
     195    if response is not None: # It's not a Ajax POST request 
     196        return response # Return HttpResponseBadRequest 
     197 
     198    user_profile = None 
     199    form = UsernameForm(request.POST) 
     200    if form.is_valid(): 
     201        try: 
     202            user_profile = form.get_user_profile() 
     203        except WrongUserError, err: 
     204            if settings.DEBUG: 
     205                request.page_msg.error(err) 
     206 
     207    if user_profile is None: # Wrong user? 
     208        username = request.POST["username"] 
     209        if settings.DEBUG: 
     210            request.page_msg.error("Wrong user %r !" % username) 
     211        salt = crypt.get_pseudo_salt(username) 
     212    else: 
     213        salt = user_profile.sha_login_salt 
     214 
     215    return HttpResponse(salt, content_type="text/plain") 
     216 
     217 
     218def _login_view(request, next_url): 
     219    if request.method != 'GET': 
     220        debug_msg = "request method %r wrong, only GET allowed" % request.method 
     221        return _bad_request(debug_msg) # Return HttpResponseBadRequest 
     222 
     223    if "://" in next_url: # FIXME: How to validate this better? 
     224        # Don't redirect to other pages. 
     225        debug_msg = "next url %r seems to be wrong!" % next_url 
     226        return _bad_request(debug_msg) # Return HttpResponseBadRequest 
     227 
     228    form = ShaLoginForm() 
     229 
     230    # create a new challenge and add it to session 
     231    challenge = _get_challenge(request) 
     232 
     233    context = { 
     234        "challenge": challenge, 
     235        "salt_len": crypt.SALT_LEN, 
     236        "hash_len": crypt.HASH_LEN, 
     237        "get_salt_url": request.path + "?auth=get_salt", 
     238        "sha_auth_url": request.path + "?auth=sha_auth", 
     239        "next_url": next_url, 
     240        "form": form, 
     241        "pass_reset_link": "#TODO", 
     242    } 
     243 
     244    # return a string for replacing the normal cms page content 
     245    return render_pylucid_response(request, 'auth/sha_form.html', context, context_instance=RequestContext(request)) 
    73246 
    74247 
     
    80253 
    81254 
    82 def _login(request, user, next_url): 
    83     last_login = user.last_login 
    84  
    85     auth.login(request, user) 
    86  
    87     message = render_to_string('auth/login_info.html', {"last_login":last_login}) 
    88     request.page_msg.successful(message) 
    89  
    90     return HttpResponseRedirect(next_url) 
    91  
    92  
    93 def _plaintext_login(request, context, username, next_url): 
    94     """ input the password and login if auth ok """ 
    95     if "password" in request.POST: 
    96         password_form = PasswordForm(request.POST) 
    97         if password_form.is_valid(username): 
    98             user = password_form.user # User instance added in UsernameForm.is_valid() 
    99             return _login(request, user, next_url) 
    100     else: 
    101         password_form = PasswordForm() 
    102  
    103     context["form"] = password_form 
    104  
    105     # return a string for replacing the normal cms page content 
    106     return render_pylucid_response(request, 'auth/plaintext_login.html', context) 
    107  
    108  
    109  
    110 def _sha_login(request, context, user, next_url): 
    111     """ 
    112     Login via JS-SHA-Login. 
    113     Display the JS-SHA-Login form and login if password is ok. 
    114     """ 
    115     try: 
    116         user_profile = user.get_profile() 
    117     except UserProfile.DoesNotExist, err: 
    118         try: 
    119             UserProfile.objects.get(user=user)#.count() 
    120         except UserProfile.DoesNotExist, err: 
    121             # User profile doesn't generally not exist for this user. e.g. Update from v0.8.x ? 
    122             msg = _("There exist no UserProfile for user %(user)r: %(err)s") % {"user":user, "err":err} 
    123         else: 
    124             # A UserProfile exist -> User can't access *this* site. 
    125             site = Site.objects.get_current() 
    126             msg = _("User %(user)r can't access this site: %(site)r") % {"user":user, "site": site} 
    127  
    128         LogEntry.objects.log_action( 
    129             app_label="pylucid_plugin.auth", action="login", message=msg 
    130         ) 
    131  
    132         if settings.DEBUG: 
    133             request.page_msg.error(msg) 
    134         else: 
    135             request.page_msg.error(_("User unknown.")) 
    136  
    137         return 
    138  
    139     if "sha_a2" in request.POST and "sha_b" in request.POST: 
    140         SHA_login_form = SHA_LoginForm(request.POST) 
    141         if not SHA_login_form.is_valid(): 
    142             msg = _("Form data is not valid. Please correct.") 
    143             request.page_msg.error(msg) 
    144             LogEntry.objects.log_action( 
    145                 app_label="pylucid_plugin.auth", action="login", 
    146                 message="%s (%r)" % (msg, SHA_login_form.errors) 
    147             ) 
    148             if DEBUG: request.page_msg(SHA_login_form.errors) 
    149         else: 
    150             sha_a2 = SHA_login_form.cleaned_data["sha_a2"] 
    151             sha_b = SHA_login_form.cleaned_data["sha_b"] 
    152             if DEBUG: 
    153                 request.page_msg("sha_a2:", sha_a2) 
    154                 request.page_msg("sha_b:", sha_b) 
    155  
    156             # A submited SHA1-JS-Login form 
    157             try: 
    158                 challenge = request.session['challenge'] 
    159                 if DEBUG: request.page_msg("challenge:", challenge) 
    160             except KeyError, e: 
    161                 msg = _("Session Error.") 
    162                 LogEntry.objects.log_action( 
    163                     app_label="pylucid_plugin.auth", action="login", message=msg, 
    164                     data={"user_username": user.username} 
    165                 ) 
    166                 if DEBUG: msg = "%s (%s)" % (msg, e) 
    167                 request.page_msg.error(msg) 
    168                 return 
    169  
    170             sha_checksum = user_profile.sha_login_checksum 
    171             if DEBUG: request.page_msg("sha_checksum:", sha_checksum) 
    172  
    173             # authenticate with: 
    174             # pylucid.system.auth_backends.SiteSHALoginAuthBackend 
    175             user2 = auth.authenticate( 
    176                 user=user, challenge=challenge, 
    177                 sha_a2=sha_a2, sha_b=sha_b, 
    178                 sha_checksum=sha_checksum 
    179             ) 
    180             if user2: 
    181                 return _login(request, user2, next_url) 
    182  
    183             msg = _("Wrong password.") 
    184             LogEntry.objects.log_action( 
    185                 app_label="pylucid_plugin.auth", action="login", message=msg, 
    186                 data={"user_username": user.username} 
    187             ) 
    188             request.page_msg.error(msg) 
    189     else: 
    190         SHA_login_form = UsernameForm() 
    191  
    192  
    193     context["salt"] = user_profile.sha_login_salt 
    194     if DEBUG: 
    195         challenge = "debug" # Use same challenge in debug mode 
    196         context["debug"] = "true" # For JavaScript debug 
    197     else: 
    198         # Create a new random salt value for the password challenge: 
    199         challenge = crypt.get_new_salt() 
    200         context["debug"] = "false" 
    201  
    202     # For later comparing with form data 
    203     request.session['challenge'] = challenge 
    204     context["challenge"] = challenge 
    205  
    206     context["form"] = SHA_login_form 
    207  
    208     # return a string for replacing the normal cms page content 
    209     return render_pylucid_response(request, 'auth/input_password.html', context, 
    210         context_instance=RequestContext(request) 
    211     ) 
    212  
    213  
    214 def _login_view(request, form_url, next_url): 
    215     if DEBUG: 
    216         request.page_msg( 
    217             "Warning: DEBUG is ON! Should realy only use for debugging!" 
    218         ) 
    219  
    220     pref_form = AuthPreferencesForm() 
    221     preferences = pref_form.get_preferences() 
    222     min_pause = preferences["min_pause"] 
    223     ban_limit = preferences["ban_limit"] 
    224  
    225     try: 
    226         LogEntry.objects.request_limit(request, min_pause, ban_limit, app_label="pylucid_plugin.auth") 
    227     except LogEntry.RequestTooFast: 
    228         # min_pause is not observed, page_msg has been created -> display the normal cms page. 
    229         return 
    230  
    231     context = request.PYLUCID.context 
    232     context["form_url"] = form_url 
    233  
    234     if request.method == 'POST': 
    235         username_form = UsernameForm(request.POST) 
    236         if username_form.is_valid(): 
    237             user = username_form.user # User instance added in UsernameForm.is_valid() 
    238             if not user.is_active: 
    239                 msg = _("Error: Your account is disabled!") 
    240                 LogEntry.objects.log_action( 
    241                     app_label="pylucid_plugin.auth", action="login", message=msg, 
    242                     data={"user_username": user.username} 
    243                 ) 
    244                 request.page_msg.error(msg) 
    245                 return 
    246  
    247             context["username"] = user.username 
    248  
    249             if "plaintext_login" in request.POST: 
    250                 return _plaintext_login(request, context, user.username, next_url) 
    251             else: 
    252                 return _sha_login(request, context, user, next_url) 
    253     else: 
    254         username_form = UsernameForm() 
    255  
    256     context["form"] = username_form 
    257  
    258     # return a string for replacing the normal cms page content 
    259     return render_pylucid_response(request, 'auth/input_username.html', context) 
    260  
    261  
    262255def http_get_view(request): 
    263256    """ 
     
    265258    """ 
    266259    action = request.GET["auth"] 
     260 
     261 
     262 
     263 
     264 
    267265    if action == "login": 
    268         next_url = request.GET.get("next_url", None) 
    269         if next_url: 
    270             form_url = settings.PYLUCID.AUTH_NEXT_URL % {"path": request.path, "next_url": next_url} 
    271         else: 
    272             next_url = request.path 
    273             form_url = "%s?%s" % (request.path, settings.PYLUCID.AUTH_GET_VIEW) 
    274  
    275         return _login_view(request, form_url, next_url) 
     266#        next_url = request.GET.get("next_url", None) 
     267#        if next_url: 
     268#            form_url = settings.PYLUCID.AUTH_NEXT_URL % {"path": request.path, "next_url": next_url} 
     269#        else: 
     270#            next_url = request.path 
     271#            form_url = "%s?%s" % (request.path, settings.PYLUCID.AUTH_GET_VIEW) 
     272 
     273        next_url = request.GET.get("next_url", request.path) 
     274        return _login_view(request, next_url) 
     275    elif action == "get_salt": 
     276        return _get_salt(request) 
     277    elif action == "sha_auth": 
     278        return _sha_auth(request) 
    276279    elif action == "logout": 
    277280        next_url = request.path 
    278281        return _logout_view(request, next_url) 
    279  
    280     msg = _("Wrong get view parameter!") 
    281     LogEntry.objects.log_action(app_label="pylucid_plugin.auth", action="login", message=msg) 
    282     if settings.DEBUG: 
    283         request.page_msg.error(msg) 
    284  
    285  
    286 def authenticate(request): 
    287     """ 
    288     Login+Logout view via PluginPage 
    289     """ 
    290     if request.user.is_authenticated(): 
    291         return _logout_view(request) 
    292     else: 
    293         next_url = request.GET.get("next_url", None) 
    294         form_url = request.path 
    295         if next_url == None: 
    296             next_url = "/" 
    297         else: 
    298             form_url += "?next_url=" + next_url 
    299  
    300         return _login_view(request, form_url, next_url) 
    301  
     282    else: 
     283        debug_msg = "Wrong get view parameter!" 
     284        return _bad_request(debug_msg) # Return HttpResponseBadRequest 
     285 
     286 
     287#def authenticate(request): 
     288#    """ 
     289#    Login+Logout view via PluginPage 
     290#    """ 
     291#    if request.user.is_authenticated(): 
     292#        return _logout_view(request) 
     293#    else: 
     294#        next_url = request.GET.get("next_url", None) 
     295#        form_url = request.path 
     296#        if next_url == None: 
     297#            next_url = "/" 
     298#        else: 
     299#            form_url += "?next_url=" + next_url 
     300# 
     301#        return _login_view(request, form_url, next_url) 
  • branches/0.9/pylucid_project/pylucid_plugins/auth/__init__.py

    r1634 r2572  
    11# -*- coding: utf-8 -*- 
     2 
     3from django.contrib.admin import AdminSite 
     4 
     5# Change the template for django's normal login. 
     6# So we can insert a link to JS-SHA-Login. 
     7AdminSite.login_template = "auth/django_login.html" 
  • branches/0.9/pylucid_project/utils/crypt.py

    r2161 r2572  
    135135    return seed[:SALT_LEN] 
    136136 
     137def get_pseudo_salt(*args): 
     138    """ 
     139    generate a pseudo salt (used, if user is wrong) 
     140    """ 
     141    temp = "".join([repr(arg) for arg in args]) 
     142    return sha_constructor(temp).hexdigest()[:SALT_LEN] 
     143 
    137144 
    138145def make_hash(txt, salt): 
     
    148155    hash = sha_constructor(salt + smart_str(txt)).hexdigest() 
    149156    return hash 
     157 
    150158 
    151159def get_salt_and_hash(txt):