ba83b153d6e6552be35f0519f2bddecd4c36c6c6
[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 responsability 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 # garantees and support are strongly adviced 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27 ###############################################################################
28
29 #.apidoc title: HTTP and XML-RPC Server
30
31 """ This module offers the family of HTTP-based servers. These are not a single
32     class/functionality, but a set of network stack layers, implementing
33     extendable HTTP protocols.
34
35     The OpenERP server defines a single instance of a HTTP server, listening at
36     the standard 8069, 8071 ports (well, it is 2 servers, and ports are 
37     configurable, of course). This "single" server then uses a `MultiHTTPHandler`
38     to dispatch requests to the appropriate channel protocol, like the XML-RPC,
39     static HTTP, DAV or other.
40 """
41
42 import base64
43 import posixpath
44 import urllib
45 import os
46 import logging
47 from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
48
49 from websrv_lib import *
50 import openerp.netsvc as netsvc
51 import openerp.tools as tools
52
53 try:
54     import fcntl
55 except ImportError:
56     fcntl = None
57
58 try:
59     from ssl import SSLError
60 except ImportError:
61     class SSLError(Exception): pass
62
63 _logger = logging.getLogger(__name__)
64
65 class StaticHTTPHandler(FixSendError, HttpOptions, HTTPHandler):
66
67     _HTTP_OPTIONS = { 'Allow': ['OPTIONS', 'GET', 'HEAD'] }
68
69     def __init__(self,request, client_address, server):
70         HTTPHandler.__init__(self,request,client_address,server)
71         document_root = tools.config.get('static_http_document_root', False)
72         assert document_root, "Please specify static_http_document_root in configuration, or disable static-httpd!"
73         self.__basepath = document_root
74
75     def translate_path(self, path):
76         """Translate a /-separated PATH to the local filename syntax.
77
78         Components that mean special things to the local file system
79         (e.g. drive or directory names) are ignored.  (XXX They should
80         probably be diagnosed.)
81
82         """
83         # abandon query parameters
84         path = path.split('?',1)[0]
85         path = path.split('#',1)[0]
86         path = posixpath.normpath(urllib.unquote(path))
87         words = path.split('/')
88         words = filter(None, words)
89         path = self.__basepath
90         for word in words:
91             if word in (os.curdir, os.pardir): continue
92             path = os.path.join(path, word)
93         return path
94
95 def init_static_http():
96     if not tools.config.get('static_http_enable', False):
97         return
98     
99     document_root = tools.config.get('static_http_document_root', False)
100     assert document_root, "Document root must be specified explicitly to enable static HTTP service (option --static-http-document-root)"
101     
102     base_path = tools.config.get('static_http_url_prefix', '/')
103     
104     reg_http_service(base_path, StaticHTTPHandler)
105     
106     _logger.info("Registered HTTP dir %s for %s", document_root, base_path)
107
108 import security
109
110 class OpenERPAuthProvider(AuthProvider):
111     """ Require basic authentication."""
112     def __init__(self,realm='OpenERP User'):
113         self.realm = realm
114         self.auth_creds = {}
115         self.auth_tries = 0
116         self.last_auth = None
117
118     def authenticate(self, db, user, passwd, client_address):
119         try:
120             uid = security.login(db,user,passwd)
121             if uid is False:
122                 return False
123             return (user, passwd, db, uid)
124         except Exception,e:
125             _logger.debug("Fail auth: %s" % e )
126             return False
127
128     def checkRequest(self,handler,path, db=False):        
129         auth_str = handler.headers.get('Authorization',False)
130         try:
131             if not db:
132                 db = handler.get_db_from_path(path)
133         except Exception:
134             if path.startswith('/'):
135                 path = path[1:]
136             psp= path.split('/')
137             if len(psp)>1:
138                 db = psp[0]
139             else:
140                 #FIXME!
141                 _logger.info("Wrong path: %s, failing auth" %path)
142                 raise AuthRejectedExc("Authorization failed. Wrong sub-path.") 
143         if self.auth_creds.get(db):
144             return True 
145         if auth_str and auth_str.startswith('Basic '):
146             auth_str=auth_str[len('Basic '):]
147             (user,passwd) = base64.decodestring(auth_str).split(':')
148             _logger.info("Found user=\"%s\", passwd=\"***\" for db=\"%s\"", user, db)
149             acd = self.authenticate(db,user,passwd,handler.client_address)
150             if acd != False:
151                 self.auth_creds[db] = acd
152                 self.last_auth = db
153                 return True
154         if self.auth_tries > 5:
155             _logger.info("Failing authorization after 5 requests w/o password")
156             raise AuthRejectedExc("Authorization failed.")
157         self.auth_tries += 1
158         raise AuthRequiredExc(atype='Basic', realm=self.realm)
159
160 #eof
161
162 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: