Trac is being migrated to new services! Issues can be found in our new YouTrack instance and WIKI pages can be found on our website.

LocalTracChanges: acct_mgr_plugin_ssl_remember_me_rev_4111.patch

File acct_mgr_plugin_ssl_remember_me_rev_4111.patch, 9.1 KB (added by datallah, 15 years ago)

AccountManager? rev. 4111 patch to implement SSL login and admin screens and persistent sessions

  • acct_mgr/web_ui.py

     
    1313import os
    1414import random
    1515import string
     16import time
     17import re
     18import urlparse
    1619
    1720from trac import perm, util
    1821from trac.core import *
    1922from trac.config import IntOption
    2023from trac.notification import NotificationSystem, NotifyEmail
    2124from trac.prefs import IPreferencePanelProvider
     25from trac.util.text import unicode_urlencode
    2226from trac.web import auth
    2327from trac.web.api import IAuthenticator
    2428from trac.web.main import IRequestHandler, IRequestFilter
     
    5357    if password != req.args.get('password_confirm'):
    5458        raise TracError('The passwords must match.')
    5559
     60    if not req.args.get('email'):
     61        raise TracError('You must enter a valid Email address.')
     62
    5663    mgr.set_password(user, password)
    5764
    5865    db = env.get_db_cnx()
     
    157164                          'store does not support writing.')
    158165        return writable
    159166
     167
     168    def _redirect_to_scheme(self, req, scheme):
     169        self.log.debug("Forwarding to '%s' for '%s%s'" % (scheme, req.base_path, req.path_info))
     170        referer_param = req.args.get('referer')
     171        if referer_param:
     172            referer_param = "referer=%s" % referer_param
     173        req.redirect(urlparse.urlunparse((scheme, req.server_name, req.base_path + req.path_info, None, referer_param, None)))
     174
     175    def _needs_ssl(self, path_info):
     176        return re.match('/prefs(?:/([^/]+))?', path_info) \
     177               or path_info == '/reset_password' \
     178               or path_info == '/register' \
     179               or path_info == '/login' \
     180               or path_info == '/admin/accounts/users'
     181
     182    def _needs_session_cookie(self, path_info):
     183        return re.match('/admin(?:/([^/]+))?', path_info) \
     184               or path_info == '/prefs/account'
     185
    160186    #IPreferencePanelProvider methods
    161187    def get_preference_panels(self, req):
    162188        if not self._write_check():
     
    179205
    180206    # IRequestFilter methods
    181207    def pre_process_request(self, req, handler):
     208
     209        ssl_path = self._needs_ssl(req.path_info)
     210        # Catch the user leaving an ssl path /prefs and redirect to non-secure
     211        if req.scheme.lower() == 'https' and not ssl_path:
     212            self._redirect_to_scheme(req, 'http')
     213        # Catch the user entering /prefs or and redirect to secure
     214        elif ssl_path:
     215            if req.scheme.lower() != 'https':
     216                self._redirect_to_scheme(req, 'https')
     217
     218        # If we're accessing a page that you should be logged in the current session for, deal with it
     219        if self._needs_session_cookie(req.path_info) \
     220                and not req.incookie.has_key('trac_auth_session'):
     221            self.log.debug("Accessing %s requires a session login, forwarding to login screen." % req.path_info)
     222            referal_url = urlparse.urlunparse((ssl_path and 'https' or 'http', req.server_name, req.base_path + req.path_info, None, None, None))
     223            req.redirect(self.env.href.login() + '?%s' % unicode_urlencode({'referer' : referal_url, 'force_session' : '1'}))
     224
    182225        return handler
    183226
    184227    def post_process_request(self, req, template, data, content_type):
     
    377420            except TracError, e:
    378421                data['registration_error'] = e.message
    379422            else:
    380                 req.redirect(req.href.login())
     423                redirect_url = None
     424                referer = req.args.get('referer')
     425                if referer:
     426                    redirect_url = "%s?referer=%s" % (req.href.login(), referer)
     427                req.redirect(redirect_url or req.href.login())
    381428        data['reset_password_enabled'] = \
    382429            (self.env.is_component_enabled(AccountModule)
    383430             and NotificationSystem(self.env).smtp_enabled)
     
    422469    match_request = if_enabled(auth.LoginModule.match_request)
    423470
    424471    def process_request(self, req):
    425         if req.path_info.startswith('/login') and req.authname == 'anonymous':
     472        if req.path_info.startswith('/login') and (req.authname == 'anonymous' or req.args.get('force_session')):
    426473            data = {
    427474                'referer': self._referer(req),
    428475                'reset_password_enabled': AccountModule(self.env).reset_password_enabled
     
    432479            return 'login.html', data, None
    433480        return auth.LoginModule.process_request(self, req)
    434481
     482    #def _get_name_for_cookie(self, req, cookie):
     483    #    name = auth.LoginModule._get_name_for_cookie(self, req, cookie)
     484    #    if name and not req.incookie.has_key('trac_auth_session'):
     485    #        self.env.log.debug('Updating auth cookie %s for user %s' %
     486    #                           (cookie.value, name))
     487    #        db = self.env.get_db_cnx()
     488    #        cursor = db.cursor()
     489    #        cursor.execute('UPDATE auth_cookie SET time=%s WHERE cookie=%s',
     490    #                       (int(time.time()), cookie.value))
     491    #        req.outcookie['trac_auth'] = cookie.value
     492    #        req.outcookie['trac_auth']['path'] = self.env.href()
     493    #        req.outcookie['trac_auth']['expires'] = 86400 * 30
     494    #        req.outcookie['trac_auth_session'] = '1'
     495    #        req.outcookie['trac_auth_session']['path'] = self.env.href()
     496    #    return name
     497
    435498    def _do_login(self, req):
    436499        if not req.remote_user:
    437500            req.redirect(self.env.abs_href())
    438         return auth.LoginModule._do_login(self, req)
     501        res = auth.LoginModule._do_login(self, req)
     502        if req.args.get('rememberme', '0') == '1':
     503            req.outcookie['trac_auth']['expires'] = 86400 * 30
     504            req.outcookie['trac_auth_session'] = '1'
     505            req.outcookie['trac_auth_session']['path'] = self.env.href()
     506        return res
    439507
     508    def _do_logout(self, req): 
     509        """Log the user out.
     510
     511           Simply deletes the corresponding record from the auth_cookie table.
     512        """
     513        if req.authname == 'anonymous':
     514            # Not logged in
     515            return
     516
     517        # While deleting this cookie we also take the opportunity to delete
     518        # cookies older than 30 days
     519        db = self.env.get_db_cnx()
     520        cursor = db.cursor()
     521        cursor.execute("DELETE FROM auth_cookie WHERE name=%s OR time < %s",
     522                       (req.authname, int(time.time()) - 86400 * 30))
     523        db.commit()
     524        self._expire_cookie(req)
     525
    440526    def _remote_user(self, req):
    441527        user = req.args.get('user')
    442528        password = req.args.get('password')
     
    449535    def _redirect_back(self, req):
    450536        """Redirect the user back to the URL she came from."""
    451537        referer = self._referer(req)
    452         if referer and not referer.startswith(req.base_url):
    453             # don't redirect to external sites
    454             referer = None
     538        if referer:
     539            u = urlparse.urlparse(referer)
     540            r = urlparse.urlparse(req.base_url)
     541            if u[1] and u[1] != r[1]:
     542                # don't redirect to external sites
     543                referer = None
    455544        req.redirect(referer or self.env.abs_href())
    456545
    457546    def _referer(self, req):
     
    576665    def _send_email(self, req):
    577666        notifier = EmailVerificationNotification(self.env)
    578667        notifier.notify(req.authname, req.session['email_verification_token'])
     668
  • acct_mgr/templates/register.html

     
    2424      </div>
    2525
    2626      <form method="post" id="acctmgr_registerform" action="">
     27        <input type="hidden" name="referer" value="${req.args.get('referer')}" />
    2728        <fieldset>
    2829          <legend>Required</legend>
    2930          <div>
     
    4344                     class="textwidget" size="20" />
    4445            </label>
    4546          </div>
    46         </fieldset>
    47         <fieldset>
    48           <legend>Optional</legend>
    4947          <div>
    5048            <label>Name:
    5149              <input type="text" name="name" class="textwidget" size="20" />
  • acct_mgr/templates/login.html

     
    2828        <input type="hidden" name="referer" value="${referer}" />
    2929        <div>
    3030          <label for="user">Username:</label>
    31           <input type="text" id="user" name="user" class="textwidget" size="20" />
     31          <input type="text" id="user" name="user" class="textwidget" size="20" value="${req.authname != 'anonymous' and req.authname or None}"/>
    3232        </div>
    3333        <div>
    3434          <label for="password">Password:</label>
    3535          <input type="password" id="password" name="password" class="textwidget" size="20" />
    3636        </div>
     37        <div> 
     38          <input type="checkbox" id="rememberme" name="rememberme" value="1" checked="${req.authname != 'anonymous' and req.authname or None}"/> 
     39          <label for="rememberme">Remember me</label> 
     40        </div> 
    3741        <input type="submit" value="Login" />
    3842
    3943        <p py:if="reset_password_enabled">
All information, including names and email addresses, entered onto this website or sent to mailing lists affiliated with this website will be public. Do not post confidential information, especially passwords!