diff --git a/openerp/service/http_server.py b/openerp/service/http_server.py index 8bf40024282..9bfb9397d19 100644 --- a/openerp/service/http_server.py +++ b/openerp/service/http_server.py @@ -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 diff --git a/openerp/service/web_services.py b/openerp/service/web_services.py index 8d05b5b776d..c941d3a809f 100644 --- a/openerp/service/web_services.py +++ b/openerp/service/web_services.py @@ -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', diff --git a/openerp/service/websrv_lib.py b/openerp/service/websrv_lib.py index 5b98714e80e..ac83cb893c0 100644 --- a/openerp/service/websrv_lib.py +++ b/openerp/service/websrv_lib.py @@ -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," \ diff --git a/openerp/wsgi.py b/openerp/wsgi.py index ad36eb14c26..3519cdf4090 100644 --- a/openerp/wsgi.py +++ b/openerp/wsgi.py @@ -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