| | |
| | | import time as time_mod |
| | | |
| | | from repoze.who._compat import encodestring |
| | | from repoze.who._compat import get_cookies |
| | | from repoze.who._compat import SimpleCookie |
| | | from repoze.who._compat import STRING_TYPES |
| | | from repoze.who._compat import url_quote |
| | | from repoze.who._compat import url_unquote |
| | | |
| | |
| | | return digest |
| | | |
| | | |
| | | if type(chr(1)) == type(b''): |
| | | if type(chr(1)) == type(b''): #pragma NO COVER Python < 3.0 |
| | | def ints2bytes(ints): |
| | | return b''.join(map(chr, ints)) |
| | | else: #pragma NO COVER Python >= 3.0 |
| | |
| | | return s |
| | | |
| | | |
| | | class AuthTKTMiddleware(object): |
| | | |
| | | """ |
| | | Middleware that checks for signed cookies that match what |
| | | `mod_auth_tkt <http://www.openfusion.com.au/labs/mod_auth_tkt/>`_ |
| | | looks for (if you have mod_auth_tkt installed, you don't need this |
| | | middleware, since Apache will set the environmental variables for |
| | | you). |
| | | |
| | | Arguments: |
| | | |
| | | ``secret``: |
| | | A secret that should be shared by any instances of this application. |
| | | If this app is served from more than one machine, they should all |
| | | have the same secret. |
| | | |
| | | ``cookie_name``: |
| | | The name of the cookie to read and write from. Default ``auth_tkt``. |
| | | |
| | | ``secure``: |
| | | If the cookie should be set as 'secure' (only sent over SSL) and if |
| | | the login must be over SSL. (Defaults to False) |
| | | |
| | | ``httponly``: |
| | | If the cookie should be marked as HttpOnly, which means that it's |
| | | not accessible to JavaScript. (Defaults to False) |
| | | |
| | | ``include_ip``: |
| | | If the cookie should include the user's IP address. If so, then |
| | | if they change IPs their cookie will be invalid. |
| | | |
| | | ``logout_path``: |
| | | The path under this middleware that should signify a logout. The |
| | | page will be shown as usual, but the user will also be logged out |
| | | when they visit this page. |
| | | |
| | | If used with mod_auth_tkt, then these settings (except logout_path) should |
| | | match the analogous Apache configuration settings. |
| | | |
| | | This also adds two functions to the request: |
| | | |
| | | ``environ['repoze.who._auth_tkt.set_user'](userid, tokens='', |
| | | user_data='')`` |
| | | |
| | | This sets a cookie that logs the user in. ``tokens`` is a |
| | | string (comma-separated groups) or a list of strings. |
| | | ``user_data`` is a string for your own use. |
| | | |
| | | ``environ['repoze.who._auth_tkt.logout_user']()`` |
| | | |
| | | Logs out the user. |
| | | """ |
| | | |
| | | def __init__(self, app, secret, cookie_name='auth_tkt', secure=False, |
| | | include_ip=True, logout_path=None, httponly=False, |
| | | no_domain_cookie=True, current_domain_cookie=True, |
| | | wildcard_cookie=True): |
| | | self.app = app |
| | | self.secret = secret |
| | | self.cookie_name = cookie_name |
| | | self.secure = secure |
| | | self.httponly = httponly |
| | | self.include_ip = include_ip |
| | | self.logout_path = logout_path |
| | | self.no_domain_cookie = no_domain_cookie |
| | | self.current_domain_cookie = current_domain_cookie |
| | | self.wildcard_cookie = wildcard_cookie |
| | | |
| | | def __call__(self, environ, start_response): |
| | | #cookies = request.get_cookies(environ) |
| | | cookies = get_cookies(environ) |
| | | if self.cookie_name in cookies: |
| | | cookie_value = cookies[self.cookie_name].value |
| | | else: |
| | | cookie_value = '' |
| | | if cookie_value: |
| | | if self.include_ip: |
| | | remote_addr = environ['REMOTE_ADDR'] |
| | | else: |
| | | # mod_auth_tkt uses this dummy value when IP is not |
| | | # checked: |
| | | remote_addr = '0.0.0.0' |
| | | # @@: This should handle bad signatures better: |
| | | # Also, timeouts should cause cookie refresh |
| | | try: |
| | | timestamp, userid, tokens, user_data = parse_ticket( |
| | | self.secret, cookie_value, remote_addr) |
| | | tokens = ','.join(tokens) |
| | | environ['REMOTE_USER'] = userid |
| | | if environ.get('REMOTE_USER_TOKENS'): |
| | | # We want to add tokens/roles to what's there: |
| | | tokens = environ['REMOTE_USER_TOKENS'] + ',' + tokens |
| | | environ['REMOTE_USER_TOKENS'] = tokens |
| | | environ['REMOTE_USER_DATA'] = user_data |
| | | environ['AUTH_TYPE'] = 'cookie' |
| | | except BadTicket: |
| | | # bad credentials, just ignore without logging the user |
| | | # in or anything |
| | | pass |
| | | set_cookies = [] |
| | | |
| | | def set_user(userid, tokens='', user_data=''): |
| | | set_cookies.extend(self.set_user_cookie( |
| | | environ, userid, tokens, user_data)) |
| | | |
| | | def logout_user(): |
| | | set_cookies.extend(self.logout_user_cookie(environ)) |
| | | |
| | | environ['repoze.who._auth_tkt.set_user'] = set_user |
| | | environ['repoze.who._auth_tkt.logout_user'] = logout_user |
| | | if self.logout_path and environ.get('PATH_INFO') == self.logout_path: |
| | | logout_user() |
| | | |
| | | def cookie_setting_start_response(status, headers, exc_info=None): |
| | | headers.extend(set_cookies) |
| | | return start_response(status, headers, exc_info) |
| | | |
| | | return self.app(environ, cookie_setting_start_response) |
| | | |
| | | def set_user_cookie(self, environ, userid, tokens, user_data): |
| | | if not isinstance(tokens, STRING_TYPES): |
| | | tokens = ','.join(tokens) |
| | | if self.include_ip: |
| | | remote_addr = environ['REMOTE_ADDR'] |
| | | else: |
| | | remote_addr = '0.0.0.0' |
| | | ticket = AuthTicket( |
| | | self.secret, |
| | | userid, |
| | | remote_addr, |
| | | tokens=tokens, |
| | | user_data=user_data, |
| | | cookie_name=self.cookie_name, |
| | | secure=self.secure) |
| | | # @@: Should we set REMOTE_USER etc in the current |
| | | # environment right now as well? |
| | | cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) |
| | | wild_domain = '.' + cur_domain |
| | | |
| | | cookie_options = "" |
| | | if self.secure: |
| | | cookie_options += "; secure" |
| | | if self.httponly: |
| | | cookie_options += "; HttpOnly" |
| | | |
| | | cookies = [] |
| | | if self.no_domain_cookie: |
| | | cookies.append(('Set-Cookie', '%s=%s; Path=/%s' % ( |
| | | self.cookie_name, ticket.cookie_value(), cookie_options))) |
| | | if self.current_domain_cookie: |
| | | cookies.append(('Set-Cookie', '%s=%s; Path=/; Domain=%s%s' % ( |
| | | self.cookie_name, ticket.cookie_value(), cur_domain, |
| | | cookie_options))) |
| | | if self.wildcard_cookie: |
| | | cookies.append(('Set-Cookie', '%s=%s; Path=/; Domain=%s%s' % ( |
| | | self.cookie_name, ticket.cookie_value(), wild_domain, |
| | | cookie_options))) |
| | | |
| | | return cookies |
| | | |
| | | def logout_user_cookie(self, environ): |
| | | cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) |
| | | wild_domain = '.' + cur_domain |
| | | expires = 'Sat, 01-Jan-2000 12:00:00 GMT' |
| | | cookies = [ |
| | | ('Set-Cookie', '%s=""; Expires="%s"; Path=/' % |
| | | (self.cookie_name, expires)), |
| | | ('Set-Cookie', '%s=""; Expires="%s"; Path=/; Domain=%s' % |
| | | (self.cookie_name, expires, cur_domain)), |
| | | ('Set-Cookie', '%s=""; Expires="%s"; Path=/; Domain=%s' % |
| | | (self.cookie_name, expires, wild_domain)), |
| | | ] |
| | | return cookies |
| | | |
| | | |
| | | def asbool(obj): |
| | | # Lifted from paste.deploy.converters |
| | | if isinstance(obj, STRING_TYPES): |
| | | obj = obj.strip().lower() |
| | | if obj in ['true', 'yes', 'on', 'y', 't', '1']: |
| | | return True |
| | | elif obj in ['false', 'no', 'off', 'n', 'f', '0']: |
| | | return False |
| | | else: |
| | | raise ValueError( |
| | | "String is not true/false: %r" % obj) |
| | | return bool(obj) |
| | | |
| | | def make_auth_tkt_middleware( |
| | | app, |
| | | global_conf, |
| | | secret=None, |
| | | cookie_name='auth_tkt', |
| | | secure=False, |
| | | include_ip=True, |
| | | logout_path=None, |
| | | ): |
| | | """ |
| | | Creates the `AuthTKTMiddleware |
| | | <class-repoze.who._auth_tkt.AuthTKTMiddleware.html>`_. |
| | | |
| | | ``secret`` is required, but can be set globally or locally. |
| | | """ |
| | | secure = asbool(secure) |
| | | include_ip = asbool(include_ip) |
| | | if secret is None: |
| | | secret = global_conf.get('secret') |
| | | if not secret: |
| | | raise ValueError( |
| | | "You must provide a 'secret' (in global or local configuration)") |
| | | return AuthTKTMiddleware( |
| | | app, secret, cookie_name, secure, include_ip, logout_path or None) |
| | | # Original Paste AuthTktMiddleware stripped: we don't have a use for it. |