[IMP] openerp/service/server.py: lint file
[odoo/odoo.git] / openerp / service / http_server.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright P. Christeas <p_christ@hol.gr> 2008-2010
4 # Copyright 2010 OpenERP SA. (http://www.openerp.com)
5 #
6 #
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsibility of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # guarantees and support are strongly advised to contract a Free Software
12 # Service Company
13 #
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27 ###############################################################################
28
29
30 """ This module offers the family of HTTP-based servers. These are not a single
31     class/functionality, but a set of network stack layers, implementing
32     extendable HTTP protocols.
33
34     The OpenERP server defines a single instance of a HTTP server, listening at
35     the standard 8069, 8071 ports (well, it is 2 servers, and ports are 
36     configurable, of course). This "single" server then uses a `MultiHTTPHandler`
37     to dispatch requests to the appropriate channel protocol, like the XML-RPC,
38     static HTTP, DAV or other.
39 """
40
41 import base64
42 import posixpath
43 import urllib
44 import os
45 import logging
46
47 from websrv_lib import *
48 import openerp.tools as tools
49
50 try:
51     import fcntl
52 except ImportError:
53     fcntl = None
54
55 try:
56     from ssl import SSLError
57 except ImportError:
58     class SSLError(Exception): pass
59
60 _logger = logging.getLogger(__name__)
61
62 # TODO delete this for 6.2, it is still needed for 6.1.
63 class HttpLogHandler:
64     """ helper class for uniform log handling
65     Please define self._logger at each class that is derived from this
66     """
67     _logger = None
68     
69     def log_message(self, format, *args):
70         self._logger.debug(format % args) # todo: perhaps other level
71
72     def log_error(self, format, *args):
73         self._logger.error(format % args)
74         
75     def log_exception(self, format, *args):
76         self._logger.exception(format, *args)
77
78     def log_request(self, code='-', size='-'):
79         self._logger.debug('"%s" %s %s',
80             self.requestline, str(code), str(size))
81
82 class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler):
83     _logger = logging.getLogger(__name__)
84
85     _HTTP_OPTIONS = { 'Allow': ['OPTIONS', 'GET', 'HEAD'] }
86
87     def __init__(self,request, client_address, server):
88         HTTPHandler.__init__(self,request,client_address,server)
89         document_root = tools.config.get('static_http_document_root', False)
90         assert document_root, "Please specify static_http_document_root in configuration, or disable static-httpd!"
91         self.__basepath = document_root
92
93     def translate_path(self, path):
94         """Translate a /-separated PATH to the local filename syntax.
95
96         Components that mean special things to the local file system
97         (e.g. drive or directory names) are ignored.  (XXX They should
98         probably be diagnosed.)
99
100         """
101         # abandon query parameters
102         path = path.split('?',1)[0]
103         path = path.split('#',1)[0]
104         path = posixpath.normpath(urllib.unquote(path))
105         words = path.split('/')
106         words = filter(None, words)
107         path = self.__basepath
108         for word in words:
109             if word in (os.curdir, os.pardir): continue
110             path = os.path.join(path, word)
111         return path
112
113 def init_static_http():
114     if not tools.config.get('static_http_enable', False):
115         return
116     
117     document_root = tools.config.get('static_http_document_root', False)
118     assert document_root, "Document root must be specified explicitly to enable static HTTP service (option --static-http-document-root)"
119     
120     base_path = tools.config.get('static_http_url_prefix', '/')
121     
122     reg_http_service(base_path, StaticHTTPHandler)
123     
124     _logger.info("Registered HTTP dir %s for %s", document_root, base_path)
125
126 import security
127
128 class OpenERPAuthProvider(AuthProvider):
129     """ Require basic authentication."""
130     def __init__(self,realm='OpenERP User'):
131         self.realm = realm
132         self.auth_creds = {}
133         self.auth_tries = 0
134         self.last_auth = None
135
136     def authenticate(self, db, user, passwd, client_address):
137         try:
138             uid = security.login(db,user,passwd)
139             if uid is False:
140                 return False
141             return user, passwd, db, uid
142         except Exception,e:
143             _logger.debug("Fail auth: %s" % e )
144             return False
145
146     def checkRequest(self,handler,path, db=False):        
147         auth_str = handler.headers.get('Authorization',False)
148         try:
149             if not db:
150                 db = handler.get_db_from_path(path)
151         except Exception:
152             if path.startswith('/'):
153                 path = path[1:]
154             psp= path.split('/')
155             if len(psp)>1:
156                 db = psp[0]
157             else:
158                 #FIXME!
159                 _logger.info("Wrong path: %s, failing auth" %path)
160                 raise AuthRejectedExc("Authorization failed. Wrong sub-path.") 
161         if self.auth_creds.get(db):
162             return True 
163         if auth_str and auth_str.startswith('Basic '):
164             auth_str=auth_str[len('Basic '):]
165             (user,passwd) = base64.decodestring(auth_str).split(':')
166             _logger.info("Found user=\"%s\", passwd=\"***\" for db=\"%s\"", user, db)
167             acd = self.authenticate(db,user,passwd,handler.client_address)
168             if acd != False:
169                 self.auth_creds[db] = acd
170                 self.last_auth = db
171                 return True
172         if self.auth_tries > 5:
173             _logger.info("Failing authorization after 5 requests w/o password")
174             raise AuthRejectedExc("Authorization failed.")
175         self.auth_tries += 1
176         raise AuthRequiredExc(atype='Basic', realm=self.realm)
177
178 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: