[MERGE] users and groups: new implementation of group fields on users form view
[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 from websrv_lib import *
43 import openerp.netsvc as netsvc
44 import errno
45 import threading
46 import openerp.tools as tools
47 import posixpath
48 import urllib
49 import os
50 import select
51 import socket
52 import xmlrpclib
53 import logging
54
55 from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
56
57 try:
58     import fcntl
59 except ImportError:
60     fcntl = None
61
62 try:
63     from ssl import SSLError
64 except ImportError:
65     class SSLError(Exception): pass
66
67 class HttpLogHandler:
68     """ helper class for uniform log handling
69     Please define self._logger at each class that is derived from this
70     """
71     _logger = None
72     
73     def log_message(self, format, *args):
74         self._logger.debug(format % args) # todo: perhaps other level
75
76     def log_error(self, format, *args):
77         self._logger.error(format % args)
78         
79     def log_exception(self, format, *args):
80         self._logger.exception(format, *args)
81
82     def log_request(self, code='-', size='-'):
83         self._logger.log(netsvc.logging.DEBUG_RPC, '"%s" %s %s',
84                         self.requestline, str(code), str(size))
85
86 class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler):
87     _logger = logging.getLogger('httpd')
88     _HTTP_OPTIONS = { 'Allow': ['OPTIONS', 'GET', 'HEAD'] }
89
90     def __init__(self,request, client_address, server):
91         HTTPHandler.__init__(self,request,client_address,server)
92         document_root = tools.config.get('static_http_document_root', False)
93         assert document_root, "Please specify static_http_document_root in configuration, or disable static-httpd!"
94         self.__basepath = document_root
95
96     def translate_path(self, path):
97         """Translate a /-separated PATH to the local filename syntax.
98
99         Components that mean special things to the local file system
100         (e.g. drive or directory names) are ignored.  (XXX They should
101         probably be diagnosed.)
102
103         """
104         # abandon query parameters
105         path = path.split('?',1)[0]
106         path = path.split('#',1)[0]
107         path = posixpath.normpath(urllib.unquote(path))
108         words = path.split('/')
109         words = filter(None, words)
110         path = self.__basepath
111         for word in words:
112             if word in (os.curdir, os.pardir): continue
113             path = os.path.join(path, word)
114         return path
115
116 def init_static_http():
117     if not tools.config.get('static_http_enable', False):
118         return
119     
120     document_root = tools.config.get('static_http_document_root', False)
121     assert document_root, "Document root must be specified explicitly to enable static HTTP service (option --static-http-document-root)"
122     
123     base_path = tools.config.get('static_http_url_prefix', '/')
124     
125     reg_http_service(base_path, StaticHTTPHandler)
126     
127     logging.getLogger("web-services").info("Registered HTTP dir %s for %s" % \
128                         (document_root, base_path))
129
130 import security
131
132 class OpenERPAuthProvider(AuthProvider):
133     """ Require basic authentication."""
134     def __init__(self,realm='OpenERP User'):
135         self.realm = realm
136         self.auth_creds = {}
137         self.auth_tries = 0
138         self.last_auth = None
139
140     def authenticate(self, db, user, passwd, client_address):
141         try:
142             uid = security.login(db,user,passwd)
143             if uid is False:
144                 return False
145             return (user, passwd, db, uid)
146         except Exception,e:
147             logging.getLogger("auth").debug("Fail auth: %s" % e )
148             return False
149
150     def log(self, msg, lvl=logging.INFO):
151         logging.getLogger("auth").log(lvl,msg)
152
153     def checkRequest(self,handler,path, db=False):        
154         auth_str = handler.headers.get('Authorization',False)
155         try:
156             if not db:
157                 db = handler.get_db_from_path(path)
158         except Exception:
159             if path.startswith('/'):
160                 path = path[1:]
161             psp= path.split('/')
162             if len(psp)>1:
163                 db = psp[0]
164             else:
165                 #FIXME!
166                 self.log("Wrong path: %s, failing auth" %path)
167                 raise AuthRejectedExc("Authorization failed. Wrong sub-path.") 
168         if self.auth_creds.get(db):
169             return True 
170         if auth_str and auth_str.startswith('Basic '):
171             auth_str=auth_str[len('Basic '):]
172             (user,passwd) = base64.decodestring(auth_str).split(':')
173             self.log("Found user=\"%s\", passwd=\"***\" for db=\"%s\"" %(user,db))
174             acd = self.authenticate(db,user,passwd,handler.client_address)
175             if acd != False:
176                 self.auth_creds[db] = acd
177                 self.last_auth = db
178                 return True
179         if self.auth_tries > 5:
180             self.log("Failing authorization after 5 requests w/o password")
181             raise AuthRejectedExc("Authorization failed.")
182         self.auth_tries += 1
183         raise AuthRequiredExc(atype='Basic', realm=self.realm)
184
185 #eof
186
187 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: