| OLD | NEW |
| (Empty) |
| 1 | |
| 2 import os | |
| 3 from zope.interface import Interface, implements | |
| 4 from buildbot.status.web.base import HtmlResource | |
| 5 | |
| 6 class IAuth(Interface): | |
| 7 """Represent an authentication method.""" | |
| 8 | |
| 9 def authenticate(self, user, passwd): | |
| 10 """Check whether C{user} / C{passwd} are valid.""" | |
| 11 | |
| 12 def errmsg(self): | |
| 13 """Get the reason authentication failed.""" | |
| 14 | |
| 15 class AuthBase: | |
| 16 err = "" | |
| 17 | |
| 18 def errmsg(self): | |
| 19 return self.err | |
| 20 | |
| 21 class BasicAuth(AuthBase): | |
| 22 implements(IAuth) | |
| 23 """Implement basic authentication against a list of user/passwd.""" | |
| 24 | |
| 25 userpass = [] | |
| 26 """List of user/pass tuples.""" | |
| 27 | |
| 28 def __init__(self, userpass): | |
| 29 """C{userpass} is a list of (user, passwd).""" | |
| 30 for item in userpass: | |
| 31 assert isinstance(item, tuple) | |
| 32 u, p = item | |
| 33 assert isinstance(u, str) | |
| 34 assert isinstance(p, str) | |
| 35 self.userpass = userpass | |
| 36 | |
| 37 def authenticate(self, user, passwd): | |
| 38 """Check that C{user}/C{passwd} is a valid user/pass tuple.""" | |
| 39 if not self.userpass: | |
| 40 self.err = "Bad self.userpass data" | |
| 41 return False | |
| 42 for u, p in self.userpass: | |
| 43 if user == u and passwd == p: | |
| 44 self.err = "" | |
| 45 return True | |
| 46 self.err = "Invalid username or password" | |
| 47 return False | |
| 48 | |
| 49 class HTPasswdAuth(AuthBase): | |
| 50 implements(IAuth) | |
| 51 """Implement authentication against an .htpasswd file.""" | |
| 52 | |
| 53 file = "" | |
| 54 """Path to the .htpasswd file to use.""" | |
| 55 | |
| 56 def __init__(self, file): | |
| 57 """C{file} is a path to an .htpasswd file.""" | |
| 58 assert os.path.exists(file) | |
| 59 self.file = file | |
| 60 | |
| 61 def authenticate(self, user, passwd): | |
| 62 """Authenticate C{user} and C{passwd} against an .htpasswd file""" | |
| 63 if not os.path.exists(self.file): | |
| 64 self.err = "No such file: " + self.file | |
| 65 return False | |
| 66 # Fetch each line from the .htpasswd file and split it into a | |
| 67 # [user, passwd] array. | |
| 68 lines = [l.rstrip().split(':', 1) | |
| 69 for l in file(self.file).readlines()] | |
| 70 # Keep only the line for this login | |
| 71 lines = [l for l in lines if l[0] == user] | |
| 72 if not lines: | |
| 73 self.err = "Invalid user/passwd" | |
| 74 return False | |
| 75 # This is the DES-hash of the password. The first two characters are | |
| 76 # the salt used to introduce disorder in the DES algorithm. | |
| 77 hash = lines[0][1] | |
| 78 from crypt import crypt | |
| 79 res = hash == crypt(passwd, hash[0:2]) | |
| 80 if res: | |
| 81 self.err = "" | |
| 82 else: | |
| 83 self.err = "Invalid user/passwd" | |
| 84 return res | |
| 85 | |
| 86 class AuthFailResource(HtmlResource): | |
| 87 title = "Authentication Failed" | |
| 88 | |
| 89 def body(self, request): | |
| 90 data = '' | |
| 91 data += '<h1>Authentication Failed</h1>\n' | |
| 92 data += '<p>The username or password you entered were not correct. Plea
se go back and try again.</p>\n' | |
| 93 | |
| 94 return data | |
| 95 | |
| OLD | NEW |