diff options
author | Benjamin W. Smith <benjaminwarfield@just-another.net> | 2009-10-19 11:08:44 -0400 |
---|---|---|
committer | Benjamin W. Smith <benjaminwarfield@just-another.net> | 2009-10-19 11:08:44 -0400 |
commit | 00edbd67c326a4c71c882f409c8459e5a2df13fa (patch) | |
tree | b06b69369ef51e179ed037e32732ceea157b2b7f | |
parent | e179fec1f865ede349873a436d5eb2ed4e98da9b (diff) | |
download | pyapache-master.tar.bz2 pyapache-master.tar.xz pyapache-master.zip |
htdigest/htpasswd setup.
-rwxr-xr-x[-rw-r--r--] | __init__.py | 0 | ||||
-rwxr-xr-x | dbm.py | 60 | ||||
-rwxr-xr-x[-rw-r--r--] | htpasswd.py | 6 | ||||
-rwxr-xr-x | md5.py | 25 | ||||
-rwxr-xr-x[-rw-r--r--] | password.py | 5 |
5 files changed, 83 insertions, 13 deletions
diff --git a/__init__.py b/__init__.py index e69de29..e69de29 100644..100755 --- a/__init__.py +++ b/__init__.py | |||
@@ -0,0 +1,60 @@ | |||
1 | #!/usr/bin/env python | ||
2 | """ | ||
3 | Utilities to manage a hash db of users for apache | ||
4 | """ | ||
5 | import anydbm | ||
6 | import htpasswd | ||
7 | __all__ = ["add_user", "delete_user", "update_user", | ||
8 | "get_user", "list_users", "raw_add"] | ||
9 | |||
10 | |||
11 | def get_user(user, db): | ||
12 | """ Get user info from the DB """ | ||
13 | db_dict = anydbm.open(db, 'r') | ||
14 | ret_user = "%s:%s" %(user, db_dict[user]) | ||
15 | db_dict.close() | ||
16 | return ret_user | ||
17 | |||
18 | |||
19 | def list_users(db): | ||
20 | """ List all the users in the DB """ | ||
21 | db_dict = anydbm.open(db, 'r') | ||
22 | ret_str = '\n'.join(["%s:%s" %(k, v) for k, v in db_dict.iteritems()]) | ||
23 | db_dict.close() | ||
24 | return ret_str | ||
25 | |||
26 | |||
27 | def add_user(user, passwd, db): | ||
28 | """ Add user to the DB, creating if need be """ | ||
29 | db_dict = anydbm.open(db, 'c') | ||
30 | db_dict[user] = htpasswd.hash_password(passwd) | ||
31 | db_dict.close() | ||
32 | return True | ||
33 | |||
34 | |||
35 | def raw_add(line, db): | ||
36 | """ Add raw line to the DB """ | ||
37 | db_dict = anydbm.open(db, 'w') | ||
38 | user, passwd = line.split(':') | ||
39 | db_dict[user] = passwd | ||
40 | db_dict.close() | ||
41 | return True | ||
42 | |||
43 | |||
44 | def delete_user(user, db): | ||
45 | """ Remove user from the DB """ | ||
46 | db_dict = anydbm.open(db, 'w') | ||
47 | del db_dict[user] | ||
48 | db_dict.close() | ||
49 | return True | ||
50 | |||
51 | |||
52 | def update_user(user, passwd, db): | ||
53 | """ Change the users pass in the DB """ | ||
54 | db_dict = anydbm.open(db, 'w') | ||
55 | if user in db_dict: | ||
56 | db_dict[user] = htpasswd.hash_password(passwd) | ||
57 | db_dict.close() | ||
58 | return True | ||
59 | else: | ||
60 | return False | ||
diff --git a/htpasswd.py b/htpasswd.py index 3641c34..0b79445 100644..100755 --- a/htpasswd.py +++ b/htpasswd.py | |||
@@ -8,7 +8,8 @@ Released under the terms of the BSD license. | |||
8 | A collection of classes and functions to manipulate apache htaccess files. | 8 | A collection of classes and functions to manipulate apache htaccess files. |
9 | """ | 9 | """ |
10 | 10 | ||
11 | __all__ = [ "generate_user" ] | 11 | __all__ = ["generate_user"] |
12 | |||
12 | 13 | ||
13 | def hash_password(passwd, ctype="crypt"): | 14 | def hash_password(passwd, ctype="crypt"): |
14 | """Create an Apache-style password hash. | 15 | """Create an Apache-style password hash. |
@@ -25,10 +26,11 @@ def hash_password(passwd, ctype="crypt"): | |||
25 | elif ctype is "md5": | 26 | elif ctype is "md5": |
26 | from apachelib.password import md5_password | 27 | from apachelib.password import md5_password |
27 | return md5_password(passwd) | 28 | return md5_password(passwd) |
28 | 29 | ||
29 | # We should never get here | 30 | # We should never get here |
30 | raise ValueError("%s is not a valid value for ctype." % ctype) | 31 | raise ValueError("%s is not a valid value for ctype." % ctype) |
31 | 32 | ||
33 | |||
32 | def generate_user(username, passwd, ctype="crypt"): | 34 | def generate_user(username, passwd, ctype="crypt"): |
33 | """Generate a single htaccess line. | 35 | """Generate a single htaccess line. |
34 | """ | 36 | """ |
@@ -12,7 +12,7 @@ little bit wierd. | |||
12 | 12 | ||
13 | This is a pythonic adaption of the C code in the APR library. | 13 | This is a pythonic adaption of the C code in the APR library. |
14 | If there are any questions please refer directly to the C code. | 14 | If there are any questions please refer directly to the C code. |
15 | You'll find it in apache subversion under | 15 | You'll find it in apache subversion under |
16 | /ap/apr-util/crypto/apr_md5.c | 16 | /ap/apr-util/crypto/apr_md5.c |
17 | """ | 17 | """ |
18 | 18 | ||
@@ -21,11 +21,12 @@ from hashlib import md5 | |||
21 | from random import random | 21 | from random import random |
22 | from math import floor | 22 | from math import floor |
23 | 23 | ||
24 | __all__ = [ "generate_md5", "generate_salt", "generate_short_salt" ] | 24 | __all__ = ["generate_md5", "generate_salt", "generate_short_salt"] |
25 | 25 | ||
26 | # Defined as such for brevity | 26 | # Defined as such for brevity |
27 | md5digest = lambda x: md5(x).digest() | 27 | md5digest = lambda x: md5(x).digest() |
28 | 28 | ||
29 | |||
29 | def ap_to64(input, count=4): | 30 | def ap_to64(input, count=4): |
30 | """Weird-ass implementation of base64 conversion used by the | 31 | """Weird-ass implementation of base64 conversion used by the |
31 | APR library. | 32 | APR library. |
@@ -33,19 +34,21 @@ def ap_to64(input, count=4): | |||
33 | chars = "./0123456789%s%s" % (string.uppercase, string.lowercase) | 34 | chars = "./0123456789%s%s" % (string.uppercase, string.lowercase) |
34 | output = "" | 35 | output = "" |
35 | input = int(input) # Need ints to do binary math | 36 | input = int(input) # Need ints to do binary math |
36 | 37 | ||
37 | for i in range(0, count): | 38 | for i in range(0, count): |
38 | output += chars[input & 0x3f] # Take 6 bits right | 39 | output += chars[input & 0x3f] # Take 6 bits right |
39 | input >>= 6 # shift by 6 bits | 40 | input >>= 6 # shift by 6 bits |
40 | 41 | ||
41 | return output | 42 | return output |
42 | 43 | ||
44 | |||
43 | def generate_short_salt(): | 45 | def generate_short_salt(): |
44 | """Generate a short, 2-character salt. | 46 | """Generate a short, 2-character salt. |
45 | This is suitable for use in the htpasswd crypt routine. | 47 | This is suitable for use in the htpasswd crypt routine. |
46 | """ | 48 | """ |
47 | return generate_salt()[0:2] | 49 | return generate_salt()[0:2] |
48 | 50 | ||
51 | |||
49 | def generate_salt(): | 52 | def generate_salt(): |
50 | """Mine a little salt for your passwords. | 53 | """Mine a little salt for your passwords. |
51 | Returns 8 random characters in base64. It is 4 + 4 because the original | 54 | Returns 8 random characters in base64. It is 4 + 4 because the original |
@@ -55,6 +58,7 @@ def generate_salt(): | |||
55 | salt += ap_to64(floor(random() * 16777215)) | 58 | salt += ap_to64(floor(random() * 16777215)) |
56 | return salt | 59 | return salt |
57 | 60 | ||
61 | |||
58 | def generate_md5(passwd, salt=None): | 62 | def generate_md5(passwd, salt=None): |
59 | """Generate an APRfied MD5 hash. | 63 | """Generate an APRfied MD5 hash. |
60 | This was adapted directly from the C code in the APR library. | 64 | This was adapted directly from the C code in the APR library. |
@@ -66,10 +70,10 @@ def generate_md5(passwd, salt=None): | |||
66 | # I don't think it is really "checked" by Apache but I'm too | 70 | # I don't think it is really "checked" by Apache but I'm too |
67 | # lazy to confirm this. | 71 | # lazy to confirm this. |
68 | MAGIC_TOKEN = "$apr1$" | 72 | MAGIC_TOKEN = "$apr1$" |
69 | 73 | ||
70 | # Mainly just used for testing but why not leave it? | 74 | # Mainly just used for testing but why not leave it? |
71 | salt = salt if salt else generate_salt() | 75 | salt = salt if salt else generate_salt() |
72 | 76 | ||
73 | # Start with our password in the clear, a little magic and | 77 | # Start with our password in the clear, a little magic and |
74 | # a pinch of salt | 78 | # a pinch of salt |
75 | message = "%s%s%s" % (passwd, MAGIC_TOKEN, salt) | 79 | message = "%s%s%s" % (passwd, MAGIC_TOKEN, salt) |
@@ -89,21 +93,21 @@ def generate_md5(passwd, salt=None): | |||
89 | message += chr(0) | 93 | message += chr(0) |
90 | else: | 94 | else: |
91 | message += passwd[0] | 95 | message += passwd[0] |
92 | passlen >>= 1 | 96 | passlen >>= 1 |
93 | 97 | ||
94 | retval = md5digest(message) | 98 | retval = md5digest(message) |
95 | 99 | ||
96 | for i in range(0, 1000): | 100 | for i in range(0, 1000): |
97 | if i & 1: | 101 | if i & 1: |
98 | message = passwd | 102 | message = passwd |
99 | else: | 103 | else: |
100 | message = retval[0:16] | 104 | message = retval[0:16] |
101 | 105 | ||
102 | if i % 3: | 106 | if i % 3: |
103 | message += salt | 107 | message += salt |
104 | 108 | ||
105 | if i % 7: | 109 | if i % 7: |
106 | message += passwd | 110 | message += passwd |
107 | 111 | ||
108 | if i & 1: | 112 | if i & 1: |
109 | message += retval[0:16] | 113 | message += retval[0:16] |
@@ -111,7 +115,7 @@ def generate_md5(passwd, salt=None): | |||
111 | message += passwd | 115 | message += passwd |
112 | 116 | ||
113 | retval = md5digest(message) | 117 | retval = md5digest(message) |
114 | 118 | ||
115 | # Now make the output string | 119 | # Now make the output string |
116 | output = ap_to64((ord(retval[0]) << 16) | (ord(retval[6]) << 8) | ord(retval[12])) | 120 | output = ap_to64((ord(retval[0]) << 16) | (ord(retval[6]) << 8) | ord(retval[12])) |
117 | output += ap_to64((ord(retval[1]) << 16) | (ord(retval[7]) << 8) | ord(retval[13])) | 121 | output += ap_to64((ord(retval[1]) << 16) | (ord(retval[7]) << 8) | ord(retval[13])) |
@@ -122,6 +126,7 @@ def generate_md5(passwd, salt=None): | |||
122 | 126 | ||
123 | return "%s%s$%s" % (MAGIC_TOKEN, salt, output) | 127 | return "%s%s$%s" % (MAGIC_TOKEN, salt, output) |
124 | 128 | ||
129 | |||
125 | def test_driver(): | 130 | def test_driver(): |
126 | """Run this to verify that the library is functioning properly. | 131 | """Run this to verify that the library is functioning properly. |
127 | This one test case should be enough to verify the functionality. | 132 | This one test case should be enough to verify the functionality. |
diff --git a/password.py b/password.py index 3570cf4..fe861e0 100644..100755 --- a/password.py +++ b/password.py | |||
@@ -10,6 +10,7 @@ Algorithm information was collected for various sources around the web | |||
10 | and from analysis of the APR C code. | 10 | and from analysis of the APR C code. |
11 | """ | 11 | """ |
12 | 12 | ||
13 | |||
13 | def crypt_password(passwd): | 14 | def crypt_password(passwd): |
14 | """Generate Apache-style CRYPT password hash. | 15 | """Generate Apache-style CRYPT password hash. |
15 | """ | 16 | """ |
@@ -17,6 +18,7 @@ def crypt_password(passwd): | |||
17 | from apachelib.md5 import generate_short_salt | 18 | from apachelib.md5 import generate_short_salt |
18 | return crypt(passwd, generate_short_salt()) | 19 | return crypt(passwd, generate_short_salt()) |
19 | 20 | ||
21 | |||
20 | def sha_password(passwd): | 22 | def sha_password(passwd): |
21 | """Generate Apache-style SHA1 password hash. | 23 | """Generate Apache-style SHA1 password hash. |
22 | """ | 24 | """ |
@@ -24,8 +26,9 @@ def sha_password(passwd): | |||
24 | from base64 import b64encode | 26 | from base64 import b64encode |
25 | return "{SHA}%s" % b64encode(sha1(passwd).digest()) | 27 | return "{SHA}%s" % b64encode(sha1(passwd).digest()) |
26 | 28 | ||
29 | |||
27 | def md5_password(passwd): | 30 | def md5_password(passwd): |
28 | """Generate Apache-style MD5 password hash. | 31 | """Generate Apache-style MD5 password hash. |
29 | """ | 32 | """ |
30 | from apachelib.md5 import generate_md5 | 33 | from apachelib.md5 import generate_md5 |
31 | return generate_md5(passwd) | 34 | return generate_md5(passwd) |