A CherryPy tool which hooks at before_handler to perform HTTP Basic Access Authentication, as specified in :rfc:`2617` and :rfc:`7617`. If the request has an 'authorization' header with a 'Basic' scheme, this tool attempts to authenticate the credentials supplied in that header. If
(realm, checkpassword, debug=False, accept_charset='utf-8')
| 48 | |
| 49 | |
| 50 | def basic_auth(realm, checkpassword, debug=False, accept_charset='utf-8'): |
| 51 | """A CherryPy tool which hooks at before_handler to perform |
| 52 | HTTP Basic Access Authentication, as specified in :rfc:`2617` |
| 53 | and :rfc:`7617`. |
| 54 | |
| 55 | If the request has an 'authorization' header with a 'Basic' scheme, this |
| 56 | tool attempts to authenticate the credentials supplied in that header. If |
| 57 | the request has no 'authorization' header, or if it does but the scheme is |
| 58 | not 'Basic', or if authentication fails, the tool sends a 401 response with |
| 59 | a 'WWW-Authenticate' Basic header. |
| 60 | |
| 61 | realm |
| 62 | A string containing the authentication realm. |
| 63 | |
| 64 | checkpassword |
| 65 | A callable which checks the authentication credentials. |
| 66 | Its signature is checkpassword(realm, username, password). where |
| 67 | username and password are the values obtained from the request's |
| 68 | 'authorization' header. If authentication succeeds, checkpassword |
| 69 | returns True, else it returns False. |
| 70 | |
| 71 | """ |
| 72 | |
| 73 | fallback_charset = 'ISO-8859-1' |
| 74 | |
| 75 | if '"' in realm: |
| 76 | raise ValueError('Realm cannot contain the " (quote) character.') |
| 77 | request = cherrypy.serving.request |
| 78 | |
| 79 | auth_header = request.headers.get('authorization') |
| 80 | if auth_header is not None: |
| 81 | # split() error, base64.decodestring() error |
| 82 | msg = 'Bad Request' |
| 83 | with cherrypy.HTTPError.handle((ValueError, binascii.Error), 400, msg): |
| 84 | scheme, params = auth_header.split(' ', 1) |
| 85 | if scheme.lower() == 'basic': |
| 86 | charsets = accept_charset, fallback_charset |
| 87 | decoded_params = base64.b64decode(params.encode('ascii')) |
| 88 | decoded_params = _try_decode(decoded_params, charsets) |
| 89 | decoded_params = ntou(decoded_params) |
| 90 | decoded_params = unicodedata.normalize('NFC', decoded_params) |
| 91 | decoded_params = tonative(decoded_params) |
| 92 | username, password = decoded_params.split(':', 1) |
| 93 | if checkpassword(realm, username, password): |
| 94 | if debug: |
| 95 | cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC') |
| 96 | request.login = username |
| 97 | return # successful authentication |
| 98 | |
| 99 | charset = accept_charset.upper() |
| 100 | charset_declaration = ( |
| 101 | (', charset="%s"' % charset) |
| 102 | if charset != fallback_charset |
| 103 | else '' |
| 104 | ) |
| 105 | # Respond with 401 status and a WWW-Authenticate header |
| 106 | cherrypy.serving.response.headers['www-authenticate'] = ( |
| 107 | 'Basic realm="%s"%s' % (realm, charset_declaration) |
nothing calls this directly
no test coverage detected
searching dependent graphs…