class AuthPolicy(object): def _log(self, msg, methodname, request): logger = request.registry.queryUtility(IDebugLogger) if logger: cls = self.__class__ classname = cls.__module__ + "." + cls.__name__ methodname = classname + "." + methodname logger.debug(methodname + ": " + msg) def __init__( self, secret, cookie_name="auth", secure=False, max_age=None, httponly=False, path="/", domains=None, timeout=None, reissue_time=None, debug=False, hashalg="sha512", ): self.domains = domains self.cookie = CookieHelper( secret, "alexandria-auth", cookie_name, secure=secure, max_age=max_age, httponly=httponly, path=path, domains=domains, hashalg=hashalg, ) self.debug = debug def unauthenticated_userid(self, request): """ No support for the unauthenticated userid """ return None def authenticated_userid(self, request): """ Return the authenticated userid or ``None``.""" try: return request.state["auth"]["userinfo"].id except: pass result = self.cookie.bind(request).get_value() self.debug and self._log("Got result from cookie: %s" % (result,), "authenticated_userid", request) class UserInfo(object): def __init__(self): self.id = None self.auth = {} self.user = None self.ticket = None userinfo = UserInfo() request.state["auth"] = {} request.state["auth"]["userinfo"] = userinfo if result: request.state["auth"]["principal"] = result["principal"] request.state["auth"]["ticket"] = result["ticket"] request.state["auth"]["tokens"] = result["tokens"] ticket = self.find_user_ticket(request) if ticket is None: return None userinfo.id = ticket.user.email userinfo.user = ticket.user userinfo.ticket = ticket return userinfo.id else: return None def find_user_ticket(self, request): """ Return the user object if valid for the ticket or ``None``.""" auth = request.state.get("auth", {}) ticket = auth.get("ticket", "") principal = auth.get("principal", "") if not ticket or not principal: return None ticket = UserTickets.find_ticket_userid(request.dbsession, ticket, principal) if ticket is None: self.debug and self._log("No ticket found", "find_user_ticket", request) self.cookie.set_cookies(request.response, "", max_age=0) return ticket def effective_principals(self, request): """ A list of effective principals derived from request. This will return a list of principals including, at least, :data:`pyramid.security.Everyone`. If there is no authenticated userid, or the ``callback`` returns ``None``, this will be the only principal: .. code-block:: python return [Everyone] """ debug = self.debug effective_principals = [Everyone] userid = self.authenticated_userid(request) if userid is None: debug and self._log( "authenticated_userid returned %r; returning %r" % (userid, effective_principals), "effective_principals", request, ) return effective_principals groups = [] # Get the groups here ... effective_principals.append(Authenticated) effective_principals.append(userid) effective_principals.extend(groups) debug and self._log( "returning effective principals: %r" % (effective_principals,), "effective_principals", request ) return effective_principals def remember(self, request, principal, tokens=None, **kw): """ Accepts the following kw args: ``max_age=<int-seconds>`` Return a list of headers which will set appropriate cookies on the response. """ debug = self.debug hashalg = "sha256" digestmethod = lambda string=b"": hashlib.new(hashalg, string) value = {} value["principal"] = principal value["ticket"] = ticket = digestmethod(urandom(32)).hexdigest() value["tokens"] = tokens if tokens is not None else [] user = request.dbsession.query(User).filter(User.email == principal).first() if user is None: raise ValueError("Invalid principal provided") debug and self._log("Remember user: %s, ticket: %s" % (user.email, value["ticket"]), "remember", request) ticket = value["ticket"] remote_addr = request.environ["REMOTE_ADDR"] if "REMOTE_ADDR" in request.environ else None user.tickets.append(UserTickets(ticket=ticket, remote_addr=remote_addr)) if self.domains is None: self.domains = [] self.domains.append(request.domain) return self.cookie.get_headers(value, domains=self.domains) def forget(self, request): """ A list of headers which will delete appropriate cookies.""" debug = self.debug user = request.user if user.ticket: debug and self._log( "forgetting user: %s, removing ticket: %s" % (user.id, user.ticket.ticket), "forget", request ) request.dbsession.delete(user.ticket) return self.cookie.get_headers("", max_age=0)
class AuthPolicy(object): def _log(self, msg, methodname, request): logger = request.registry.queryUtility(IDebugLogger) if logger: cls = self.__class__ classname = cls.__module__ + '.' + cls.__name__ methodname = classname + '.' + methodname logger.debug(methodname + ': ' + msg) def __init__( self, secret, cookie_name='auth', secure=False, max_age=None, httponly=False, path="/", domains=None, timeout=None, reissue_time=None, debug=False, hashalg='sha512', ): self.domains = domains self.cookie = CookieHelper( secret, 'alexandria-auth', cookie_name, secure=secure, max_age=max_age, httponly=httponly, path=path, domains=domains, hashalg=hashalg, ) self.debug = debug def unauthenticated_userid(self, request): """ No support for the unauthenticated userid """ return None def authenticated_userid(self, request): """ Return the authenticated userid or ``None``.""" try: return request.state['auth']['userinfo'].id except: pass result = self.cookie.bind(request).get_value() self.debug and self._log('Got result from cookie: %s' % (result, ), 'authenticated_userid', request) class UserInfo(object): def __init__(self): self.id = None self.auth = {} self.user = None self.ticket = None userinfo = UserInfo() request.state['auth'] = {} request.state['auth']['userinfo'] = userinfo if result: request.state['auth']['principal'] = result['principal'] request.state['auth']['ticket'] = result['ticket'] request.state['auth']['tokens'] = result['tokens'] ticket = self.find_user_ticket(request) if ticket is None: return None userinfo.id = ticket.user.email userinfo.user = ticket.user userinfo.ticket = ticket return userinfo.id else: return None def find_user_ticket(self, request): """ Return the user object if valid for the ticket or ``None``.""" auth = request.state.get('auth', {}) ticket = auth.get('ticket', '') principal = auth.get('principal', '') if not ticket or not principal: return None ticket = UserTickets.find_ticket_userid(request.dbsession, ticket, principal) if ticket is None: self.debug and self._log('No ticket found', 'find_user_ticket', request) self.cookie.set_cookies(request.response, '', max_age=0) return ticket def effective_principals(self, request): """ A list of effective principals derived from request. This will return a list of principals including, at least, :data:`pyramid.security.Everyone`. If there is no authenticated userid, or the ``callback`` returns ``None``, this will be the only principal: .. code-block:: python return [Everyone] """ debug = self.debug effective_principals = [Everyone] userid = self.authenticated_userid(request) if userid is None: debug and self._log( 'authenticated_userid returned %r; returning %r' % (userid, effective_principals), 'effective_principals', request) return effective_principals groups = [] # Get the groups here ... effective_principals.append(Authenticated) effective_principals.append(userid) effective_principals.extend(groups) debug and self._log( 'returning effective principals: %r' % (effective_principals, ), 'effective_principals', request) return effective_principals def remember(self, request, principal, tokens=None, **kw): """ Accepts the following kw args: ``max_age=<int-seconds>`` Return a list of headers which will set appropriate cookies on the response. """ debug = self.debug hashalg = 'sha256' digestmethod = lambda string=b'': hashlib.new(hashalg, string) value = {} value['principal'] = principal value['ticket'] = ticket = digestmethod(urandom(32)).hexdigest() value['tokens'] = tokens if tokens is not None else [] user = request.dbsession.query(User).filter( User.email == principal).first() if user is None: raise ValueError('Invalid principal provided') debug and self._log( 'Remember user: %s, ticket: %s' % (user.email, value['ticket']), 'remember', request) ticket = value['ticket'] remote_addr = request.environ[ 'REMOTE_ADDR'] if 'REMOTE_ADDR' in request.environ else None user.tickets.append(UserTickets(ticket=ticket, remote_addr=remote_addr)) if self.domains is None: self.domains = [] self.domains.append(request.domain) return self.cookie.get_headers(value, domains=self.domains) def forget(self, request): """ A list of headers which will delete appropriate cookies.""" debug = self.debug user = request.user if user.ticket: debug and self._log( 'forgetting user: %s, removing ticket: %s' % (user.id, user.ticket.ticket), 'forget', request) request.dbsession.delete(user.ticket) return self.cookie.get_headers('', max_age=0)
class PluggableSession(dict): """ Dictionary-like session object """ # configuration parameters _cookie_on_exception = set_on_exception _timeout = timeout _reissue_time = reissue_time # dirty flag _dirty = False def __init__(self, request): self._cookie = CookieHelper( secret, salt, cookie_name, secure=secure, max_age=max_age, httponly=httponly, path=path, domains=domain, hashalg=hashalg, ) self._session_id = None self.request = request reg = request.registry plug = reg.queryUtility(IPlugSession) if plug is None: raise RuntimeError('Unable to find any registered IPlugSession') now = time.time() created = renewed = now new = True value = None state = {} # Get the session_id self._session_id = self._cookie.bind(request).get_value() if self._session_id is not None: try: sess_val = plug.loads(self, request) value = serializer.loads(bytes_(sess_val)) except ValueError: value = None # Cleanup the session, since it failed to deserialize plug.clear(self, request) self._session_id = None if value is not None: try: rval, cval, sval = value renewed = float(rval) created = float(cval) state = sval new = False except (TypeError, ValueError): # value failed to unpack properly or renewed was not # a numeric type so we'll fail deserialization here state = {} # Clean up the session since it failed to unpack plug.clear(self, request) self._session_id = None if self._timeout is not None: if now - renewed > self._timeout: # expire the session because it was not renewed # before the timeout threshold state = {} # Session expired, cleanup this session plug.clear(self, request) self._session_id = None # Generate a new session id if self._session_id is None: self._generate_new_id() self.created = created self.accessed = renewed self.renewed = renewed self.new = new self._plug = plug dict.__init__(self, state) # ISession methods def changed(self): if not self._dirty: self._dirty = True def save_session_callback(request, response): self._save_session(response) self.request = None # explicitly break cycle for gc self.request.add_response_callback(save_session_callback) def invalidate(self): self._plug.clear(self, self.request) self._generate_new_id() now = time.time() self.created = self.renewed = now self.new = True self.clear() # non-modifying dictionary methods get = manage_accessed(dict.get) __getitem__ = manage_accessed(dict.__getitem__) items = manage_accessed(dict.items) values = manage_accessed(dict.values) keys = manage_accessed(dict.keys) __contains__ = manage_accessed(dict.__contains__) __len__ = manage_accessed(dict.__len__) __iter__ = manage_accessed(dict.__iter__) if not PY3: iteritems = manage_accessed(dict.iteritems) itervalues = manage_accessed(dict.itervalues) iterkeys = manage_accessed(dict.iterkeys) has_key = manage_accessed(dict.has_key) # modifying dictionary methods clear = manage_changed(dict.clear) update = manage_changed(dict.update) setdefault = manage_changed(dict.setdefault) pop = manage_changed(dict.pop) popitem = manage_changed(dict.popitem) __setitem__ = manage_changed(dict.__setitem__) __delitem__ = manage_changed(dict.__delitem__) # flash API methods @manage_changed def flash(self, msg, queue='', allow_duplicate=True): storage = self.setdefault('_f_' + queue, []) if allow_duplicate or (msg not in storage): storage.append(msg) @manage_changed def pop_flash(self, queue=''): storage = self.pop('_f_' + queue, []) return storage @manage_accessed def peek_flash(self, queue=''): storage = self.get('_f_' + queue, []) return storage # CSRF API methods @manage_changed def new_csrf_token(self): token = text_(binascii.hexlify(os.urandom(20))) self['_csrft_'] = token return token @manage_accessed def get_csrf_token(self): token = self.get('_csrft_', None) if token is None: token = self.new_csrf_token() return token # non-API methods def _save_session(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False sess_val = native_( serializer.dumps( (self.accessed, self.created, dict(self)) ) ) self._plug.dumps(self, self.request, sess_val) self._cookie.set_cookies(response, self._session_id) return True def _generate_new_id(self): self._session_id = text_(binascii.hexlify(os.urandom(20)))
class AuthPolicy(object): def _log(self, msg, methodname, request): logger = request.registry.queryUtility(IDebugLogger) if logger: cls = self.__class__ classname = cls.__module__ + '.' + cls.__name__ methodname = classname + '.' + methodname logger.debug(methodname + ': ' + msg) def __init__(self, secret, cookie_name='auth', secure=False, max_age=None, httponly=False, path="/", domains=None, timeout=None, reissue_time=None, debug=False, hashalg='sha512', ): self.domains = domains self.cookie = CookieHelper( secret, 'usingnamespace-auth', cookie_name, secure=secure, max_age=max_age, httponly=httponly, path=path, domains=domains, hashalg=hashalg, ) self.debug = debug def unauthenticated_userid(self, request): """ The userid key within the auth_tkt cookie.""" result = self.cookie.bind(request).get_value() self.debug and self._log('Got result from cookie: %s' % (result,), 'unauthenticated_userid', request) if result: principal = result['principal'] if _clean_principal(principal) is None: self.debug and self._log('use of principal %r is disallowed by any ' 'built-in Pyramid security policy, returning None' % principal) return None auth = {'principal': principal} if 'tokens' in result: auth['tokens'] = result['tokens'] if 'auth_ticket' in result: auth['ticket'] = result['auth_ticket'] request.state['auth'] = auth return principal def authenticated_userid(self, request): """ Return the authenticated userid or ``None``.""" userid = request.user.id return userid def find_user_ticket(self, request): """ Return the user object if valid for the ticket or ``None``.""" auth = request.state.get('auth', {}) ticket = auth.get('ticket', '') principal = auth.get('principal', '') if not ticket or not principal: return None ticket = UserTickets.find_ticket_userid(ticket, principal) if ticket is None: self.debug and self._log('No ticket found', 'find_user_ticket', request) self.cookie.set_cookies(request.response, '', max_age=0) return ticket def effective_principals(self, request): """ A list of effective principals derived from request. This will return a list of principals including, at least, :data:`pyramid.security.Everyone`. If there is no authenticated userid, or the ``callback`` returns ``None``, this will be the only principal: .. code-block:: python return [Everyone] """ debug = self.debug effective_principals = [Everyone] userid = self.authenticated_userid(request) if userid is None: debug and self._log( 'authenticated_userid returned %r; returning %r' % ( userid, effective_principals), 'effective_principals', request ) return effective_principals groups = [] # Get the groups here ... effective_principals.append(Authenticated) effective_principals.append(userid) effective_principals.extend(groups) debug and self._log( 'returning effective principals: %r' % ( effective_principals,), 'effective_principals', request ) return effective_principals def remember(self, request, principal, tokens=None, max_age=None): """ Accepts the following kw args: ``max_age=<int-seconds>`` Return a list of headers which will set appropriate cookies on the response. """ debug = self.debug value = {} value['principal'] = principal value['auth_ticket'] = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(128)) value['tokens'] = tokens if tokens is not None else [] user = DBSession.query(User).filter(User.email == principal).first() if user is None: raise ValueError('Invalid principal provided') debug and self._log('Remember user: %s, ticket: %s' % (user.email, value['auth_ticket']), 'remember', request) ticket = value['auth_ticket'] remote_addr = request.environ['REMOTE_ADDR'] if 'REMOTE_ADDR' in request.environ else None user.tickets.append(UserTickets(ticket=ticket, remote_addr=remote_addr)) if self.domains is None: self.domains = [] self.domains.append(request.domain) return self.cookie.get_headers(value, domains=self.domains) def forget(self, request): """ A list of headers which will delete appropriate cookies.""" debug = self.debug user = request.user if user.ticket: debug and self._log('forgetting user: %s, removing ticket: %s' % (user.id, user.ticket.ticket), 'forget', request) DBSession.delete(user.ticket) return self.cookie.get_headers('', max_age=0)
class PluggableSession(dict): """ Dictionary-like session object """ # configuration parameters _cookie_on_exception = set_on_exception _timeout = timeout _reissue_time = reissue_time # dirty flag _dirty = False def __init__(self, request): self._cookie = CookieHelper( secret, salt, cookie_name, secure=secure, max_age=max_age, httponly=httponly, path=path, domains=domain, hashalg=hashalg, ) self._session_id = None self.request = request reg = request.registry plug = reg.queryUtility(IPlugSession) if plug is None: raise RuntimeError( 'Unable to find any registered IPlugSession') now = time.time() created = renewed = now new = True value = None state = {} # Get the session_id self._session_id = self._cookie.bind(request).get_value() if self._session_id is not None: try: sess_val = plug.loads(self, request) value = serializer.loads(bytes_(sess_val)) except ValueError: value = None # Cleanup the session, since it failed to deserialize plug.clear(self, request) self._session_id = None if value is not None: try: rval, cval, sval = value renewed = float(rval) created = float(cval) state = sval new = False except (TypeError, ValueError): # value failed to unpack properly or renewed was not # a numeric type so we'll fail deserialization here state = {} # Clean up the session since it failed to unpack plug.clear(self, request) self._session_id = None if self._timeout is not None: if now - renewed > self._timeout: # expire the session because it was not renewed # before the timeout threshold state = {} # Session expired, cleanup this session plug.clear(self, request) self._session_id = None # Generate a new session id if self._session_id is None: self._generate_new_id() self.created = created self.accessed = renewed self.renewed = renewed self.new = new self._plug = plug dict.__init__(self, state) # ISession methods def changed(self): if not self._dirty: self._dirty = True def save_session_callback(request, response): self._save_session(response) self.request = None # explicitly break cycle for gc self.request.add_response_callback(save_session_callback) def invalidate(self): self._plug.clear(self, self.request) self._generate_new_id() now = time.time() self.created = self.renewed = now self.new = True self.clear() # non-modifying dictionary methods get = manage_accessed(dict.get) __getitem__ = manage_accessed(dict.__getitem__) items = manage_accessed(dict.items) values = manage_accessed(dict.values) keys = manage_accessed(dict.keys) __contains__ = manage_accessed(dict.__contains__) __len__ = manage_accessed(dict.__len__) __iter__ = manage_accessed(dict.__iter__) if not PY3: iteritems = manage_accessed(dict.iteritems) itervalues = manage_accessed(dict.itervalues) iterkeys = manage_accessed(dict.iterkeys) has_key = manage_accessed(dict.has_key) # modifying dictionary methods clear = manage_changed(dict.clear) update = manage_changed(dict.update) setdefault = manage_changed(dict.setdefault) pop = manage_changed(dict.pop) popitem = manage_changed(dict.popitem) __setitem__ = manage_changed(dict.__setitem__) __delitem__ = manage_changed(dict.__delitem__) # flash API methods @manage_changed def flash(self, msg, queue='', allow_duplicate=True): storage = self.setdefault('_f_' + queue, []) if allow_duplicate or (msg not in storage): storage.append(msg) @manage_changed def pop_flash(self, queue=''): storage = self.pop('_f_' + queue, []) return storage @manage_accessed def peek_flash(self, queue=''): storage = self.get('_f_' + queue, []) return storage # CSRF API methods @manage_changed def new_csrf_token(self): token = text_(binascii.hexlify(os.urandom(20))) self['_csrft_'] = token return token @manage_accessed def get_csrf_token(self): token = self.get('_csrft_', None) if token is None: token = self.new_csrf_token() return token # non-API methods def _save_session(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False sess_val = native_( serializer.dumps((self.accessed, self.created, dict(self)))) self._plug.dumps(self, self.request, sess_val) self._cookie.set_cookies(response, self._session_id) return True def _generate_new_id(self): self._session_id = text_(binascii.hexlify(os.urandom(20)))