Пример #1
0
    def get_or_create_user(self, username, request):
        """Always check Bugzilla for updates."""
        username = username.strip()

        try:
            bugzilla = Bugzilla(get_bugzilla_api_key(request.user))
        except BugzillaUrlError:
            return None
        except BugzillaError:
            raise PermissionDenied

        user_data = bugzilla.get_user(username)

        if not user_data:
            raise self.bz_error_response(request)

        # Just store the results.
        get_or_create_bugzilla_users(user_data)

        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            return None
Пример #2
0
    def get_or_create_user(self, username, request):
        """Always check Bugzilla for updates."""
        username = username.strip()

        try:
            bugzilla = Bugzilla(get_bugzilla_api_key(request.user))
        except BugzillaUrlError:
            return None
        except BugzillaError:
            raise PermissionDenied

        user_data = bugzilla.get_user(username)

        if not user_data:
            raise self.bz_error_response(request)

        # Just store the results.
        get_or_create_bugzilla_users(user_data)

        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            return None
Пример #3
0
def get_bmo_auth_callback(request):
    """Handler for the third part of the Bugzilla auth-delegation process.

    After the above POST call is executed, Bugzilla then redirects back to
    this view, passing the return value of the POST handler, as
    `callback_result`, and optionally a redirect, passed from the original
    redirect to Bugzilla (from the MozReview login view).

    This handler then verifies the API key with Bugzilla and attempts to
    create or update the user in MozReview.  If everything succeeds, the
    user is again redirected back to the original page (or the root page if
    there was no redirect passed in, e.g., in tests).  Otherwise the user is
    shown an error page.
    """
    bmo_username = request.GET.get('client_api_login', None)
    callback_result = request.GET.get('callback_result', None)
    redirect = request.GET.get('redirect', None)
    secret = request.GET.get('secret', None)

    if not (bmo_username and callback_result):
        logger.error('Bugzilla auth callback called without required '
                     'parameters.')
        return render_login_error(request)

    # Delete expired unverified keys (5 minute lifetime).
    UnverifiedBugzillaApiKey.objects.filter(timestamp__lte=timezone.now() -
                                            timedelta(minutes=5)).delete()

    parsed = None if not redirect else urlparse(redirect)

    # Enforce relative redirects; we don't want people crafting auth links
    # that redirect to other sites.  We check the scheme as well as the netloc
    # to catch data, file, and other such server-less URIs.

    if not parsed or parsed.scheme or parsed.netloc:
        redirect = '/'

    unverified_keys = UnverifiedBugzillaApiKey.objects.filter(
        bmo_username=bmo_username).order_by('timestamp')

    if not unverified_keys:
        logger.error('No unverified keys found for BMO user %s.' %
                     bmo_username)
        return render_login_error(request)

    unverified_key = unverified_keys.last()

    if len(unverified_keys) > 1:
        logger.warning('Multiple unverified keys on file for BMO user %s. '
                       'Using most recent, from %s.' %
                       (bmo_username, unverified_key.timestamp))

    if callback_result != unverified_key.callback_result:
        logger.error('Callback result does not match for BMO user %s.' %
                     bmo_username)
        return render_login_error(request)

    if secret is None or request.COOKIES['bmo_auth_secret'] != secret:
        logger.error('Callback secret does not match cookie for user %s.' %
                     bmo_username)
        return render_login_error(request)

    bmo_api_key = unverified_key.api_key
    unverified_key.delete()

    b = Bugzilla()

    try:
        if not b.valid_api_key(bmo_username, bmo_api_key):
            logger.error('Invalid API key for %s.' % bmo_username)
            return render_login_error(request)
    except BugzillaError as e:
        logger.error('Error validating API key: %s' % e.msg)
        return render_login_error(request)

    b.api_key = bmo_api_key

    try:
        user_data = b.get_user(bmo_username)
    except BugzillaError as e:
        logger.error('Error getting user data: %s' % e.msg)
        return render_login_error(request)

    if not user_data:
        logger.warning('Could not retrieve user info for %s after '
                       'validating API key.' % bmo_username)
        return render_login_error(request)

    users = get_or_create_bugzilla_users(user_data)

    if not users:
        logger.warning('Failed to create user %s after validating API key.' %
                       bmo_username)
        return render_login_error(request)

    user = users[0]
    assert user.email == bmo_username

    if not user.is_active:
        logger.warning('Validated API key but user %s is inactive.' %
                       bmo_username)
        return render_login_error(request)

    set_bugzilla_api_key(user, bmo_api_key)

    try:
        associate_employee_ldap(user)
    except LDAPAssociationException as e:
        logger.info('LDAP association failed: %s' % str(e))
    except Exception:
        logger.exception('Error while performing LDAP association')

    user.backend = 'mozreview.bugzilla.auth.BugzillaBackend'
    logger.info('BMO Auth callback succeeded for user: %s' % bmo_username)
    login(request, user)
    response = HttpResponseRedirect(redirect)
    response.delete_cookie('bmo_auth_secret')
    return response
Пример #4
0
def get_bmo_auth_callback(request):
    """Handler for the third part of the Bugzilla auth-delegation process.

    After the above POST call is executed, Bugzilla then redirects back to
    this view, passing the return value of the POST handler, as
    `callback_result`, and optionally a redirect, passed from the original
    redirect to Bugzilla (from the MozReview login view).

    This handler then verifies the API key with Bugzilla and attempts to
    create or update the user in MozReview.  If everything succeeds, the
    user is again redirected back to the original page (or the root page if
    there was no redirect passed in, e.g., in tests).  Otherwise the user is
    shown an error page.
    """
    bmo_username = request.GET.get('client_api_login', None)
    callback_result = request.GET.get('callback_result', None)
    redirect = request.GET.get('redirect', None)
    secret = request.GET.get('secret', None)

    if not (bmo_username and callback_result):
        logger.error('Bugzilla auth callback called without required '
                     'parameters.')
        return show_error_page(request)

    # Delete expired unverified keys (5 minute lifetime).
    UnverifiedBugzillaApiKey.objects.filter(
        timestamp__lte=timezone.now() - timedelta(minutes=5)).delete()

    parsed = None if not redirect else urlparse(redirect)

    # Enforce relative redirects; we don't want people crafting auth links
    # that redirect to other sites.  We check the scheme as well as the netloc
    # to catch data, file, and other such server-less URIs.

    if not parsed or parsed.scheme or parsed.netloc:
        redirect = '/'

    unverified_keys = UnverifiedBugzillaApiKey.objects.filter(
        bmo_username=bmo_username).order_by('timestamp')

    if not unverified_keys:
        logger.error('No unverified keys found for BMO user %s.' %
                     bmo_username)
        return show_error_page(request)

    unverified_key = unverified_keys.last()

    if len(unverified_keys) > 1:
        logger.warning('Multiple unverified keys on file for BMO user %s. '
                       'Using most recent, from %s.' %
                       (bmo_username, unverified_key.timestamp))

    if callback_result != unverified_key.callback_result:
        logger.error('Callback result does not match for BMO user %s.' %
                     bmo_username)
        return show_error_page(request)

    if secret is None or request.COOKIES['bmo_auth_secret'] != secret:
        logger.error('Callback secret does not match cookie for user %s.' %
                     bmo_username)
        return show_error_page(request)

    bmo_api_key = unverified_key.api_key
    unverified_key.delete()

    b = Bugzilla()

    try:
        if not b.valid_api_key(bmo_username, bmo_api_key):
            logger.error('Invalid API key for %s.' % bmo_username)
            return show_error_page(request)
    except BugzillaError as e:
        logger.error('Error validating API key: %s' % e.msg)
        return show_error_page(request)

    b.api_key = bmo_api_key

    try:
        user_data = b.get_user(bmo_username)
    except BugzillaError as e:
        logger.error('Error getting user data: %s' % e.msg)
        return show_error_page(request)

    if not user_data:
        logger.warning('Could not retrieve user info for %s after '
                       'validating API key.' % bmo_username)
        return show_error_page(request)

    users = get_or_create_bugzilla_users(user_data)

    if not users:
        logger.warning('Failed to create user %s after validating API key.' %
                       bmo_username)
        return show_error_page(request)

    user = users[0]
    assert user.email == bmo_username

    if not user.is_active:
        logger.warning('Validated API key but user %s is inactive.' %
                       bmo_username)
        return show_error_page(request)

    set_bugzilla_api_key(user, bmo_api_key)

    try:
        associate_employee_ldap(user)
    except LDAPAssociationException as e:
        logger.info('LDAP association failed: %s' % str(e))
    except Exception:
        logger.exception('Error while performing LDAP association')

    user.backend = 'rbbz.auth.BugzillaBackend'
    logger.info('BMO Auth callback succeeded for user: %s' % bmo_username)
    login(request, user)
    response = HttpResponseRedirect(redirect)
    response.delete_cookie('bmo_auth_secret')
    return response
Пример #5
0
def get_bmo_auth_callback(request):
    """Handler for the second part of the Bugzilla auth-delegation process.

    After the above POST call is executed, Bugzilla then redirects back to
    this view, passing the return value of the POST handler, as
    `callback_result`, and optionally a redirect, passed from the original
    redirect to Bugzilla (from the MozReview login view).

    This handler then verifies the API key with Bugzilla and attempts to
    create or update the user in MozReview.  If everything succeeds, the
    user is again redirected back to the original page (or the root page if
    there was no redirect passed in, e.g., in tests).  Otherwise the user is
    shown an error page.
    """
    bmo_username = request.GET.get('client_api_login', None)
    callback_result = request.GET.get('callback_result', None)
    redirect = request.GET.get('redirect', None)

    if not (bmo_username and callback_result):
        logging.error('Bugzilla auth callback called without required '
                      'parameters.')
        return show_error_page(request)

    if not redirect:
        redirect = '/'

    unverified_keys = UnverifiedBugzillaApiKey.objects.filter(
        bmo_username=bmo_username).order_by('timestamp')

    if not unverified_keys:
        logging.error('No unverified keys found for BMO user %s.' %
                      bmo_username)
        return show_error_page(request)

    unverified_key = unverified_keys.last()

    if len(unverified_keys) > 1:
        logging.warning('Multiple unverified keys on file for BMO user %s. '
                        'Using most recent, from %s.' %
                        (bmo_username, unverified_key.timestamp))

    if callback_result != unverified_key.callback_result:
        logging.error('Callback result does not match for BMO user %s.' %
                      bmo_username)
        return show_error_page(request)

    bmo_api_key = unverified_key.api_key
    unverified_key.delete()

    b = Bugzilla()

    try:
        if not b.valid_api_key(bmo_username, bmo_api_key):
            logging.error('Invalid API key for %s.' % bmo_username)
            return show_error_page(request)
    except BugzillaError as e:
        logging.error('Error validating API key: %s' % e.msg)
        return show_error_page(request)

    b.api_key = bmo_api_key

    try:
        user_data = b.get_user(bmo_username)
    except BugzillaError as e:
        logging.error('Error getting user data: %s' % e.msg)
        return show_error_page(request)

    if not user_data:
        logging.warning('Could not retrieve user info for %s after '
                        'validating API key.' % bmo_username)
        return show_error_page(request)

    users = get_or_create_bugzilla_users(user_data)

    if not users:
        logging.warning('Failed to create user %s after validating API key.' %
                        bmo_username)
        return show_error_page(request)

    user = users[0]
    assert user.email == bmo_username

    if not user.is_active:
        logging.warning('Validated API key but user %s is inactive.' %
                        bmo_username)
        return show_error_page(request)

    set_bugzilla_api_key(user, bmo_api_key)
    user.backend = 'rbbz.auth.BugzillaBackend'
    login(request, user)
    return HttpResponseRedirect(redirect)
Пример #6
0
    def authenticate_api_key(self, username, api_key):
        """Authenticate a user from a username and API key.

        This is intended to be used by the Web API and not a user-facing
        login form. We enforce that the user already exists and has an
        API key - not necessarily the same API key - on file. The API key
        passed in is only used for authentication: all subsequent communication
        with Bugzilla should be performed using the API key on file.

        We require the user already exist in the database because having
        the user go through the browser-facing login flow is the most sane
        (and secure) way to obtain an API key. We don't want to store the
        API key provided to us from the client because API keys obtained by
        the browser login may have special permissions not granted to normal
        API keys.
        """
        username = username.strip()

        try:
            bugzilla = Bugzilla()
        except BugzillaUrlError:
            logging.warn('Login failure for user %s: Bugzilla URL not set.' %
                         username)

        try:
            valid = bugzilla.valid_api_key(username, api_key)
        except BugzillaError as e:
            logging.error('Login failure for user %s: %s' % (username, e))
            return None

        if not valid:
            logging.error('Login failure for user %s: invalid API key' %
                          username)
            assert bugzilla.base_url.endswith('/')
            raise BugzillaAPIKeyNeededError(
                    bugzilla.base_url + 'userprefs.cgi?tab=apikey')

        # Assign the API key to the Bugzilla connection so the user info
        # lookup uses it.
        # TODO can we skip valid_api_key() and just get user info straight up?
        bugzilla.api_key = api_key

        try:
            user_data = bugzilla.get_user(username)
        except BugzillaError as e:
            logging.error('Login failure for user %s: unable to retrieve '
                          'Bugzilla user info: %s' % (username, e))
            return None

        if not user_data:
            logging.warning('Could not retrieve user info for %s after '
                            'validating API key' % username)
            return None

        bz_user = user_data['users'][0]

        try:
            bum = BugzillaUserMap.objects.get(bugzilla_user_id=bz_user['id'])
            user = bum.user
        except BugzillaUserMap.DoesNotExist:
            logging.warning('Login failure for user %s: API key valid but '
                            'user missing from database' % username)
            raise WebLoginNeededError()

        if not user.is_active:
            logging.error('Login failure for user %s: user not active' %
                          username)
            return None

        # We require a local API key to be on file, as it will be used for
        # subsequent requests.
        if not get_bugzilla_api_key(user):
            logging.warning('Login failure for user %s: no API key in '
                            'database' % username)
            raise WebLoginNeededError()

        return user
Пример #7
0
    def authenticate_api_key(self, username, api_key):
        """Authenticate a user from a username and API key.

        This is intended to be used by the Web API and not a user-facing
        login form. We enforce that the user already exists and has an
        API key - not necessarily the same API key - on file. The API key
        passed in is only used for authentication: all subsequent communication
        with Bugzilla should be performed using the API key on file.

        We require the user already exist in the database because having
        the user go through the browser-facing login flow is the most sane
        (and secure) way to obtain an API key. We don't want to store the
        API key provided to us from the client because API keys obtained by
        the browser login may have special permissions not granted to normal
        API keys.
        """
        username = username.strip()

        logger.info('Login attempt (apikey) for user %s: ' % username)

        try:
            bugzilla = Bugzilla()
        except BugzillaUrlError:
            logger.warn('Login failure (apikey) for user %s: Bugzilla URL '
                        'not set.' % username)

        try:
            valid = bugzilla.valid_api_key(username, api_key)
        except BugzillaError as e:
            logger.error('Login failure (apikey) for user %s: %s' %
                         (username, e))
            return None

        if not valid:
            logger.error('Login failure for user %s: invalid API key' %
                         username)
            assert bugzilla.base_url.endswith('/')
            raise BugzillaAPIKeyNeededError(bugzilla.base_url +
                                            'userprefs.cgi?tab=apikey')

        # Assign the API key to the Bugzilla connection so the user info
        # lookup uses it.
        # TODO can we skip valid_api_key() and just get user info straight up?
        bugzilla.api_key = api_key

        try:
            user_data = bugzilla.get_user(username)
        except BugzillaError as e:
            logger.error('Login failure (apikey) for user %s: unable to '
                         'retrieve Bugzilla user info: %s' % (username, e))
            return None

        if not user_data:
            logger.warning('Could not retrieve user info for %s after '
                           'validating API key' % username)
            return None

        bz_user = user_data['users'][0]

        try:
            bum = BugzillaUserMap.objects.get(bugzilla_user_id=bz_user['id'])
            user = bum.user
        except BugzillaUserMap.DoesNotExist:
            logger.warning('Login failure for user %s: API key valid but '
                           'user missing from database' % username)
            raise WebLoginNeededError()

        if not user.is_active:
            logger.error('Login failure (apikey) for user %s: user not '
                         'active' % username)
            return None

        # We require a local API key to be on file, as it will be used for
        # subsequent requests.
        if not get_bugzilla_api_key(user):
            logger.warning('Login failure for user %s: no API key in '
                           'database' % username)
            raise WebLoginNeededError()

        logger.info('Login successful (apikey) for user %s: ' % username)

        return user