Changeset 6

Show
Ignore:
Timestamp:
11/24/06 16:13:08 (2 years ago)
Author:
pacopablo
Message:
  • Begin migration to 0.11
Location:
trac_plugins/pacopablodbauth
Files:
1 added
2 removed
3 modified
1 copied

Legend:

Unmodified
Added
Removed
  • trac_plugins/pacopablodbauth/trunk/dbauth/__init__.py

    r3 r6  
    11from auth import * 
    2 from admin import * 
     2from web_ui import * 
  • trac_plugins/pacopablodbauth/trunk/dbauth/auth.py

    r3 r6  
    1717import re 
    1818import time 
     19import random 
     20from time import time 
     21from crypt import crypt 
     22from string import ascii_letters, digits 
    1923 
    2024from trac.core import * 
     
    2832from acct_mgr.web_ui import if_enabled 
    2933 
    30 __all__ = ['PostgresDbAuth', 'LoginModule'] 
     34__all__ = ['PostgresDbAuth'] 
     35 
     36SALT_VALS = ''.join(['.', '/', ascii_letters, digits]) 
    3137 
    3238SQL_USERS = """SELECT u.uname  
     
    6773 
    6874 
    69 class PostgresDbAuth(Component): 
     75class PacopabloPasswordStore(Component): 
    7076    """Manages user accounts stored in the Casa de Pacopablo database 
    7177 
     
    7379    {{{ 
    7480    [account-manager] 
    75     password_format = pacopablodb 
     81    password_store = PacopabloPasswordStore 
    7682    }}} 
    7783    """ 
     
    7985    implements(IPasswordStore) 
    8086 
     87    domain = Option('account-manager', 'auth_domain', '', 
     88        """Domain for which auth covers.""") 
     89 
    8190    def config_key(self): 
    82         return 'pacopablodb' 
     91        """ Deprecated """ 
     92        pass 
    8393 
    8494    def get_users(self): 
     
    8696        global SQL_USERS 
    8797        cnx = self.env.get_db_cnx() 
    88         domain = self.env.config.get('acct_mgr', 'password_domain', 'pacopablo.com') 
     98        
     99        domain = self.domain or ''  
    89100        cur = cnx.cursor() 
    90101        cur.execute(SQL_USERS, [domain]) 
     
    95106    def has_user(self, user): 
    96107        """ Returns whether or not the specified user exists """ 
     108        if self._has_user(user): 
     109            return True 
     110        return False 
     111 
     112    def _has_user(self, user): 
     113        """Return the uid and password of the user if they exist""" 
    97114        global SQL_USER_EXISTS 
    98115        cnx = self.env.get_db_cnx() 
    99         domain = self.env.config.get('acct_mgr', 'password_domain', 'pacopablo.com') 
     116        domain = selfdomain or '' 
    100117        cur = cnx.cursor() 
    101118        cur.execute(SQL_USER_EXISTS, [user, domain]) 
    102119        exists = cur.fetchone() 
    103120        cnx.close() 
    104         return (exists and True) or False 
     121        return (exists and exists[0]) or False 
     122 
     123    def _get_uid(self, user): 
     124        """Returns the uid for the given user, or a new UID if the user  
     125        doesn't exist""" 
     126        return self._has_user(user) 
     127 
     128    def _get_next_uid(self): 
     129        """Returns the next availiable UID""" 
     130        cnx = self.env.get_db_cnx() 
     131        domain = selfdomain or '' 
     132        cur = cnx.cursor() 
     133        cur.execute(SQL_GET_NEXT_UID) 
     134        uid = cur.fetchone() 
     135        cnx.close() 
     136        return uid and int(uid[0]) or None 
    105137 
    106138    def set_password(self, user, password): 
     
    116148        newacct = False 
    117149        cnx = self.env.get_db_cnx() 
    118         domain = self.env.config.get('acct_mgr', 'password_domain', 'pacopablo.com') 
    119         cur = cnx.cursor() 
    120         cur.execute(SQL_USER_EXISTS, [user, domain]) 
    121         exists = cur.fetchone() 
    122         if exists: 
    123             uid = exists[0] 
    124         else: 
    125             cur.execute(SQL_GET_NEXT_UID) 
    126             uid = cur.fetchone()[0] 
     150        domain = self.domain or '' 
     151        uid = self._get_uid(user) 
     152        cur = cnx.cursor() 
    127153        try: 
    128             if exists: 
     154            if uid: 
    129155                cur.execute(SQL_UPDATE_PASSWD, [password, uid]) 
    130156            else: 
     157                uid = self._get_next_uid() 
    131158                cur.execute(SQL_ADD_USER, [uid, user, password]) 
    132159                cur.execute(SQL_ADD_USER_MEMBERSHIP, [uid, domain]) 
     
    138165        cnx.close() 
    139166        return newacct 
     167 
     168    def _cryptpw(self, passwd, salt=None): 
     169        """Use crypt(2) to encrypt the password""" 
     170        global SALT_VALS 
     171        if not salt: 
     172            random.seed(time()) 
     173            salt1 = SALT_VALS[random.randint(1,64) - 1] 
     174            salt2 = SALT_VALS[random.randint(1,64) - 1] 
     175            salt = salt1 + salt2 
     176        return crypt(passwd, salt) 
     177 
    140178     
    141179    def check_password(self, user, password): 
    142180        """Checks if the password is valid for the user.""" 
    143         global SQL_USER_EXISTS 
    144         cnx = self.env.get_db_cnx() 
    145         domain = self.env.config.get('acct_mgr', 'password_domain', 'pacopablo.com') 
    146         cur = cnx.cursor() 
    147         cur.execute(SQL_USER_EXISTS, [user, domain]) 
    148         row = cur.fetchone() 
     181        domain = self.domain or '' 
     182        uinfo = self._has_user(user) 
    149183        valid = False 
    150         if row: 
    151             uid, dbpass = row 
    152             valid = dbpass == password 
    153         cnx.close() 
     184        if uinfo: 
     185            uid = uinfo[0] 
     186            dbpass = uinfo[1] 
     187            valid = dbpass == self._cryptpw(password, dbpass) 
    154188        return valid 
    155189     
     
    163197         
    164198        cnx = self.env.get_db_cnx() 
    165         domain = self.env.config.get('acct_mgr', 'password_domain', 'pacopablo.com') 
    166         cur = cnx.cursor() 
    167         cur.execute(SQL_USER_EXISTS, [user, domain]) 
    168         uid = cur.fetchone()[0] 
     199        domain = self.domain or '' 
     200        uinfo = self._has_user(user) 
     201        cur = cnx.cursor() 
    169202        try: 
    170             cur.execute(SQL_DELETE_USER, [uid]) 
    171             cur.execute(SQL_DELETE_MEMBERSHIP, [uid]) 
    172             cnx.commit() 
    173             self.log.info('Delete account for %s' % str(user)) 
     203            if uinfo: 
     204                uid = uinfo[0] 
     205                cur.execute(SQL_DELETE_USER, [uid]) 
     206                cur.execute(SQL_DELETE_MEMBERSHIP, [uid]) 
     207                cnx.commit() 
     208                self.log.info('Delete account for %s' % str(user)) 
     209            else: 
     210                raise TracError, "Unable to delete non-existant account: %s" % user 
    174211        except: 
    175212            self.log.debug('Error encountered while deleting user %s' % str(user)) 
     
    180217        return True 
    181218 
    182 def get_schema(obj): 
    183     """Return the schema name of the trac environment 
    184      
    185     """ 
    186     db = obj.env.get_db_cnx() 
    187     schema = db.schema 
    188     db.close() 
    189     del db 
    190     return schema 
    191  
    192 class LoginModule(Component): 
    193     """Implements user authentication based on a central database 
    194  
    195     """ 
    196  
    197     implements(IAuthenticator, INavigationContributor, IRequestHandler) 
    198  
    199     check_ip = BoolOption('trac', 'check_auth_ip', 'true', 
    200          """Whether the IP address of the user should be checked for 
    201          authentication (''since 0.9'').""") 
    202  
    203     ignore_case = BoolOption('trac', 'ignore_auth_case', 'false', 
    204         """Whether case should be ignored for login names (''since 0.9'').""") 
    205  
    206     auth_schema = Option('account-manager', 'auth_schema', '', 
    207         """PostgreSQL schema in which the auth_cookie table resides.""") 
    208  
    209     # IAuthenticator methods 
    210  
    211     def authenticate(self, req): 
    212         if req.method == 'POST' and req.path_info.startswith('/login'): 
    213             req.environ['REMOTE_USER'] = self._remote_user(req) 
    214         authname = None 
    215         if req.remote_user: 
    216             authname = req.remote_user 
    217         elif req.incookie.has_key('trac_auth'): 
    218             authname = self._get_name_for_cookie(req, req.incookie['trac_auth']) 
    219  
    220         if not authname: 
    221             return None 
    222  
    223         if self.ignore_case: 
    224             authname = authname.lower() 
    225  
    226         return authname 
    227     authenticate = if_enabled(authenticate) 
    228  
    229     # INavigationContributor methods 
    230  
    231     def get_active_navigation_item(self, req): 
    232         return 'login' 
    233  
    234     def get_navigation_items(self, req): 
    235         if req.authname and req.authname != 'anonymous': 
    236             yield ('metanav', 'login', 'logged in as %s' % req.authname) 
    237             yield ('metanav', 'logout', 
    238                    Markup('<a href="%s">Logout</a>'  
    239                           % escape(self.env.href.logout()))) 
    240         else: 
    241             yield ('metanav', 'login', 
    242                    Markup('<a href="%s">Login</a>'  
    243                           % escape(self.env.href.login()))) 
    244  
    245     # IRequestHandler methods 
    246  
    247     def match_request(self, req): 
    248         return re.match('/(login|logout)/?', req.path_info) 
    249     match_request = if_enabled(match_request) 
    250  
    251     def process_request(self, req): 
    252         self.log.debug('path_info: %s' % str(req.path_info)) 
    253         self.log.debug('authname: %s' % str(req.authname)) 
    254         if req.path_info.startswith('/login') and req.authname == 'anonymous': 
    255             req.hdf['referer'] = self._referer(req) 
    256             if req.method == 'POST': 
    257                 req.hdf['login.error'] = 'Invalid username or password' 
    258             return 'login.cs', None 
    259         if req.path_info.startswith('/login'): 
    260             self._do_login(req) 
    261         elif req.path_info.startswith('/logout'): 
    262             self._do_logout(req) 
    263         self._redirect_back(req) 
    264  
    265     # Protect against module contention 
    266     def enabled(self): 
    267         # Users should disable the built-in authentication to use this one 
    268         return not self.env.is_component_enabled(auth.LoginModule) 
    269     enabled = property(enabled) 
    270  
    271     # Internal methods 
    272  
    273     def _remote_user(self, req): 
    274         user = req.args.get('user') 
    275         if AccountManager(self.env).check_password(user, 
    276                 req.args.get('password')): 
    277             return user 
    278         return None 
    279  
    280     def _do_login(self, req): 
    281         if not req.remote_user: 
    282             req.redirect(self.env.abs_href()) 
    283         assert req.remote_user, 'Authentication information not available.' 
    284  
    285         remote_user = req.remote_user 
    286         if self.ignore_case: 
    287             remote_user = remote_user.lower() 
    288  
    289         assert req.authname in ('anonymous', remote_user), \ 
    290                'Already logged in as %s.' % req.authname 
    291  
    292         cookie = hex_entropy() 
    293         db = self.env.get_db_cnx() 
    294         cursor = db.cursor() 
    295 #        auth_schema = self.env.config.get('acct_mgr', 'auth_schema', db.schema) 
    296         sql = "INSERT INTO %s.auth_cookie (cookie,name,ipnr,time)" % \ 
    297               self.auth_schema or db.schema 
    298         cursor.execute(sql +  " VALUES (%s, %s, %s, %s)", (cookie, remote_user, 
    299                        req.remote_addr, int(time.time()))) 
    300         db.commit() 
    301  
    302         req.authname = remote_user 
    303         req.outcookie['trac_auth'] = cookie 
    304         req.outcookie['trac_auth']['path'] = '/' 
    305         domain = self.env.config.get('acct_mgr', 'password_domain', 'pacopablo.com') 
    306         req.outcookie['trac_auth']['domain'] = ''.join(['.', domain]) 
    307  
    308     def _do_logout(self, req): 
    309         """Log the user out. 
    310  
    311         Simply deletes the corresponding record from the auth_cookie table. 
    312         """ 
    313         if req.authname == 'anonymous': 
    314             # Not logged in 
    315             return 
    316  
    317         # While deleting this cookie we also take the opportunity to delete 
    318         # cookies older than 10 days 
    319         db = self.env.get_db_cnx() 
    320         cursor = db.cursor() 
    321 #        auth_schema = self.env.config.get('acct_mgr', 'auth_schema', db.schema) 
    322         sql = "DELETE FROM %s.auth_cookie " % self.auth_schema or db.schema 
    323         cursor.execute(sql + " WHERE name=%s OR time < %s", 
    324                        (req.authname, int(time.time()) - 86400 * 10)) 
    325         db.commit() 
    326         self._expire_cookie(req) 
    327  
    328     def _expire_cookie(self, req): 
    329         """Instruct the user agent to drop the auth cookie by setting the 
    330         "expires" property to a date in the past. 
    331         """ 
    332         req.outcookie['trac_auth'] = '' 
    333         req.outcookie['trac_auth']['path'] = self.env.href() 
    334         req.outcookie['trac_auth']['expires'] = -10000 
    335  
    336     def _get_name_for_cookie(self, req, cookie): 
    337         db = self.env.get_db_cnx() 
    338         cursor = db.cursor() 
    339 #        auth_schema = self.env.config.get('acct_mgr', 'auth_schema', db.schema) 
    340         sql = "SELECT name FROM %s.auth_cookie " % self.auth_schema or db.schema 
    341         if self.check_ip: 
    342             self.log.debug('cooke.value: %s' % str(cookie.value)) 
    343             self.log.debug('req.remote_addr: %s' % str(req.remote_addr)) 
    344             self.log.debug('sql: %s' % str(sql)) 
    345             cursor.execute(sql + " WHERE cookie=%s AND ipnr=%s", 
    346                            (cookie.value, req.remote_addr)) 
    347         else: 
    348             cursor.execute(sql + " WHERE cookie=%s", (cookie.value,)) 
    349         row = cursor.fetchone() 
    350         if not row: 
    351             # The cookie is invalid (or has been purged from the database), so 
    352             # tell the user agent to drop it as it is invalid 
    353             self._expire_cookie(req) 
    354             return None 
    355  
    356         return row[0] 
    357  
    358     def _redirect_back(self, req): 
    359         """Redirect the user back to the URL she came from.""" 
    360         referer = self._referer(req) 
    361         if referer and not referer.startswith(req.base_url): 
    362             # don't redirect to external sites 
    363             referer = None 
    364         req.redirect(referer or self.env.abs_href()) 
    365  
    366     def _referer(self, req): 
    367         return req.args.get('referer') or req.get_header('Referer') 
    368  
    369  
    370  
    371  
    372  
  • trac_plugins/pacopablodbauth/trunk/setup.py

    r3 r6  
    44 
    55PACKAGE = 'PacopabloDbAuth' 
    6 VERSION = '0.1' 
     6VERSION = '0.11' 
    77 
    88setup(  name=PACKAGE, version=VERSION, 
     
    1515        package_dir = { 'PacopabloDbAuth' : 'dbauth' }, 
    1616        packages = ['PacopabloDbAuth'], 
    17         package_data = { 'PacopabloDbAuth' : ['templates/*.cs', ]}, 
     17        package_data = { 'PacopabloDbAuth' : ['templates/*.html', ]}, 
    1818        entry_points = {'trac.plugins': ['PacopabloDbAuth = PacopabloDbAuth']}, 
    19         install_requires = ['TracAccountManager', 'TracWebAdmin'] 
     19        install_requires = ['TracAccountManager'] 
    2020)