Edgewall Software

Ticket #287: trac-registration.diff

File trac-registration.diff, 11.8 KB (added by lunar@…, 4 years ago)

Registration interface using htpasswd

  • templates/header.cs

    diff -urN trac.orig/templates/header.cs trac/templates/header.cs
    old new  
    5353    logged in as <?cs var:trac.authname ?> </li> 
    5454    <li><a href="<?cs var:trac.href.logout ?>">Logout</a> 
    5555  <?cs /if ?></li> 
     56  <li><a href="<?cs var:trac.href.registration ?>">Registration</a></li> 
    5657  <li><a href="<?cs var:trac.href.settings ?>">Settings</a></li> 
    5758  <li><a accesskey="6" href="<?cs var:trac.href.wiki ?>/TracGuide">Help/Guide</a></li> 
    5859  <li style="display: none"><a accesskey="5" href="http://projects.edgewall.com/trac/wiki/TracFaq">FAQ</a></li> 
  • templates/registration.cs

    diff -urN trac.orig/templates/registration.cs trac/templates/registration.cs
    old new  
     1<?cs include "header.cs"?> 
     2<?cs include "macros.cs"?> 
     3 
     4<div id="contextnav" class="nav"></div> 
     5 
     6<div id="content" class="registration"> 
     7 <h1>Registration</h1> 
     8<?cs if:registration_success ?> 
     9 <p>Operation successful.</p> 
     10<?cs else ?> 
     11<?cs if:trac.authname == "anonymous" || !trac.authname ?> 
     12 
     13 <h2>Create new user</h2> 
     14 <p> 
     15 By filling this form, you will create a new unpriviliged user which you 
     16 can use to identify your changes in the wiki and tickets. 
     17 </p> 
     18 <p> 
     19 The login must contain only lowercase letters and numbers. 
     20 </p> 
     21 <?cs if:registration_error ?> 
     22  <div class="system-message"><?cs var:registration_error ?></div> 
     23 <?cs /if ?> 
     24 <form method="post" action="<?cs var:cgi_location?>/registration"> 
     25 <div> 
     26  <div class="field"> 
     27   <input type="hidden" name="action" value="create" /> 
     28   <label for="login">Login:</label><br /> 
     29   <input type="text" id="login" name="login" class="textwidget" size="30" 
     30          value="<?cs var:login ?>" /> 
     31  </div> 
     32  <div class="field"> 
     33   <label for="password">Password:</label><br /> 
     34   <input type="password" id="password" name="password" class="textwidget" 
     35          size="30" /> 
     36  </div> 
     37  <div class="field"> 
     38   <label for="confirm_password">Confirm password:</label><br /> 
     39   <input type="password" id="confirm_password" name="confirm_password" 
     40          class="textwidget" size="30" /> 
     41  </div> 
     42 
     43  <div> 
     44   <br /> 
     45   <input type="submit" value="Register" /> 
     46  </div> 
     47 </div> 
     48 </form> 
     49<?cs else ?> 
     50 <h2>Change password</h2> 
     51 <form method="post" action="<?cs var:cgi_location?>/registration"> 
     52 <div> 
     53  <div class="field"> 
     54   <input type="hidden" name="action" value="change_password" /> 
     55   <label for="password">New password:</label><br /> 
     56   <input type="password" id="password" name="password" class="textwidget" 
     57          size="30" /> 
     58  </div> 
     59  <div class="field"> 
     60   <label for="confirm_password">Confirm password:</label><br /> 
     61   <input type="password" id="confirm_password" name="confirm_password" 
     62          class="textwidget" size="30" /> 
     63  </div> 
     64 
     65  <div> 
     66   <br /> 
     67   <input type="submit" value="Update" /> 
     68  </div> 
     69 </div> 
     70 </form> 
     71 
     72 <hr /> 
     73 
     74 <h2>Delete account</h2> 
     75 <form method="post" action="<?cs var:cgi_location?>/registration"> 
     76 <div> 
     77  <div class="field"> 
     78   <input type="hidden" name="action" value="delete" /> 
     79   <input type="checkbox" id="confirm" name="confirm" /> 
     80   <label for="confirm">I am really sure.</label><br /> 
     81  </div> 
     82 
     83  <div> 
     84   <br /> 
     85   <input type="submit" value="Delete my account" /> 
     86  </div> 
     87 </div> 
     88 </form> 
     89<?cs /if ?> 
     90<?cs /if ?> 
     91</div> 
     92<?cs include:"footer.cs"?> 
  • trac/Href.py

    diff -urN trac.orig/trac/Href.py trac/trac/Href.py
    old new  
    5757    def login(self): 
    5858        return href_join(self.base, 'login') 
    5959 
     60    def registration(self): 
     61        return href_join(self.base, 'registration') 
     62 
    6063    def logout(self): 
    6164        return href_join(self.base, 'logout') 
    6265 
  • trac/Registration.py

    diff -urN trac.orig/trac/Registration.py trac/trac/Registration.py
    old new  
     1# -*- coding: iso8859-1 -*- 
     2# 
     3# Copyright (C) 2004 Edgewall Software 
     4# Copyright (C) 2004 Daniel Lundin <daniel@edgewall.com> 
     5# 
     6# Trac is free software; you can redistribute it and/or 
     7# modify it under the terms of the GNU General Public License as 
     8# published by the Free Software Foundation; either version 2 of the 
     9# License, or (at your option) any later version. 
     10# 
     11# Trac is distributed in the hope that it will be useful, 
     12# but WITHOUT ANY WARRANTY; without even the implied warranty of 
     13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
     14# General Public License for more details. 
     15# 
     16# You should have received a copy of the GNU General Public License 
     17# along with this program; if not, write to the Free Software 
     18# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
     19# 
     20# Author: Lunar <lunar@anargeek.net> 
     21 
     22import fcntl 
     23import os 
     24import os.path 
     25import re 
     26from tempfile import mkstemp 
     27 
     28from util import TracError 
     29from Module import Module 
     30 
     31class Registration(Module): 
     32    template_name = 'registration.cs' 
     33 
     34    _form_fields = ['login','password'] 
     35 
     36    def render(self): 
     37        self.passwd_file = self.env.get_config('registration', 'passwd_file') 
     38        self.htpasswd_bin = self.env.get_config('registration', 'htpasswd_bin') 
     39        self.use_md5 = self.env.get_config('registration', 'use_md5') 
     40        self.req.hdf.setValue('title', 'Registration') 
     41        action = self.args.get('action') 
     42        self.env.log.debug('Registration action: %s' % action) 
     43        if action == 'create': 
     44            self.create_user() 
     45        elif action == 'change_password': 
     46            self.change_password() 
     47        elif action == 'delete': 
     48            self.delete_user() 
     49 
     50    def create_user(self): 
     51        login = self.args.get('login') 
     52        self.req.hdf.setValue('login', login) 
     53        if not self.is_login_valid(login): 
     54            self.req.hdf.setValue('registration_error', 'Invalid login.') 
     55            return 
     56        if self.user_exists(login): 
     57            self.req.hdf.setValue('registration_error', 'Login already used.') 
     58            return 
     59        password = self.args.get('password') 
     60        if password != self.args.get('confirm_password'): 
     61            self.req.hdf.setValue('registration_error', 'Password mismatch.') 
     62            return 
     63        self.htpasswd(login, password) 
     64        self.req.hdf.setValue('registration_success', 'true') 
     65 
     66    def change_password(self): 
     67        login = self.req.authname 
     68        if not login or login == "anonymous": 
     69            self.req.hdf.setValue('registration_error', 'Not logged on.') 
     70            return 
     71        if not self.user_exists(login): 
     72            self.req.hdf.setValue('registration_error', 'Unknown user.') 
     73            return 
     74        password = self.args.get('password') 
     75        if password != self.args.get('confirm_password'): 
     76            self.req.hdf.setValue('registration_error', 'Password mismatch.') 
     77            return 
     78        self.htpasswd(login, password) 
     79        self.req.hdf.setValue('registration_success', 'true') 
     80 
     81    def delete_user(self): 
     82        if not self.args.get('confirm'): 
     83            return 
     84        login = self.req.authname 
     85        if not login or login == "anonymous": 
     86            self.req.hdf.setValue('registration_error', 'Not logged on.') 
     87            return 
     88        if not self.user_exists(login): 
     89            self.req.hdf.setValue('registration_error', 'Unknown user.') 
     90            return 
     91        self.remove_from_passwd(login) 
     92        self.req.hdf.setValue('registration_success', 'true') 
     93              
     94    def htpasswd(self, login, password): 
     95        if self.use_md5 == 'true': 
     96            flags = '-b -m' 
     97        else: 
     98            flags = '-b' 
     99        cmd = ('"%s" %s "%s" "%s" "%s"' 
     100               % (self.htpasswd_bin, flags, self.passwd_file, login, password)) 
     101        ret = os.system(cmd) 
     102        if ret == 0: 
     103             return True 
     104        elif ret == 1: 
     105             raise TracError, 'htpasswd: unable to access password file' 
     106        elif ret == 2: 
     107             raise TracError, 'htpasswd: bad command line' 
     108        elif ret == 3: 
     109             # Should not happen 
     110             raise TracError, 'htpasswd: password mismatch' 
     111        elif ret == 4: 
     112             raise TracError, 'htpasswd: interrupted' 
     113        elif ret == 5: 
     114             # Should not happen 
     115             raise TracError, 'htpasswd: value too long' 
     116        elif ret == 6: 
     117             # Should not happen 
     118             raise TracError, 'htpasswd: username contains illegal characters' 
     119        else: 
     120             raise TracError, 'htpasswd: unknown error (%d)' % ret 
     121 
     122    def remove_from_passwd(self, login): 
     123        fd = os.open(self.passwd_file, os.O_RDWR) 
     124        try: 
     125            fcntl.flock(fd, fcntl.LOCK_EX) 
     126            passwd = os.fdopen(fd, 'r')  
     127            new_passwd = '' 
     128            for line in passwd: 
     129                if not (line.strip() == '' or line.startswith("%s:" % login)): 
     130                    new_passwd = new_passwd + ("%s" % line) 
     131            os.lseek(fd, 0, 0) 
     132            os.ftruncate(fd, 0) 
     133            os.write(fd, new_passwd) 
     134        finally: 
     135            os.close(fd) 
     136 
     137    def user_exists(self, login): 
     138        for line in open(self.passwd_file, 'r'): 
     139            if line.startswith(login + ':'): 
     140                return True 
     141        return False 
     142         
     143    def is_login_valid(self, login): 
     144        return None != re.match('[a-z0-9]+', login) 
  • trac/core.py

    diff -urN trac.orig/trac/core.py trac/trac/core.py
    old new  
    5959    'attachment'  : ('File', 'Attachment', 0), 
    6060    'roadmap'     : ('Roadmap', 'Roadmap', 0), 
    6161    'settings'    : ('Settings', 'Settings', 0), 
    62     'milestone'   : ('Milestone', 'Milestone', 0) 
     62    'milestone'   : ('Milestone', 'Milestone', 0), 
     63    'registration': ('Registration', 'Registration', 0), 
    6364    } 
    6465 
    6566class TracFieldStorage(cgi.FieldStorage): 
     
    9394        if match.group(2): 
    9495            set_if_missing(args, 'page', match.group(2)) 
    9596        return args 
    96     match = re.search('^/(newticket|timeline|search|roadmap|settings|query)/?', path_info) 
     97    match = re.search('^/(newticket|timeline|search|roadmap|settings|query|registration)/?', path_info) 
    9798    if match: 
    9899        set_if_missing(args, 'mode', match.group(1)) 
    99100        return args 
     
    227228    hdf.setValue('trac.href.about', env.href.about()) 
    228229    hdf.setValue('trac.href.about_config', env.href.about('config')) 
    229230    hdf.setValue('trac.href.login', env.href.login()) 
     231    hdf.setValue('trac.href.registration', env.href.registration()) 
    230232    hdf.setValue('trac.href.logout', env.href.logout()) 
    231233    hdf.setValue('trac.href.settings', env.href.settings()) 
    232234    hdf.setValue('trac.href.homepage', 'http://trac.edgewall.com/') 
  • trac/db_default.py

    diff -urN trac.orig/trac/db_default.py trac/trac/db_default.py
    old new  
    445445  ('notification', 'smtp_from', 'trac@localhost'), 
    446446  ('notification', 'smtp_replyto', 'trac@localhost'), 
    447447  ('timeline', 'changeset_show_files', 'false'), 
    448   ('timeline', 'changeset_files_count', 3)) 
     448  ('timeline', 'changeset_files_count', 3), 
     449  ('registration', 'htpasswd_bin', '/usr/bin/htpasswd'), 
     450  ('registration', 'passwd_file', '/path/to/htaccess-passwd'), 
     451  ('registration', 'use_md5', 'true')) 
    449452