def check_authentication(self): """Read and verify the front-end headers, update the user dict with information about the authorized user.""" headers = lowerCmsHeaders(cherrypy.request.headers) user = cherrypy.request.user if 'cms-auth-status' not in headers: # Non SSL request raise cherrypy.HTTPError(403, "You are not allowed to access this resource.") if headers['cms-auth-status'] == 'NONE': # User authentication is optional return # authn accepted # User information is available on headers prefix = suffix = "" hkeys = sorted(headers.keys()) for hk in hkeys: hk = hk.lower() if hk[0:9] in ["cms-authn", "cms-authz"] and hk != "cms-authn-hmac": prefix += "h%xv%x" % (len(hk), len(headers[hk])) suffix += "%s%s" % (hk, headers[hk]) hkname = hk.split('-', 2)[-1] if hk.startswith("cms-authn"): user[hkname] = headers[hk] if hk.startswith("cms-authz"): user['roles'][hkname] = {'site': set(), 'group': set()} for r in headers[hk].split(): ste_or_grp, name = r.split(':') user['roles'][hkname][ste_or_grp].add(name); vfy = hmac.new(self.key, prefix + "#" + suffix, hashlib.sha1).hexdigest() if vfy != headers["cms-authn-hmac"]: # HMAC does not match raise cherrypy.HTTPError(403, "You are not allowed to access this resource, hmac mismatch")
def check_authentication(self): """Read and verify the front-end headers, update the user dict with information about the authorized user.""" headers = lowerCmsHeaders(cherrypy.request.headers) user = get_user_info() if 'cms-auth-status' not in headers: # Non SSL request raise cherrypy.HTTPError(403, "You are not allowed to access this resource.") if headers['cms-auth-status'] == 'NONE': # User authentication is optional return # authn accepted # User information is available on headers prefix = suffix = "" hkeys = sorted(headers.keys()) for hk in hkeys: hk = hk.lower() if hk[0:9] in ["cms-authn", "cms-authz"] and hk != "cms-authn-hmac": prefix += "h%xv%x" % (len(hk), len(headers[hk])) suffix += "%s%s" % (hk, headers[hk]) hkname = hk.split('-', 2)[-1] if hk.startswith("cms-authn"): user[hkname] = headers[hk] if hk.startswith("cms-authz"): user['roles'][hkname] = {'site': set(), 'group': set()} for r in headers[hk].split(): ste_or_grp, name = r.split(':') user['roles'][hkname][ste_or_grp].add(name) vfy = hmac.new(self.key, prefix + "#" + suffix, hashlib.sha1).hexdigest() if vfy != headers["cms-authn-hmac"]: # HMAC does not match raise cherrypy.HTTPError(403, "You are not allowed to access this resource, hmac mismatch")
def user_info_from_headers(key, verbose=False): """Read the user information HTTP request headers added by front-end. Validates the HMAC on them to check for tampering, and if all is ok, returns user info object with the data from the headers.""" # Set initial user information for this request log = cherrypy.log headers = lowerCmsHeaders(cherrypy.request.headers) user = { 'dn': None, 'method': None, 'login': None, 'name': None, 'roles': {} } # Reject if request was not authenticated. if 'cms-auth-status' not in headers: log("ERROR: authz denied, front-end headers not present") raise cherrypy.HTTPError( 403, "You are not allowed to access this resource.") # If authentication is optional and wasn't done, accept. if headers['cms-auth-status'] == 'NONE': if verbose: log("DEBUG: authn optional and missing") return # Extract user information from the headers. Collect data required # for HMAC validation while processing headers. prefix = suffix = "" hkeys = sorted(headers.keys()) for hk in hkeys: hk = hk.lower() if hk[0:9] in ("cms-authn", "cms-authz") and hk != "cms-authn-hmac": prefix += "h%xv%x" % (len(hk), len(headers[hk])) suffix += "%s%s" % (hk, headers[hk]) hkname = hk.split('-', 2)[-1] if hk.startswith("cms-authn"): val = headers[hk] if hk in ("cms-authn-name", "cms-authn-dn"): val = unicode(val, "utf-8") user[hkname] = val if hk.startswith("cms-authz"): user['roles'][hkname] = {'site': set(), 'group': set()} for r in headers[hk].split(): site_or_group, name = r.split(':') user['roles'][hkname][site_or_group].add(name) # Check HMAC over authn/z headers with server key. If differs, reject. cksum = hmac.new(key, prefix + "#" + suffix, hashlib.sha1).hexdigest() if cksum != headers["cms-authn-hmac"]: log("ERROR: authz hmac mismatch, %s vs. %s" % (cksum, headers["cms-authn-hmac"])) raise cherrypy.HTTPError( 403, "You are not allowed to access this resource.") # Authn/z is legal, accept if verbose: log("DEBUG: authn accepted for user %s" % user) return user
def access(self): """ Record a client access """ request = cherrypy.request remote = request.remote response = cherrypy.response inheaders = lowerCmsHeaders(request.headers) outheaders = response.headers # identify size of body from HTTP Content-Length header rbytes = int(cherrypy.request.headers.get('Content-Length', 0)) if not rbytes: try: # request.rfile.rfile.bytes_read is a custom CMS web # cherrypy patch not always available, hence the test rbytes = (getattr(request.rfile, 'rfile', None) and getattr(request.rfile.rfile, "bytes_read", None) and request.rfile.rfile.bytes_read) or "-" except: try: # this will work only when body is read from request rbytes = cherrypy.request.body.fp.bytes_read except: rbytes = "-" msg = ('%(t)s %(H)s %(h)s "%(r)s" %(s)s' + ' [data: %(i)s in %(b)s out %(T).0f us ]' + ' [auth: %(AS)s "%(AU)s" "%(AC)s" ]' + ' [ref: "%(f)s" "%(a)s" ]') % { 't': self.time(), 'H': self.host, 'h': remote.name or remote.ip, 'r': request.request_line, 's': response.status, 'i': rbytes, 'b': outheaders.get('Content-Length', '') or "-", 'T': (time.time() - request.start_time) * 1e6, 'AS': inheaders.get("cms-auth-status", "-"), 'AU': inheaders.get("cms-auth-cert", inheaders.get("cms-auth-host", "")), 'AC': getattr(request.cookie.get("cms-auth", None), "value", ""), 'f': inheaders.get("Referer", ""), 'a': inheaders.get("User-Agent", "") } self.access_log.log(logging.INFO, msg)
def testLowerCmsHeaders(self): "Test lowerCmsHeaders function" val = 'cms-xx-yy' headers = {'CAPITAL': 1, 'Camel': 1, 'Cms-Xx-Yy': val, 'CMS-XX-YY': val, 'cms-xx-yy': val} lheaders = lowerCmsHeaders(headers) self.assertEqual(sorted(lheaders.keys()), sorted(['CAPITAL', 'Camel', val])) self.assertEqual(lheaders['CAPITAL'], 1) self.assertEqual(lheaders['Camel'], 1) self.assertEqual(lheaders[val], val) self.assertEqual(len(lheaders), 3)
def testLowerCmsHeaders(self): "Test lowerCmsHeaders function" val = 'cms-xx-yy' headers = {'CAPITAL':1, 'Camel':1, 'Cms-Xx-Yy': val, 'CMS-XX-YY': val, 'cms-xx-yy': val} lheaders = lowerCmsHeaders(headers) self.assertEqual(sorted(lheaders.keys()), sorted(['CAPITAL', 'Camel', val])) self.assertEqual(lheaders['CAPITAL'], 1) self.assertEqual(lheaders['Camel'], 1) self.assertEqual(lheaders[val], val) self.assertEqual(len(lheaders.keys()), 3)
def user_info_from_headers(key, verbose=False): """Read the user information HTTP request headers added by front-end. Validates the HMAC on them to check for tampering, and if all is ok, returns user info object with the data from the headers.""" # Set initial user information for this request log = cherrypy.log headers = lowerCmsHeaders(cherrypy.request.headers) user = {'dn': None, 'method': None, 'login': None, 'name': None, 'roles': {}} # Reject if request was not authenticated. if 'cms-auth-status' not in headers: log("ERROR: authz denied, front-end headers not present") raise cherrypy.HTTPError(403, "You are not allowed to access this resource.") # If authentication is optional and wasn't done, accept. if headers['cms-auth-status'] == 'NONE': if verbose: log("DEBUG: authn optional and missing") return # Extract user information from the headers. Collect data required # for HMAC validation while processing headers. prefix = suffix = "" hkeys = sorted(headers.keys()) for hk in hkeys: hk = hk.lower() if hk[0:9] in ("cms-authn", "cms-authz") and hk != "cms-authn-hmac": prefix += "h%xv%x" % (len(hk), len(headers[hk])) suffix += "%s%s" % (hk, headers[hk]) hkname = hk.split('-', 2)[-1] if hk.startswith("cms-authn"): val = headers[hk] if hk in ("cms-authn-name", "cms-authn-dn"): val = unicode(val, "utf-8") user[hkname] = val if hk.startswith("cms-authz"): user['roles'][hkname] = {'site': set(), 'group': set()} for r in headers[hk].split(): site_or_group, name = r.split(':') user['roles'][hkname][site_or_group].add(name) # Check HMAC over authn/z headers with server key. If differs, reject. cksum = hmac.new(key, prefix + "#" + suffix, hashlib.sha1).hexdigest() if cksum != headers["cms-authn-hmac"]: log("ERROR: authz hmac mismatch, %s vs. %s" % (cksum, headers["cms-authn-hmac"])) raise cherrypy.HTTPError(403, "You are not allowed to access this resource.") # Authn/z is legal, accept if verbose: log("DEBUG: authn accepted for user %s" % user) return user
def access(self): """ Record a client access """ request = cherrypy.request remote = request.remote response = cherrypy.response inheaders = lowerCmsHeaders(request.headers) outheaders = response.headers # identify size of body from HTTP Content-Length header rbytes = int(cherrypy.request.headers.get('Content-Length', 0)) if not rbytes: try: # request.rfile.rfile.bytes_read is a custom CMS web # cherrypy patch not always available, hence the test rbytes = (getattr(request.rfile, 'rfile', None) and getattr(request.rfile.rfile, "bytes_read", None) and request.rfile.rfile.bytes_read) or "-" except: try: # this will work only when body is read from request rbytes = cherrypy.request.body.fp.bytes_read except: rbytes = "-" msg = ('%(t)s %(H)s %(h)s "%(r)s" %(s)s' + ' [data: %(i)s in %(b)s out %(T).0f us ]' + ' [auth: %(AS)s "%(AU)s" "%(AC)s" ]' + ' [ref: "%(f)s" "%(a)s" ]') % {'t': self.time(), 'H': self.host, 'h': remote.name or remote.ip, 'r': request.request_line, 's': response.status, 'i': rbytes, 'b': outheaders.get('Content-Length', '') or "-", 'T': (time.time() - request.start_time) * 1e6, 'AS': inheaders.get("cms-auth-status", "-"), 'AU': inheaders.get("cms-auth-cert", inheaders.get("cms-auth-host", "")), 'AC': getattr(request.cookie.get("cms-auth", None), "value", ""), 'f': inheaders.get("Referer", ""), 'a': inheaders.get("User-Agent", "")} self.access_log.log(logging.INFO, msg)
def access(self): """Record one client access.""" request = cherrypy.request remote = request.remote response = cherrypy.response inheaders = lowerCmsHeaders(request.headers) outheaders = response.headers wfile = request.wsgi_environ.get('cherrypy.wfile', None) nout = (wfile and wfile.bytes_written) or outheaders.get( 'Content-Length', 0) if hasattr(request, 'start_time'): delta_time = (time.time() - request.start_time) * 1e6 else: delta_time = 0 msg = ('%(t)s %(H)s %(h)s "%(r)s" %(s)s' ' [data: %(i)s in %(b)s out %(T).0f us ]' ' [auth: %(AS)s "%(AU)s" "%(AC)s" ]' ' [ref: "%(f)s" "%(a)s" ]') % \ {'t': self.time(), 'H': self.host, 'h': remote.name or remote.ip, 'r': request.request_line, 's': response.status, # request.rfile.rfile.bytes_read is a custom CMS web # cherrypy patch not always available, hence the test 'i': (getattr(request.rfile, 'rfile', None) and getattr(request.rfile.rfile, "bytes_read", None) and request.rfile.rfile.bytes_read) or "-", 'b': nout or "-", 'T': delta_time, 'AS': inheaders.get("cms-auth-status", "-"), 'AU': inheaders.get("cms-auth-cert", inheaders.get("cms-auth-host", "")), 'AC': getattr(request.cookie.get("cms-auth", None), "value", ""), 'f': inheaders.get("Referer", ""), 'a': inheaders.get("User-Agent", "")} self.access_log.log(logging.INFO, msg) self.access_log.propagate = False # to avoid duplicate records on the log self.error_log.propagate = False # to avoid duplicate records on the log
def access(self): """Record one client access.""" request = cherrypy.request remote = request.remote response = cherrypy.response inheaders = lowerCmsHeaders(request.headers) outheaders = response.headers wfile = request.wsgi_environ.get('cherrypy.wfile', None) nout = (wfile and wfile.bytes_written) or outheaders.get('Content-Length', 0) if hasattr(request, 'start_time'): delta_time = (time.time() - request.start_time) * 1e6 else: delta_time = 0 msg = ('%(t)s %(H)s %(h)s "%(r)s" %(s)s' ' [data: %(i)s in %(b)s out %(T).0f us ]' ' [auth: %(AS)s "%(AU)s" "%(AC)s" ]' ' [ref: "%(f)s" "%(a)s" ]') % \ {'t': self.time(), 'H': self.host, 'h': remote.name or remote.ip, 'r': request.request_line, 's': response.status, # request.rfile.rfile.bytes_read is a custom CMS web # cherrypy patch not always available, hence the test 'i': (getattr(request.rfile, 'rfile', None) and getattr(request.rfile.rfile, "bytes_read", None) and request.rfile.rfile.bytes_read) or "-", 'b': nout or "-", 'T': delta_time, 'AS': inheaders.get("cms-auth-status", "-"), 'AU': inheaders.get("cms-auth-cert", inheaders.get("cms-auth-host", "")), 'AC': getattr(request.cookie.get("cms-auth", None), "value", ""), 'f': inheaders.get("Referer", ""), 'a': inheaders.get("User-Agent", "")} self.access_log.log(logging.INFO, msg) self.access_log.propagate = False # to avoid duplicate records on the log self.error_log.propagate = False # to avoid duplicate records on the log