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()
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"))
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))
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.")
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]
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)
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)
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]
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))
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"))
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
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()
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)})
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)
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)
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")
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)
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