class Install_CR(Resource):
    def __init__(self):
        super(Install_CR, self).__init__()
        self.helpers = Helpers(current_app.config)
        self.is_sink = current_app.config["IS_SINK"]
        self.is_source = current_app.config["IS_SOURCE"]
        self.operator_url = current_app.config["OPERATOR_URL"]
        self.db_path = current_app.config["DATABASE_PATH"]

    @error_handler
    @api_logging
    def post(self):
        debug_log.info("arrived at Install_CR")
        cr_stuff = request.json

        sq.activate()

        sq.task("Install CR/CSR")
        '''post

        :return: Returns 202
        '''

        crt = CR_tool()
        crt.cr = cr_stuff
        role = crt.get_role()
        sq.task("Verify CR format and mandatory fields")
        if role == "Source" and self.is_source:
            debug_log.info("Source CR")
            debug_log.info(dumps(crt.get_CR_payload(), indent=2))
            debug_log.info(type(crt.get_CR_payload()))
            errors = validate_json(source_cr_schema, crt.get_CR_payload())
            for e in errors:
                raise DetailedHTTPException(detail={"msg": "Validating Source CR format and fields failed",
                                                    "validation_errors": errors},
                                            title="Failure in CR validation",
                                            status=400)
        if role == "Sink" and self.is_sink:
            debug_log.info("Sink CR")
            errors = validate_json(sink_cr_schema, crt.get_CR_payload())
            for e in errors:
                raise DetailedHTTPException(detail={"msg": "Validating Sink CR format and fields failed",
                                                    "validation_errors": errors},
                                            title="Failure in CR validation",
                                            status=400)

        if ((role == "Source" and self.is_source) or (role == "Sink" and self.is_sink)) is False:
            raise DetailedHTTPException(detail={"msg": "Validating CR format and fields failed."
                                                       " It is possible CR didn't specify role or that Service is "
                                                       "configured to be nether sink, source or both"},
                                        title="Failure in CR validation",
                                        status=400)

        debug_log.info(dumps(crt.get_CR_payload(), indent=2))
        debug_log.info(dumps(crt.get_CSR_payload(), indent=2))

        # SLR includes CR keys which means we need to get key from stored SLR and use it to verify this
        # 1) Fetch surrogate_id so we can query our database for slr
        sq.task("Fetch surrogate_id and slr_id from the CR")
        surr_id = crt.get_surrogate_id()
        slr_id = crt.get_slr_id()

        sq.task("Verify SLR is Active for this CR")
        # Verify SLR is Active: TODO: This could probably return SLR so we don't fetch it second time below.
        if self.helpers.verify_slr_is_active(slr_id) is False:
            raise DetailedHTTPException(detail={"msg": "SLR not Active",},
                                        title="Consent Can't be granted with inactive SLR",
                                        status=403)

        debug_log.info("Fetched surr_id({}) and slr_id({})".format(surr_id, slr_id))

        sq.task("Verify CR is signed with keys in associated SLR")
        slrt = SLR_tool()
        slrt.slr = self.helpers.get_slr(surr_id)
        verify_is_success = crt.verify_cr(slrt.get_cr_keys())
        if verify_is_success:
            debug_log.info("CR was verified with key from SLR")
        else:
            raise DetailedHTTPException(detail={"msg": "Verifying CR failed", },
                                        title="Failure in CR verifying",
                                        status=403)


        sq.task("Verify CSR format and mandatory fields")
        errors = validate_json(csr_schema, crt.get_CSR_payload())
        for e in errors:
            raise DetailedHTTPException(detail={"msg": "Validating CSR format and fields failed",
                                                "validation_errors": errors},
                                        title="Failure in CSR validation",
                                        status=400)
        # 1) CSR has link to CR
        csr_has_correct_cr_id = crt.cr_id_matches_in_csr_and_cr()
        if csr_has_correct_cr_id:
            debug_log.info("Verified CSR links to CR")
        else:
            raise DetailedHTTPException(detail={"msg": "Verifying CSR cr_id == CR cr_id failed",},
                                        title="Failure in CSR verifying",
                                        status=403)


        sq.task("Verify CSR is signed with keys in associated SLR")
        # SLR includes CR keys which means we need to get key from stored SLR and use it to verify this
        verify_is_success = crt.verify_csr(slrt.get_cr_keys())

        if verify_is_success:
            debug_log.info("CSR was verified with key from SLR")
        else:
            raise DetailedHTTPException(detail={"msg": "Verifying CSR failed",},
                                        title="Failure in CSR verifying",
                                        status=403)


        # # Verify CR before we do intense DB lookups
        # verify_is_success = crt.verify_cr(slrt.get_cr_keys())
        # if verify_is_success:
        #     sq.task("Verify CR is issued by authorized party")
        #     debug_log.info("CR was verified with key from SLR")
        # else:
        #     raise DetailedHTTPException(detail={"msg": "Verifying CR failed",},
        #                                 title="Failure in CR verifying")


        # 2) CSR has link to previous CSR
        # If prev csr id is null it means this is first time we handle this CR, thus its the first CSR
        # Check that previous CSR has not been withdrawn or paused
        # If previous_id is null this step can be ignored.
        # Else fetch previous_id from db and check the status.

        sq.task("Verify CSR chain is intact.")
        prev_csr_id_refers_to_null = crt.get_prev_record_id() == "null"
        if prev_csr_id_refers_to_null:
            debug_log.info("prev_csr_id_referred to null as it should.")
        else:
            try:
                last_csr_state = self.helpers.introspection(crt.get_cr_id_from_csr(), self.operator_url)
                if last_csr_state in ["Active", "Paused"]:
                    raise DetailedHTTPException(detail={"msg":"There is existing CR that is active,"
                                                          " before creating new CR you should change"
                                                          " status of old accordingly."})
            except LookupError as e:
                debug_log.info("Cr_id({}) doesn't have Active status in its last CSR".format(crt.get_cr_id_from_cr()))
            raise DetailedHTTPException(detail={"msg": "Verifying CSR previous_id == 'null' failed",},
                                        title="Failure in CSR verifying",
                                        status=403)


        sq.task("Store CR and CSR")
        store_dict = {
            "rs_id": crt.get_rs_id(),
            "csr_id": crt.get_csr_id(),
            "consent_status": crt.get_consent_status(),
            "previous_record_id": crt.get_prev_record_id(),
            "cr_id": crt.get_cr_id_from_cr(),
            "surrogate_id": surr_id,
            "slr_id": crt.get_slr_id(),
            "json": crt.cr["cr"]  # possibly store the base64 representation
        }
        self.helpers.storeCR_JSON(store_dict)

        # Remove unused items from dict, csr db doesn't need all of those.
        store_dict.pop("rs_id", None)
        store_dict.pop("slr_id", None)

        store_dict["json"] = crt.cr["csr"]
        self.helpers.storeCSR_JSON(store_dict)
        sq.reply_to("OpMgmt", "201 cr_id")
        if role == "Sink" and self.is_sink:
            debug_log.info("Requesting auth_token")
            sq.task("Request AuthToken from Operator")
            get_AuthToken.delay(crt.get_cr_id_from_cr(), self.operator_url, current_app.config)
        sq.deactivate()
        return {"id": crt.get_cr_id_from_cr()}, 201

    @error_handler
    @api_logging
    def patch(self):
        # TODO: Currently only Operator actually verifies we're not being fed crap.
        payload = request.json

        # Decode payload
        decoded_payload = base_token_tool.decode_payload(payload["data"]["attributes"]["payload"])

        # Create template for StoreCSR_JSON
        store_dict = {
            "csr_id": decoded_payload["record_id"],
            "consent_status": decoded_payload["consent_status"],
            "previous_record_id": decoded_payload["prev_record_id"],
            "cr_id": decoded_payload["cr_id"],
            "surrogate_id": decoded_payload["surrogate_id"],
            "json": payload["data"]["attributes"]  # possibly store the base64 representation
        }
        sq.activate()
        sq.task("Store new CSR into Database.")
        # Store CSR to database
        self.helpers.storeCSR_JSON(store_dict)

        # Forward change to Service
        # To be implemented.....

        sq.reply_to("OpMgmt", "201 csr_id")
        sq.deactivate()
        return {"id": decoded_payload["record_id"]}, 201
Exemple #2
0
class DataRequest(Resource):
    def __init__(self):
        super(DataRequest, self).__init__()
        self.service_url = current_app.config["SERVICE_URL"]
        self.operator_url = current_app.config[
            "OPERATOR_URL"]  # TODO: Where do we really get this?
        self.helpers = Helpers(current_app.config)

    @error_handler
    @api_logging
    def get(self):
        sq.task("Fetch PoP from authorization header")
        authorization = request.headers["Authorization"]
        debug_log.info(authorization)
        pop_h = pop_handler(
            token=authorization.split(" ")
            [1])  # TODO: Logic to pick up PoP, this TODO needs clarification.
        sq.task("Fetch at field from PoP")
        decoded_pop_token = loads(pop_h.get_decoded_token())
        debug_log.info(
            "Token verified state should be False here, it is: {}".format(
                pop_h.verified))

        debug_log.info(type(decoded_pop_token))
        debug_log.info(dumps(decoded_pop_token, indent=2))

        sq.task("Decode auth_token from PoP and get cr_id.")
        token = decoded_pop_token["at"]["auth_token"]
        jws_holder = jwt.JWS()
        jws_holder.deserialize(raw_jws=token)
        auth_token_payload = loads(jws_holder.__dict__["objects"]["payload"])
        debug_log.info(
            "We got auth_token_payload: {}".format(auth_token_payload))

        cr_id = auth_token_payload["pi_id"]
        debug_log.info(
            "We got cr_id {} from auth_token_payload.".format(cr_id))

        sq.task("Fetch surrogate_id with cr_id")
        surrogate_id = self.helpers.get_surrogate_from_cr_id(cr_id)

        sq.task("Verify CR")
        cr = self.helpers.validate_cr(cr_id, surrogate_id)
        pop_key = cr["cr"]["role_specific_part"]["pop_key"]
        pop_key = jwk.JWK(**pop_key)

        token_issuer_key = cr["cr"]["role_specific_part"]["token_issuer_key"]
        token_issuer_key = jwk.JWK(**token_issuer_key)

        sq.task("Validate auth token")
        auth_token = jwt.JWT(jwt=token, key=token_issuer_key)

        debug_log.info(
            "Following auth_token claims successfully verified with token_issuer_key: {}"
            .format(auth_token.claims))

        sq.task("Validate Request(PoP token)")
        pop_h = pop_handler(token=authorization.split(" ")[1], key=pop_key)
        decoded_pop_token = loads(pop_h.get_decoded_token(
        ))  # This step affects verified state of object.
        debug_log.info(
            "Token verified state should be True here, it is: {}".format(
                pop_h.verified))
        # Validate Request
        if pop_h.verified is False:
            raise ValueError("Request verification failed.")

        try:
            sq.task("Intropection")
            status_of_last_csr = self.helpers.introspection(
                cr_id, self.operator_url)
            if status_of_last_csr == "Active":
                # Process request
                sq.task("Return requested data.")
                # This is where the data requested gets fetched and returned.
                return {
                    "Some test data": "like so",
                    "and it continues": "like so!"
                }
            else:
                # TODO Write somewhere that this returns status of last csr source has verified to Sink
                debug_log.info(
                    "Status of last csr is: {}".format(status_of_last_csr))
                return {
                    "error message is": "appropriate.",
                    "csr_status": status_of_last_csr
                }

        except LookupError as e:
            debug_log.exception(e)
            e.message = "Introspection Failed."
            raise e