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
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}