Пример #1
0
    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
Пример #2
0
    def __init__(self, secret, salt='paildocket-auth', cookie_name='auth',
                 secure=False, http_only=False, path='/', domains=None,
                 hashalg='sha512', timeout=None, reissue_time=None,
                 max_age=None, debug=False):
        """
        :param secret:
            Secret for the secure cookie generator.
        :param salt:
            Salt for collision protection (but don't use the same
            secret anyway).
        :param cookie_name:
            Name of the auth ticket cookie.
        :param secure:
            Only send the cookie over a secure connection.
        :param http_only:
            Set HttpOnly flag on the cookie to prevent access by
            JavaScript (on conforming browsers).
        :param path:
            The path for the cookie.
        :param domains:
            The domains for the cookie.
        :param hashalg:
            The hashing algorithm to use for the cookie signature.
        :param timeout:
            The maximum age of the ticket, in seconds. When this amount
            of time passes after the ticket is created, the ticket will
            no longer be valid.
        :param reissue_time:
            The number of seconds before an authentication token cookie
            is reissued. If provided, must be less than `timeout`.
        :param max_age:
            The maximum age of the cookie in the browser, in seconds.
            If provided, must be greater than `timeout` and
            `reissue_time`.
        :param debug:
            If true, log verbosely.
        """
        if secure:
            raise NotImplementedError
        if reissue_time:
            if not timeout or reissue_time >= timeout:
                raise ValueError('reissue_time must be less than timeout')
        if max_age:
            if not timeout or timeout >= max_age:
                raise ValueError('max_age must be greater than timeout')
            if not reissue_time or reissue_time >= max_age:
                raise ValueError('max_age must be greater than reissue_time')

        self.cookie = SignedCookieProfile(
            secret, salt, cookie_name,
            secure=secure,
            httponly=http_only,
            path=path,
            domains=domains,
            hashalg=hashalg,
            max_age=max_age,
        )
        self.reissue_time = reissue_time
        self.timeout = timeout
        self.debug = debug
Пример #3
0
    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
Пример #4
0
def _adapter_for(name, profile):
    return match(
        Profile, {
            SignedProfile: (lambda config, secret, salt: SignedCookieProfile(
                secret, salt, name, **config)),
            UnsignedProfile: (lambda config: CookieProfile(name, **config))
        }, profile)
Пример #5
0
    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
Пример #6
0
def factory(_context, request):
    """Return a AuthCookieService instance for the passed context and request."""

    cookie = SignedCookieProfile(
        # This value is set in `h.auth` at the moment
        secret=request.registry.settings["h_auth_cookie_secret"],
        salt="authsanity",
        cookie_name="auth",
        secure=False,
        max_age=30 * 24 * 3600,  # 30 days
        httponly=True,
    )

    return AuthCookieService(
        request.db,
        user_service=request.find_service(name="user"),
        cookie=cookie.bind(request),
    )
Пример #7
0
        def __init__(self, context, request):
            self.domains = domains

            if self.domains is None:
                self.domains = []
                self.domains.append(request.domain)

            self.cookie = SignedCookieProfile(
                secret,
                'authsanity',
                cookie_name,
                secure=secure,
                max_age=max_age,
                httponly=httponly,
                path=path,
                domains=domains,
                hashalg=hashalg,
            )
            # Bind the cookie to the current request
            self.cookie = self.cookie.bind(request)
Пример #8
0
    def test_it(self, pyramid_request, SignedCookieProfile, AuthCookieService,
                user_service):
        pyramid_request.registry.settings[
            "h_auth_cookie_secret"] = sentinel.cookie_secret

        cookie_service = factory(sentinel.context, pyramid_request)

        SignedCookieProfile.assert_called_once_with(
            secret=pyramid_request.registry.settings["h_auth_cookie_secret"],
            salt="authsanity",
            cookie_name="auth",
            secure=False,
            max_age=2592000,
            httponly=True,
        )
        SignedCookieProfile.return_value.bind.assert_called_once_with(
            pyramid_request)
        AuthCookieService.assert_called_once_with(
            pyramid_request.db,
            user_service=user_service,
            cookie=SignedCookieProfile.return_value.bind.return_value,
        )
        assert cookie_service == AuthCookieService.return_value
Пример #9
0
    class CookieAuthSource(object):
        vary = ['Cookie']

        def __init__(self, context, request):
            self.domains = domains

            if self.domains is None:
                self.domains = []
                self.domains.append(request.domain)

            self.cookie = SignedCookieProfile(
                secret,
                'authsanity',
                cookie_name,
                secure=secure,
                max_age=max_age,
                httponly=httponly,
                path=path,
                domains=domains,
                hashalg=hashalg,
            )
            # Bind the cookie to the current request
            self.cookie = self.cookie.bind(request)

        def get_value(self):
            val = self.cookie.get_value()

            if val is None:
                return [None, None]

            return val

        def headers_remember(self, value):
            return self.cookie.get_headers(value, domains=self.domains)

        def headers_forget(self):
            return self.cookie.get_headers('', max_age=0)
Пример #10
0
    class CookieAuthSource(object):
        vary = ['Cookie']

        def __init__(self, context, request):
            self.domains = domains

            if self.domains is None:
                self.domains = []
                self.domains.append(request.domain)

            self.cookie = SignedCookieProfile(
                secret,
                'authsanity',
                cookie_name,
                secure=secure,
                max_age=max_age,
                httponly=httponly,
                path=path,
                domains=domains,
                hashalg=hashalg,
                )
            # Bind the cookie to the current request
            self.cookie = self.cookie.bind(request)

        def get_value(self):
            val = self.cookie.get_value()

            if val is None:
                return [None, None]

            return val

        def headers_remember(self, value):
            return self.cookie.get_headers(value, domains=self.domains)

        def headers_forget(self):
            return self.cookie.get_headers('', max_age=0)
Пример #11
0
        def __init__(self, context, request):
            self.domains = domains

            if self.domains is None:
                self.domains = []
                self.domains.append(request.domain)

            self.cookie = SignedCookieProfile(
                secret,
                'authsanity',
                cookie_name,
                secure=secure,
                max_age=max_age,
                httponly=httponly,
                path=path,
                domains=domains,
                hashalg=hashalg,
                )
            # Bind the cookie to the current request
            self.cookie = self.cookie.bind(request)
Пример #12
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)
Пример #13
0
        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)
Пример #14
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)
Пример #15
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)))
Пример #16
0
        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)
Пример #17
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,
            '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)
Пример #18
0
class PaildocketAuthenticationPolicy(object):
    def __init__(self, secret, salt='paildocket-auth', cookie_name='auth',
                 secure=False, http_only=False, path='/', domains=None,
                 hashalg='sha512', timeout=None, reissue_time=None,
                 max_age=None, debug=False):
        """
        :param secret:
            Secret for the secure cookie generator.
        :param salt:
            Salt for collision protection (but don't use the same
            secret anyway).
        :param cookie_name:
            Name of the auth ticket cookie.
        :param secure:
            Only send the cookie over a secure connection.
        :param http_only:
            Set HttpOnly flag on the cookie to prevent access by
            JavaScript (on conforming browsers).
        :param path:
            The path for the cookie.
        :param domains:
            The domains for the cookie.
        :param hashalg:
            The hashing algorithm to use for the cookie signature.
        :param timeout:
            The maximum age of the ticket, in seconds. When this amount
            of time passes after the ticket is created, the ticket will
            no longer be valid.
        :param reissue_time:
            The number of seconds before an authentication token cookie
            is reissued. If provided, must be less than `timeout`.
        :param max_age:
            The maximum age of the cookie in the browser, in seconds.
            If provided, must be greater than `timeout` and
            `reissue_time`.
        :param debug:
            If true, log verbosely.
        """
        if secure:
            raise NotImplementedError
        if reissue_time:
            if not timeout or reissue_time >= timeout:
                raise ValueError('reissue_time must be less than timeout')
        if max_age:
            if not timeout or timeout >= max_age:
                raise ValueError('max_age must be greater than timeout')
            if not reissue_time or reissue_time >= max_age:
                raise ValueError('max_age must be greater than reissue_time')

        self.cookie = SignedCookieProfile(
            secret, salt, cookie_name,
            secure=secure,
            httponly=http_only,
            path=path,
            domains=domains,
            hashalg=hashalg,
            max_age=max_age,
        )
        self.reissue_time = reissue_time
        self.timeout = timeout
        self.debug = debug

    def _new_ticket(self):
        randbytes = 32
        hashalg = 'sha256'
        return hashlib.new(hashalg, urandom(randbytes)).hexdigest()

    def remember(self, request, principal, **kwargs):
        if self.debug:
            logger.debug(
                '`remember` called with principal {0!r}'.format(principal))
        value = {}
        value['principal'] = principal
        value['ticket'] = ticket = self._new_ticket()
        value['issued'] = datetime.datetime.utcnow().strftime(_iso_format)

        q = request.db_session.query(User).filter(User.email == principal)
        user = q.first()

        if user is None:
            raise ValueError('Unknown principal {0!r}'.format(principal))

        remote_address = request.environ.get('REMOTE_ADDR')
        user.tickets.append(
            UserTicket(ticket=ticket, remote_address=remote_address))

        return self.cookie.get_headers(value)

    def forget(self, request):
        if self.debug:
            logger.debug('`forget` called')
        ticket_instance = request.auth.get('ticket_instance')
        if ticket_instance:
            request.db_session.delete(ticket_instance)
        request.auth['revoked'] = True
        return self.cookie.get_headers('', max_age=0)

    def unauthenticated_userid(self, request):
        """No support for unauthenticated userid"""
        if self.debug:
            logger.debug('`unauthenticated_userid` called')
        return None

    def authenticated_userid(self, request):
        # TODO: break this up, it's way too complex
        if self.debug:
            logger.debug('`authenticated_userid` called')

        userid = request.auth.get('userid')
        if userid is not None:
            if self.debug:
                fmt = 'Found userid {0!r} already in request.auth'
                logger.debug(fmt.format(userid))
            return userid

        result = self.cookie.bind(request).get_value()

        if not result:
            if self.debug:
                logger.debug('Failed to find auth ticket in cookie')
            return None

        principal = result['principal']
        ticket = result['ticket']
        issued_unparsed = result['issued']
        issued = datetime.datetime.strptime(issued_unparsed, _iso_format)

        if self.debug:
            fmt = (
                'Cookie contains ticket {0!r} for principal {1!r} issued {2!r}'
            )
            logger.debug(fmt.format(ticket, principal, issued_unparsed))

        ticket_instance = UserTicket.find_ticket_with_principal(
            request.db_session, ticket, principal)

        if ticket_instance is None:
            fmt = (
                'Failed to locate ticket {0!r} for principal {1!r} in database'
            )
            logger.debug(fmt.format(ticket, principal))
            return None

        userid = ticket_instance.user_id

        # TODO fix this, authenticated_userid must return None if timed out
        self._timeout_or_reissue(request, ticket_instance, issued, principal)

        request.auth['userid'] = userid
        request.auth['ticket_instance'] = ticket_instance

        return userid

    def effective_principals(self, request):
        principals = [Everyone]
        userid = self.authenticated_userid(request)

        if userid is None:
            return principals

        if request.user.admin:
            principals.append(Administrator)

        principals.append(Authenticated)
        principals.append(request.user.principal)

        return principals

    def _timeout_or_reissue(self, request, ticket_instance, issued, principal):
        now = datetime.datetime.utcnow()
        headers = self._get_timeout_headers(request, now, ticket_instance)
        if not headers:
            headers = self._get_reissue_headers(
                request, now, ticket_instance, issued, principal)

        if headers:
            def add_reissue_or_revoke_headers(request, response):
                auth = request.auth
                if 'reissued' not in auth and 'revoked' not in auth:
                    for k, v in headers:
                        response.headerlist.append((k, v))
            request.add_response_callback(add_reissue_or_revoke_headers)

    def _get_timeout_headers(self, request, now, ticket_instance):
        if self.timeout:
            elapsed = (now - ticket_instance.created)
            if elapsed.total_seconds() > self.timeout:
                return self.forget(request)

    def _get_reissue_headers(self, request, now, ticket_instance, issued,
                             principal):
        if self.reissue_time and 'reissued' not in request.auth:
            elapsed = (now - issued)
            if elapsed.total_seconds() > self.reissue_time:
                request.db_session.delete(ticket_instance)
                request.auth['reissued'] = True
                return self.remember(request, principal)
Пример #19
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)))