Implement basic-authenticated services. Log messages.

bzr revid: p_christ@hol.gr-20090903210802-7bmexrzduofhhxm2
This commit is contained in:
P. Christeas 2009-09-04 00:08:02 +03:00
parent 77cf69258c
commit feca055dea
3 changed files with 116 additions and 8 deletions

View File

@ -83,6 +83,9 @@ class MultiHandler2(MultiHTTPHandler):
def log_message(self, format, *args):
netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args)
def log_error(self, format, *args):
netsvc.Logger().notifyChannel('http',netsvc.LOG_ERROR,format % args)
class SecureMultiHandler2(SecureMultiHTTPHandler):
def log_message(self, format, *args):
@ -94,6 +97,12 @@ class SecureMultiHandler2(SecureMultiHTTPHandler):
fkey = tc.get_misc('httpsd','sslkey', 'ssl/server.key')
return (fcert,fkey)
def log_message(self, format, *args):
netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args)
def log_error(self, format, *args):
netsvc.Logger().notifyChannel('http',netsvc.LOG_ERROR,format % args)
class HttpDaemon(threading.Thread, netsvc.Server):
def __init__(self, interface, port):
threading.Thread.__init__(self)
@ -196,7 +205,7 @@ def reg_http_service(hts, secure_only = False):
httpsd.server.vdirs.append(hts)
if (not httpd) and (not httpsd):
netsvc.Logger().notifyChannel('httpd',netsvc.LOG_WARNING,"No httpd available to register service %s",hts.path)
netsvc.Logger().notifyChannel('httpd',netsvc.LOG_WARNING,"No httpd available to register service %s" % hts.path)
return
import SimpleXMLRPCServer
@ -234,4 +243,77 @@ def init_xmlrpc():
# reg_http_service(HTTPDir('/test/',HTTPHandler))
netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO,
"Registered XML-RPC over HTTP")
class OerpAuthProxy(AuthProxy):
""" Require basic authentication..
This is a copy of the BasicAuthProxy, which however checks/caches the db
as well.
"""
def __init__(self,provider):
AuthProxy.__init__(self,provider)
self.auth_creds = {}
self.auth_tries = 0
self.last_auth = None
def checkRequest(self,handler,path = '/'):
if self.auth_creds:
print "found creds"
return True
auth_str = handler.headers.get('Authorization',False)
try:
print "Handler",handler
db = handler.get_db_from_path(path)
print "Got db:",db
except:
if path.startswith('/'):
path = path[1:]
psp= path.split('/')
print "Path \"%s\" split:" %path,psp
if len(psp)>1:
db = psp[0]
else:
#FIXME!
self.provider.log("Wrong path: %s, failing auth" %path)
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
if auth_str and auth_str.startswith('Basic '):
auth_str=auth_str[len('Basic '):]
(user,passwd) = base64.decodestring(auth_str).split(':')
self.provider.log("Found user=\"%s\", passwd=\"%s\" for db=\"%s\"" %(user,passwd,db))
acd = self.provider.authenticate(db,user,passwd,handler.client_address)
if acd != False:
self.auth_creds[db] = acd
self.last_auth=db
return True
if self.auth_tries > 5:
self.provider.log("Failing authorization after 5 requests w/o password")
raise AuthRejectedExc("Authorization failed.")
self.auth_tries += 1
raise AuthRequiredExc(atype = 'Basic', realm=self.provider.realm)
import security
class OpenERPAuthProvider(AuthProvider):
def __init__(self,realm = 'OpenERP User'):
self.realm = realm
def setupAuth(self, multi, handler):
if not multi.sec_realms.has_key(self.realm):
multi.sec_realms[self.realm] = OerpAuthProxy(self)
handler.auth_proxy = multi.sec_realms[self.realm]
def authenticate(self, db, user, passwd, client_address):
try:
uid = security.login(db,user,passwd)
if uid is False:
return False
return (user, passwd, db, uid)
except Exception,e:
netsvc.Logger().notifyChannel("auth",netsvc.LOG_DEBUG,"Fail auth:"+ str(e))
return False
def log(self, msg):
netsvc.Logger().notifyChannel("auth",netsvc.LOG_INFO,msg)
#eof

View File

@ -84,5 +84,6 @@ def access(db, uid, passwd, sec_level, ids):
raise ExceptionNoTb('Bad username or password')
return res[0]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -55,10 +55,10 @@ class AuthProvider:
pass
def authenticate(self, user, passwd, client_address):
#if user == 'user' and passwd == 'password':
# return (user, passwd)
#else:
return False
return False
def log(self, msg):
print msg
class BasicAuthProvider(AuthProvider):
def setupAuth(self, multi, handler):
@ -93,11 +93,12 @@ class BasicAuthProxy(AuthProxy):
if auth_str and auth_str.startswith('Basic '):
auth_str=auth_str[len('Basic '):]
(user,passwd) = base64.decodestring(auth_str).split(':')
print "Found user=\"%s\", passwd=\"%s\"" %(user,passwd)
self.provider.log("Found user=\"%s\", passwd=\"%s\"" %(user,passwd))
self.auth_creds = self.provider.authenticate(user,passwd,handler.client_address)
if self.auth_creds:
return True
if self.auth_tries > 5:
self.provider.log("Failing authorization after 5 requests w/o password")
raise AuthRejectedExc("Authorization failed.")
self.auth_tries += 1
raise AuthRequiredExc(atype = 'Basic', realm=self.provider.realm)
@ -150,6 +151,7 @@ def _quote_html(html):
return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
class FixSendError:
#error_message_format = """ """
def send_error(self, code, message=None):
#overriden from BaseHTTPRequestHandler, we also send the content-length
try:
@ -168,6 +170,7 @@ class FixSendError:
self.send_header('Connection', 'close')
self.send_header('Content-Length', len(content) or 0)
self.end_headers()
print "Error content:", content
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
self.wfile.write(content)
@ -181,6 +184,9 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
auth_required_msg = """ <html><head><title>Authorization required</title></head>
<body>You must authenticate to use this service</body><html>\r\r"""
def __init__(self, request, client_address, server):
self.in_handlers = {}
self.sec_realms = {}
@ -205,14 +211,19 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler):
self.log_error("Cannot require auth at %s",self.request_version)
self.send_error(401)
return
self._get_ignore_body(fore) # consume any body that came, not loose sync with input
self.send_response(401,'Authorization required')
self.send_header('WWW-Authenticate','%s realm="%s"' % (ae.atype,ae.realm))
print 'sending WWW-Authenticate','%s realm="%s"' % (ae.atype,ae.realm)
self.send_header('Connection', 'keep-alive')
self.send_header('Content-Type','text/html')
self.send_header('Content-Length','0')
self.send_header('Content-Length',len(self.auth_required_msg))
self.end_headers()
#self.wfile.write("\r\n")
self.wfile.write(self.auth_required_msg)
return
except AuthRejectedExc,e:
print "auth rejected!",e.args[0]
self.log_error("Rejected auth: %s" % e.args[0])
self.send_error(401,e.args[0])
self.close_connection = 1
return
@ -327,6 +338,20 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler):
self.send_error(404, "Path not found: %s" % self.path)
return
def _get_ignore_body(self,fore):
if not fore.headers.has_key("content-length"):
return
max_chunk_size = 10*1024*1024
size_remaining = int(fore.headers["content-length"])
got = ''
if size_remaining:
print "Must consume %d bytes",size_remaining
while size_remaining:
chunk_size = min(size_remaining, max_chunk_size)
got = fore.rfile.read(chunk_size)
print "c:",got
size_remaining -= len(got)
class SecureMultiHTTPHandler(MultiHTTPHandler):
def getcert_fnames(self):