Example #1
0
    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
Example #2
0
    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
Example #3
0
    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)
Example #4
0
    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
Example #5
0
    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)
Example #6
0
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
Example #7
0
 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}')
Example #8
0
    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"])
Example #9
0
 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)
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
 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)
Example #13
0
    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
Example #14
0
    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"])
Example #15
0
 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}')
Example #16
0
 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)
Example #17
0
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')
Example #18
0
 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}')
Example #19
0
    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
Example #20
0
    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)
Example #21
0
    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)
Example #22
0
 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)
Example #24
0
    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)
Example #25
0
    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')
Example #26
0
    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())
Example #28
0
 def on_head(self, req: Request, resp: Response):
     resp.set_header('Replay-Nonce', self.context.new_nonce)
     resp.set_header('Cache-Control', 'no-store')
Example #29
0
 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')
Example #30
0
 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
Example #31
0
    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)