def _db_group_to_response(self, resp: Response, db_group: ScimApiGroup) -> None: members = self._get_group_members(db_group) location = self.url_for("Groups", str(db_group.scim_id)) meta = Meta( location=location, last_modified=db_group.last_modified or db_group.created, resource_type=SCIMResourceType.GROUP, created=db_group.created, version=db_group.version, ) schemas = [SCIMSchema.CORE_20_GROUP] if db_group.extensions.data: schemas.append(SCIMSchema.NUTID_GROUP_V1) group = GroupResponse( display_name=db_group.graph.display_name, members=members, id=db_group.scim_id, external_id=db_group.external_id, meta=meta, schemas=list( schemas ), # extra list() needed to work with _both_ mypy and marshmallow nutid_group_v1=NutidGroupExtensionV1( data=db_group.extensions.data), ) resp.set_header("Location", location) resp.set_header("ETag", make_etag(db_group.version)) dumped_group = GroupResponseSchema().dump(group) if SCIMSchema.NUTID_GROUP_V1 not in group.schemas and SCIMSchema.NUTID_GROUP_V1.value in dumped_group: # Serialization will always put the NUTID_GROUP_V1 in the dumped_group, even if there was no data del dumped_group[SCIMSchema.NUTID_GROUP_V1.value] resp.media = dumped_group
def write_response(req: Request, res: Response) -> None: res.content_type = 'application/cbor' res.set_header('Access-Control-Allow-Origin', '*') if req.path.endswith('/_entry-names'): path = root_dir / req.path[1:-len('/_entry-names')] if path.is_file(): raise HTTPStatus(HTTP_404) res.data = cbor2.dumps(dict( type='plain-object', content=sorted([ re.sub(r'\.h5$', '', p.name) + ('/' if p.is_dir() else '') for p in path.glob('[!_]*') ]) )) elif req.path.endswith('/_meta'): key = req.path[1:-len('/_meta')] res.data = cbor2.dumps(_read_meta(root_dir, key)) else: t_last = float(req.get_param('t_last') or 0) / 1000 entry = _read(root_dir, req.path[1:], t_last) if entry['type'] == 'file': res.data = (root_dir / entry['content']).read_bytes() else: res.data = cbor2.dumps(entry) res.status = HTTP_200
def authorize_request(self, request: falcon.Request, response: falcon.Response): uri, http_method, body, headers = extract_params(request) scope = request.params.get('scope') or '' scopes = scope.split() credentials = dict( client_id=request.params.get('client_id'), redirect_uri=request.params.get('redirect_uri', None), response_type=request.params.get('response_type', None), state=request.params.get('state', None) ) log.debug('Fetched credentials from request %r.', credentials) try: headers, body, status = self._auth_endpoint.create_authorization_response( uri, http_method, body, headers, scopes, credentials) log.debug('Authorization successful.') except FatalClientError as e: log.debug('Fatal client error %r', e) response.status = falcon.HTTP_SEE_OTHER response.set_header('Location', e.in_uri(self.error_url)) except OAuth2Error as e: log.debug('OAuth2Error: %r', e) response.status = falcon.HTTP_SEE_OTHER response.set_header('Location', e.in_uri(self.error_url)) else: patch_response(response, headers, body, status)
def on_post(self, req: Request, resp: Response, order_id: str): order = self.context.store.load_order(order_id) self.context.logger.info(f'Processing order {order}') self.update_order_state(order) if order.status == 'processing': # certificate still not issued resp.set_header('Retry-After', 30 + random.randint(-5, 5)) data = { 'status': order.status, 'identifiers': order.identifiers, #'notBefore': '2016-01-01T00:00:00Z', # optional #'notAfter': '2016-01-08T00:00:00Z', # optional 'authorizations': [ self.url_for('authz', authz_id) for authz_id in order.authorization_ids ], } if order.status == 'ready': data['finalize'] = self.url_for('order', order.id, 'finalize') if order.status == 'valid': data['certificate'] = self.url_for('certificate', order.certificate_id) if order.status in ['pending', 'valid']: # XXX more states than these perhaps? data['expires'] = str(order.expires) resp.media = data
def authorize_request(self, request: falcon.Request, response: falcon.Response): uri, http_method, body, headers = extract_params(request) scope = request.params.get('scope') or '' scopes = scope.split() credentials = dict( client_id=request.params.get('client_id'), redirect_uri=request.params.get('redirect_uri', None), response_type=request.params.get('response_type', None), state=request.params.get('state', None)) log.debug('Fetched credentials from request %r.', credentials) try: headers, body, status = self._auth_endpoint.create_authorization_response( uri, http_method, body, headers, scopes, credentials) log.debug('Authorization successful.') except FatalClientError as e: log.debug('Fatal client error %r', e) response.status = falcon.HTTP_SEE_OTHER response.set_header('Location', e.in_uri(self.error_url)) except OAuth2Error as e: log.debug('OAuth2Error: %r', e) response.status = falcon.HTTP_SEE_OTHER response.set_header('Location', e.in_uri(self.error_url)) else: patch_response(response, headers, body, status)
def set_response_headers(resp: falcon.Response, ct: str="application/ld+json", headers: Dict[str, Any]={}, status_code = falcon.HTTP_200) -> falcon.Response: resp.status = status_code resp.set_headers(headers) resp.set_header('Content-type', ct) resp.set_header('Link' ,'<' + get_hydrus_server_url(resp) + \ get_api_name(resp)+'/vocab>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"') return resp
def process_response(self, req: Request, resp: Response, resource: BaseResource, req_succeeded: bool): if req.method == 'POST': resp.set_header('Replay-Nonce', self.context.new_nonce) self.context.logger.debug( f'Added a new nonce to POST {req.path} - ' f'{resource} {req_succeeded}')
def on_get(self, req: Request, res: Response, name: str): info = self.store.load(name) if info is None: raise HTTPNotFound() res.body = info["ip"] res.set_header("X-Updated", info["updated"])
def handle(ex: HTTPErrorDetail, req: falcon.Request, resp: falcon.Response, params): resp.status = ex.status resp.content_type = 'application/problem+json' if not ex.error_detail.instance: ex.error_detail.instance = req.uri resp.body = json.dumps(ex.to_dict()) if ex.extra_headers: for key, value in ex.extra_headers.items(): resp.set_header(key, value)
def on_post_offchain(self, req: falcon.Request, resp: falcon.Response) -> None: request_id = req.get_header(offchain.X_REQUEST_ID) resp.set_header(offchain.X_REQUEST_ID, request_id) request_sender_address = req.get_header(offchain.X_REQUEST_SENDER_ADDRESS) input_data = req.stream.read() resp_obj = self.app.offchain_api(request_id, request_sender_address, input_data) if resp_obj.error is not None: resp.status = falcon.HTTP_400 resp.body = self.app.jws_serialize(resp_obj)
def _db_invite_to_response(self, req: Request, resp: Response, db_invite: ScimApiInvite, signup_invite: SignupInvite): location = self.url_for("Invites", db_invite.scim_id) meta = Meta( location=location, last_modified=db_invite.last_modified, resource_type=SCIMResourceType.INVITE, created=db_invite.created, version=db_invite.version, ) schemas = [ SCIMSchema.NUTID_INVITE_CORE_V1, SCIMSchema.NUTID_INVITE_V1, SCIMSchema.NUTID_USER_V1 ] _profiles = { k: Profile(attributes=v.attributes, data=v.data) for k, v in db_invite.profiles.items() } invite_extension = NutidInviteExtensionV1( completed=db_invite.completed, name=Name(**asdict(db_invite.name)), emails=[Email(**asdict(email)) for email in db_invite.emails], phone_numbers=[ PhoneNumber(**asdict(number)) for number in db_invite.phone_numbers ], national_identity_number=db_invite.nin, preferred_language=db_invite.preferred_language, groups=db_invite.groups, send_email=signup_invite.send_email, finish_url=signup_invite.finish_url, expires_at=signup_invite.expires_at, inviter_name=signup_invite.inviter_name, ) # Only add invite url in response if no email should be sent to the invitee if signup_invite.send_email is False: invite_url = f'{self.context.config.invite_url}/{signup_invite.invite_code}' invite_extension = replace(invite_extension, invite_url=invite_url) scim_invite = InviteResponse( id=db_invite.scim_id, external_id=db_invite.external_id, meta=meta, schemas=list( schemas ), # extra list() needed to work with _both_ mypy and marshmallow nutid_invite_v1=invite_extension, nutid_user_v1=NutidUserExtensionV1(profiles=_profiles), ) resp.set_header("Location", location) resp.set_header("ETag", make_etag(db_invite.version)) resp.media = InviteResponseSchema().dump(scim_invite)
def on_post(self, req: Request, resp: Response, certificate_id): certificate = self.context.store.load_certificate(certificate_id) self.context.logger.info(f'Processing certificate {certificate}') if not certificate.certificate: resp.status = falcon.HTTP_404 return resp.set_header('Content-Type', 'application/pem-certificate-chain') certs = [certificate.certificate] if certificate.cert_chain: certs += certificate.cert_chain resp.body = ''.join(certs)
def on_post(self, request: falcon.Request, response: falcon.Response): """Create a trade """ trade_request = self._parse_trade_request(request) trade = self._create_trade(request.context, trade_request) body = TradeResponse.from_trade(trade).dict() response.body = TransformerBinding.get().serialize(body) response.set_header('Location', '{}/{}'.format(request.uri, trade.id)) response.status = falcon.HTTP_201
def on_put(self, req: Request, res: Response, name: str): secret_header = req.get_header("X-Secret") if self.secret is not None and secret_header != self.secret: print("PUT %s (invalid secret %s)" % (name, secret_header)) raise HTTPUnauthorized() info = self.store.save(name, req.access_route[0]) print("PUT %s = %s" % (name, info["ip"])) res.body = info["ip"] res.set_header("X-Updated", info["updated"])
def set_cached_response(self, resp: Response, key: str, data: Mapping) -> None: cache_for_seconds = self.context.config.status_cache_seconds now = datetime.utcnow() expires = now + timedelta(seconds=cache_for_seconds) resp.media = data resp.set_header('Expires', expires.strftime("%a, %d %b %Y %H:%M:%S UTC")) self.SIMPLE_CACHE[key] = SimpleCacheItem(expire_time=expires, data=data) if self.context.config.debug: self.context.logger.debug( f'Cached response for {key} until {expires}')
def on_post_offchain(self, req: falcon.Request, resp: falcon.Response) -> None: request_id = req.get_header(offchain.X_REQUEST_ID) resp.set_header(offchain.X_REQUEST_ID, request_id) request_sender_address = req.get_header(offchain.X_REQUEST_SENDER_ADDRESS) input_data = req.stream.read() try: resp_obj = self.app.offchain_api(request_sender_address, input_data) except offchain.Error as e: self.app.logger.info(input_data) self.app.logger.exception(e) resp_obj = offchain.reply_request(cid=None, err=e.obj) resp.status = falcon.HTTP_400 resp.body = self.app.jws_serialize(resp_obj)
def set_graphql_allow_header( req: falcon.Request, resp: falcon.Response, resource: object, ): """Sets the 'Allow' header on responses to GraphQL requests. Args: req (falcon.Request): The incoming request. resp (falcon.Response): The outgoing response. resource (object): The falcon resource-class associated with the incoming request. """ # Set the `Allow` header to permit given commands. resp.set_header('Allow', 'GET, POST, OPTIONS')
def on_post(self, req: Request, resp: Response): self.context.logger.info(f'Logging in') data_owner = req.media['data_owner'] if data_owner not in self.context.config.data_owners: raise Unauthorized() now = datetime.datetime.now(tz=datetime.timezone.utc) expire = now + datetime.timedelta( seconds=self.context.config.authorization_token_expire) claims = { 'data_owner': data_owner, 'exp': expire, } token = jwt.encode(claims, self.context.config.authorization_token_secret, algorithm='HS256') resp.set_header('Authorization', f'Bearer {token}')
def get_cached_response(self, resp: Response, key: str) -> bool: cache_for_seconds = self.context.config.status_cache_seconds resp.set_header('Cache-Control', f'public,max-age={cache_for_seconds}') now = datetime.utcnow() if self.SIMPLE_CACHE.get(key) is not None: if now < self.SIMPLE_CACHE[key].expire_time: if self.context.config.debug: self.context.logger.debug( f'Returned cached response for {key}' f' {now} < {self.SIMPLE_CACHE[key].expire_time}') resp.media = self.SIMPLE_CACHE[key].data resp.set_header( 'Expires', self.SIMPLE_CACHE[key].expire_time.strftime( "%a, %d %b %Y %H:%M:%S UTC")) return True return False
def process_request(self, req: Request, resp: Response) -> None: """ Request Id generator. This is used for the tracing between services Check if the request id is part of the header. If not, create a uuid :param req: Request :param resp: Response :return: None """ x_request_id = req.get_header(REQUEST_ID) if x_request_id is None: _uuid = str(uuid.uuid4()) req._params[REQUEST_ID] = _uuid resp.set_header(REQUEST_ID, _uuid) else: req._params[REQUEST_ID] = x_request_id resp.set_header(REQUEST_ID, x_request_id)
def _db_user_to_response(self, req: Request, resp: Response, db_user: ScimApiUser): location = self.url_for("Users", db_user.scim_id) meta = Meta( location=location, last_modified=db_user.last_modified, resource_type=SCIMResourceType.USER, created=db_user.created, version=db_user.version, ) schemas = [SCIMSchema.CORE_20_USER] if db_user.profiles: schemas.append(SCIMSchema.NUTID_USER_V1) # Convert one type of Profile into another _profiles = { k: Profile(attributes=v.attributes, data=v.data) for k, v in db_user.profiles.items() } user = UserResponse( id=db_user.scim_id, external_id=db_user.external_id, name=Name(**asdict(db_user.name)), emails=[Email(**asdict(email)) for email in db_user.emails], phone_numbers=[ PhoneNumber(**asdict(number)) for number in db_user.phone_numbers ], preferred_language=db_user.preferred_language, groups=self._get_user_groups(req=req, db_user=db_user), meta=meta, schemas=list( schemas ), # extra list() needed to work with _both_ mypy and marshmallow nutid_user_v1=NutidUserExtensionV1(profiles=_profiles), ) resp.set_header("Location", location) resp.set_header("ETag", make_etag(db_user.version)) resp.media = UserResponseSchema().dump(user)
def process_request(self, req: Request, res: Response) -> None: res.set_header('Access-Control-Allow-Origin', '*') res.set_header('Access-Control-Allow-Methods', '*') res.set_header('Access-Control-Allow-Headers', '*') res.set_header('Access-Control-Max-Age', 600) if req.method == 'OPTIONS': raise HTTPStatus(HTTP_200)
def set_token(response: Response, salted_token: str) -> None: """We're setting multiple cookies to support debugging on localhost.""" response.set_header(settings.API_CSRF_HEADER_NAME, salted_token) for cookie_domain in settings.API_CSRF_COOKIE_DOMAINS: falcon_set_cookie( response, settings.API_CSRF_COOKIE_NAME, salted_token, same_site=settings.SESSION_COOKIE_SAMESITE, path=settings.SESSION_COOKIE_PATH, secure=settings.SESSION_COOKIE_SECURE, http_only=False, domain=cookie_domain, ) # Set the Vary header since content varies with the CSRF cookie. vary_val = response.headers.get('Vary') if not vary_val: vary_val = 'Cookie' elif 'Cookie' not in vary_val: vary_val = f"{vary_val}, Cookie" response.set_header('Vary', vary_val)
def _db_event_to_response(self, req: Request, resp: Response, db_event: ScimApiEvent): location = self.resource_url(SCIMResourceType.EVENT, db_event.scim_id) meta = Meta( location=location, last_modified=db_event.last_modified, resource_type=SCIMResourceType.EVENT, created=db_event.created, version=db_event.version, ) schemas = [SCIMSchema.NUTID_EVENT_CORE_V1, SCIMSchema.NUTID_EVENT_V1] response = EventResponse( id=db_event.scim_id, meta=meta, schemas=list( schemas ), # extra list() needed to work with _both_ mypy and marshmallow nutid_event_v1=NutidEventExtensionV1( level=db_event.level, data=db_event.data, source=db_event.source, expires_at=db_event.expires_at, timestamp=db_event.timestamp, resource=NutidEventResource( resource_type=db_event.resource.resource_type, scim_id=db_event.resource.scim_id, external_id=db_event.resource.external_id, location=self.resource_url(db_event.resource.resource_type, db_event.resource.scim_id), ), ), ) resp.set_header("Location", location) resp.set_header("ETag", make_etag(db_event.version)) resp.media = EventResponseSchema().dump(response)
def process_response( self, req: falcon.Request, resp: falcon.Response, resource: object, req_succeeded: bool, ): """ Intercepts outgoing responses and handles incoming CORS OPTIONS preflight requests. Args: req (falcon.Request): The Falcon `Request` object. resp (falcon.Response): The Falcon `Response` object. resource (object): Resource object to which the request was routed. May be None if no route was found for the request. req_succeeded (bool): True if no exceptions were raised while the framework processed and routed the request; otherwise False. """ # Set the `Access-Control-Allow-Origin` header. resp.set_header('Access-Control-Allow-Origin', '*') # Skip the request if it doesn't exhibit the characteristics of a CORS # OPTIONS preflight request. if not self.is_req_cors(req=req): return None msg_fmt = "Processing CORS preflight OPTIONS request." self.logger.info(msg_fmt) # Retrieve and remove the `Allow` header from the response. allow = resp.get_header('Allow') resp.delete_header('Allow') # Retrieve the `Access-Control-Request-Headers` header from the # request. allow_headers = req.get_header('Access-Control-Request-Headers', default='*') # Set the appropriate CORS headers in the response. resp.set_header(name="Access-Control-Allow-Methods", value=allow) resp.set_header( name="Access-Control-Allow-Headers", value=allow_headers, ) resp.set_header(name="Access-Control-Max-Age", value='86400')
def on_post(self, req: Request, resp: Response): self.context.logger.info( f'Pre-authorization for account {req.context["account"].id}') token = req.context['jose_verified_data'] # The JOSE implementation currently in use fails to sign string data, so we # put it in a dict in ici-acme-pre-auth.py. try: _data = json.loads(token) if 'token' in _data: token = _data['token'] except TypeError: pass audience = self.url_for('new-authz') preauth = validate_token_signature(token, audience, self.context) authorized_for_names = get_authorized_names(preauth, self.context) if not authorized_for_names: raise RejectedIdentifier # Create Authorization objects for each identifier, and add them to the # accounts preauth_ids so that they will be found in newOrder account = req.context['account'] now = datetime.datetime.now(tz=datetime.timezone.utc) authz = None chall = None for name in authorized_for_names: challenge_id = b64_encode(os.urandom(128 // 8)) acme_method = self.context.config.get('ACME_PREAUTH_METHOD', 'x-sunet-01') chall = Challenge( id=challenge_id, type=acme_method, url=self.url_for('challenge', challenge_id), status='valid', created=now, validated=now, token=challenge_id if acme_method == 'http-01' else None, ) self.context.store.save('challenge', chall.id, chall.to_dict()) ident = { 'type': 'dns', 'value': name, } authz = Authorization( id=b64_encode(os.urandom(128 // 8)), status='valid', created=now, expires=now + datetime.timedelta(minutes=5), identifier=ident, challenge_ids=[challenge_id], ) self.context.store.save('authorization', authz.id, authz.to_dict()) self.context.logger.info(f'Created pre-authorization {authz}') account.preauth_ids += [{ 'id': authz.id, 'expires': authz.expires, }] self.context.store.save('account', account.id, account.to_dict()) resp.status = falcon.HTTP_201 if len(authorized_for_names) == 1 and authz: # RFC compliant response, when the request was RFC compliant (meaning a # preauth request for a single identifier) resp.set_header('Location', self.url_for('authz', authz.id)) resp.media = authz.to_response(challenges=[chall.to_response()]) else: # non-RFC compliant responses for non-compliant clients ;) resp.media = {'status': 'OK'}
def process_request(self, req: Request, resp: Response) -> None: resp.set_header('timer', time.time())
def on_head(self, req: Request, resp: Response): resp.set_header('Replay-Nonce', self.context.new_nonce) resp.set_header('Cache-Control', 'no-store')
def on_put(self, req: falcon.Request, resp: falcon.Response) -> None: """Support PUT method.""" key = req.get_param('key') value = req.get_param('value') resp.body = f'Audited - {key} {value}' resp.set_header('content-type', 'application/json')
def on_get(self, req: falcon.Request, resp: falcon.Response) -> None: """Support GET method.""" key: str = req.get_param('key') resp.body = f'Audited - {key}' resp.set_header('content-type', 'application/json') self.my_audit_data = key # additional data to be added to audit event
def process_request(self, req: falcon.Request, resp: falcon.Response): resp.set_header('Access-Control-Allow-Origin', '*') resp.set_header('Access-Control-Allow-Headers', '*') if req.method == 'OPTIONS': raise HTTPStatus(falcon.HTTP_200)