Example #1
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
Example #2
0
class DataFlow(Resource):
    def __init__(self):
        super(DataFlow, self).__init__()
        self.service_url = current_app.config["SERVICE_URL"]
        self.operator_url = current_app.config["OPERATOR_URL"]
        self.helpers = Helpers(current_app.config)

    @error_handler
    @api_logging
    def post(self):  # TODO Make this a GET, is this valid anymore?
        def renew_token(operator_url, record_id):
            sq.task("Renewing Auth Token.")
            token = requests.get("{}/api/1.3/cr/auth_token/{}".format(
                operator_url,
                record_id))  # TODO Get api path from some config?
            debug_log.info("{}, {}, {}, {}".format(token.url, token.reason,
                                                   token.status_code,
                                                   token.text))
            store_dict = {cr_id: dumps(loads(token.text.encode()))}
            self.helpers.storeToken(store_dict)

        def fetch_data_request_urls():
            params = request.json
            debug_log.info(params)
            debug_log.info(request.json)
            user_id = params["user_id"]
            cr_id = params["cr_id"]
            rs_id = params["rs_id"]
            sq.task("Get data_set_id from POST json")
            data_set_id = request.args.get("dataset_id", None)
            debug_log.info(
                "data_set_id is ({}), cr_id is ({}), user_id ({}) and rs_id ({})"
                .format(data_set_id, cr_id, user_id, rs_id))
            sq.task("Create request")
            req = {"we want": "data"}

            sq.task("Validate CR")
            cr = self.helpers.validate_cr(cr_id, surrogate_id=user_id)

            sq.task("Validate Request from UI")
            distribution_urls = self.helpers.validate_request_from_ui(
                cr, data_set_id, rs_id)

            # Fetch data request urls
            # Data request urls fetched.
            debug_log.info("Data request urls fetched.")
            return cr_id, cr, distribution_urls

        cr_id, cr, distribution_urls = fetch_data_request_urls()

        sq.task("Validate Authorisation Token")
        surrogate_id = cr["cr"]["common_part"]["surrogate_id"]
        our_key = self.helpers.get_key()
        our_key_pub = our_key["pub"]
        tries = 3  # TODO: Get this from config
        while True:
            try:
                aud = self.helpers.validate_authorization_token(
                    cr_id, surrogate_id, our_key_pub)
                break
            except ValueError as e:
                debug_log.exception(e)
                renew_token(self.operator_url, cr_id)
                if tries == 0:
                    raise EnvironmentError(
                        "Auth token validation failed and retry counter exceeded."
                    )
                tries -= 1
            except TypeError as e:
                debug_log.exception(e)
                raise EnvironmentError("Token used too soon, halting.")

        # Most verifying and checking below is done in the validate_authorization_token function by jwcrypto
        # Fetch Authorisation Token related to CR from data storage by rs_id (cr_id?)
        # Check Integrity ( Signed by operator, Operator's public key can be found from SLR)
        # Check "Issued" timestamp
        # Check "Not Before" timestamp
        # Check "Not After" timestamp

        # Check that "sub" contains correct public key(Our key.)

        # OPT: Token expired
        # Get new Authorization token, start again from validation. # TODO: Make these steps work as functions that call the next step.

        # Check URL patterns in "aud" field
        # Check that fetched distribution urls can be found from "aud" field

        # Token validated
        debug_log.info("Auth Token Validated.")
        # With these two steps Sink has verified that it's allowed to make request.

        # Construct request
        sq.task("Construct request")
        # Select request URL from "aud" field
        # Add Authorisation Token to request
        # Request constructed.
        # Sign request
        # Fetch private key pair of public key specified in Authorisation Token's "sub" field.
        # Sign with fetched private key
        sq.task("Fetch key used to sign request")
        our_key_full = jwk.JWK()
        our_key_full.import_key(**our_key["key"])
        # Add signature to request
        # Request signed.
        # Request created.
        sq.send_to("Service_Components Mgmnt (Source)",
                   "Data Request (PoP stuff)")
        # Make Data Request
        data = []
        for url in distribution_urls:
            req = requests.get(url,
                               auth=SignedRequest(token=aud,
                                                  sign_method=True,
                                                  sign_path=True,
                                                  key=our_key_full,
                                                  protected=dumps(
                                                      our_key["prot"])))
            if req.ok:
                data.append(loads(req.content))
        debug_log.info(
            "Made data request and received following data from Source: \n{}".
            format(dumps(loads(req.content), indent=2)))

        return {"response_data": data}