Implement basic-authenticated services. Log messages.
bzr revid: p_christ@hol.gr-20090903210802-7bmexrzduofhhxm2
This commit is contained in:
parent
77cf69258c
commit
feca055dea
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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("&", "&").replace("<", "<").replace(">", ">")
|
||||
|
||||
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):
|
||||
|
|
Loading…
Reference in New Issue