<commit_before>"""
flask_kvsession is a drop-in replacement module for Flask sessions that uses a
:class:`simplekv.KeyValueStore` as a backend for server-side sessions.
"""

import calendar
try:
    import cPickle as pickle
except ImportError:
    import pickle
from datetime import datetime
from random import SystemRandom
import re

from flask import current_app
from flask.sessions import SessionMixin, SessionInterface
from itsdangerous import Signer, BadSignature
from simplekv import TimeToLiveMixin
from werkzeug.datastructures import CallbackDict

class SessionID(object):
    """Helper class for parsing session ids.

    Internally, Flask-KVSession stores session ids that are serialized as
    ``KEY_CREATED``, where ``KEY`` is a random number (the sessions "true" id)
    and ``CREATED`` a UNIX-timestamp of when the session was created.

    :param id: An integer to be used as the session key.
    :param created: A :class:`~datetime.datetime` instance or None. A value of
                    None will result in :meth:`~datetime.datetime.utcnow()` to
                    be used.
    """
    def __init__(self, id, created=None):
        if None == created:
            created = datetime.utcnow()

        self.id = id
        self.created = created

    def has_expired(self, lifetime, now=None):
        """Report if the session key has expired.

        :param lifetime: A :class:`datetime.timedelta` that specifies the
                         maximum age this :class:`SessionID` should be checked
                         against.
        :param now: If specified, use this :class:`~datetime.datetime` instance
                         instead of :meth:`~datetime.datetime.utcnow()` as the
                         current time.
        """
        now = now or datetime.utcnow()
        return now > self.created + lifetime

    def serialize(self):
        """Serializes to the standard form of ``KEY_CREATED``"""
        return '%x_%x' % (self.id,
                          calendar.timegm(self.created.utctimetuple()))

    @classmethod
    def unserialize(cls, string):
        """Unserializes from a string.

        :param string: A string created by :meth:`serialize`.
        """
        id_s, created_s = string.split('_')
        return cls(int(id_s, 16),
                   datetime.utcfromtimestamp(int(created_s, 16)))

class KVSession(CallbackDict, SessionMixin):
    """Replacement session class.

    Instances of this class will replace the session (and thus be available
    through things like :attr:`flask.session`.

    The session class will save data to the store only when necessary, empty
    sessions will not be stored at all."""
    def __init__(self, initial=None):
        def _on_update(d):
            d.modified = True

        CallbackDict.__init__(self, initial, _on_update)

        # we no initial data was supplied for the session, it starts out as
        # non-modified
        self.modified = True if initial else False

    def destroy(self):
        """Destroys a session completely, by deleting all keys and removing it
        from the internal store immediately.

        This allows removing a session for security reasons, e.g. a login
        stored in a session will cease to exist if the session is destroyed.
        """
        for k in list(self.keys()):
            del self[k]

        if getattr(self, 'sid_s', None):
            current_app.kvsession_store.delete(self.sid_s)
            self.sid_s = None

        self.modified = False
        self.new = False

    def regenerate(self):
        """Generate a new session id for this session.

        To avoid vulnerabilities through `session fixation attacks
        <http://en.wikipedia.org/wiki/Session_fixation>`_, this function can be
        called after an action like a login has taken place. The session will
        be copied over to a new session id and the old one removed.
        """
        self.modified = True

        if getattr(self, 'sid_s', None):
            # delete old session
            current_app.kvsession_store.delete(self.sid_s)

            # remove sid_s, set modified
            self.sid_s = None
            self.modified = True

            # save_session() will take care of saving the session now

class KVSessionInterface(SessionInterface):
    serialization_method = pickle
    session_class = KVSession

    def open_session(self, app, request):
        key = app.secret_key

        if key is not None:
            session_cookie = request.cookies.get(
                app.config['SESSION_COOKIE_NAME'],
                None
            )

            s = None

            if session_cookie:
                try:
                    # restore the cookie, if it has been manipulated,
                    # we will find out here
                    sid_s = Signer(app.secret_key).unsign(
                        session_cookie
                    ).decode('ascii')
                    sid = SessionID.unserialize(sid_s)

                    if sid.has_expired(
                            app.config['PERMANENT_SESSION_LIFETIME']):
                        # we reach this point if a "non-permanent" session has
                        # expired, but is made permanent. silently ignore the
                        # error with a new session
                        raise KeyError

                    # retrieve from store
                    s = self.session_class(self.serialization_method.loads(
                        current_app.kvsession_store.get(sid_s)
                    ))
                    s.sid_s = sid_s
                except (BadSignature, KeyError):
                    # either the cookie was manipulated or we did not find the
                    # session in the backend.
                    pass

            if s is None:
                s = self.session_class()  # create an empty session
                s.new = True

            return s

    def save_session(self, app, session, response):
        # we only save modified sessions
        if session.modified:
            # create a new session id if requested (by setting sid_s to None)
            # this makes it possible to avoid session fixation
            if not getattr(session, 'sid_s', None):
                session.sid_s = SessionID(
                    current_app.config['SESSION_RANDOM_SOURCE'].getrandbits(
                        app.config['SESSION_KEY_BITS']
                    )
                ).serialize()

            # save the session, now its no longer new (or modified)
            data = self.serialization_method.dumps(dict(session))
            store = current_app.kvsession_store

            if isinstance(store, TimeToLiveMixin):
                # TTL is supported
                ttl = current_app.permanent_session_lifetime.total_seconds()
                store.put(session.sid_s, data, ttl)
            else:
                store.put(session.sid_s, data)

            session.new = False
            session.modified = False

            # save sid_s in cookie
            cookie_data = Signer(app.secret_key).sign(
                session.sid_s.encode('ascii')
            )

            response.set_cookie(key=app.config['SESSION_COOKIE_NAME'],
                                value=cookie_data,
                                expires=self.get_expiration_time(app, session),
                                domain=self.get_cookie_domain(app),
                                secure=app.config['SESSION_COOKIE_SECURE'],
                                httponly=app.config['SESSION_COOKIE_HTTPONLY'])

class KVSessionExtension(object):
    """Activates Flask-KVSession for an application.

    :param session_kvstore: An object supporting the
                            `simplekv.KeyValueStore` interface that session
                            data will be store in.
    :param app: The app to activate. If not `None`, this is essentially the
                same as calling :meth:`init_app` later."""
    key_regex = re.compile('^[0-9a-f]+_[0-9a-f]+$')

    def __init__(self, session_kvstore=None, app=None):
        self.default_kvstore = session_kvstore

        if app and session_kvstore:
            self.init_app(app)

    def cleanup_sessions(self, app=None):
        """Removes all expired session from the store.

        Periodically, this function can be called to remove sessions from
        the backend store that have expired, as they are not removed
        automatically unless the backend supports time-to-live and has been
        configured appropriately (see :class:`~simplekv.TimeToLiveMixin`).

        This function retrieves all session keys, checks they are older than
        ``PERMANENT_SESSION_LIFETIME`` and if so, removes them.

        Note that no distinction is made between non-permanent and permanent
        sessions.

        :param app: The app whose sessions should be cleaned up. If ``None``,
                    uses :py:attr:`flask.current_app`."""

        if not app:
            app = current_app
        for key in app.kvsession_store.keys():
            m = self.key_regex.match(key)
            now = datetime.utcnow()
            if m:
                # read id
                sid = SessionID.unserialize(key)

                # remove if expired
                if sid.has_expired(
                    app.config['PERMANENT_SESSION_LIFETIME'],
                    now
                ):
                    app.kvsession_store.delete(key)

    def init_app(self, app, session_kvstore=None):
        """Initialize application and KVSession.

        This will replace the session management of the application with
        Flask-KVSession's.

        :param app: The :class:`~flask.Flask` app to be initialized."""
        app.config.setdefault('SESSION_KEY_BITS', 64)
        app.config.setdefault('SESSION_RANDOM_SOURCE', SystemRandom())

        if not session_kvstore and not self.default_kvstore:
            raise ValueError('Must supply session_kvstore either on '
                             'construction or init_app().')

        # set store on app, either use default
        # or supplied argument
        app.kvsession_store = session_kvstore or self.default_kvstore

        app.session_interface = KVSessionInterface()
<commit_msg>Fixed self.modified default value on session (fixes #20).<commit_after>"""
flask_kvsession is a drop-in replacement module for Flask sessions that uses a
:class:`simplekv.KeyValueStore` as a backend for server-side sessions.
"""

import calendar
try:
    import cPickle as pickle
except ImportError:
    import pickle
from datetime import datetime
from random import SystemRandom
import re

from flask import current_app
from flask.sessions import SessionMixin, SessionInterface
from itsdangerous import Signer, BadSignature
from simplekv import TimeToLiveMixin
from werkzeug.datastructures import CallbackDict

class SessionID(object):
    """Helper class for parsing session ids.

    Internally, Flask-KVSession stores session ids that are serialized as
    ``KEY_CREATED``, where ``KEY`` is a random number (the sessions "true" id)
    and ``CREATED`` a UNIX-timestamp of when the session was created.

    :param id: An integer to be used as the session key.
    :param created: A :class:`~datetime.datetime` instance or None. A value of
                    None will result in :meth:`~datetime.datetime.utcnow()` to
                    be used.
    """
    def __init__(self, id, created=None):
        if None == created:
            created = datetime.utcnow()

        self.id = id
        self.created = created

    def has_expired(self, lifetime, now=None):
        """Report if the session key has expired.

        :param lifetime: A :class:`datetime.timedelta` that specifies the
                         maximum age this :class:`SessionID` should be checked
                         against.
        :param now: If specified, use this :class:`~datetime.datetime` instance
                         instead of :meth:`~datetime.datetime.utcnow()` as the
                         current time.
        """
        now = now or datetime.utcnow()
        return now > self.created + lifetime

    def serialize(self):
        """Serializes to the standard form of ``KEY_CREATED``"""
        return '%x_%x' % (self.id,
                          calendar.timegm(self.created.utctimetuple()))

    @classmethod
    def unserialize(cls, string):
        """Unserializes from a string.

        :param string: A string created by :meth:`serialize`.
        """
        id_s, created_s = string.split('_')
        return cls(int(id_s, 16),
                   datetime.utcfromtimestamp(int(created_s, 16)))

class KVSession(CallbackDict, SessionMixin):
    # modified is hardcoded as true in SessionMixin, override this
    # upon modification, we set this manually through _on_update (see
    # __init__)
    modified = False

    """Replacement session class.

    Instances of this class will replace the session (and thus be available
    through things like :attr:`flask.session`.

    The session class will save data to the store only when necessary, empty
    sessions will not be stored at all."""
    def __init__(self, initial=None):
        def _on_update(d):
            d.modified = True

        CallbackDict.__init__(self, initial, _on_update)

    def destroy(self):
        """Destroys a session completely, by deleting all keys and removing it
        from the internal store immediately.

        This allows removing a session for security reasons, e.g. a login
        stored in a session will cease to exist if the session is destroyed.
        """
        for k in list(self.keys()):
            del self[k]

        if getattr(self, 'sid_s', None):
            current_app.kvsession_store.delete(self.sid_s)
            self.sid_s = None

        self.modified = False
        self.new = False

    def regenerate(self):
        """Generate a new session id for this session.

        To avoid vulnerabilities through `session fixation attacks
        <http://en.wikipedia.org/wiki/Session_fixation>`_, this function can be
        called after an action like a login has taken place. The session will
        be copied over to a new session id and the old one removed.
        """
        self.modified = True

        if getattr(self, 'sid_s', None):
            # delete old session
            current_app.kvsession_store.delete(self.sid_s)

            # remove sid_s, set modified
            self.sid_s = None
            self.modified = True

            # save_session() will take care of saving the session now

class KVSessionInterface(SessionInterface):
    serialization_method = pickle
    session_class = KVSession

    def open_session(self, app, request):
        key = app.secret_key

        if key is not None:
            session_cookie = request.cookies.get(
                app.config['SESSION_COOKIE_NAME'],
                None
            )

            s = None

            if session_cookie:
                try:
                    # restore the cookie, if it has been manipulated,
                    # we will find out here
                    sid_s = Signer(app.secret_key).unsign(
                        session_cookie
                    ).decode('ascii')
                    sid = SessionID.unserialize(sid_s)

                    if sid.has_expired(
                            app.config['PERMANENT_SESSION_LIFETIME']):
                        # we reach this point if a "non-permanent" session has
                        # expired, but is made permanent. silently ignore the
                        # error with a new session
                        raise KeyError

                    # retrieve from store
                    s = self.session_class(self.serialization_method.loads(
                        current_app.kvsession_store.get(sid_s)
                    ))
                    s.sid_s = sid_s
                except (BadSignature, KeyError):
                    # either the cookie was manipulated or we did not find the
                    # session in the backend.
                    pass

            if s is None:
                s = self.session_class()  # create an empty session
                s.new = True

            return s

    def save_session(self, app, session, response):
        # we only save modified sessions
        if session.modified:
            # create a new session id if requested (by setting sid_s to None)
            # this makes it possible to avoid session fixation
            if not getattr(session, 'sid_s', None):
                session.sid_s = SessionID(
                    current_app.config['SESSION_RANDOM_SOURCE'].getrandbits(
                        app.config['SESSION_KEY_BITS']
                    )
                ).serialize()

            # save the session, now its no longer new (or modified)
            data = self.serialization_method.dumps(dict(session))
            store = current_app.kvsession_store

            if isinstance(store, TimeToLiveMixin):
                # TTL is supported
                ttl = current_app.permanent_session_lifetime.total_seconds()
                store.put(session.sid_s, data, ttl)
            else:
                store.put(session.sid_s, data)

            session.new = False
            session.modified = False

            # save sid_s in cookie
            cookie_data = Signer(app.secret_key).sign(
                session.sid_s.encode('ascii')
            )

            response.set_cookie(key=app.config['SESSION_COOKIE_NAME'],
                                value=cookie_data,
                                expires=self.get_expiration_time(app, session),
                                domain=self.get_cookie_domain(app),
                                secure=app.config['SESSION_COOKIE_SECURE'],
                                httponly=app.config['SESSION_COOKIE_HTTPONLY'])

class KVSessionExtension(object):
    """Activates Flask-KVSession for an application.

    :param session_kvstore: An object supporting the
                            `simplekv.KeyValueStore` interface that session
                            data will be store in.
    :param app: The app to activate. If not `None`, this is essentially the
                same as calling :meth:`init_app` later."""
    key_regex = re.compile('^[0-9a-f]+_[0-9a-f]+$')

    def __init__(self, session_kvstore=None, app=None):
        self.default_kvstore = session_kvstore

        if app and session_kvstore:
            self.init_app(app)

    def cleanup_sessions(self, app=None):
        """Removes all expired session from the store.

        Periodically, this function can be called to remove sessions from
        the backend store that have expired, as they are not removed
        automatically unless the backend supports time-to-live and has been
        configured appropriately (see :class:`~simplekv.TimeToLiveMixin`).

        This function retrieves all session keys, checks they are older than
        ``PERMANENT_SESSION_LIFETIME`` and if so, removes them.

        Note that no distinction is made between non-permanent and permanent
        sessions.

        :param app: The app whose sessions should be cleaned up. If ``None``,
                    uses :py:attr:`flask.current_app`."""

        if not app:
            app = current_app
        for key in app.kvsession_store.keys():
            m = self.key_regex.match(key)
            now = datetime.utcnow()
            if m:
                # read id
                sid = SessionID.unserialize(key)

                # remove if expired
                if sid.has_expired(
                    app.config['PERMANENT_SESSION_LIFETIME'],
                    now
                ):
                    app.kvsession_store.delete(key)

    def init_app(self, app, session_kvstore=None):
        """Initialize application and KVSession.

        This will replace the session management of the application with
        Flask-KVSession's.

        :param app: The :class:`~flask.Flask` app to be initialized."""
        app.config.setdefault('SESSION_KEY_BITS', 64)
        app.config.setdefault('SESSION_RANDOM_SOURCE', SystemRandom())

        if not session_kvstore and not self.default_kvstore:
            raise ValueError('Must supply session_kvstore either on '
                             'construction or init_app().')

        # set store on app, either use default
        # or supplied argument
        app.kvsession_store = session_kvstore or self.default_kvstore

        app.session_interface = KVSessionInterface()
<|endoftext|>


<commit_before>"""
rate limiting strategies
"""

from abc import ABCMeta, abstractmethod
import weakref
import six

@six.add_metaclass(ABCMeta)
class RateLimiter(object):
    def __init__(self, storage):
        self.storage = weakref.ref(storage)

    @abstractmethod
    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        raise NotImplementedError

    @abstractmethod
    def check(self, item, *identifiers):
        """
        checks whether the rate limit has been exceeded or not

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        raise NotImplementedError

class MovingWindowRateLimiter(RateLimiter):
    def __init__(self, storage):
        if not hasattr(storage, "acquire_entry"):
            raise NotImplementedError("MovingWindowRateLimiting is not implemented for storage of type %s" % storage.__class__)
        super(MovingWindowRateLimiter, self).__init__(storage)

    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return self.storage().acquire_entry(item.key_for(*identifiers), item.amount, item.expiry)

    def check(self, item, *identifiers):
        """
        checks whether the rate limit has been exceeded or not

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return self.storage().acquire_entry(item.key_for(*identifiers), item.amount, item.expiry, True)

class FixedWindowRateLimiter(RateLimiter):
    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return (
            self.storage().incr(item.key_for(*identifiers), item.expiry)
            <= item.amount
        )
    def check(self, item, *identifiers):
        """
        checks whether the rate limit has been exceeded or not

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return self.storage().get(item.key_for(*identifiers)) <= item.amount

class FixedWindowElasticExpiryRateLimiter(FixedWindowRateLimiter):
    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return (
            self.storage().incr(item.key_for(*identifiers), item.expiry, True)
            <= item.amount
        )

STRATEGIES = {
    "fixed-window": FixedWindowRateLimiter,
    "fixed-window-elastic": FixedWindowElasticExpiryRateLimiter,
    "moving-window": MovingWindowRateLimiter
}<commit_msg>sync configuration string with docs<commit_after>"""
rate limiting strategies
"""

from abc import ABCMeta, abstractmethod
import weakref
import six

@six.add_metaclass(ABCMeta)
class RateLimiter(object):
    def __init__(self, storage):
        self.storage = weakref.ref(storage)

    @abstractmethod
    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        raise NotImplementedError

    @abstractmethod
    def check(self, item, *identifiers):
        """
        checks whether the rate limit has been exceeded or not

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        raise NotImplementedError

class MovingWindowRateLimiter(RateLimiter):
    def __init__(self, storage):
        if not hasattr(storage, "acquire_entry"):
            raise NotImplementedError("MovingWindowRateLimiting is not implemented for storage of type %s" % storage.__class__)
        super(MovingWindowRateLimiter, self).__init__(storage)

    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return self.storage().acquire_entry(item.key_for(*identifiers), item.amount, item.expiry)

    def check(self, item, *identifiers):
        """
        checks whether the rate limit has been exceeded or not

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return self.storage().acquire_entry(item.key_for(*identifiers), item.amount, item.expiry, True)

class FixedWindowRateLimiter(RateLimiter):
    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return (
            self.storage().incr(item.key_for(*identifiers), item.expiry)
            <= item.amount
        )
    def check(self, item, *identifiers):
        """
        checks whether the rate limit has been exceeded or not

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return self.storage().get(item.key_for(*identifiers)) <= item.amount

class FixedWindowElasticExpiryRateLimiter(FixedWindowRateLimiter):
    def hit(self, item, *identifiers):
        """
        creates a hit on the rate limit and returns True if successful.

        :param item: a :class:`RateLimitItem` instance
        :param identifiers: variable list of strings to uniquely identify the
         limit
        :return: True/False
        """
        return (
            self.storage().incr(item.key_for(*identifiers), item.expiry, True)
            <= item.amount
        )

STRATEGIES = {
    "fixed-window": FixedWindowRateLimiter,
    "fixed-window-elastic-expiry": FixedWindowElasticExpiryRateLimiter,
    "moving-window": MovingWindowRateLimiter
}<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    flask_principal
    ~~~~~~~~~~~~~~~

    Identity management for Flask.

    :copyright: (c) 2012 by Ali Afshar.
    :license: MIT, see LICENSE for more details.

"""

from __future__ import with_statement

__version__ = '0.3.2'

import sys
from functools import partial, wraps
from collections import deque

try:
    from collections import namedtuple
except ImportError:
    from .backport import namedtuple

from flask import g, session, current_app, abort, request
from flask.signals import Namespace

signals = Namespace()
"""Namespace for principal's signals.
"""

identity_changed = signals.signal('identity-changed', doc=(
"""Signal sent when the identity for a request has been changed.

Actual name: ``identity-changed``

Authentication providers should send this signal when authentication has been
successfully performed. Flask-IdentityContext connects to this signal and
causes the identity to be saved in the session.

For example::

    from flaskext.principal import Identity, identity_changed

    def login_view(req):
        username = req.form.get('username')
        # check the credentials
        identity_changed.send(app, identity=Identity(username))
"""))

identity_loaded = signals.signal('identity-loaded', doc=(
"""Signal sent when the identity has been initialised for a request.

Actual name: ``identity-loaded``

Identity information providers should connect to this signal to perform two
major activities:

    1. Populate the identity object with the necessary authorization provisions.
    2. Load any additional user information.

For example::

    from flaskext.principal import identity_loaded, RoleNeed, UserNeed

    @identity_loaded.connect
    def on_identity_loaded(sender, identity):
        # Get the user information from the db
        user = db.get(identity.name)
        # Update the roles that a user can provide
        for role in user.roles:
            identity.provides.add(RoleNeed(role.name))
        # Save the user somewhere so we only look it up once
        identity.user = user
"""))

Need = namedtuple('Need', ['method', 'value'])
"""A required need

This is just a named tuple, and practically any tuple will do.

The ``method`` attribute can be used to look up element 0, and the ``value``
attribute can be used to look up element 1.
"""

UserNeed = partial(Need, 'name')
UserNeed.__doc__ = """A need with the method preset to `"name"`."""

RoleNeed = partial(Need, 'role')
RoleNeed.__doc__ = """A need with the method preset to `"role"`."""

TypeNeed = partial(Need, 'type')
TypeNeed.__doc__ = """A need with the method preset to `"role"`."""

ActionNeed = partial(Need, 'action')
TypeNeed.__doc__ = """A need with the method preset to `"action"`."""

ItemNeed = namedtuple('RowNeed', ['method', 'value', 'type'])
"""A required item need

An item need is just a named tuple, and practically any tuple will do. In
addition to other Needs, there is a type, for example this could be specified
as::

    RowNeed('update', 27, 'posts')
    ('update', 27, 'posts') # or like this

And that might describe the permission to update a particular blog post. In
reality, the developer is free to choose whatever convention the permissions
are.
"""

class PermissionDenied(RuntimeError):
    """Permission denied to the resource
    """

class Identity(object):
    """Represent the user's identity.

    :param name: The username
    :param auth_type: The authentication type used to confirm the user's
                      identity.

    The identity is used to represent the user's identity in the system. This
    object is created on login, or on the start of the request as loaded from
    the user's session.

    Once loaded it is sent using the `identity-loaded` signal, and should be
    populated with additional required information.

    Needs that are provided by this identity should be added to the `provides`
    set after loading.
    """
    def __init__(self, name, auth_type=''):
        self.name = name
        self.auth_type = auth_type

        self.provides = set()
        """A set of needs provided by this user

        Provisions can be added using the `add` method, for example::

            identity = Identity('ali')
            identity.provides.add(('role', 'admin'))
        """

    def can(self, permission):
        """Whether the identity has access to the permission.

        :param permission: The permission to test provision for.
        """
        return permission.allows(self)

class AnonymousIdentity(Identity):
    """An anonymous identity

    :attr name: `"anon"`
    """

    def __init__(self):
        Identity.__init__(self, 'anon')

class IdentityContext(object):
    """The context of an identity for a permission.

    .. note:: The principal is usually created by the flaskext.Permission.require method
              call for normal use-cases.

    The principal behaves as either a context manager or a decorator. The
    permission is checked for provision in the identity, and if available the
    flow is continued (context manager) or the function is executed (decorator).
    """

    def __init__(self, permission, http_exception=None):
        self.permission = permission
        self.http_exception = http_exception
        """The permission of this principal
        """

    @property
    def identity(self):
        """The identity of this principal
        """
        return g.identity

    def can(self):
        """Whether the identity has access to the permission
        """
        return self.identity.can(self.permission)

    def __call__(self, f):
        @wraps(f)
        def _decorated(*args, **kw):
            with self:
                rv = f(*args, **kw)
            return rv
        return _decorated

    def __enter__(self):
        # check the permission here
        if not self.can():
            if self.http_exception:
                abort(self.http_exception, self.permission)
            raise PermissionDenied(self.permission)

    def __exit__(self, *args):
        return False

class Permission(object):
    """Represents needs, any of which must be present to access a resource

    :param needs: The needs for this permission
    """
    def __init__(self, *needs):
        """A set of needs, any of which must be present in an identity to have
        access.
        """

        self.needs = set(needs)
        self.excludes = set()

    def __nonzero__(self):
        """Equivalent to ``self.can()``.
        """
        return bool(self.can())

    def __and__(self, other):
        """Does the same thing as ``self.union(other)``
        """
        return self.union(other)

    def __or__(self, other):
        """Does the same thing as ``self.difference(other)``
        """
        return self.difference(other)

    def __contains__(self, other):
        """Does the same thing as ``other.issubset(self)``.
        """
        return other.issubset(self)

    def require(self, http_exception=None):
        """Create a principal for this permission.

        The principal may be used as a context manager, or a decroator.

        If ``http_exception`` is passed then ``abort()`` will be called
        with the HTTP exception code. Otherwise a ``PermissionDenied``
        exception will be raised if the identity does not meet the
        requirements.

        :param http_exception: the HTTP exception code (403, 401 etc)
        """
        return IdentityContext(self, http_exception)

    def test(self, http_exception=None):
        """
        Checks if permission available and raises relevant exception
        if not. This is useful if you just want to check permission
        without wrapping everything in a require() block.

        This is equivalent to::

            with permission.require():
                pass
        """

        with self.require(http_exception):
            pass

    def reverse(self):
        """
        Returns reverse of current state (needs->excludes, excludes->needs)
        """

        p = Permission()
        p.needs.update(self.excludes)
        p.excludes.update(self.needs)
        return p

    def union(self, other):
        """Create a new permission with the requirements of the union of this
        and other.

        :param other: The other permission
        """
        p = Permission(*self.needs.union(other.needs))
        p.excludes.update(self.excludes.union(other.excludes))
        return p

    def difference(self, other):
        """Create a new permission consisting of requirements in this
        permission and not in the other.
        """

        p = Permission(*self.needs.difference(other.needs))
        p.excludes.update(self.excludes.difference(other.excludes))
        return p

    def issubset(self, other):
        """Whether this permission needs are a subset of another

        :param other: The other permission
        """
        return self.needs.issubset(other.needs) and \
               self.excludes.issubset(other.excludes)

    def allows(self, identity):
        """Whether the identity can access this permission.

        :param identity: The identity
        """
        if self.needs and not self.needs.intersection(identity.provides):
            return False

        if self.excludes and self.excludes.intersection(identity.provides):
            return False

        return True

    def can(self):
        """Whether the required context for this permission has access

        This creates an identity context and tests whether it can access this
        permission
        """
        return self.require().can()

class Denial(Permission):
    """
    Shortcut class for passing excluded needs.
    """

    def __init__(self, *excludes):
        self.excludes = set(excludes)
        self.needs = set()

def session_identity_loader():
    if 'identity.name' in session and 'identity.auth_type' in session:
        identity = Identity(session['identity.name'],
                            session['identity.auth_type'])
        return identity

def session_identity_saver(identity):
    session['identity.name'] = identity.name
    session['identity.auth_type'] = identity.auth_type
    session.modified = True

class Principal(object):
    """Principal extension

    :param app: The flask application to extend
    :param use_sessions: Whether to use sessions to extract and store
                         identification.
    :param skip_static: Whether to ignore static endpoints.
    """
    def __init__(self, app=None, use_sessions=True, skip_static=False):
        self.identity_loaders = deque()
        self.identity_savers = deque()
        # XXX This will probably vanish for a better API
        self.use_sessions = use_sessions
        self.skip_static = skip_static

        if hasattr(app, 'static_url_path'):
            self._static_path = app.static_url_path
        else:
            self._static_path = app.static_path

        if app is not None:
            self._init_app(app)

    def _init_app(self, app):
        app.before_request(self._on_before_request)
        identity_changed.connect(self._on_identity_changed, app)

        if self.use_sessions:
            self.identity_loader(session_identity_loader)
            self.identity_saver(session_identity_saver)

    def set_identity(self, identity):
        """Set the current identity.

        :param identity: The identity to set
        """
        if self._is_static_route():
            return

        self._set_thread_identity(identity)
        for saver in self.identity_savers:
            saver(identity)

    def identity_loader(self, f):
        """Decorator to define a function as an identity loader.

        An identity loader function is called before request to find any
        provided identities. The first found identity is used to load from.

        For example::

            app = Flask(__name__)

            principals = Principal(app)

            @principals.identity_loader
            def load_identity_from_weird_usecase():
                return Identity('ali')
        """
        self.identity_loaders.appendleft(f)
        return f

    def identity_saver(self, f):
        """Decorator to define a function as an identity saver.

        An identity loader saver is called when the identity is set to persist
        it for the next request.

        For example::

            app = Flask(__name__)

            principals = Principal(app)

            @principals.identity_saver
            def save_identity_to_weird_usecase(identity):
                my_special_cookie['identity'] = identity
        """
        self.identity_savers.appendleft(f)
        return f

    def _set_thread_identity(self, identity):
        g.identity = identity
        identity_loaded.send(current_app._get_current_object(),
                             identity=identity)

    def _on_identity_changed(self, app, identity):
        self.set_identity(identity)

    def _on_before_request(self):
        if self._is_static_route():
            return

        g.identity = AnonymousIdentity()
        for loader in self.identity_loaders:
            identity = loader()
            if identity is not None:
                self.set_identity(identity)
                return

    def _is_static_route(self):
        return (self.skip_static and \
                request.path.startswith(self._static_path))
<commit_msg>Modified Principal to work in an application factory configuration.<commit_after># -*- coding: utf-8 -*-
"""
    flask_principal
    ~~~~~~~~~~~~~~~

    Identity management for Flask.

    :copyright: (c) 2012 by Ali Afshar.
    :license: MIT, see LICENSE for more details.

"""

from __future__ import with_statement

__version__ = '0.3.2'

import sys
from functools import partial, wraps
from collections import deque

try:
    from collections import namedtuple
except ImportError:
    from .backport import namedtuple

from flask import g, session, current_app, abort, request
from flask.signals import Namespace

signals = Namespace()
"""Namespace for principal's signals.
"""

identity_changed = signals.signal('identity-changed', doc=(
"""Signal sent when the identity for a request has been changed.

Actual name: ``identity-changed``

Authentication providers should send this signal when authentication has been
successfully performed. Flask-IdentityContext connects to this signal and
causes the identity to be saved in the session.

For example::

    from flaskext.principal import Identity, identity_changed

    def login_view(req):
        username = req.form.get('username')
        # check the credentials
        identity_changed.send(app, identity=Identity(username))
"""))

identity_loaded = signals.signal('identity-loaded', doc=(
"""Signal sent when the identity has been initialised for a request.

Actual name: ``identity-loaded``

Identity information providers should connect to this signal to perform two
major activities:

    1. Populate the identity object with the necessary authorization provisions.
    2. Load any additional user information.

For example::

    from flaskext.principal import identity_loaded, RoleNeed, UserNeed

    @identity_loaded.connect
    def on_identity_loaded(sender, identity):
        # Get the user information from the db
        user = db.get(identity.name)
        # Update the roles that a user can provide
        for role in user.roles:
            identity.provides.add(RoleNeed(role.name))
        # Save the user somewhere so we only look it up once
        identity.user = user
"""))

Need = namedtuple('Need', ['method', 'value'])
"""A required need

This is just a named tuple, and practically any tuple will do.

The ``method`` attribute can be used to look up element 0, and the ``value``
attribute can be used to look up element 1.
"""

UserNeed = partial(Need, 'name')
UserNeed.__doc__ = """A need with the method preset to `"name"`."""

RoleNeed = partial(Need, 'role')
RoleNeed.__doc__ = """A need with the method preset to `"role"`."""

TypeNeed = partial(Need, 'type')
TypeNeed.__doc__ = """A need with the method preset to `"role"`."""

ActionNeed = partial(Need, 'action')
TypeNeed.__doc__ = """A need with the method preset to `"action"`."""

ItemNeed = namedtuple('RowNeed', ['method', 'value', 'type'])
"""A required item need

An item need is just a named tuple, and practically any tuple will do. In
addition to other Needs, there is a type, for example this could be specified
as::

    RowNeed('update', 27, 'posts')
    ('update', 27, 'posts') # or like this

And that might describe the permission to update a particular blog post. In
reality, the developer is free to choose whatever convention the permissions
are.
"""

class PermissionDenied(RuntimeError):
    """Permission denied to the resource
    """

class Identity(object):
    """Represent the user's identity.

    :param name: The username
    :param auth_type: The authentication type used to confirm the user's
                      identity.

    The identity is used to represent the user's identity in the system. This
    object is created on login, or on the start of the request as loaded from
    the user's session.

    Once loaded it is sent using the `identity-loaded` signal, and should be
    populated with additional required information.

    Needs that are provided by this identity should be added to the `provides`
    set after loading.
    """
    def __init__(self, name, auth_type=''):
        self.name = name
        self.auth_type = auth_type

        self.provides = set()
        """A set of needs provided by this user

        Provisions can be added using the `add` method, for example::

            identity = Identity('ali')
            identity.provides.add(('role', 'admin'))
        """

    def can(self, permission):
        """Whether the identity has access to the permission.

        :param permission: The permission to test provision for.
        """
        return permission.allows(self)

class AnonymousIdentity(Identity):
    """An anonymous identity

    :attr name: `"anon"`
    """

    def __init__(self):
        Identity.__init__(self, 'anon')

class IdentityContext(object):
    """The context of an identity for a permission.

    .. note:: The principal is usually created by the flaskext.Permission.require method
              call for normal use-cases.

    The principal behaves as either a context manager or a decorator. The
    permission is checked for provision in the identity, and if available the
    flow is continued (context manager) or the function is executed (decorator).
    """

    def __init__(self, permission, http_exception=None):
        self.permission = permission
        self.http_exception = http_exception
        """The permission of this principal
        """

    @property
    def identity(self):
        """The identity of this principal
        """
        return g.identity

    def can(self):
        """Whether the identity has access to the permission
        """
        return self.identity.can(self.permission)

    def __call__(self, f):
        @wraps(f)
        def _decorated(*args, **kw):
            with self:
                rv = f(*args, **kw)
            return rv
        return _decorated

    def __enter__(self):
        # check the permission here
        if not self.can():
            if self.http_exception:
                abort(self.http_exception, self.permission)
            raise PermissionDenied(self.permission)

    def __exit__(self, *args):
        return False

class Permission(object):
    """Represents needs, any of which must be present to access a resource

    :param needs: The needs for this permission
    """
    def __init__(self, *needs):
        """A set of needs, any of which must be present in an identity to have
        access.
        """

        self.needs = set(needs)
        self.excludes = set()

    def __nonzero__(self):
        """Equivalent to ``self.can()``.
        """
        return bool(self.can())

    def __and__(self, other):
        """Does the same thing as ``self.union(other)``
        """
        return self.union(other)

    def __or__(self, other):
        """Does the same thing as ``self.difference(other)``
        """
        return self.difference(other)

    def __contains__(self, other):
        """Does the same thing as ``other.issubset(self)``.
        """
        return other.issubset(self)

    def require(self, http_exception=None):
        """Create a principal for this permission.

        The principal may be used as a context manager, or a decroator.

        If ``http_exception`` is passed then ``abort()`` will be called
        with the HTTP exception code. Otherwise a ``PermissionDenied``
        exception will be raised if the identity does not meet the
        requirements.

        :param http_exception: the HTTP exception code (403, 401 etc)
        """
        return IdentityContext(self, http_exception)

    def test(self, http_exception=None):
        """
        Checks if permission available and raises relevant exception
        if not. This is useful if you just want to check permission
        without wrapping everything in a require() block.

        This is equivalent to::

            with permission.require():
                pass
        """

        with self.require(http_exception):
            pass

    def reverse(self):
        """
        Returns reverse of current state (needs->excludes, excludes->needs)
        """

        p = Permission()
        p.needs.update(self.excludes)
        p.excludes.update(self.needs)
        return p

    def union(self, other):
        """Create a new permission with the requirements of the union of this
        and other.

        :param other: The other permission
        """
        p = Permission(*self.needs.union(other.needs))
        p.excludes.update(self.excludes.union(other.excludes))
        return p

    def difference(self, other):
        """Create a new permission consisting of requirements in this
        permission and not in the other.
        """

        p = Permission(*self.needs.difference(other.needs))
        p.excludes.update(self.excludes.difference(other.excludes))
        return p

    def issubset(self, other):
        """Whether this permission needs are a subset of another

        :param other: The other permission
        """
        return self.needs.issubset(other.needs) and \
               self.excludes.issubset(other.excludes)

    def allows(self, identity):
        """Whether the identity can access this permission.

        :param identity: The identity
        """
        if self.needs and not self.needs.intersection(identity.provides):
            return False

        if self.excludes and self.excludes.intersection(identity.provides):
            return False

        return True

    def can(self):
        """Whether the required context for this permission has access

        This creates an identity context and tests whether it can access this
        permission
        """
        return self.require().can()

class Denial(Permission):
    """
    Shortcut class for passing excluded needs.
    """

    def __init__(self, *excludes):
        self.excludes = set(excludes)
        self.needs = set()

def session_identity_loader():
    if 'identity.name' in session and 'identity.auth_type' in session:
        identity = Identity(session['identity.name'],
                            session['identity.auth_type'])
        return identity

def session_identity_saver(identity):
    session['identity.name'] = identity.name
    session['identity.auth_type'] = identity.auth_type
    session.modified = True

class Principal(object):
    """Principal extension

    :param app: The flask application to extend
    :param use_sessions: Whether to use sessions to extract and store
                         identification.
    :param skip_static: Whether to ignore static endpoints.
    """
    def __init__(self, app=None, use_sessions=True, skip_static=False):
        self.identity_loaders = deque()
        self.identity_savers = deque()
        # XXX This will probably vanish for a better API
        self.use_sessions = use_sessions
        self.skip_static = skip_static

        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        if hasattr(app, 'static_url_path'):
            self._static_path = app.static_url_path
        else:
            self._static_path = app.static_path

        app.before_request(self._on_before_request)
        identity_changed.connect(self._on_identity_changed, app)

        if self.use_sessions:
            self.identity_loader(session_identity_loader)
            self.identity_saver(session_identity_saver)

    def set_identity(self, identity):
        """Set the current identity.

        :param identity: The identity to set
        """
        if self._is_static_route():
            return

        self._set_thread_identity(identity)
        for saver in self.identity_savers:
            saver(identity)

    def identity_loader(self, f):
        """Decorator to define a function as an identity loader.

        An identity loader function is called before request to find any
        provided identities. The first found identity is used to load from.

        For example::

            app = Flask(__name__)

            principals = Principal(app)

            @principals.identity_loader
            def load_identity_from_weird_usecase():
                return Identity('ali')
        """
        self.identity_loaders.appendleft(f)
        return f

    def identity_saver(self, f):
        """Decorator to define a function as an identity saver.

        An identity loader saver is called when the identity is set to persist
        it for the next request.

        For example::

            app = Flask(__name__)

            principals = Principal(app)

            @principals.identity_saver
            def save_identity_to_weird_usecase(identity):
                my_special_cookie['identity'] = identity
        """
        self.identity_savers.appendleft(f)
        return f

    def _set_thread_identity(self, identity):
        g.identity = identity
        identity_loaded.send(current_app._get_current_object(),
                             identity=identity)

    def _on_identity_changed(self, app, identity):
        self.set_identity(identity)

    def _on_before_request(self):
        if self._is_static_route():
            return

        g.identity = AnonymousIdentity()
        for loader in self.identity_loaders:
            identity = loader()
            if identity is not None:
                self.set_identity(identity)
                return

    def _is_static_route(self):
        return (self.skip_static and \
                request.path.startswith(self._static_path))
<|endoftext|>


<commit_before>from cassandra import ConsistencyLevel
from ccmlib.common import is_win
from distutils.version import LooseVersion

from dtest import Tester, debug
from jmxutils import JolokiaAgent, make_mbean, remove_perf_disable_shared_mem
from tools import insert_c1c2, since, known_failure

@since("2.2")
class TestDeprecatedRepairAPI(Tester):
    """
    @jira_ticket CASSANDRA-9570

    Test if deprecated repair JMX API runs with expected parameters
    """

    @known_failure(failure_source='test',
                   jira_url='https://issues.apache.org/jira/browse/CASSANDRA-12451',
                   flaky=True)
    def force_repair_async_1_test(self):
        """
        test forceRepairAsync(String keyspace, boolean isSequential,
                              Collection<String> dataCenters,
                              Collection<String> hosts,
                              boolean primaryRange, boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairAsync(java.lang.String,boolean,java.util.Collection,java.util.Collection,boolean,boolean,[Ljava.lang.String;)",
                                          ['ks', True, [], [], False, False, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "sequential", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "true", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    @known_failure(failure_source='test',
                   jira_url='https://issues.apache.org/jira/browse/CASSANDRA-12451',
                   flaky=True)
    def force_repair_async_2_test(self):
        """
        test forceRepairAsync(String keyspace, int parallelismDegree,
                              Collection<String> dataCenters,
                              Collection<String> hosts,
                              boolean primaryRange, boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairAsync(java.lang.String,int,java.util.Collection,java.util.Collection,boolean,boolean,[Ljava.lang.String;)",
                                          ['ks', 1, [], [], True, True, []])
        self.assertEqual(opt["parallelism"], "parallel", opt)
        self.assertEqual(opt["primary_range"], "true", opt)
        self.assertEqual(opt["incremental"], "false", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["column_families"], "[]", opt)

    @known_failure(failure_source='test',
                   jira_url='https://issues.apache.org/jira/browse/CASSANDRA-12451',
                   flaky=True)
    def force_repair_async_3_test(self):
        """
        test forceRepairAsync(String keyspace, boolean isSequential,
                              boolean isLocal, boolean primaryRange,
                              boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairAsync(java.lang.String,boolean,boolean,boolean,boolean,[Ljava.lang.String;)",
                                          ['ks', False, False, False, False, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "true", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    @known_failure(failure_source='test',
                   jira_url='https://issues.apache.org/jira/browse/CASSANDRA-12451',
                   flaky=True)
    def force_repair_range_async_1_test(self):
        """
        test forceRepairRangeAsync(String beginToken, String endToken,
                                   String keyspaceName, boolean isSequential,
                                   Collection<String> dataCenters,
                                   Collection<String> hosts, boolean fullRepair,
                                   String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairRangeAsync(java.lang.String,java.lang.String,java.lang.String,boolean,java.util.Collection,java.util.Collection,boolean,[Ljava.lang.String;)",
                                          ["0", "1000", "ks", True, ["dc1"], [], False, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "sequential", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "true", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[dc1]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["ranges"], "1", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    @known_failure(failure_source='test',
                   jira_url='https://issues.apache.org/jira/browse/CASSANDRA-12451',
                   flaky=True)
    def force_repair_range_async_2_test(self):
        """
        test forceRepairRangeAsync(String beginToken, String endToken,
                                   String keyspaceName, int parallelismDegree,
                                   Collection<String> dataCenters,
                                   Collection<String> hosts,
                                   boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairRangeAsync(java.lang.String,java.lang.String,java.lang.String,int,java.util.Collection,java.util.Collection,boolean,[Ljava.lang.String;)",
                                          ["0", "1000", "ks", 2, [], [], True, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "dc_parallel", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "false", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["ranges"], "1", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    @known_failure(failure_source='test',
                   jira_url='https://issues.apache.org/jira/browse/CASSANDRA-12451',
                   flaky=True)
    def force_repair_range_async_3_test(self):
        """
        test forceRepairRangeAsync(String beginToken, String endToken,
                                   String keyspaceName, boolean isSequential,
                                   boolean isLocal, boolean fullRepair,
                                   String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairRangeAsync(java.lang.String,java.lang.String,java.lang.String,boolean,boolean,boolean,[Ljava.lang.String;)",
                                          ["0", "1000", "ks", True, True, True, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "sequential", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "false", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[dc1]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["ranges"], "1", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    def _deprecated_repair_jmx(self, method, arguments):
        """
        * Launch a two node, two DC cluster
        * Create a keyspace and table
        * Insert some data
        * Call the deprecated repair JMX API based on the arguments passed into this method
        * Check the node log to see if the correct repair was performed based on the jmx args
        """
        cluster = self.cluster

        debug("Starting cluster..")
        cluster.populate([1, 1])
        node1, node2 = cluster.nodelist()
        remove_perf_disable_shared_mem(node1)
        cluster.start()
        supports_pull_repair = LooseVersion(cluster.version()) >= LooseVersion('3.10')

        session = self.patient_cql_connection(node1)
        self.create_ks(session, 'ks', 2)
        self.create_cf(session, 'cf', read_repair=0.0, columns={'c1': 'text', 'c2': 'text'})

        insert_c1c2(session, n=1000, consistency=ConsistencyLevel.ALL)

        # Run repair
        mbean = make_mbean('db', 'StorageService')
        with JolokiaAgent(node1) as jmx:
            # assert repair runs and returns valid cmd number
            self.assertEqual(jmx.execute_method(mbean, method, arguments), 1)
        # wait for log to start
        node1.watch_log_for("Starting repair command")
        # get repair parameters from the log
        l = node1.grep_log(("Starting repair command #1, repairing keyspace ks with repair options \(parallelism: (?P<parallelism>\w+), primary range: (?P<pr>\w+), "
                            "incremental: (?P<incremental>\w+), job threads: (?P<jobs>\d+), ColumnFamilies: (?P<cfs>.+), dataCenters: (?P<dc>.+), "
                            "hosts: (?P<hosts>.+), # of ranges: (?P<ranges>\d+)(, pull repair: (?P<pullrepair>true|false))?\)"))

        self.assertEqual(len(l), 1)
        line, m = l[0]

        if supports_pull_repair:
            self.assertEqual(m.group("pullrepair"), "false", "Pull repair cannot be enabled through the deprecated API so the pull repair option should always be false.")

        return {"parallelism": m.group("parallelism"),
                "primary_range": m.group("pr"),
                "incremental": m.group("incremental"),
                "job_threads": m.group("jobs"),
                "column_families": m.group("cfs"),
                "data_centers": m.group("dc"),
                "hosts": m.group("hosts"),
                "ranges": m.group("ranges")}
<commit_msg>Remove 12451 annotation<commit_after>from cassandra import ConsistencyLevel
from ccmlib.common import is_win
from distutils.version import LooseVersion

from dtest import Tester, debug
from jmxutils import JolokiaAgent, make_mbean, remove_perf_disable_shared_mem
from tools import insert_c1c2, since

@since("2.2")
class TestDeprecatedRepairAPI(Tester):
    """
    @jira_ticket CASSANDRA-9570

    Test if deprecated repair JMX API runs with expected parameters
    """

    def force_repair_async_1_test(self):
        """
        test forceRepairAsync(String keyspace, boolean isSequential,
                              Collection<String> dataCenters,
                              Collection<String> hosts,
                              boolean primaryRange, boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairAsync(java.lang.String,boolean,java.util.Collection,java.util.Collection,boolean,boolean,[Ljava.lang.String;)",
                                          ['ks', True, [], [], False, False, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "sequential", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "true", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    def force_repair_async_2_test(self):
        """
        test forceRepairAsync(String keyspace, int parallelismDegree,
                              Collection<String> dataCenters,
                              Collection<String> hosts,
                              boolean primaryRange, boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairAsync(java.lang.String,int,java.util.Collection,java.util.Collection,boolean,boolean,[Ljava.lang.String;)",
                                          ['ks', 1, [], [], True, True, []])
        self.assertEqual(opt["parallelism"], "parallel", opt)
        self.assertEqual(opt["primary_range"], "true", opt)
        self.assertEqual(opt["incremental"], "false", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["column_families"], "[]", opt)

    def force_repair_async_3_test(self):
        """
        test forceRepairAsync(String keyspace, boolean isSequential,
                              boolean isLocal, boolean primaryRange,
                              boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairAsync(java.lang.String,boolean,boolean,boolean,boolean,[Ljava.lang.String;)",
                                          ['ks', False, False, False, False, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "true", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    def force_repair_range_async_1_test(self):
        """
        test forceRepairRangeAsync(String beginToken, String endToken,
                                   String keyspaceName, boolean isSequential,
                                   Collection<String> dataCenters,
                                   Collection<String> hosts, boolean fullRepair,
                                   String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairRangeAsync(java.lang.String,java.lang.String,java.lang.String,boolean,java.util.Collection,java.util.Collection,boolean,[Ljava.lang.String;)",
                                          ["0", "1000", "ks", True, ["dc1"], [], False, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "sequential", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "true", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[dc1]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["ranges"], "1", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    def force_repair_range_async_2_test(self):
        """
        test forceRepairRangeAsync(String beginToken, String endToken,
                                   String keyspaceName, int parallelismDegree,
                                   Collection<String> dataCenters,
                                   Collection<String> hosts,
                                   boolean fullRepair, String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairRangeAsync(java.lang.String,java.lang.String,java.lang.String,int,java.util.Collection,java.util.Collection,boolean,[Ljava.lang.String;)",
                                          ["0", "1000", "ks", 2, [], [], True, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "dc_parallel", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "false", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["ranges"], "1", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    def force_repair_range_async_3_test(self):
        """
        test forceRepairRangeAsync(String beginToken, String endToken,
                                   String keyspaceName, boolean isSequential,
                                   boolean isLocal, boolean fullRepair,
                                   String... columnFamilies)
        """
        opt = self._deprecated_repair_jmx("forceRepairRangeAsync(java.lang.String,java.lang.String,java.lang.String,boolean,boolean,boolean,[Ljava.lang.String;)",
                                          ["0", "1000", "ks", True, True, True, ["cf"]])
        self.assertEqual(opt["parallelism"], "parallel" if is_win() else "sequential", opt)
        self.assertEqual(opt["primary_range"], "false", opt)
        self.assertEqual(opt["incremental"], "false", opt)
        self.assertEqual(opt["job_threads"], "1", opt)
        self.assertEqual(opt["data_centers"], "[dc1]", opt)
        self.assertEqual(opt["hosts"], "[]", opt)
        self.assertEqual(opt["ranges"], "1", opt)
        self.assertEqual(opt["column_families"], "[cf]", opt)

    def _deprecated_repair_jmx(self, method, arguments):
        """
        * Launch a two node, two DC cluster
        * Create a keyspace and table
        * Insert some data
        * Call the deprecated repair JMX API based on the arguments passed into this method
        * Check the node log to see if the correct repair was performed based on the jmx args
        """
        cluster = self.cluster

        debug("Starting cluster..")
        cluster.populate([1, 1])
        node1, node2 = cluster.nodelist()
        remove_perf_disable_shared_mem(node1)
        cluster.start()
        supports_pull_repair = LooseVersion(cluster.version()) >= LooseVersion('3.10')

        session = self.patient_cql_connection(node1)
        self.create_ks(session, 'ks', 2)
        self.create_cf(session, 'cf', read_repair=0.0, columns={'c1': 'text', 'c2': 'text'})

        insert_c1c2(session, n=1000, consistency=ConsistencyLevel.ALL)

        # Run repair
        mbean = make_mbean('db', 'StorageService')
        with JolokiaAgent(node1) as jmx:
            # assert repair runs and returns valid cmd number
            self.assertEqual(jmx.execute_method(mbean, method, arguments), 1)
        # wait for log to start
        node1.watch_log_for("Starting repair command")
        # get repair parameters from the log
        l = node1.grep_log(("Starting repair command #1, repairing keyspace ks with repair options \(parallelism: (?P<parallelism>\w+), primary range: (?P<pr>\w+), "
                            "incremental: (?P<incremental>\w+), job threads: (?P<jobs>\d+), ColumnFamilies: (?P<cfs>.+), dataCenters: (?P<dc>.+), "
                            "hosts: (?P<hosts>.+), # of ranges: (?P<ranges>\d+)(, pull repair: (?P<pullrepair>true|false))?\)"))

        self.assertEqual(len(l), 1)
        line, m = l[0]

        if supports_pull_repair:
            self.assertEqual(m.group("pullrepair"), "false", "Pull repair cannot be enabled through the deprecated API so the pull repair option should always be false.")

        return {"parallelism": m.group("parallelism"),
                "primary_range": m.group("pr"),
                "incremental": m.group("incremental"),
                "job_threads": m.group("jobs"),
                "column_families": m.group("cfs"),
                "data_centers": m.group("dc"),
                "hosts": m.group("hosts"),
                "ranges": m.group("ranges")}
<|endoftext|>


<commit_before>from django.conf import settings
from os.path import getsize
from reports.models import ReportTaskStatus
from reports.tasks.base import BaseTask
from reports.utils import ExportWorkbook, generate_random_filename
from seed_services_client import IdentityStoreApiClient

class GenerateMSISDNMessageReport(BaseTask):
    """
    Generate an Excel spreadsheet for a Pathfinder <http://www.pathfinder.org/>
    cohort study which includes details of messages sent to the specified
    MSISDNs.
    """

    def run(self, start_date, end_date, task_status_id, msisdns=[]):
        task_status = ReportTaskStatus.objects.get(id=task_status_id)
        task_status.status = ReportTaskStatus.RUNNING
        task_status.save()

        spreadsheet = self.create_spreadsheet(msisdns)
        output_file = generate_random_filename()
        spreadsheet.save(output_file)

        task_status.status = ReportTaskStatus.SENDING
        task_status.file_size = getsize(output_file)
        task_status.save()

        # TODO: Use SendEmail task to send the email

    def create_spreadsheet(self, msisdns):
        logger = self.get_logger()
        workbook = ExportWorkbook()
        sheet = workbook.add_sheet('Data for study cohort', 0)

        identity_store = IdentityStoreApiClient(
            settings.IDENTITY_STORE_TOKEN,
            settings.IDENTITY_STORE_URL,
        )

        sheet.set_header([
            'Phone number',
            'Date registered',
            'Facility',
            'Pregnancy week',
            'Message type',
            'Message 1: content',
            'Message 1: date sent',
            'Message 1: status',
        ])

        for msisdn in msisdns:
            response = identity_store.get_identity_by_address('msisdn', msisdn)
            results = list(response['results'])

            if len(results) < 1:
                logger.info(
                    'No results from identity store for {0}'.format(msisdn))
                continue

            if len(results) > 1:
                logger.info(
                    'Multiple results from identity store for {0}'
                    .format(msisdn))
                continue

            identity = results[0]

            sheet.add_row({
                'Phone number': msisdn,
                'Date registered': identity['created_at'],
            })

        return workbook

generate_msisdn_message_report = GenerateMSISDNMessageReport()
<commit_msg>Build spreadsheet seperately to retrieving identity info<commit_after>from django.conf import settings
from os.path import getsize
from reports.models import ReportTaskStatus
from reports.tasks.base import BaseTask
from reports.utils import ExportWorkbook, generate_random_filename
from seed_services_client import IdentityStoreApiClient

class GenerateMSISDNMessageReport(BaseTask):
    """
    Generate an Excel spreadsheet for a Pathfinder <http://www.pathfinder.org/>
    cohort study which includes details of messages sent to the specified
    MSISDNs.
    """

    def run(self, start_date, end_date, task_status_id, msisdns=[]):
        task_status = ReportTaskStatus.objects.get(id=task_status_id)
        task_status.status = ReportTaskStatus.RUNNING
        task_status.save()

        self.is_client = IdentityStoreApiClient(
            settings.IDENTITY_STORE_TOKEN,
            settings.IDENTITY_STORE_URL,
        )

        data = self.retrieve_identity_info(msisdns)

        spreadsheet = self.populate_spreadsheet(data)

        output_file = generate_random_filename()
        spreadsheet.save(output_file)

        task_status.status = ReportTaskStatus.SENDING
        task_status.file_size = getsize(output_file)
        task_status.save()

        # TODO: Use SendEmail task to send the email

    def retrieve_identity_info(self, msisdns):
        logger = self.get_logger()

        data = {}
        for msisdn in msisdns:
            response = self.is_client.get_identity_by_address('msisdn', msisdn)
            results = list(response['results'])

            if len(results) < 1:
                logger.info(
                    'No results from identity store for {0}'.format(msisdn))
                data[msisdn] = {}
                continue

            if len(results) > 1:
                logger.info(
                    'Multiple results from identity store for {0}'
                    .format(msisdn))
                data[msisdn] = {}
                continue

            data[msisdn] = {'id': results[0]['id'],
                            'created_at': results[0]['created_at']}

        return data

    def populate_spreadsheet(self, data):
        workbook = ExportWorkbook()
        sheet = workbook.add_sheet('Data for study cohort', 0)

        sheet.set_header([
            'Phone number',
            'Date registered',
            'Facility',
            'Pregnancy week',
            'Message type',
            'Message 1: content',
            'Message 1: date sent',
            'Message 1: status'
        ])

        for msisdn in data:
            sheet.add_row({
                'Phone number': msisdn,
                'Date registered': data[msisdn]['created_at']
            })

        return workbook

generate_msisdn_message_report = GenerateMSISDNMessageReport()
<|endoftext|>