Beispiel #1
0
 def _clean_environ(self, environ):
     ''' Delete the ``keys`` from the supplied ``environ`` '''
     log.debug(b_('clean_environ(%s)') % to_bytes(self.clear_env))
     for key in self.clear_env:
         if key in environ:
             log.debug(b_('Deleting %(key)s from environ') %
                 {'key': to_bytes(key)})
             del(environ[key])
Beispiel #2
0
 def _clean_environ(self, environ):
     ''' Delete the ``keys`` from the supplied ``environ`` '''
     log.debug(b_('clean_environ(%s)') % to_bytes(self.clear_env))
     for key in self.clear_env:
         if key in environ:
             log.debug(
                 b_('Deleting %(key)s from environ') %
                 {'key': to_bytes(key)})
             del (environ[key])
    def __init__(self, base_url, useragent=None, session_name='tg-visit',
            session_as_cookie=True, debug=False, insecure=False, retries=0):
        '''Create a client configured for a particular service.

        :arg base_url: Base of every URL used to contact the server

        :kwarg useragent: useragent string to use.  If not given, default to
            "Fedora ProxyClient/VERSION"
        :kwarg session_name: name of the cookie to use with session handling
        :kwarg session_as_cookie: If set to True, return the session as a
            SimpleCookie.  If False, return a session_id.  This flag allows us
            to maintain compatibility for the 0.3 branch.  In 0.4, code will
            have to deal with session_id's instead of cookies.
        :kwarg debug: If True, log debug information
        :kwarg insecure: If True, do not check server certificates against
            their CA's.  This means that man-in-the-middle attacks are
            possible against the `BaseClient`. You might turn this option on
            for testing against a local version of a server with a self-signed
            certificate but it should be off in production.
        :kwarg retries: if we get an unknown or possibly transient error from
            the server, retry this many times.  Setting this to a negative
            number makes it try forever.  Defaults to zero, no retries.

        '''
        # Setup our logger
        self._log_handler = logging.StreamHandler()
        self.debug = debug
        format = logging.Formatter("%(message)s")
        self._log_handler.setFormatter(format)
        self.log.addHandler(self._log_handler)

        self.log.debug(b_('proxyclient.__init__:entered'))
        if base_url[-1] != '/':
            base_url = base_url +'/'
        self.base_url = base_url
        self.domain = urlparse(self.base_url).netloc
        self.useragent = useragent or 'Fedora ProxyClient/%(version)s' % {
                'version': __version__}
        self.session_name = session_name
        self.session_as_cookie = session_as_cookie
        if session_as_cookie:
            warnings.warn(b_('Returning cookies from send_request() is'
                ' deprecated and will be removed in 0.4.  Please port your'
                ' code to use a session_id instead by calling the ProxyClient'
                ' constructor with session_as_cookie=False'),
                DeprecationWarning, stacklevel=2)
        self.insecure = insecure
        self.retries = retries or 0
        self.log.debug(b_('proxyclient.__init__:exited'))
Beispiel #4
0
    def identify(self, environ):
        '''Extract information to identify a user

        Retrieve either a username and password or a session_id that can be
        passed on to FAS to authenticate the user.
        '''
        log.info(b_('in identify()'))

        # friendlyform compat
        if not 'repoze.who.logins' in environ:
            environ['repoze.who.logins'] = 0

        req = webob.Request(environ, charset='utf-8')
        cookie = req.cookies.get(self.session_cookie)

        # This is compatible with TG1 and it gives us a way to authenticate
        # a user without making two requests
        query = req.GET
        form = Bunch(req.POST)
        form.update(query)
        if form.get('login', None) == 'Login' and \
                'user_name' in form and \
                'password' in form:
            identity = {
                'login': form['user_name'],
                'password': form['password']
            }
            keys = ('login', 'password', 'user_name')
            for k in keys:
                if k in req.GET:
                    del (req.GET[k])
                if k in req.POST:
                    del (req.POST[k])
            return identity

        if cookie is None:
            return None

        log.info(
            b_('Request identify for cookie %(cookie)s') %
            {'cookie': to_bytes(cookie)})
        try:
            user_data = self._retrieve_user_info(
                environ, auth_params={'session_id': cookie})
        except Exception, e:  # pylint:disable-msg=W0703
            # For any exceptions, returning None means we failed to identify
            log.warning(e)
            return None
Beispiel #5
0
    def add_metadata(self, environ, identity):
        log.info(b_('In add_metadata'))
        req = webob.Request(environ)

        if identity.get('error'):
            log.info(b_('Error exists in session, no need to set metadata'))
            return 'error'

        plugin_user_info = {}
        for plugin in self._metadata_plugins:
            plugin(plugin_user_info)
        identity.update(plugin_user_info)
        del plugin_user_info

        user = identity.get('repoze.who.userid')
        (session_id,
         user_info) = fas_cache.get_value(key=user,
                                          expiretime=FAS_CACHE_TIMEOUT)

        #### FIXME: Deprecate this line!!!
        # If we make a new version of fas.who middleware, get rid of saving
        # user information directly into identity.  Instead, save it into
        # user, as is done below
        identity.update(user_info)

        identity['userdata'] = user_info
        identity['user'] = Bunch()
        identity['user'].created = user_info['creation']
        identity['user'].display_name = user_info['human_name']
        identity['user'].email_address = user_info['email']
        identity['user'].groups = user_info['groups']
        identity['user'].password = None
        identity['user'].permissions = user_info['permissions']
        identity['user'].user_id = user_info['id']
        identity['user'].user_name = user_info['username']
        identity['groups'] = user_info['groups']
        identity['permissions'] = user_info['permissions']

        if 'repoze.what.credentials' not in environ:
            environ['repoze.what.credentials'] = {}

        environ['repoze.what.credentials']['groups'] = user_info['groups']
        environ['repoze.what.credentials']['permissions'] = user_info[
            'permissions']

        # Adding the userid:
        userid = identity['repoze.who.userid']
        environ['repoze.what.credentials']['repoze.what.userid'] = userid
Beispiel #6
0
    def __init__(self, base_url='https://admin.fedoraproject.org/accounts/',
            *args, **kwargs):
        '''A threadsafe client to the Fedora Account System.

        This class is optimized to proxy multiple users to the account system.
        ProxyClient is designed to be threadsafe so that code can instantiate
        one instance of the class and use it for multiple requests for
        different users from different threads.

        If you want something that can manage a single user's connection to
        the Account System then use fedora.client.AccountSystem instead.

        :kwargs base_url: Base of every URL used to contact the server.
            Defaults to the Fedora Project FAS instance.
        :kwargs useragent: useragent string to use.  If not given, default to
            "FAS Proxy Client/VERSION"
        :kwarg session_name: name of the cookie to use with session handling
        :kwarg debug: If True, log debug information
        :kwarg insecure: If True, do not check server certificates against
            their CA's.  This means that man-in-the-middle attacks are
            possible against the `BaseClient`. You might turn this option on
            for testing against a local version of a server with a self-signed
            certificate but it should be off in production.
        '''
        if 'useragent' not in kwargs:
            kwargs['useragent'] = 'FAS Proxy Client/%s' % __version__
        if 'session_as_cookie' in kwargs and kwargs['session_as_cookie']:
            # No need to allow this in FasProxyClient as it's deprecated in
            # ProxyClient
            raise TypeError(b_('FasProxyClient() got an unexpected keyword'
                ' argument \'session_as_cookie\''))
        kwargs['session_as_cookie'] = False
        super(FasProxyClient, self).__init__(base_url, *args, **kwargs)
Beispiel #7
0
    def group_data(self, force_refresh=None):
        '''Return administrators/sponsors/users and group type for all groups

        :arg force_refresh: If true, the returned data will be queried from the
            database, as opposed to memcached.
        :raises AppError: if the query failed on the server
        :returns: A dict mapping group names to the group type and the
            user IDs of the administrator, sponsors, and users of the group.

        .. versionadded:: 0.3.8
        '''
        params = {}
        if force_refresh:
            params['force_refresh'] = True

        try:
            request = self.send_request('json/fas_client/group_data',
                                        req_params=params,
                                        auth=True)
            if request['success']:
                return request['data']
            else:
                raise AppError(message=b_('FAS server unable to retrieve'
                                          ' group members'),
                               name='FASError')
        except FedoraServiceError:
            raise
Beispiel #8
0
    def group_data(self, force_refresh=None):
        '''Return administrators/sponsors/users and group type for all groups

        :arg force_refresh: If true, the returned data will be queried from the
            database, as opposed to memcached.
        :raises AppError: if the query failed on the server
        :returns: A dict mapping group names to the group type and the
            user IDs of the administrator, sponsors, and users of the group.

        .. versionadded:: 0.3.8
        '''
        params = {}
        if force_refresh:
            params['force_refresh'] = True

        try:
            request = self.send_request('json/fas_client/group_data',
                req_params=params, auth=True)
            if request['success']:
                return request['data']
            else:
                raise AppError(message=b_('FAS server unable to retrieve'
                    ' group members'), name='FASError')
        except FedoraServiceError:
            raise
    def validate_identity(self, user_name, password, visit_key):
        '''
        Look up the identity represented by user_name and determine whether the
        password is correct.

        Must return either None if the credentials weren't valid or an object
        with the following properties:

            :user_name: original user name
            :user: a provider dependant object (TG_User or similar)
            :groups: a set of group IDs
            :permissions: a set of permission IDs

        :arg user_name: user_name we're authenticating.  If None, we'll try
            to lookup a username from SSL variables
        :arg password: password to authenticate user_name with
        :arg visit_key: visit_key from the user's session
        '''
        using_ssl = False
        if not user_name and config.get('identity.ssl'):
            if cherrypy.request.headers['X-Client-Verify'] == 'SUCCESS':
                user_name = cherrypy.request.headers['X-Client-CN']
                cherrypy.request.fas_provided_username = user_name
                using_ssl = True

        # pylint: disable-msg=R0201
        # TG identity providers have this method so we can't get rid of it.
        try:
            user = JsonFasIdentity(visit_key, username=user_name,
                    password=password, using_ssl=using_ssl)
        except FedoraServiceError, e:
            self.log.warning(b_('Error logging in %(user)s: %(error)s') % {
                'user': to_bytes(user_name), 'error': to_bytes(e)})
            return None
Beispiel #10
0
    def validate_identity(self, user_name, password, visit_key):
        '''
        Look up the identity represented by user_name and determine whether the
        password is correct.

        Must return either None if the credentials weren't valid or an object
        with the following properties:

            :user_name: original user name
            :user: a provider dependant object (TG_User or similar)
            :groups: a set of group IDs
            :permissions: a set of permission IDs
        '''
        # pylint: disable-msg=R0201
        # TG identity providers have this method so we can't get rid of it.
        try:
            user = JsonFasIdentity(visit_key,
                                   username=user_name,
                                   password=password)
        except FedoraServiceError, e:
            log.warning(
                b_('Error logging in %(user)s: %(error)s') % {
                    'user': to_bytes(user_name),
                    'error': to_bytes(e)
                })
            return None
Beispiel #11
0
    def remove_user(self,
                    username,
                    pkg_name,
                    collctn_list=None,
                    collectn_list=None):
        '''Remove user from a package

        :arg username: Name of user to remove from the package
        :arg pkg_name: Name of the package
        :kwarg collctn_list: list of collection simple names like
            'F-10','devel'.  Default: None which means user removed from all
            collections associated with the package.
        :kwarg collectn_list: *Deprecated* Use collctn_list instead.
        :returns: status code from the request

        .. versionadded:: 0.3.12

        .. versionchanged:: 0.3.17
            Rename collectn_list to collctn_list
        '''
        if (collctn_list and collectn_list):
            warnings.warn(b_(
                'collectn_list is a deprecated name for'
                ' collctn_list.\nIgnoring the value of collectn_list.'),
                          DeprecationWarning,
                          stacklevel=2)

        if collectn_list and not collctn_list:
            warnings.warn(b_(
                'collectn_list has been renamed to collctn_list.'
                '\nPlease start using the new name.  collectn_list will go'
                ' away in 0.4.x.'),
                          DeprecationWarning,
                          stacklevel=2)
            collctn_list = collectn_list

        if collctn_list:
            params = {
                'username': username,
                'pkg_name': pkg_name,
                'collectn_list': collctn_list
            }
        else:
            params = {'username': username, 'pkg_name': pkg_name}
        return self.send_request('/acls/dispatcher/remove_user',
                                 auth=True,
                                 req_params=params)
Beispiel #12
0
    def _del_session_cookie(self):
        '''**Deprecated** Use session_id instead.

        Delete the session cookie from the filesystem.
        '''
        warnings.warn(b_('session_cookie is deprecated, use session_id'
            ' instead'), DeprecationWarning, stacklevel=2)
        del(self.session_id)
Beispiel #13
0
    def get_package_list(self, collctn=None, collectn=None):
        """Retrieve a list of all package names in a collection.

        :kwarg collctn: Collection to look for packages in.  This is a collctn
            shortname like 'devel' or 'F-13'.  If unset, return packages in
            all collections
        :kwarg collectn: *Deprecated*.  Please use collctn instead
        :returns: list of package names present in the collection.

        .. versionadded:: 0.3.15

        .. versionchanged:: 0.3.17
            Rename collectn to collctn
        """
        if collctn and collectn:
            warnings.warn(
                b_("collectn is a deprecated name for" " collctn.\nIgnoring the value of collectn."),
                DeprecationWarning,
                stacklevel=2,
            )

        if collectn and not collctn:
            warnings.warn(
                b_(
                    "collectn has been renamed to collctn.\n"
                    "Please start using the new name.  collectn will go"
                    " away in 0.4.x."
                ),
                DeprecationWarning,
                stacklevel=2,
            )
            collctn = collectn

        params = {"packages_tgp_limit": "0"}
        if collctn:
            try:
                collctn_id = self.branches[collctn]["id"]
            except KeyError:
                raise PackageDBError(
                    b_("Collection shortname %(collctn)s" " is unknown.") % {"collctn": to_bytes(collctn)}
                )
            data = self.send_request("/collections/name/%s/" % collctn, params)
        else:
            data = self.send_request("/acls/list/*", params)
        names = [p["name"] for p in data.packages]
        return names
    def add_metadata(self, environ, identity):
        log.info(b_('In add_metadata'))
        req = webob.Request(environ)

        if identity.get('error'):
            log.info(b_('Error exists in session, no need to set metadata'))
            return 'error'

        plugin_user_info = {}
        for plugin in self._metadata_plugins:
            plugin(plugin_user_info)
        identity.update(plugin_user_info)
        del plugin_user_info

        user = identity.get('repoze.who.userid')
        (session_id, user_info) = fas_cache.get_value(key=user,
                expiretime=FAS_CACHE_TIMEOUT)

        #### FIXME: Deprecate this line!!!
        # If we make a new version of fas.who middleware, get rid of saving
        # user information directly into identity.  Instead, save it into
        # user, as is done below
        identity.update(user_info)

        identity['userdata'] = user_info
        identity['user'] = Bunch()
        identity['user'].created = user_info['creation']
        identity['user'].display_name = user_info['human_name']
        identity['user'].email_address = user_info['email']
        identity['user'].groups = user_info['groups']
        identity['user'].password = None
        identity['user'].permissions = user_info['permissions']
        identity['user'].user_id = user_info['id']
        identity['user'].user_name = user_info['username']
        identity['groups'] = user_info['groups']
        identity['permissions'] = user_info['permissions']

        if 'repoze.what.credentials' not in environ:
            environ['repoze.what.credentials'] = {}

        environ['repoze.what.credentials']['groups'] = user_info['groups']
        environ['repoze.what.credentials']['permissions'] = user_info['permissions']

        # Adding the userid:
        userid = identity['repoze.who.userid']
        environ['repoze.what.credentials']['repoze.what.userid'] = userid
Beispiel #15
0
    def edit_package(
        self, pkg, owner=None, description=None, branches=None, cc_list=None, comaintainers=None, groups=None
    ):
        """Edit a package.

        :arg pkg: Name of the package to edit
        :kwarg owner: If set, make this person the owner of both branches
        :kwarg description: If set, make this the description of both branches
        :kwarg branches: List of branches to operate on
        :kwarg cc_list: If set, list or tuple of usernames to watch the
            package.
        :kwarg comaintainers: If set, list or tuple of usernames to comaintain
            the package.
        :kwarg groups: If set, list or tuple of group names that can commit to
            the package.
        :raises AppError: If the server returns an error

        This method takes information about a package and either edits the
        package to reflect the changes to information.

        Note: This method will be going away in favor of methods that do
        smaller chunks of work:

        1) A method to add a new branch
        2) A method to edit an existing package
        3) A method to edit an existing branch

        .. versionadded:: 0.3.13
        """
        # Change the branches, owners, or anything else that needs changing
        data = {}
        if owner:
            data["owner"] = owner
        if description:
            data["summary"] = description
        if cc_list:
            data["ccList"] = json.dumps(cc_list)
        if comaintainers:
            data["comaintList"] = json.dumps(comaintainers)

        # Parse the groups information
        if groups:
            data["groups"] = json.dumps(groups)

        # Parse the Branch abbreviations into collections
        if branches:
            data["collections"] = []
            data["collections"] = branches

        # Request the changes
        response = self.send_request("/acls/dispatcher/edit_package/%s" % pkg, auth=True, req_params=data)
        if "status" in response and not response["status"]:
            raise AppError(
                name="PackageDBError",
                message=b_("Unable to save" " all information for %(pkg)s: %(msg)s")
                % {"pkg": to_bytes(pkg), "msg": to_bytes(response["message"])},
            )
    def identify(self, environ):
        '''Extract information to identify a user

        Retrieve either a username and password or a session_id that can be
        passed on to FAS to authenticate the user.
        '''
        log.info(b_('in identify()'))

        # friendlyform compat
        if not 'repoze.who.logins' in environ:
            environ['repoze.who.logins'] = 0

        req = webob.Request(environ, charset='utf-8')
        cookie = req.cookies.get(self.session_cookie)

        # This is compatible with TG1 and it gives us a way to authenticate
        # a user without making two requests
        query = req.GET
        form = Bunch(req.POST)
        form.update(query)
        if form.get('login', None) == 'Login' and \
                'user_name' in form and \
                'password' in form:
            identity = {'login': form['user_name'], 'password': form['password']}
            keys = ('login', 'password', 'user_name')
            for k in keys:
                if k in req.GET:
                    del(req.GET[k])
                if k in req.POST:
                    del(req.POST[k])
            return identity

        if cookie is None:
            return None

        log.info(b_('Request identify for cookie %(cookie)s') %
                {'cookie': to_bytes(cookie)})
        try:
            user_data = self._retrieve_user_info(environ,
                    auth_params={'session_id': cookie})
        except Exception, e: # pylint:disable-msg=W0703
            # For any exceptions, returning None means we failed to identify
            log.warning(e)
            return None
Beispiel #17
0
    def __init__(self, base_url, useragent=None, debug=False, insecure=False,
            username=None, password=None, httpauth=None, session_cookie=None,
            session_id=None, session_name='tg-visit', cache_session=True,
            retries=None, timeout=None):
        '''
        :arg base_url: Base of every URL used to contact the server
        :kwarg useragent: Useragent string to use.  If not given, default to
            "Fedora BaseClient/VERSION"
        :kwarg session_name: name of the cookie to use with session handling
        :kwarg debug: If True, log debug information
        :kwarg insecure: If True, do not check server certificates against
            their CA's.  This means that man-in-the-middle attacks are
            possible against the `BaseClient`. You might turn this option on
            for testing against a local version of a server with a self-signed
            certificate but it should be off in production.
        :kwarg username: Username for establishing authenticated connections
        :kwarg password: Password to use with authenticated connections
        :kwarg httpauth: If this is set to ``basic`` then use HTTP Basic
            Authentication to send the username and password.  Default: None,
            means do not use HTTP Authentication.
        :kwarg session_cookie: *Deprecated* Use session_id instead.  If both
            session_id and session_cookie is given, only session_id will be
            used.  User's session_cookie to connect to the server.
        :kwarg session_id: id of the user's session
        :kwarg cache_session: If set to true, cache the user's session data on
            the filesystem between runs
        :kwarg retries: if we get an unknown or possibly transient error from
            the server, retry this many times.  Setting this to a negative
            number makes it try forever.  Defaults to zero, no retries.
        :kwarg timeout: A float describing the timeout of the connection. The
            timeout only affects the connection process itself, not the
            downloading of the response body. Defaults to 30 seconds.

        .. versionchanged:: 0.3.33
            Added the timeout kwarg
        '''
        self.log = log
        self.useragent = useragent or 'Fedora BaseClient/%(version)s' % {
                'version': __version__}
        super(BaseClient, self).__init__(base_url, useragent=self.useragent,
                session_name=session_name, session_as_cookie=False,
                debug=debug, insecure=insecure, retries=retries, timeout=timeout)

        self.username = username
        self.password = password
        self.httpauth = httpauth
        self.cache_session = cache_session
        self._session_id = None
        if session_id:
            self.session_id = session_id
        elif session_cookie:
            warnings.warn(b_('session_cookie is deprecated, use session_id'
                    ' instead'), DeprecationWarning, stacklevel=2)
            session_id = session_cookie.get(self.session_name, '')
            if session_id:
                self.session_id = session_id.value
Beispiel #18
0
    def authenticate(self, environ, identity):
        log.info(b_('In authenticate()'))

        def set_error(msg):
            log.info(msg)
            err = 1
            environ['FAS_AUTH_ERROR'] = err
            # HTTPForbidden ?
            err_app = HTTPFound(err_goto + '?' + 'came_from=' +
                                quote_plus(came_from))
            environ['repoze.who.application'] = err_app

        err_goto = '/login'
        default_came_from = '/'
        if 'SCRIPT_NAME' in environ:
            sn = environ['SCRIPT_NAME']
            err_goto = sn + err_goto
            default_came_from = sn + default_came_from

        query = parse_dict_querystring(environ)
        form = parse_formvars(environ)
        form.update(query)
        came_from = form.get('came_from', default_came_from)

        try:
            auth_params = {
                'username': identity['login'],
                'password': identity['password']
            }
        except KeyError:
            try:
                auth_params = {'session_id': identity['session_id']}
            except:
                # On error we return None which means that auth failed
                set_error(b_('Parameters for authenticating not found'))
                return None

        try:
            user_data = self._retrieve_user_info(environ, auth_params)
        except AuthError, e:
            set_error(b_('Authentication failed: %s') % exception_to_bytes(e))
            log.warning(e)
            return None
Beispiel #19
0
    def remember(self, environ, identity):
        log.info(b_('In remember()'))
        req = webob.Request(environ)
        result = []

        user_data = fas_cache.get_value(identity['login'])
        try:
            session_id = user_data[0]
        except Exception, e:
            return None
Beispiel #20
0
    def _del_session_cookie(self):
        '''**Deprecated** Use session_id instead.

        Delete the session cookie from the filesystem.
        '''
        warnings.warn(b_('session_cookie is deprecated, use session_id'
                         ' instead'),
                      DeprecationWarning,
                      stacklevel=2)
        del (self.session_id)
    def remember(self, environ, identity):
        log.info(b_('In remember()'))
        req = webob.Request(environ)
        result = []

        user_data = fas_cache.get_value(identity['login'])
        try:
            session_id = user_data[0]
        except Exception, e:
            return None
Beispiel #22
0
    def get_package_list(self, collctn=None, collectn=None):
        '''Retrieve a list of all package names in a collection.

        :kwarg collctn: Collection to look for packages in.  This is a collctn
            shortname like 'devel' or 'F-13'.  If unset, return packages in
            all collections
        :kwarg collectn: *Deprecated*.  Please use collctn instead
        :returns: list of package names present in the collection.

        .. versionadded:: 0.3.15

        .. versionchanged:: 0.3.17
            Rename collectn to collctn
        '''
        if (collctn and collectn):
            warnings.warn(b_('collectn is a deprecated name for'
                             ' collctn.\nIgnoring the value of collectn.'),
                          DeprecationWarning,
                          stacklevel=2)

        if collectn and not collctn:
            warnings.warn(b_(
                'collectn has been renamed to collctn.\n'
                'Please start using the new name.  collectn will go'
                ' away in 0.4.x.'),
                          DeprecationWarning,
                          stacklevel=2)
            collctn = collectn

        params = {'packages_tgp_limit': '0'}
        if collctn:
            try:
                collctn_id = self.branches[collctn]['id']
            except KeyError:
                raise PackageDBError(
                    b_('Collection shortname %(collctn)s'
                       ' is unknown.') % {'collctn': to_bytes(collctn)})
            data = self.send_request('/collections/name/%s/' % collctn, params)
        else:
            data = self.send_request('/acls/list/*', params)
        names = [p['name'] for p in data.packages]
        return names
    def authenticate(self, environ, identity):
        log.info(b_('In authenticate()'))

        def set_error(msg):
            log.info(msg)
            err = 1
            environ['FAS_AUTH_ERROR'] = err
            # HTTPForbidden ?
            err_app = HTTPFound(err_goto + '?' +
                                'came_from=' + quote_plus(came_from))
            environ['repoze.who.application'] = err_app


        err_goto = '/login'
        default_came_from = '/'
        if 'SCRIPT_NAME' in environ:
            sn = environ['SCRIPT_NAME']
            err_goto = sn + err_goto
            default_came_from = sn + default_came_from

        query = parse_dict_querystring(environ)
        form = parse_formvars(environ)
        form.update(query)
        came_from = form.get('came_from', default_came_from)

        try:
            auth_params = {'username': identity['login'],
                    'password': identity['password']}
        except KeyError:
            try:
                auth_params = {'session_id': identity['session_id']}
            except:
                # On error we return None which means that auth failed
                set_error(b_('Parameters for authenticating not found'))
                return None

        try:
            user_data = self._retrieve_user_info(environ, auth_params)
        except AuthError, e:
            set_error(b_('Authentication failed: %s') % exception_to_bytes(e))
            log.warning(e)
            return None
Beispiel #24
0
    def gravatar_url(self, username, size=64, default=None, lookup_email=True):
        ''' Returns a URL to a gravatar for a given username.

        :arg username: FAS username to construct a gravatar url for
        :kwarg size: size of the gravatar.  Allowed sizes are 32, 64, 140.
            Default: 64
        :kwarg default: If gravatar does not have a gravatar image for the
            email address, this url is returned instead.  Default:
            the fedora logo at the specified size.
        :kwarg lookup_email:  If true, use the email from FAS, otherwise just
            append @fedoraproject.org to the username.  Not that this will be
            much slower if lookup_email is set to True since we'd have to make a
            query against FAS itself.
        :raises ValueError: if the size parameter is not allowed
        :rtype: :obj:`str`
        :returns: url of a gravatar for the user

        If that user has no gravatar entry, instruct gravatar.com to redirect
        us to the Fedora logo.

        If that user has no email attribute, then make a fake request to
        gravatar.

        .. versionadded:: 0.3.26
        .. versionchanged: 0.3.30
            Add lookup_email parameter to control whether we generate gravatar
            urls with the email in fas or [email protected]
        '''
        if size not in self._valid_gravatar_sizes:
            raise ValueError(
                b_('Size %(size)i disallowed.  Must be in'
                   ' %(valid_sizes)r') % {
                       'size': size,
                       'valid_sizes': self._valid_gravatar_sizes
                   })

        if not default:
            default = "http://fedoraproject.org/static/images/" + \
                    "fedora_infinity_%ix%i.png" % (size, size)

        query_string = urllib.urlencode({
            's': size,
            'd': default,
        })

        if lookup_email:
            person = self.person_by_username(username)
            email = person.get('email', 'no_email')
        else:
            email = "*****@*****.**" % username

        hash = md5(email).hexdigest()

        return "http://www.gravatar.com/avatar/%s?%s" % (hash, query_string)
Beispiel #25
0
 def group_by_name(self, groupname):
     '''Returns a group object based on its name'''
     params = {'groupname': groupname}
     request = self.send_request('json/group_by_name', auth = True,
             req_params = params)
     if request['success']:
         return request['group']
     else:
         raise AppError(message=b_('FAS server unable to retrieve group'
             ' %(group)s') % {'group': to_bytes(groupname)},
             name='FASError')
Beispiel #26
0
    def extract_csrf_token(self, request):
        '''Extract and remove the CSRF token from a given
        :class:`webob.Request`
        '''
        csrf_token = None

        if self.csrf_token_id in request.GET:
            log.debug(b_("%(token)s in GET") % {'token':
                to_bytes(self.csrf_token_id)})
            csrf_token = request.GET[self.csrf_token_id]
            del(request.GET[self.csrf_token_id])
            request.query_string = '&'.join(['%s=%s' % (k, v) for k, v in
                                             request.GET.items()])

        if self.csrf_token_id in request.POST:
            log.debug(b_("%(token)s in POST") % {'token':
                to_bytes(self.csrf_token_id)})
            csrf_token = request.POST[self.csrf_token_id]
            del(request.POST[self.csrf_token_id])

        return csrf_token
Beispiel #27
0
    def __save_ids(self, save):
        '''Save the cached ids file.

        :arg save: The dict of usernames to ids to save.
        '''
        # Make sure the directory exists
        if not path.isdir(b_SESSION_DIR):
            try:
                os.mkdir(b_SESSION_DIR, 0755)
            except OSError, e:
                self.log.warning(b_('Unable to create %(dir)s: %(error)s') %
                    {'dir': b_SESSION_DIR, 'error': to_bytes(e)})
Beispiel #28
0
    def remove_user(self, username, pkg_name, collctn_list=None, collectn_list=None):
        """Remove user from a package

        :arg username: Name of user to remove from the package
        :arg pkg_name: Name of the package
        :kwarg collctn_list: list of collection simple names like
            'F-10','devel'.  Default: None which means user removed from all
            collections associated with the package.
        :kwarg collectn_list: *Deprecated* Use collctn_list instead.
        :returns: status code from the request

        .. versionadded:: 0.3.12

        .. versionchanged:: 0.3.17
            Rename collectn_list to collctn_list
        """
        if collctn_list and collectn_list:
            warnings.warn(
                b_("collectn_list is a deprecated name for" " collctn_list.\nIgnoring the value of collectn_list."),
                DeprecationWarning,
                stacklevel=2,
            )

        if collectn_list and not collctn_list:
            warnings.warn(
                b_(
                    "collectn_list has been renamed to collctn_list."
                    "\nPlease start using the new name.  collectn_list will go"
                    " away in 0.4.x."
                ),
                DeprecationWarning,
                stacklevel=2,
            )
            collctn_list = collectn_list

        if collctn_list:
            params = {"username": username, "pkg_name": pkg_name, "collectn_list": collctn_list}
        else:
            params = {"username": username, "pkg_name": pkg_name}
        return self.send_request("/acls/dispatcher/remove_user", auth=True, req_params=params)
Beispiel #29
0
    def gravatar_url(self, username, size=64,
                     default=None, lookup_email=True):
        ''' Returns a URL to a gravatar for a given username.

        :arg username: FAS username to construct a gravatar url for
        :kwarg size: size of the gravatar.  Allowed sizes are 32, 64, 140.
            Default: 64
        :kwarg default: If gravatar does not have a gravatar image for the
            email address, this url is returned instead.  Default:
            the fedora logo at the specified size.
        :kwarg lookup_email:  If true, use the email from FAS, otherwise just
            append @fedoraproject.org to the username.  Not that this will be
            much slower if lookup_email is set to True since we'd have to make a
            query against FAS itself.
        :raises ValueError: if the size parameter is not allowed
        :rtype: :obj:`str`
        :returns: url of a gravatar for the user

        If that user has no gravatar entry, instruct gravatar.com to redirect
        us to the Fedora logo.

        If that user has no email attribute, then make a fake request to
        gravatar.

        .. versionadded:: 0.3.26
        .. versionchanged: 0.3.30
            Add lookup_email parameter to control whether we generate gravatar
            urls with the email in fas or [email protected]
        '''
        if size not in self._valid_gravatar_sizes:
            raise ValueError(b_('Size %(size)i disallowed.  Must be in'
                ' %(valid_sizes)r') % { 'size': size,
                    'valid_sizes': self._valid_gravatar_sizes})

        if not default:
            default = "http://fedoraproject.org/static/images/" + \
                    "fedora_infinity_%ix%i.png" % (size, size)

        query_string = urllib.urlencode({
            's': size,
            'd': default,
        })

        if lookup_email:
            person = self.person_by_username(username)
            email = person.get('email', 'no_email')
        else:
            email = "*****@*****.**" % username

        hash = md5(email).hexdigest()

        return "http://www.gravatar.com/avatar/%s?%s" % (hash, query_string)
Beispiel #30
0
 def group_by_name(self, groupname):
     '''Returns a group object based on its name'''
     params = {'groupname': groupname}
     request = self.send_request('json/group_by_name',
                                 auth=True,
                                 req_params=params)
     if request['success']:
         return request['group']
     else:
         raise AppError(message=b_('FAS server unable to retrieve group'
                                   ' %(group)s') %
                        {'group': to_bytes(groupname)},
                        name='FASError')
    def forget(self, environ, identity):
        log.info(b_('In forget()'))
        # return a expires Set-Cookie header
        req = webob.Request(environ)

        user_data = fas_cache.get_value(identity['login'])
        try:
            session_id = user_data[0]
        except Exception:
            return None

        log.info(b_('Forgetting login data for cookie %(s_id)s') %
                {'s_id': to_bytes(session_id)})

        self.fas.logout(session_id)

        result = []
        fas_cache.remove_value(key=identity['login'])
        expired = '%s=\'\'; Path=/; Expires=Sun, 10-May-1971 11:59:00 GMT'\
                % self.session_cookie
        result.append(('Set-Cookie', expired))
        return result
Beispiel #32
0
    def forget(self, environ, identity):
        log.info(b_('In forget()'))
        # return a expires Set-Cookie header
        req = webob.Request(environ)

        user_data = fas_cache.get_value(identity['login'])
        try:
            session_id = user_data[0]
        except Exception:
            return None

        log.info(
            b_('Forgetting login data for cookie %(s_id)s') %
            {'s_id': to_bytes(session_id)})

        self.fas.logout(session_id)

        result = []
        fas_cache.remove_value(key=identity['login'])
        expired = '%s=\'\'; Path=/; Expires=Sun, 10-May-1971 11:59:00 GMT'\
                % self.session_cookie
        result.append(('Set-Cookie', expired))
        return result
Beispiel #33
0
    def candidates(self):
        """ Get a list list of update candidates.

        This method is a generator that returns a list of koji builds that
        could potentially be pushed as updates.
        """
        if not self.username:
            raise BodhiClientException(b_('You must specify a username'))
        data = self.send_request('candidate_tags')
        koji = self.get_koji_session(login=False)
        for tag in data['tags']:
            for build in koji.listTagged(tag, latest=True):
                if build['owner_name'] == self.username:
                    yield build
Beispiel #34
0
    def extract_csrf_token(self, request):
        '''Extract and remove the CSRF token from a given
        :class:`webob.Request`
        '''
        csrf_token = None

        if self.csrf_token_id in request.GET:
            log.debug(
                b_("%(token)s in GET") %
                {'token': to_bytes(self.csrf_token_id)})
            csrf_token = request.GET[self.csrf_token_id]
            del (request.GET[self.csrf_token_id])
            request.query_string = '&'.join(
                ['%s=%s' % (k, v) for k, v in request.GET.items()])

        if self.csrf_token_id in request.POST:
            log.debug(
                b_("%(token)s in POST") %
                {'token': to_bytes(self.csrf_token_id)})
            csrf_token = request.POST[self.csrf_token_id]
            del (request.POST[self.csrf_token_id])

        return csrf_token
Beispiel #35
0
 def login(self, username, password):
     data = self.send_request('api.php', req_params={
             'action': 'login',
             'format': 'json',
             'lgname': username,
             'lgpassword': password,
             })
     if 'lgtoken' not in data.get('login', {}):
         raise AuthError(b_('Login failed: %(data)s') %
                 {'data':to_bytes(data)})
     #self.session_id = data['login']['lgtoken']
     #self.username = data['login']['lgusername']
     self.check_api_limits()
     return data
Beispiel #36
0
    def candidates(self):
        """ Get a list list of update candidates.

        This method is a generator that returns a list of koji builds that
        could potentially be pushed as updates.
        """
        if not self.username:
            raise BodhiClientException(b_('You must specify a username'))
        data = self.send_request('candidate_tags')
        koji = self.get_koji_session(login=False)
        for tag in data['tags']:
            for build in koji.listTagged(tag, latest=True):
                if build['owner_name'] == self.username:
                    yield build
Beispiel #37
0
 def login(self, username, password):
     data = self.send_request('api.php',
                              req_params={
                                  'action': 'login',
                                  'format': 'json',
                                  'lgname': username,
                                  'lgpassword': password,
                              })
     if 'lgtoken' not in data.get('login', {}):
         raise AuthError(
             b_('Login failed: %(data)s') % {'data': to_bytes(data)})
     #self.session_id = data['login']['lgtoken']
     #self.username = data['login']['lgusername']
     self.check_api_limits()
     return data
Beispiel #38
0
    def _get_session_cookie(self):
        '''**Deprecated** Use session_id instead.

        Attempt to retrieve the session cookie from the filesystem.

        :Returns: user's session cookie
        '''
        warnings.warn(b_('session_cookie is deprecated, use session_id'
            ' instead'), DeprecationWarning, stacklevel=2)
        session_id = self.session_id
        if not session_id:
            return ''
        cookie = Cookie.SimpleCookie()
        cookie[self.session_name] = session_id
        return cookie
Beispiel #39
0
    def __save_ids(self, save):
        '''Save the cached ids file.

        :arg save: The dict of usernames to ids to save.
        '''
        # Make sure the directory exists
        if not path.isdir(b_SESSION_DIR):
            try:
                os.mkdir(b_SESSION_DIR, 0755)
            except OSError, e:
                self.log.warning(
                    b_('Unable to create %(dir)s: %(error)s') % {
                        'dir': b_SESSION_DIR,
                        'error': to_bytes(e)
                    })
 def update_queued_visits(self, queue):
     '''Update the visit information on the server'''
     self.log.debug('JsonFasVisitManager.update_queued_visits: %s' % len(queue))
     # Hit any URL in fas with each visit_key to update the sessions
     for visit_key in queue:
         self.log.info(b_('updating visit (%s)'), to_bytes(visit_key))
         try:
             self.fas.refresh_session(visit_key)
         except Exception:
             # If fas returns an error, push the visit back onto the queue
             try:
                 self.lock.acquire()
                 self.queue[visit_key] = queue[visit_key]
             finally:
                 self.lock.release()
     self.log.debug('JsonFasVisitManager.update_queued_visitsr exit')
Beispiel #41
0
    def _set_session_cookie(self, session_cookie):
        '''**Deprecated** Use session_id instead.
        Store our pickled session cookie.

        :arg session_cookie: cookie to set our internal cookie to

        This method loads our existing session file and modifies our
        current user's cookie.  This allows us to retain cookies for
        multiple users.
        '''
        warnings.warn(b_('session_cookie is deprecated, use session_id'
            ' instead'), DeprecationWarning, stacklevel=2)
        session_id = session_cookie.get(self.session_name, '')
        if session_id:
            session_id = session_id.value
        self.session_id = session_id
Beispiel #42
0
    def __init__(self, application, csrf_token_id='_csrf_token',
                 clear_env='repoze.who.identity repoze.what.credentials',
                 token_env='CSRF_TOKEN', auth_state='CSRF_AUTH_STATE'):
        '''
        Initialize the CSRF Protection WSGI Middleware.

        :csrf_token_id: The name of the CSRF token variable
        :clear_env: Variables to clear out of the `environ` on invalid token
        :token_env: The name of the token variable in the environ
        :auth_state: The environ key that will be set when we are logging in
        '''
        log.info(b_('Creating CSRFProtectionMiddleware'))
        self.application = application
        self.csrf_token_id = csrf_token_id
        self.clear_env = clear_env.split()
        self.token_env = token_env
        self.auth_state = auth_state
Beispiel #43
0
    def _get_session_cookie(self):
        '''**Deprecated** Use session_id instead.

        Attempt to retrieve the session cookie from the filesystem.

        :Returns: user's session cookie
        '''
        warnings.warn(b_('session_cookie is deprecated, use session_id'
                         ' instead'),
                      DeprecationWarning,
                      stacklevel=2)
        session_id = self.session_id
        if not session_id:
            return ''
        cookie = Cookie.SimpleCookie()
        cookie[self.session_name] = session_id
        return cookie
Beispiel #44
0
 def update_queued_visits(self, queue):
     '''Update the visit information on the server'''
     self.log.debug('JsonFasVisitManager.update_queued_visits: %s' %
                    len(queue))
     # Hit any URL in fas with each visit_key to update the sessions
     for visit_key in queue:
         self.log.info(b_('updating visit (%s)'), to_bytes(visit_key))
         try:
             self.fas.refresh_session(visit_key)
         except Exception:
             # If fas returns an error, push the visit back onto the queue
             try:
                 self.lock.acquire()
                 self.queue[visit_key] = queue[visit_key]
             finally:
                 self.lock.release()
     self.log.debug('JsonFasVisitManager.update_queued_visitsr exit')
Beispiel #45
0
    def add_metadata(self, environ, identity):
        request = Request(environ)
        log.debug(
            b_('CSRFMetadataProvider.add_metadata(%(r_path)s)') %
            {'r_path': to_bytes(request.path)})

        session_id = environ.get(self.auth_session_id)
        if not session_id:
            session_id = request.cookies.get(self.session_cookie)
        log.debug(b_('session_id = %(s_id)r') % {'s_id': to_bytes(session_id)})

        if session_id and session_id != 'Set-Cookie:':
            environ[self.auth_session_id] = session_id
            token = sha1(session_id).hexdigest()
            identity.update({self.csrf_token_id: token})
            log.debug(b_('Identity updated with CSRF token'))
            path = self.strip_script(environ, request.path)
            if path == self.login_handler:
                log.debug(b_('Setting CSRF_AUTH_STATE'))
                environ[self.auth_state] = True
                environ[self.token_env] = token
            else:
                environ[self.token_env] = self.extract_csrf_token(request)

            app = environ.get('repoze.who.application')
            if app:
                # This occurs during login in some application configurations
                if isinstance(app, HTTPFound) and environ.get(self.auth_state):
                    log.debug(
                        b_('Got HTTPFound(302) from'
                           ' repoze.who.application'))
                    # What possessed people to make this a string or
                    # a function?
                    location = app.location
                    if hasattr(location, '__call__'):
                        location = location()
                    loc = update_qs(location, {self.csrf_token_id: str(token)})

                    headers = app.headers.items()
                    replace_header(headers, 'location', loc)
                    app.headers = ResponseHeaders(headers)
                    log.debug(
                        b_('Altered headers: %(headers)s') %
                        {'headers': to_bytes(app.headers)})
        else:
            log.warning(
                b_('Invalid session cookie %(s_id)r, not setting CSRF'
                   ' token!') % {'s_id': to_bytes(session_id)})
Beispiel #46
0
    def parse_file(self, input_file):
        """ Parse an update template file.

        :arg input_file: The filename of the update template.

        Returns an array of dictionaries of parsed update values which
        can be directly passed to the ``save`` method.

        """
        from iniparse.compat import ConfigParser
        self.log.info(b_('Reading from %s ') % input_file)
        input_file = expanduser(input_file)
        if exists(input_file):
            defaults = {
                'type': 'bugfix',
                'request': 'testing',
                'notes': '',
                'bugs': '',
                'close_bugs': 'True',
                'autokarma': 'True',
                'stable_karma': 3,
                'unstable_karma': -3,
                'suggest_reboot': 'False',
            }
            config = ConfigParser(defaults)
            template_file = open(input_file)
            config.readfp(template_file)
            template_file.close()
            updates = []
            for section in config.sections():
                update = {}
                update['builds'] = section
                update['type_'] = config.get(section, 'type')
                update['request'] = config.get(section, 'request')
                update['bugs'] = config.get(section, 'bugs')
                update['close_bugs'] = config.getboolean(section, 'close_bugs')
                update['notes'] = config.get(section, 'notes')
                update['autokarma'] = config.getboolean(section, 'autokarma')
                update['stable_karma'] = config.getint(section, 'stable_karma')
                update['unstable_karma'] = config.getint(
                    section, 'unstable_karma')
                update['suggest_reboot'] = config.getboolean(
                    section, 'suggest_reboot')
                updates.append(update)
        return updates
Beispiel #47
0
    def parse_file(self, input_file):
        """ Parse an update template file.

        :arg input_file: The filename of the update template.

        Returns an array of dictionaries of parsed update values which
        can be directly passed to the ``save`` method.

        """
        from iniparse.compat import ConfigParser
        self.log.info(b_('Reading from %s ') % input_file)
        input_file = expanduser(input_file)
        if exists(input_file):
            defaults = {
                'type': 'bugfix',
                'request': 'testing',
                'notes': '',
                'bugs': '',
                'close_bugs': 'True',
                'autokarma': 'True',
                'stable_karma': 3,
                'unstable_karma': -3,
                'suggest_reboot': 'False',
                }
            config = ConfigParser(defaults)
            template_file = open(input_file)
            config.readfp(template_file)
            template_file.close()
            updates = []
            for section in config.sections():
                update = {}
                update['builds'] = section
                update['type_'] = config.get(section, 'type')
                update['request'] = config.get(section, 'request')
                update['bugs'] = config.get(section, 'bugs')
                update['close_bugs'] = config.getboolean(section, 'close_bugs')
                update['notes'] = config.get(section, 'notes')
                update['autokarma'] = config.getboolean(section, 'autokarma')
                update['stable_karma'] = config.getint(section, 'stable_karma')
                update['unstable_karma'] = config.getint(section,
                                                         'unstable_karma')
                update['suggest_reboot'] = config.getboolean(section,
                                                             'suggest_reboot')
                updates.append(update)
        return updates
Beispiel #48
0
    def _set_session_cookie(self, session_cookie):
        '''**Deprecated** Use session_id instead.
        Store our pickled session cookie.

        :arg session_cookie: cookie to set our internal cookie to

        This method loads our existing session file and modifies our
        current user's cookie.  This allows us to retain cookies for
        multiple users.
        '''
        warnings.warn(b_('session_cookie is deprecated, use session_id'
                         ' instead'),
                      DeprecationWarning,
                      stacklevel=2)
        session_id = session_cookie.get(self.session_name, '')
        if session_id:
            session_id = session_id.value
        self.session_id = session_id
Beispiel #49
0
    def __load_ids(self):
        '''load id data from a file.

        :Returns: Complete mapping of users to session ids
        '''
        saved_session = {}
        session_file = None
        if path.isfile(b_SESSION_FILE):
            try:
                session_file = file(b_SESSION_FILE, 'r')
                saved_session = pickle.load(session_file)
            except (IOError, EOFError):
                self.log.info(b_('Unable to load session from %(file)s') %
                        {'file': b_SESSION_FILE})
            if session_file:
                session_file.close()

        return saved_session
Beispiel #50
0
    def __call__(self, environ, start_response):
        '''
        This method is called for each request.  It looks for a user-supplied
        CSRF token in the GET/POST parameters, and compares it to the token
        attached to ``environ['repoze.who.identity']['_csrf_token']``.  If it
        does not match, or if a token is not provided, it will remove the
        user from the ``environ``, based on the ``clear_env`` setting.
        '''
        request = Request(environ)
        log.debug(
            b_('CSRFProtectionMiddleware(%(r_path)s)') %
            {'r_path': to_bytes(request.path)})

        token = environ.get('repoze.who.identity', {}).get(self.csrf_token_id)
        csrf_token = environ.get(self.token_env)

        if token and csrf_token and token == csrf_token:
            log.debug(b_('User supplied CSRF token matches environ!'))
        else:
            if not environ.get(self.auth_state):
                log.debug(b_('Clearing identity'))
                self._clean_environ(environ)
                if 'repoze.who.identity' not in environ:
                    environ['repoze.who.identity'] = Bunch()
                if 'repoze.who.logins' not in environ:
                    # For compatibility with friendlyform
                    environ['repoze.who.logins'] = 0
                if csrf_token:
                    log.warning(
                        b_('Invalid CSRF token.  User supplied'
                           ' (%(u_token)s) does not match what\'s in our'
                           ' environ (%(e_token)s)') % {
                               'u_token': to_bytes(csrf_token),
                               'e_token': to_bytes(token)
                           })

        response = request.get_response(self.application)

        if environ.get(self.auth_state):
            log.debug(b_('CSRF_AUTH_STATE; rewriting headers'))
            token = environ.get('repoze.who.identity', {})\
                    .get(self.csrf_token_id)

            loc = update_qs(response.location,
                            {self.csrf_token_id: str(token)})
            response.location = loc
            log.debug(
                b_('response.location = %(r_loc)s') %
                {'r_loc': to_bytes(response.location)})
            environ[self.auth_state] = None

        return response(environ, start_response)
Beispiel #51
0
    def __retrieve_user(self):
        '''Attempt to load the user from the visit_key.

        :returns: a user or None
        '''
        if self.debug:
            import inspect
            caller = inspect.getouterframes(inspect.currentframe())[2][3]
            self.log.debug('JSONFASPROVIDER.send_request caller: %s' % caller)

        # The cached value can be in four states:
        # Holds a user: we successfully retrieved it last time, return it
        # Holds None: we haven't yet tried to retrieve a user, do so now
        # Holds a session_id that is the same as our session_id, we unsuccessfully
        # tried to retrieve a session with this id already, return None
        # Holds a session_id that is different than the current session_id:
        # we tried with a previous session_id; try again with the new one.
        if self._retrieved_user:
            if isinstance(self._retrieved_user, basestring):
                if self._retrieved_user == self.session_id:
                    return None
                else:
                    self._retrieved_user = None
            else:
                return self._retrieved_user
        # I hope this is a safe place to double-check the SSL variables.
        # TODO: Double check my logic with this - is it unnecessary to
        # check that the username matches up?
        if self.using_ssl:
            if cherrypy.request.headers['X-Client-Verify'] != 'SUCCESS':
                self.logout()
                return None
            # Retrieve the user information differently when using ssl
            try:
                person = fas.person_by_username(self.username, auth=True)
            except Exception, e:  # pylint: disable-msg=W0703
                # :W0703: Any errors have to result in no user being set.  The
                # rest of the framework doesn't know what to do otherwise.
                self.log.warning(
                    b_('jsonfasprovider, ssl, returned errors'
                       ' from send_request: %s') % to_bytes(e))
                person = None
            self._retrieved_user = person or None
            return self._retrieved_user
Beispiel #52
0
    def add_metadata(self, environ, identity):
        request = Request(environ)
        log.debug(b_('CSRFMetadataProvider.add_metadata(%(r_path)s)')
                % {'r_path': to_bytes(request.path)})

        session_id = environ.get(self.auth_session_id)
        if not session_id:
            session_id = request.cookies.get(self.session_cookie)
        log.debug(b_('session_id = %(s_id)r') % {'s_id':
                to_bytes(session_id)})

        if session_id and session_id != 'Set-Cookie:':
            environ[self.auth_session_id] = session_id
            token = sha1(session_id).hexdigest()
            identity.update({self.csrf_token_id: token})
            log.debug(b_('Identity updated with CSRF token'))
            path = self.strip_script(environ, request.path)
            if path == self.login_handler:
                log.debug(b_('Setting CSRF_AUTH_STATE'))
                environ[self.auth_state] = True
                environ[self.token_env] = token
            else:
                environ[self.token_env] = self.extract_csrf_token(request)

            app = environ.get('repoze.who.application')
            if app:
                # This occurs during login in some application configurations
                if isinstance(app, HTTPFound) and environ.get(self.auth_state):
                    log.debug(b_('Got HTTPFound(302) from'
                        ' repoze.who.application'))
                    # What possessed people to make this a string or
                    # a function?
                    location = app.location
                    if hasattr(location, '__call__'):
                        location = location()
                    loc = update_qs(location, {self.csrf_token_id:
                        str(token)})

                    headers = app.headers.items()
                    replace_header(headers, 'location', loc)
                    app.headers = ResponseHeaders(headers)
                    log.debug(b_('Altered headers: %(headers)s') % {'headers':
                        to_bytes(app.headers)})
        else:
            log.warning(b_('Invalid session cookie %(s_id)r, not setting CSRF'
                ' token!') % {'s_id': to_bytes(session_id)})
    def __retrieve_user(self):
        '''Attempt to load the user from the visit_key.

        :returns: a user or None
        '''
        if self.debug:
            import inspect
            caller = inspect.getouterframes(inspect.currentframe())[2][3]
            self.log.debug('JSONFASPROVIDER.send_request caller: %s' % caller)

        # The cached value can be in four states:
        # Holds a user: we successfully retrieved it last time, return it
        # Holds None: we haven't yet tried to retrieve a user, do so now
        # Holds a session_id that is the same as our session_id, we unsuccessfully
        # tried to retrieve a session with this id already, return None
        # Holds a session_id that is different than the current session_id:
        # we tried with a previous session_id; try again with the new one.
        if self._retrieved_user:
            if isinstance(self._retrieved_user, basestring):
                if self._retrieved_user == self.session_id:
                    return None
                else:
                    self._retrieved_user = None
            else:
                return self._retrieved_user
        # I hope this is a safe place to double-check the SSL variables.
        # TODO: Double check my logic with this - is it unnecessary to
        # check that the username matches up?
        if self.using_ssl:
            if cherrypy.request.headers['X-Client-Verify'] != 'SUCCESS':
                self.logout()
                return None
            # Retrieve the user information differently when using ssl
            try:
                person = fas.person_by_username(self.username, auth=True)
            except Exception, e: # pylint: disable-msg=W0703
                # :W0703: Any errors have to result in no user being set.  The
                # rest of the framework doesn't know what to do otherwise.
                self.log.warning(b_('jsonfasprovider, ssl, returned errors'
                    ' from send_request: %s') % to_bytes(e))
                person = None
            self._retrieved_user = person or None
            return self._retrieved_user
Beispiel #54
0
def isiterable(obj, include_string=True):
    '''*Deprecated* Use kitchen.iterutils.isiterable instead.

    .. warning::
        kitchen.iterutils.isiterable uses False as the default value for
        :attr:`include_string` instead of True.

    Check whether an object is an iterable.

    :arg obj: Object to test whether it is an iterable
    :include_string: If True (default), if `obj` is a str or unicode this
        function will return True.  If set to False, strings and unicodes will
        cause this function to return False.
    :returns: True if `obj` is iterable, otherwise False.
    '''
    warnings.warn(b_('fedora.iterutils.isiterable is deprecated, use'
        ' kitchen.iterutils.isiterable instead'), DeprecationWarning,
        stacklevel=2)
    return _isiterable(obj, include_string)
Beispiel #55
0
    def __load_ids(self):
        '''load id data from a file.

        :Returns: Complete mapping of users to session ids
        '''
        saved_session = {}
        session_file = None
        if path.isfile(b_SESSION_FILE):
            try:
                session_file = file(b_SESSION_FILE, 'r')
                saved_session = pickle.load(session_file)
            except (IOError, EOFError):
                self.log.info(
                    b_('Unable to load session from %(file)s') %
                    {'file': b_SESSION_FILE})
            if session_file:
                session_file.close()

        return saved_session
Beispiel #56
0
    def __init__(self,
                 application,
                 csrf_token_id='_csrf_token',
                 clear_env='repoze.who.identity repoze.what.credentials',
                 token_env='CSRF_TOKEN',
                 auth_state='CSRF_AUTH_STATE'):
        '''
        Initialize the CSRF Protection WSGI Middleware.

        :csrf_token_id: The name of the CSRF token variable
        :clear_env: Variables to clear out of the `environ` on invalid token
        :token_env: The name of the token variable in the environ
        :auth_state: The environ key that will be set when we are logging in
        '''
        log.info(b_('Creating CSRFProtectionMiddleware'))
        self.application = application
        self.csrf_token_id = csrf_token_id
        self.clear_env = clear_env.split()
        self.token_env = token_env
        self.auth_state = auth_state
Beispiel #57
0
    def validate_identity(self, user_name, password, visit_key):
        '''
        Look up the identity represented by user_name and determine whether the
        password is correct.

        Must return either None if the credentials weren't valid or an object
        with the following properties:

            :user_name: original user name
            :user: a provider dependant object (TG_User or similar)
            :groups: a set of group IDs
            :permissions: a set of permission IDs

        :arg user_name: user_name we're authenticating.  If None, we'll try
            to lookup a username from SSL variables
        :arg password: password to authenticate user_name with
        :arg visit_key: visit_key from the user's session
        '''
        using_ssl = False
        if not user_name and config.get('identity.ssl'):
            if cherrypy.request.headers['X-Client-Verify'] == 'SUCCESS':
                user_name = cherrypy.request.headers['X-Client-CN']
                cherrypy.request.fas_provided_username = user_name
                using_ssl = True

        # pylint: disable-msg=R0201
        # TG identity providers have this method so we can't get rid of it.
        try:
            user = JsonFasIdentity(visit_key,
                                   username=user_name,
                                   password=password,
                                   using_ssl=using_ssl)
        except FedoraServiceError, e:
            self.log.warning(
                b_('Error logging in %(user)s: %(error)s') % {
                    'user': to_bytes(user_name),
                    'error': to_bytes(e)
                })
            return None
Beispiel #58
0
    def user_data(self):
        '''Return user data for all users in FAS

        Note: If the user is not authorized to see password hashes,
        '*' is returned for the hash.

        :raises AppError: if the query failed on the server
        :returns: A dict mapping user IDs to a username, password hash,
            SSH public key, email address, and status.

        .. versionadded:: 0.3.8
        '''
        try:
            request = self.send_request('json/fas_client/user_data', auth=True)
            if request['success']:
                return request['data']
            else:
                raise AppError(message=b_('FAS server unable to retrieve user'
                                          ' information'),
                               name='FASError')
        except FedoraServiceError:
            raise
Beispiel #59
0
    def people_by_id(self):
        '''*Deprecated* Use people_by_key() instead.

        Returns a dict relating user IDs to human_name, email, username,
        and bugzilla email

        .. versionchanged:: 0.3.21
            Return a Bunch instead of a DictContainer
        '''
        warnings.warn(b_(
            "people_by_id() is deprecated and will be removed in"
            " 0.4.  Please port your code to use people_by_key(key='id',"
            " fields=['human_name', 'email', 'username', 'bugzilla_email'])"
            " instead"),
                      DeprecationWarning,
                      stacklevel=2)

        request = self.send_request('/json/user_id', auth=True)
        user_to_id = {}
        people = Bunch()
        for person_id, username in request['people'].items():
            person_id = int(person_id)
            # change userids from string back to integer
            people[person_id] = {'username': username, 'id': person_id}
            user_to_id[username] = person_id

        # Retrieve further useful information about the users
        request = self.send_request('/group/dump', auth=True)
        for user in request['people']:
            userid = user_to_id[user[0]]
            person = people[userid]
            person['email'] = user[1]
            person['human_name'] = user[2]
            if userid in self.__bugzilla_email:
                person['bugzilla_email'] = self.__bugzilla_email[userid]
            else:
                person['bugzilla_email'] = person['email']

        return people
Beispiel #60
0
    def _get_session_id(self):
        '''Attempt to retrieve the session id from the filesystem.

        Note: this method will cache the session_id in memory rather than fetch
        the id from the filesystem each time.

        :Returns: session_id
        '''
        if self._session_id:
            return self._session_id
        if not self.username:
            self._session_id = ''
        else:
            saved_sessions = self.__load_ids()
            self._session_id = saved_sessions.get(self.username, '')
        if isinstance(self._session_id, Cookie.SimpleCookie):
            self._session_id = ''

        if not self._session_id:
            self.log.debug(
                b_('No session cached for "%s"') % to_bytes(self.username))

        return self._session_id