Package buildbot :: Package status :: Package web :: Module auth
[frames] | no frames]

Source Code for Module buildbot.status.web.auth

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16   
 17  import os 
 18  from zope.interface import Interface, Attribute, implements 
 19  from buildbot.status.web.base import HtmlResource, ActionResource 
 20  from buildbot.status.web.base import path_to_authfail 
 21   
 22  from buildbot.process.users import users 
 23   
24 -class IAuth(Interface):
25 """ 26 Represent an authentication method. 27 28 Note that each IAuth instance contains a link to the BuildMaster that 29 will be set once the IAuth instance is initialized. 30 """ 31 32 master = Attribute('master', "Link to BuildMaster, set when initialized") 33
34 - def authenticate(self, user, passwd):
35 """Check whether C{user} / C{passwd} are valid."""
36
37 - def getUserInfo(self, user):
38 """return dict with user info. 39 dict( fullName="", email="", groups=[]) 40 """
41
42 - def errmsg(self):
43 """Get the reason authentication failed."""
44
45 -class AuthBase:
46 master = None # set in status.web.baseweb 47 err = "" 48
49 - def errmsg(self):
50 return self.err
51
52 - def getUserInfo(self, user):
53 """default dummy impl""" 54 return dict(userName=user, fullName=user, email=user+"@localhost", groups=[ user ])
55
56 -class BasicAuth(AuthBase):
57 implements(IAuth) 58 """Implement basic authentication against a list of user/passwd.""" 59 60 userpass = [] 61 """List of user/pass tuples.""" 62
63 - def __init__(self, userpass):
64 """C{userpass} is a list of (user, passwd).""" 65 for item in userpass: 66 assert isinstance(item, tuple) or isinstance(item, list) 67 u, p = item 68 assert isinstance(u, str) 69 assert isinstance(p, str) 70 self.userpass = userpass
71
72 - def authenticate(self, user, passwd):
73 """Check that C{user}/C{passwd} is a valid user/pass tuple.""" 74 if not self.userpass: 75 self.err = "Bad self.userpass data" 76 return False 77 for u, p in self.userpass: 78 if user == u and passwd == p: 79 self.err = "" 80 return True 81 self.err = "Invalid username or password" 82 return False
83
84 -class HTPasswdAuth(AuthBase):
85 implements(IAuth) 86 """Implement authentication against an .htpasswd file.""" 87 88 file = "" 89 """Path to the .htpasswd file to use.""" 90
91 - def __init__(self, file):
92 """C{file} is a path to an .htpasswd file.""" 93 assert os.path.exists(file) 94 self.file = file
95
96 - def authenticate(self, user, passwd):
97 """Authenticate C{user} and C{passwd} against an .htpasswd file""" 98 if not os.path.exists(self.file): 99 self.err = "No such file: " + self.file 100 return False 101 # Fetch each line from the .htpasswd file and split it into a 102 # [user, passwd] array. 103 lines = [l.rstrip().split(':', 1) 104 for l in file(self.file).readlines()] 105 # Keep only the line for this login 106 lines = [l for l in lines if l[0] == user] 107 if not lines: 108 self.err = "Invalid user/passwd" 109 return False 110 hash = lines[0][1] 111 res = self.validatePassword(passwd, hash) 112 if res: 113 self.err = "" 114 else: 115 self.err = "Invalid user/passwd" 116 return res
117
118 - def validatePassword(self, passwd, hash):
119 # This is the DES-hash of the password. The first two characters are 120 # the salt used to introduce disorder in the DES algorithm. 121 from crypt import crypt #@UnresolvedImport 122 return hash == crypt(passwd, hash[0:2])
123 124
125 -class HTPasswdAprAuth(HTPasswdAuth):
126 implements(IAuth) 127 """Implement authentication against an .htpasswd file based on libaprutil""" 128 129 file = "" 130 """Path to the .htpasswd file to use.""" 131
132 - def __init__(self, file):
133 HTPasswdAuth.__init__(self, file) 134 135 # Try to load libaprutil throug ctypes 136 self.apr = None 137 try: 138 from ctypes import CDLL 139 from ctypes.util import find_library 140 lib = find_library("aprutil-1") 141 if lib: 142 self.apr = CDLL(lib) 143 except: 144 self.apr = None
145
146 - def validatePassword(self, passwd, hash):
147 # Use apr_password_validate from libaprutil if libaprutil is available. 148 # Fallback to DES only checking from HTPasswdAuth 149 if self.apr: 150 return self.apr.apr_password_validate(passwd, hash) == 0 151 else: 152 return HTPasswdAuth.validatePassword(self, passwd, hash)
153
154 -class UsersAuth(AuthBase):
155 """Implement authentication against users in database""" 156 implements(IAuth) 157
158 - def authenticate(self, user, passwd):
159 """ 160 It checks for a matching uid in the database for the credentials 161 and return True if a match is found, False otherwise. 162 163 @param user: username portion of user credentials 164 @type user: string 165 166 @param passwd: password portion of user credentials 167 @type passwd: string 168 169 @returns: boolean via deferred. 170 """ 171 d = self.master.db.users.getUserByUsername(user) 172 def check_creds(user): 173 if user: 174 if users.check_passwd(passwd, user['bb_password']): 175 return True 176 self.err = "no user found with those credentials" 177 return False
178 d.addCallback(check_creds) 179 return d
180
181 -class AuthFailResource(HtmlResource):
182 pageTitle = "Authentication Failed" 183
184 - def content(self, request, cxt):
185 templates =request.site.buildbot_service.templates 186 template = templates.get_template("authfail.html") 187 return template.render(**cxt)
188
189 -class AuthzFailResource(HtmlResource):
190 pageTitle = "Authorization Failed" 191
192 - def content(self, request, cxt):
193 templates =request.site.buildbot_service.templates 194 template = templates.get_template("authzfail.html") 195 return template.render(**cxt)
196
197 -class LoginResource(ActionResource):
198
199 - def performAction(self, request):
200 authz = self.getAuthz(request) 201 d = authz.login(request) 202 def on_login(res): 203 if res: 204 status = request.site.buildbot_service.master.status 205 root = status.getBuildbotURL() 206 return request.requestHeaders.getRawHeaders('referer', 207 [root])[0] 208 else: 209 return path_to_authfail(request)
210 d.addBoth(on_login) 211 return d
212
213 -class LogoutResource(ActionResource):
214
215 - def performAction(self, request):
216 authz = self.getAuthz(request) 217 authz.logout(request) 218 status = request.site.buildbot_service.master.status 219 root = status.getBuildbotURL() 220 return request.requestHeaders.getRawHeaders('referer',[root])[0]
221