Пример #1
0
def delete_user_identity(request):
    # Grab the identity
    id = request.matchdict['identity_id']
    try:
        identity = DBSession.query(Identity) \
            .filter(Identity.id==id) \
            .first()
    except DBAPIError:
        raise httpexceptions.HTTPServiceUnavailable(
            connection_error_message,
            content_type='text/plain',
        )
    if identity is None:
        raise httpexceptions.HTTPNotFound()
    elif len(identity.user.identities) <= 1:
        raise httpexceptions.HTTPForbidden("Cannot delete the only remaining "
                                           "identity connection.")
    # Check that this user has permission to remove an identity.
    permissible = security.has_permission('delete', identity, request)
    if not permissible:
        raise httpexceptions.HTTPUnauthorized()

    # Remove the identity
    DBSession.delete(identity)
    raise httpexceptions.HTTPNoContent()
Пример #2
0
def stream_atom(request):
    params = dict(request.params)

    # The maximum value that this function allows the limit param.
    default_limit = 100
    max_limit = 500

    try:
        params["limit"] = int(params.get("limit", default_limit))
    except (ValueError, TypeError):
        params["limit"] = default_limit

    if params["limit"] < 0:
        params["limit"] = default_limit
    if params["limit"] > max_limit:
        params["limit"] = max_limit

    try:
        annotations = request.api_client.get("/search", params=params)["rows"]
    except api_client.ConnectionError as err:
        raise httpexceptions.HTTPServiceUnavailable(err)
    except api_client.Timeout as err:
        raise httpexceptions.HTTPGatewayTimeout(err)
    except api_client.APIError as err:
        raise httpexceptions.HTTPBadGateway(err)

    return dict(annotations=annotations,
                atom_url=request.route_url("stream_atom"),
                html_url=request.route_url("stream"),
                title=request.registry.settings.get("h.feed.title"),
                subtitle=request.registry.settings.get("h.feed.subtitle"))
Пример #3
0
def fxa_oauth_token(request):
    """Return OAuth token from authorization code.
    """
    state = request.validated['state']
    code = request.validated['code']

    # Require on-going session
    stored_redirect = request.registry.cache.get(state)

    # Make sure we cannot try twice with the same code
    request.registry.cache.delete(state)
    if not stored_redirect:
        error_msg = 'The OAuth session was not found, please re-authenticate.'
        return http_error(httpexceptions.HTTPRequestTimeout(),
                          errno=ERRORS.MISSING_AUTH_TOKEN,
                          message=error_msg)

    # Trade the OAuth code for a longer-lived token
    auth_client = OAuthClient(server_url=fxa_conf(request, 'oauth_uri'),
                              client_id=fxa_conf(request, 'client_id'),
                              client_secret=fxa_conf(request, 'client_secret'))
    try:
        token = auth_client.trade_code(code)
    except fxa_errors.OutOfProtocolError:
        raise httpexceptions.HTTPServiceUnavailable()
    except fxa_errors.InProtocolError as error:
        logger.error(error)
        error_details = {
            'name': 'code',
            'location': 'querystring',
            'description': 'Firefox Account code validation failed.'
        }
        raise_invalid(request, **error_details)

    return httpexceptions.HTTPFound(location='%s%s' % (stored_redirect, token))
Пример #4
0
 def test_503_can_have_custom_message(self):
     custom_503 = http_error(httpexceptions.HTTPServiceUnavailable(),
                             errno=ERRORS.BACKEND,
                             message="Unable to connect the server.")
     with mock.patch('tests.core.testapp.views.Mushroom._extract_filters',
                     side_effect=custom_503):
         response = self.app.get(self.sample_url, headers=self.headers, status=503)
     self.assertFormattedError(response, 503, ERRORS.BACKEND, "Service Unavailable",
                               "Unable to connect the server.")
Пример #5
0
    def _verify_token(self, access_token, request):
        """Verify the token extracted from the Authorization header.

        This method stores the result in two locations to avoid hitting the
        auth remote server as much as possible:

        - on the request object, in case the Pyramid authentication methods
          like `effective_principals()` or `authenticated_userid()` are called
          several times during the request cycle;

        - in the cache backend, to reuse validated token from one request to
          another (during ``cache_ttl_seconds`` seconds.)
        """
        # First check if this request was already verified.
        # `request.bound_data` is an attribute provided by Kinto to store
        # some data that is shared among sub-requests (e.g. default bucket
        # or batch requests)
        if REIFY_KEY not in request.bound_data:
            settings = request.registry.settings
            hmac_secret = settings['userid_hmac_secret']

            cache_ttl = float(facebook_conf(request, 'cache_ttl_seconds'))

            hmac_token = core_utils.hmac_digest(hmac_secret, access_token)
            cache_key = 'facebook:verify:{}'.format(hmac_token)

            payload = request.registry.cache.get(cache_key)

            if payload is None:
                # Verify token from Facebook
                url = facebook_conf(request, 'userinfo_endpoint')
                params = {
                    'input_token': access_token,
                    'access_token': facebook_conf(request, 'app_access_token'),
                }

                # XXX: Implement token validation for Facebook
                resp = requests.get(url, params=params)
                try:
                    resp.raise_for_status()
                except requests.exceptions.HTTPError:
                    logger.exception("Facebook Token Protocol Error")
                    raise httpexceptions.HTTPServiceUnavailable()
                else:
                    body = resp.json()
                    if not body['data']['is_valid']:
                        payload = {}
                    else:
                        payload = body['data']

            request.registry.cache.set(cache_key, payload, ttl=cache_ttl)

            # Save for next call.
            request.bound_data[REIFY_KEY] = payload.get('user_id')

        return request.bound_data[REIFY_KEY]
Пример #6
0
def login_complete(request):
    """This view is hit after a visitor has successfully authenticated with
    one of the registered identity providers.

    The successfully authenticated visitor will be remembered (given a cookie)
    and redirected either to their profile page or back to the remote service
    the came from. See ``capture_requesting_service`` for details on the
    information put into a remote service redirect.
    """
    context = request.context
    identifier = discover_uid(context)

    # Create user and identity entries if none are found.
    try:
        identity = DBSession.query(Identity) \
            .filter(Identity.identifier==identifier) \
            .filter(Identity.name==context.provider_name) \
            .filter(Identity.type==context.provider_type) \
            .first()
        if not identity:
            # So we have a new identity and potentially a new user, but
            #   that is unknown at this time.
            user = acquire_user(request)
            if user.id is None:
                # We need the user id to make the relationship.
                DBSession.flush()
            identity = Identity(identifier,
                                context.provider_name,
                                context.provider_type,
                                json.dumps(context.profile),
                                json.dumps(context.credentials),
                                user=user)
            DBSession.add(identity)
        user = identity.user
    except DBAPIError:
        raise httpexceptions.HTTPServiceUnavailable(
            connection_error_message,
            content_type='text/plain',
        )
    # Remember the authenticated user for future requests.
    auth_headers = remember(request, str(user.id))

    # Check the session for endpoint redirection otherwise pop the
    #   user over to their user profile /user/{id}
    if REFERRER_SESSION_KEY in request.session:
        token = str(uuid.uuid4())
        store = get_token_store()
        value = user.id
        store.store(token, value)  # XXX Never expires.
        # Send the user back to the service with the token and
        #   information about where they came from.
        referrer_info = request.session.get(REFERRER_SESSION_KEY)
        location = generate_service_validation_url(referrer_info, token)
    else:
        location = request.route_url('www-get-user', id=user.id)
    return httpexceptions.HTTPFound(location=location, headers=auth_headers)
def request_error(context, request):
    """Catch requests errors when issuing a request to Sync."""
    logger.error(context, exc_info=True)

    error_msg = ("Unable to reach the service. "
                 "Check your internet connection or firewall configuration.")
    response = http_error(httpexceptions.HTTPServiceUnavailable(),
                          errno=ERRORS.BACKEND,
                          message=error_msg)

    return service_unavailable(response, request)
Пример #8
0
def get_users(request):
    limit = request.params.get('limit', 10)
    offset = request.params.get('page', 0) * limit
    query = request.params.get('q', None)
    is_a_query = query is not None
    _tokenizer = lambda q: [t for t in q.split() if t]

    if is_a_query:
        user_query = DBSession.query(User)
        criteria = []
        for term in _tokenizer(query):
            term_set = (
                User.firstname.like('%{}%'.format(term)),
                User.surname.like('%{}%'.format(term)),
                User.email == term,
            )
            criteria.extend(term_set)
        user_query = user_query.filter(or_(*criteria))
        try:
            users = user_query.order_by(User.surname) \
                .limit(limit).offset(offset) \
                .all()
        except DBAPIError:
            raise httpexceptions.HTTPServiceUnavailable(
                connection_error_message,
                content_type='text/plain',
            )
    else:
        try:
            users = DBSession.query(User) \
                .order_by(User.surname) \
                .limit(limit).offset(offset) \
                .all()
        except DBAPIError:
            raise httpexceptions.HTTPServiceUnavailable(
                connection_error_message,
                content_type='text/plain',
            )
    return users
def response_error(context, request):
    """Catch response error from Sync and trace them."""
    message = '%s %s: %s' % (context.response.status_code,
                             context.response.reason,
                             context.response.text)

    # XXX: Make sure these HTTPError exception are coming from SyncClient.
    statsd_count(request, "syncclient.status_code.%s" %
                 context.response.status_code)

    if context.response.status_code in (400, 401, 403, 404):
        # For this code we also want to log the info about the error.
        logger.info(context, exc_info=True)

    # For this specific code we do not want to log the error.
    if context.response.status_code == 304:
        response = httpexceptions.HTTPNotModified()
    elif context.response.status_code == 400:
        response = http_error(httpexceptions.HTTPBadRequest(),
                              errno=ERRORS.INVALID_PARAMETERS,
                              message=message)
    elif context.response.status_code == 401:
        response = http_error(httpexceptions.HTTPUnauthorized(),
                              errno=ERRORS.INVALID_AUTH_TOKEN,
                              message=message)
        # Forget the current user credentials.
        response.headers.extend(forget(request))
    elif context.response.status_code == 403:
        response = http_error(httpexceptions.HTTPForbidden(),
                              errno=ERRORS.FORBIDDEN,
                              message=message)
    elif context.response.status_code == 404:
        response = http_error(httpexceptions.HTTPNotFound(),
                              errno=ERRORS.INVALID_RESOURCE_ID,
                              message=message)
    elif context.response.status_code == 412:
        message = 'Resource was modified meanwhile'
        response = http_error(httpexceptions.HTTPPreconditionFailed(),
                              errno=ERRORS.MODIFIED_MEANWHILE,
                              message=message)
    else:
        # For this code we also want to log the error.
        logger.error(context, exc_info=True)
        response = service_unavailable(
            httpexceptions.HTTPServiceUnavailable(),
            request)

    request.response = response
    export_headers(context.response, request)

    return reapply_cors(request, response)
Пример #10
0
    def _verify_token(self, token, request):
        """Verify the token extracted from the Authorization header.

        This method stores the result in two locations to avoid hitting the
        auth remote server as much as possible:

        - on the request object, in case the Pyramid authentication methods
          like `effective_principals()` or `authenticated_userid()` are called
          several times during the request cycle;

        - in the cache backend, to reuse validated token from one request to
          another (during ``cache_ttl_seconds`` seconds.)
        """
        # First check if this request was already verified.
        # `request.bound_data` is an attribute provided by Kinto to store
        # some data that is shared among sub-requests (e.g. default bucket
        # or batch requests)
        if REIFY_KEY not in request.bound_data:
            user_id = None
            client_name = None
            auth_client = self._get_auth_client(request)

            for scope, client in request.registry._fxa_oauth_scope_routing.items():
                try:
                    profile = auth_client.verify_token(token=token, scope=aslist(scope))
                    user_id = profile['user']
                    scope = profile['scope']
                    client_name = client

                    # Make sure the bearer token scopes don't match multiple configs.
                    routing_scopes = request.registry._fxa_oauth_scope_routing
                    intersecting_scopes = [x for x in routing_scopes.keys()
                                           if x and set(x.split()).issubset(set(scope))]
                    if len(intersecting_scopes) > 1:
                        logger.warn("Invalid FxA token: {} matches multiple config" % scope)
                        return None, None

                    break
                except fxa_errors.OutOfProtocolError:
                    logger.exception("Protocol error")
                    raise httpexceptions.HTTPServiceUnavailable()
                except (fxa_errors.InProtocolError, fxa_errors.TrustError) as e:
                    logger.debug("Invalid FxA token: %s" % e)

            # Save for next call.
            request.bound_data[REIFY_KEY] = (user_id, client_name)

        return request.bound_data[REIFY_KEY]
Пример #11
0
def facebook_token(request):
    """Return OAuth token from authorization code.
    """
    state = request.validated['querystring']['state']
    code = request.validated['querystring']['code']

    # Require on-going session
    stored_redirect = request.registry.cache.get(state)

    # Make sure we cannot try twice with the same code
    request.registry.cache.delete(state)
    if not stored_redirect:
        error_msg = 'The Facebook Auth session was not found, please re-authenticate.'
        return http_error(httpexceptions.HTTPRequestTimeout(),
                          errno=ERRORS.MISSING_AUTH_TOKEN,
                          message=error_msg)

    url = facebook_conf(request, 'token_endpoint')
    params = {
        'client_id': facebook_conf(request, 'client_id'),
        'client_secret': facebook_conf(request, 'client_secret'),
        'redirect_uri': request.route_url(token.name),
        'code': code,
    }

    resp = requests.get(url, params=params)
    if resp.status_code == 400:
        response_body = resp.json()
        logger.error(
            "Facebook Token Validation Failed: {}".format(response_body))
        error_details = {
            'name': 'code',
            'location': 'querystring',
            'description': 'Facebook OAuth code validation failed.'
        }
        raise_invalid(request, **error_details)

    try:
        resp.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.exception("Facebook Token Protocol Error")
        raise httpexceptions.HTTPServiceUnavailable()
    else:
        response_body = resp.json()
        access_token = response_body['access_token']

    return httpexceptions.HTTPFound(location='%s%s' %
                                    (stored_redirect, access_token))
Пример #12
0
def stream_atom(request):
    try:
        annotations = request.api_client.get("/search", params={"limit":
                                                                1000})["rows"]
    except api_client.ConnectionError as err:
        raise httpexceptions.HTTPServiceUnavailable(err)
    except api_client.Timeout as err:
        raise httpexceptions.HTTPGatewayTimeout(err)
    except api_client.APIError as err:
        raise httpexceptions.HTTPBadGateway(err)

    return dict(annotations=annotations,
                atom_url=request.route_url("stream_atom"),
                html_url=request.route_url("stream"),
                title=request.registry.settings.get("h.feed.title"),
                subtitle=request.registry.settings.get("h.feed.subtitle"))
Пример #13
0
def get_user(request):
    id = request.matchdict['user_id']
    try:
        user = DBSession.query(User).filter(User.id == id).first()
    except DBAPIError:
        raise httpexceptions.HTTPServiceUnavailable(
            connection_error_message,
            content_type='text/plain',
        )
    if user is None:
        raise httpexceptions.HTTPNotFound()

    permissible = security.has_permission('view', user, request)
    if not permissible:
        raise httpexceptions.HTTPForbidden()
    return user
Пример #14
0
 def wrapper(*args, **kwargs):
     try:
         response = function(*args, **kwargs)
         storage.persist()
         return response
     except storage.Error:
         logger.exception('Storage failure')
         try:
             storage.abort()
         except storage.Error:
             logger.exception('Storage failed to abort')
             try:
                 storage.restart()
             except storage.Error:
                 logger.exception('Storage failed to restart')
         finally:
             raise httpexceptions.HTTPServiceUnavailable()
Пример #15
0
 def checkService(self, req):
     if req.registry.settings.get('service_until', None):
         # When do we expect the service to be done?"
         delay_until = req.registry.settings.get('service_until')
         # plus a bit of fuzz to dither out the herd
         delay_fuzz = int(req.registry.settings.get('service_fuzz', 30))
         try:
             delay_until = max(
                 int(parser.parse(delay_until).strftime('%s')) -
                 int(time()), 0)
         except ValueError, e:
             logger.log(msg="Could not calculate delay: " + str(e),
                        type="error")
             delay_until = 0
         if delay_until > 0:
             delay_until += random.randrange(0, delay_fuzz)
             raise http.HTTPServiceUnavailable(
                 headers={'Retry-After': str(delay_until)})
Пример #16
0
def error(context, request):
    """Catch server errors and trace them."""
    if isinstance(context, httpexceptions.Response):
        return reapply_cors(request, context)

    if isinstance(context, storage_exceptions.IntegrityError):
        error_msg = 'Integrity constraint violated, please retry.'
        response = http_error(httpexceptions.HTTPConflict(),
                              errno=ERRORS.CONSTRAINT_VIOLATED,
                              message=error_msg)
        retry_after = request.registry.settings['retry_after_seconds']
        response.headers['Retry-After'] = str(retry_after)
        return reapply_cors(request, response)

    # Log some information about current request.
    extra = {
      'path': request.path,
      'method': request.method,
    }
    qs = dict(request_GET(request))
    if qs:
        extra['querystring'] = qs
    # Take errno from original exception, or undefined if unknown/unhandled.
    try:
        extra['errno'] = context.errno.value
    except AttributeError:
        extra['errno'] = ERRORS.UNDEFINED.value

    if isinstance(context, storage_exceptions.BackendError):
        logger.critical(context.original, extra=extra, exc_info=context)
        response = httpexceptions.HTTPServiceUnavailable()
        return service_unavailable(response, request)

    # Within the exception view, sys.exc_info() will return null.
    # see https://github.com/python/cpython/blob/ce9e62544/Lib/logging/__init__.py#L1460-L1462
    logger.error(context, extra=extra, exc_info=context)

    error_msg = 'A programmatic error occured, developers have been informed.'
    info = request.registry.settings['error_info_link']
    response = http_error(httpexceptions.HTTPInternalServerError(),
                          message=error_msg,
                          info=info)

    return reapply_cors(request, response)
Пример #17
0
def error(context, request):
    """Catch server errors and trace them."""
    if isinstance(context, httpexceptions.Response):
        return reapply_cors(request, context)

    if isinstance(context, storage_exceptions.BackendError):
        logger.critical(context.original, exc_info=True)
        response = httpexceptions.HTTPServiceUnavailable()
        return service_unavailable(response, request)

    logger.error(context, exc_info=True)

    error_msg = "A programmatic error occured, developers have been informed."
    info = request.registry.settings['error_info_link']
    response = http_error(httpexceptions.HTTPInternalServerError(),
                          message=error_msg,
                          info=info)

    return reapply_cors(request, response)
Пример #18
0
def metrics(request):
    lock_acquired = bus_lock.acquire(timeout=5)
    if not lock_acquired:
        raise httpexceptions.HTTPServiceUnavailable("smbus cannot be locked")

    bus = smbus.SMBus(1)
    time.sleep(0.2)

    try:
        temp, humidity = sht3x.read_temperature_and_humidity(bus)
    except sht3x.CRCError:
        raise httpexceptions.HTTPInternalServerError(
            "Sensor data is corrupted")

    metrics = metrics_template.format(temp=temp, humidity=humidity).strip()

    bus.close()
    bus_lock.release()

    return Response(metrics, content_type="text/plain")
Пример #19
0
def login_complete(request):
    """This view is hit after a visitor has successfully authenticated with
    one of the registered identity providers.

    The successfully authenticated visitor will be remembered (given a cookie)
    and redirected either to their profile page or back to the remote service
    the came from. See ``capture_requesting_service`` for details on the
    information put into a remote service redirect.
    """
    context = request.context
    identifier = discover_uid(context)

    # Create user and identity entries if none are found.
    try:
        identity = DBSession.query(Identity) \
            .filter(Identity.identifier==identifier) \
            .filter(Identity.name==context.provider_name) \
            .filter(Identity.type==context.provider_type) \
            .first()
        if not identity:
            # So we have a new identity and potentially a new user, but
            #   that is unknown at this time.
            user = acquire_user(request)
            if user.id is None:
                # We need the user id to make the relationship.
                DBSession.flush()
            identity = Identity(identifier,
                                context.provider_name,
                                context.provider_type,
                                json.dumps(context.profile),
                                json.dumps(context.credentials),
                                user=user)
            DBSession.add(identity)
        user = identity.user
    except DBAPIError:
        raise httpexceptions.HTTPServiceUnavailable(
            connection_error_message,
            content_type='text/plain',
        )
    return _login(request, user)
Пример #20
0
    def _verify_token(self, token, request):
        """Verify the token extracted from the Authorization header.

        This method stores the result in two locations to avoid hitting the
        auth remote server as much as possible:

        - on the request object, in case the Pyramid authentication methods
          like `effective_principals()` or `authenticated_userid()` are called
          several times during the request cycle;

        - in the cache backend, to reuse validated token from one request to
          another (during ``cache_ttl_seconds`` seconds.)
        """
        # First check if this request was already verified.
        # `request.bound_data` is an attribute provided by Kinto to store
        # some data that is shared among sub-requests (e.g. default bucket
        # or batch requests)
        key = 'fxa_verified_token'
        if key in request.bound_data:
            return request.bound_data[key]

        # Use PyFxa defaults if not specified
        server_url = fxa_conf(request, 'oauth_uri')
        scope = aslist(fxa_conf(request, 'required_scope'))
        auth_cache = self._get_cache(request)
        auth_client = OAuthClient(server_url=server_url, cache=auth_cache)
        try:
            profile = auth_client.verify_token(token=token, scope=scope)
            user_id = profile['user']
        except fxa_errors.OutOfProtocolError as e:
            logger.exception("Protocol error")
            raise httpexceptions.HTTPServiceUnavailable()
        except (fxa_errors.InProtocolError, fxa_errors.TrustError) as e:
            logger.debug("Invalid FxA token: %s" % e)
            user_id = None

        # Save for next call.
        request.bound_data[key] = user_id

        return user_id