root/accountmanagerplugin/0.10/acct_mgr/pwhash.py

Revision 2120, 3.0 kB (checked in by mgood, 2 years ago)

AccountManagerPlugin:

add a SessionStore class for storing user passwords as a session attribute

Line 
1 # -*- coding: utf8 -*-
2 #
3 # Copyright (C) 2007 Matthew Good <trac@matt-good.net>
4 #
5 # "THE BEER-WARE LICENSE" (Revision 42):
6 # <trac@matt-good.net> wrote this file.  As long as you retain this notice you
7 # can do whatever you want with this stuff. If we meet some day, and you think
8 # this stuff is worth it, you can buy me a beer in return.   Matthew Good
9 #
10 # Author: Matthew Good <trac@matt-good.net>
11
12 from binascii import hexlify
13 import md5, sha
14
15 from trac.core import *
16 from trac.config import Option
17
18 from md5crypt import md5crypt
19
20 class IPasswordHashMethod(Interface):
21     def generate_hash(user, password):
22         pass
23
24     def test_hash(user, password, hash):
25         pass
26
27
28 class HtPasswdHashMethod(Component):
29     implements(IPasswordHashMethod)
30
31     def generate_hash(self, user, password):
32         password = password.encode('utf-8')
33         return htpasswd(password)
34
35     def check_hash(self, user, password, hash):
36         password = password.encode('utf-8')
37         return hash == htpasswd(password, hash)
38
39
40 class HtDigestHashMethod(Component):
41     implements(IPasswordHashMethod)
42
43     realm = Option('account-manager', 'htdigest_realm')
44
45     def generate_hash(self, user, password):
46         user,password,realm = _encode(user, password, self.realm)
47         return ':'.join([realm, htdigest(user, realm, password)])
48
49     def check_hash(self, user, password, hash):
50         user,password,realm = _encode(user, password, self.realm)
51         return hash == self.generate_hash(user, password)
52
53
54 def _encode(*args):
55     return [a.encode('utf-8') for a in args]
56
57 # check for the availability of the "crypt" module for checking passwords on
58 # Unix-like platforms
59 # MD5 is still used when adding/updating passwords
60 try:
61     from crypt import crypt
62 except ImportError:
63     crypt = None
64
65 # os.urandom was added in Python 2.4
66 # try to fall back on reading from /dev/urandom on older Python versions
67 try:
68     from os import urandom
69 except ImportError:
70     from random import randrange
71     def urandom(n):
72         return ''.join([chr(randrange(256)) for _ in xrange(n)])
73
74 def salt():
75     s = ''
76     v = long(hexlify(urandom(4)), 16)
77     itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
78     for i in range(8):
79         s += itoa64[v & 0x3f]; v >>= 6
80     return s
81
82 def htpasswd(password, salt_=None):
83 # TODO need unit test of generating new hash
84     if salt_ is None:
85         salt_ = salt()
86         if crypt is None:
87             salt_ = '$apr1$' + salt_
88     if salt_.startswith('$apr1$'):
89         return md5crypt(password, salt_[6:].split('$')[0], '$apr1$')
90     elif salt_.startswith('{SHA}'):
91         return '{SHA}' + sha.new(password).digest().encode('base64')[:-1]
92     elif crypt is None:
93         # crypt passwords are only supported on Unix-like systems
94         raise NotImplementedError('The "crypt" module is unavailable '
95                                   'on this platform.')
96     else:
97         return crypt(password, salt_)
98
99 def htdigest(user, realm, password):
100     p = ':'.join([user, realm, password])
101     return md5.new(p).hexdigest()
Note: See TracBrowser for help on using the browser.