[REF] websrv_lib:
- grouped together the concept of auth proxy and auth provider - removed the sec_realms dictionary: it was redundant as the auth proxy was set (in one of the implementation, not all) on the handler by setupAuth. Now the auth proxy is always set on the handler. - reg_http_service accepts the argument of the HTTPDir constructor. bzr revid: vmt@openerp.com-20110908123818-suc53zebgv9kcy3c
This commit is contained in:
parent
17c54e1217
commit
e643ffcc32
|
@ -250,14 +250,14 @@ class XMLRPCRequestHandler(FixSendError,HttpLogHandler,SimpleXMLRPCServer.Simple
|
|||
def init_xmlrpc():
|
||||
if tools.config.get('xmlrpc', False):
|
||||
# Example of http file serving:
|
||||
# reg_http_service(HTTPDir('/test/',HTTPHandler))
|
||||
reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler))
|
||||
# reg_http_service('/test/', HTTPHandler)
|
||||
reg_http_service('/xmlrpc/', XMLRPCRequestHandler)
|
||||
logging.getLogger("web-services").info("Registered XML-RPC over HTTP")
|
||||
|
||||
if tools.config.get('xmlrpcs', False) \
|
||||
and not tools.config.get('xmlrpc', False):
|
||||
# only register at the secure server
|
||||
reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler, secure_only=True))
|
||||
reg_http_service('/xmlrpc/', XMLRPCRequestHandler, secure_only=True)
|
||||
logging.getLogger("web-services").info("Registered XML-RPC over HTTPS only")
|
||||
|
||||
class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler):
|
||||
|
@ -299,65 +299,21 @@ def init_static_http():
|
|||
|
||||
base_path = tools.config.get('static_http_url_prefix', '/')
|
||||
|
||||
reg_http_service(HTTPDir(base_path,StaticHTTPHandler))
|
||||
reg_http_service(base_path, StaticHTTPHandler)
|
||||
|
||||
logging.getLogger("web-services").info("Registered HTTP dir %s for %s" % \
|
||||
(document_root, base_path))
|
||||
|
||||
class OerpAuthProxy(AuthProxy):
|
||||
""" Require basic authentication..
|
||||
import security
|
||||
|
||||
This is a copy of the BasicAuthProxy, which however checks/caches the db
|
||||
as well.
|
||||
"""
|
||||
def __init__(self,provider):
|
||||
AuthProxy.__init__(self,provider)
|
||||
class OpenERPAuthProvider(AuthProvider):
|
||||
""" Require basic authentication."""
|
||||
def __init__(self,realm='OpenERP User'):
|
||||
self.realm = realm
|
||||
self.auth_creds = {}
|
||||
self.auth_tries = 0
|
||||
self.last_auth = None
|
||||
|
||||
def checkRequest(self,handler,path, db=False):
|
||||
auth_str = handler.headers.get('Authorization',False)
|
||||
try:
|
||||
if not db:
|
||||
db = handler.get_db_from_path(path)
|
||||
except Exception:
|
||||
if path.startswith('/'):
|
||||
path = path[1:]
|
||||
psp= path.split('/')
|
||||
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 self.auth_creds.get(db):
|
||||
return True
|
||||
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=\"***\" for db=\"%s\"" %(user,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)
|
||||
|
@ -371,4 +327,36 @@ class OpenERPAuthProvider(AuthProvider):
|
|||
def log(self, msg, lvl=logging.INFO):
|
||||
logging.getLogger("auth").log(lvl,msg)
|
||||
|
||||
def checkRequest(self,handler,path, db=False):
|
||||
auth_str = handler.headers.get('Authorization',False)
|
||||
try:
|
||||
if not db:
|
||||
db = handler.get_db_from_path(path)
|
||||
except Exception:
|
||||
if path.startswith('/'):
|
||||
path = path[1:]
|
||||
psp= path.split('/')
|
||||
if len(psp)>1:
|
||||
db = psp[0]
|
||||
else:
|
||||
#FIXME!
|
||||
self.log("Wrong path: %s, failing auth" %path)
|
||||
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
|
||||
if self.auth_creds.get(db):
|
||||
return True
|
||||
if auth_str and auth_str.startswith('Basic '):
|
||||
auth_str=auth_str[len('Basic '):]
|
||||
(user,passwd) = base64.decodestring(auth_str).split(':')
|
||||
self.log("Found user=\"%s\", passwd=\"***\" for db=\"%s\"" %(user,db))
|
||||
acd = self.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.log("Failing authorization after 5 requests w/o password")
|
||||
raise AuthRejectedExc("Authorization failed.")
|
||||
self.auth_tries += 1
|
||||
raise AuthRequiredExc(atype='Basic', realm=self.realm)
|
||||
|
||||
#eof
|
||||
|
|
|
@ -379,7 +379,7 @@ class common(netsvc.ExportService):
|
|||
return res or False
|
||||
elif method == 'logout':
|
||||
if auth:
|
||||
auth.logout(params[1])
|
||||
auth.logout(params[1]) # TODO I didn't see any AuthProxy implementing this method.
|
||||
logger.notifyChannel("web-service", netsvc.LOG_INFO,'Logout %s from database %s'%(login,db))
|
||||
return True
|
||||
elif method in ['about', 'timezone_get', 'get_server_environment',
|
||||
|
|
|
@ -52,61 +52,17 @@ class AuthProvider:
|
|||
def __init__(self,realm):
|
||||
self.realm = realm
|
||||
|
||||
def setupAuth(self, multi,handler):
|
||||
""" Attach an AuthProxy object to handler
|
||||
"""
|
||||
pass
|
||||
|
||||
def authenticate(self, user, passwd, client_address):
|
||||
return False
|
||||
|
||||
def log(self, msg):
|
||||
print msg
|
||||
|
||||
class BasicAuthProvider(AuthProvider):
|
||||
def setupAuth(self, multi, handler):
|
||||
if not multi.sec_realms.has_key(self.realm):
|
||||
multi.sec_realms[self.realm] = BasicAuthProxy(self)
|
||||
|
||||
|
||||
class AuthProxy:
|
||||
""" This class will hold authentication information for a handler,
|
||||
i.e. a connection
|
||||
"""
|
||||
def __init__(self, provider):
|
||||
self.provider = provider
|
||||
|
||||
def checkRequest(self,handler,path = '/'):
|
||||
""" Check if we are allowed to process that request
|
||||
"""
|
||||
pass
|
||||
|
||||
class BasicAuthProxy(AuthProxy):
|
||||
""" Require basic authentication..
|
||||
"""
|
||||
def __init__(self,provider):
|
||||
AuthProxy.__init__(self,provider)
|
||||
self.auth_creds = None
|
||||
self.auth_tries = 0
|
||||
|
||||
def checkRequest(self,handler,path = '/'):
|
||||
if self.auth_creds:
|
||||
return True
|
||||
auth_str = handler.headers.get('Authorization',False)
|
||||
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\"" %(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)
|
||||
|
||||
|
||||
class HTTPHandler(SimpleHTTPRequestHandler):
|
||||
def __init__(self,request, client_address, server):
|
||||
SimpleHTTPRequestHandler.__init__(self,request,client_address,server)
|
||||
|
@ -144,13 +100,19 @@ class HTTPDir:
|
|||
return self.path
|
||||
return False
|
||||
|
||||
def reg_http_service(service):
|
||||
""" Register some handler to httpd.
|
||||
hts must be an HTTPDir
|
||||
def instanciate_handler(self, request, client_address, server):
|
||||
handler = self.handler(noconnection(request), client_address, server)
|
||||
if self.auth_provider:
|
||||
handler.auth_provider = self.auth_provider
|
||||
return handler
|
||||
|
||||
def reg_http_service(path, handler, auth_provider=None, secure_only=False):
|
||||
""" Register a HTTP handler at a given path.
|
||||
|
||||
The auth_provider will be set on the handler instances.
|
||||
"""
|
||||
assert isinstance(service, HTTPDir), "Wrong class for http service"
|
||||
|
||||
global handlers
|
||||
service = HTTPDir(path, handler, auth_provider, secure_only)
|
||||
pos = len(handlers)
|
||||
lastpos = pos
|
||||
while pos > 0:
|
||||
|
@ -286,11 +248,10 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler):
|
|||
|
||||
def __init__(self, request, client_address, server):
|
||||
self.in_handlers = {}
|
||||
self.sec_realms = {}
|
||||
SocketServer.StreamRequestHandler.__init__(self,request,client_address,server)
|
||||
self.log_message("MultiHttpHandler init for %s" %(str(client_address)))
|
||||
|
||||
def _handle_one_foreign(self,fore, path, auth_provider):
|
||||
def _handle_one_foreign(self, fore, path):
|
||||
""" This method overrides the handle_one_request for *children*
|
||||
handlers. It is required, since the first line should not be
|
||||
read again..
|
||||
|
@ -306,9 +267,9 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler):
|
|||
return
|
||||
|
||||
self.request_version = fore.request_version
|
||||
if auth_provider and auth_provider.realm:
|
||||
if hasattr(fore, 'auth_provider'):
|
||||
try:
|
||||
self.sec_realms[auth_provider.realm].checkRequest(fore,path)
|
||||
fore.auth_provider.checkRequest(fore,path)
|
||||
except AuthRequiredExc,ae:
|
||||
# Darwin 9.x.x webdav clients will report "HTTP/1.0" to us, while they support (and need) the
|
||||
# authorisation features of HTTP/1.1
|
||||
|
@ -456,15 +417,13 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler):
|
|||
npath = '/' + npath
|
||||
|
||||
if not self.in_handlers.has_key(p):
|
||||
self.in_handlers[p] = vdir.handler(noconnection(self.request),self.client_address,self.server)
|
||||
if vdir.auth_provider:
|
||||
vdir.auth_provider.setupAuth(self, self.in_handlers[p])
|
||||
self.in_handlers[p] = vdir.instanciate_handler(noconnection(self.request),self.client_address,self.server)
|
||||
hnd = self.in_handlers[p]
|
||||
hnd.rfile = self.rfile
|
||||
hnd.wfile = self.wfile
|
||||
self.rlpath = self.raw_requestline
|
||||
try:
|
||||
self._handle_one_foreign(hnd,npath, vdir.auth_provider)
|
||||
self._handle_one_foreign(hnd, npath)
|
||||
except IOError, e:
|
||||
if e.errno == errno.EPIPE:
|
||||
self.log_message("Could not complete request %s," \
|
||||
|
|
|
@ -173,15 +173,8 @@ def http_to_wsgi(http_dir):
|
|||
server.server_port = int(environ['SERVER_PORT'])
|
||||
con = openerp.service.websrv_lib.noconnection(environ['gunicorn.socket']) # None TODO
|
||||
|
||||
# Let's pretend we have a Multi handler (i.e. the
|
||||
# websrv_lib.MultiHTTPHandler class) to hand to the auth. provider.
|
||||
multi = Dummy()
|
||||
multi.sec_realms = {}
|
||||
|
||||
# Initialize the underlying handler and associated auth. provider.
|
||||
handler = http_dir.handler(openerp.service.websrv_lib.noconnection(con), environ['REMOTE_ADDR'], server)
|
||||
auth_provider = http_dir.auth_provider
|
||||
auth_provider.setupAuth(multi, handler)
|
||||
handler = http_dir.instanciate_handler(openerp.service.websrv_lib.noconnection(con), environ['REMOTE_ADDR'], server)
|
||||
|
||||
# Populate the handler as if it is called by a regular HTTP server
|
||||
# and the request is already parsed.
|
||||
|
@ -197,9 +190,9 @@ def http_to_wsgi(http_dir):
|
|||
|
||||
# Handle authentication if there is an auth. provider associated to
|
||||
# the handler.
|
||||
if auth_provider and auth_provider.realm:
|
||||
if hasattr(handler, 'auth_provider'):
|
||||
try:
|
||||
multi.sec_realms[auth_provider.realm].checkRequest(handler, path)
|
||||
handler.auth_provider.checkRequest(handler, path)
|
||||
except websrv_lib.AuthRequiredExc, ae:
|
||||
# Darwin 9.x.x webdav clients will report "HTTP/1.0" to us, while they support (and need) the
|
||||
# authorisation features of HTTP/1.1
|
||||
|
@ -245,7 +238,7 @@ def http_to_wsgi(http_dir):
|
|||
except (websrv_lib.AuthRejectedExc, websrv_lib.AuthRequiredExc):
|
||||
raise
|
||||
except Exception, e:
|
||||
start_response("500 Internal error")
|
||||
start_response("500 Internal error", [])
|
||||
return []
|
||||
|
||||
return wsgi_handler
|
||||
|
|
Loading…
Reference in New Issue