diff --git a/maap_jupyter/maap_utils/L1_MSI_pixel_value_Composite_123.tif b/maap_jupyter/maap_utils/L1_MSI_pixel_value_Composite_123.tif index 5447e95147c80263594956abd221e88699450e74..196d9a657e5be335fd1a8828f1b548908f3b4770 100644 Binary files a/maap_jupyter/maap_utils/L1_MSI_pixel_value_Composite_123.tif and b/maap_jupyter/maap_utils/L1_MSI_pixel_value_Composite_123.tif differ diff --git a/maap_jupyter/maap_utils/__pycache__/maap_authenticator.cpython-310.pyc b/maap_jupyter/maap_utils/__pycache__/maap_authenticator.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a41a6ea83c4aca778f723e844bbc89b51d4924d Binary files /dev/null and b/maap_jupyter/maap_utils/__pycache__/maap_authenticator.cpython-310.pyc differ diff --git a/maap_jupyter/maap_utils/__pycache__/maap_wpst.cpython-310.pyc b/maap_jupyter/maap_utils/__pycache__/maap_wpst.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a3a1eaa8eb27f2ec9c4b2bc40fe32810da25cdd Binary files /dev/null and b/maap_jupyter/maap_utils/__pycache__/maap_wpst.cpython-310.pyc differ diff --git a/maap_jupyter/maap_utils/maap_authenticator.py b/maap_jupyter/maap_utils/maap_authenticator.py new file mode 100644 index 0000000000000000000000000000000000000000..24b94b6a56dd02add5bd109136cf618b439a6f85 --- /dev/null +++ b/maap_jupyter/maap_utils/maap_authenticator.py @@ -0,0 +1,140 @@ +import configparser +import requests +import json +import base64 +import hashlib +import re +from bs4 import BeautifulSoup +from urllib.parse import urlparse +from urllib.parse import parse_qs +import os + + +class MaapAuthenticator(object): + + def __init__(self,auth_config_path,maap_config_path) -> None: + + config = configparser.ConfigParser() + config.read(auth_config_path) + + #Retrieve auth values + self.login = config['auth']['email'] + self.password = config['auth']['password'] + + config = configparser.ConfigParser() + config.read(maap_config_path) + + #Retrieve maap values + self.client_id = config['maap']['client_id'] + self.url_token = config['maap']['url_token'] + + + + def get_esa_token_with_esa_cred(self) -> str: + + response = requests.post(self.url_token, data={'client_id': self.client_id, 'username': self.login, 'password': self.password, + "grant_type": "password", "scope": "profile"}) + data = json.loads(response.text) + return data['access_token'] + + + def get_esa_token_with_nasa_cred(self) -> str: + + session = requests.Session() + + response = session.get("https://auth.val.esa-maap.org/realms/maap/.well-known/openid-configuration") + openid_config = json.loads(response.text) + + response = session.get(openid_config["jwks_uri"]) + certs = json.loads(response.text) + + + + code_verifier = base64.urlsafe_b64encode(os.urandom(40)).decode('utf-8') + code_verifier = re.sub('[^a-zA-Z0-9]+', '', code_verifier) + + code_challenge = hashlib.sha256(code_verifier.encode('utf-8')).digest() + code_challenge = base64.urlsafe_b64encode(code_challenge).decode('utf-8') + code_challenge = code_challenge.replace('=', '') + + response = session.get(openid_config["authorization_endpoint"], + params={"redirect_uri":"https://portal.val.esa-maap.org/portal-val/ESA/home", + "response_type":"code", + "client_id":"portal", + "scope":"openid profile offline_access email", + "code_challenge_method":"S256", + "code_challenge":code_challenge}) + + soup = BeautifulSoup(response.text, 'html.parser') + nasa_broker_url = "" + for link in soup.find_all('a'): + if 'broker/NASA/' in link.get('href'): + nasa_broker_url = link.get('href') + + # Click on NASA in keycloak + response = session.get("https://auth.val.esa-maap.org"+nasa_broker_url) + + soup = BeautifulSoup(response.text, 'html.parser') + + redirect_url = "" + for link in soup.find_all('a'): + if'EarthData' in link.text: + redirect_url = link.get("href") + + + # Click on Earth data in CAS + response = session.get("https://auth.maap-project.org/cas/"+redirect_url) + + soup = BeautifulSoup(response.text, 'html.parser') + + authenticity_token="" + client_id="" + redirect_uri="" + + for tag in soup.find_all("input", type="hidden"): + if tag.get("name") == "authenticity_token": + authenticity_token = tag.get("value") + if tag.get("name") == "client_id": + client_id = tag.get("value") + if tag.get("name") == "redirect_uri": + redirect_uri = tag.get("value") + + + + data_urs = { + "authenticity_token": authenticity_token, + "username":self.login, + "password": self.password, + "client_id": client_id, + "redirect_uri":redirect_uri, + "response_type":"code", + } + + # Click on login in URS + response = session.post("https://urs.earthdata.nasa.gov/login",data = data_urs) + + + soup = BeautifulSoup(response.text, 'html.parser') + + for tag in soup.find_all("a", id="redir_link"): + redirect_url = tag.get("href") + + # Follow redirection + response = session.get(redirect_url) + + parsed_url = urlparse(response.history[-1].headers['Location']) + code = parse_qs(parsed_url.query)['code'][0] + + + + response = session.post("https://auth.val.esa-maap.org/realms/maap/protocol/openid-connect/token", + data={ + "grant_type":"authorization_code", + "code":code, + "client_id":"portal", + "code_verifier":code_verifier, + "redirect_uri":"https://portal.val.esa-maap.org/portal-val/ESA/home" + + }) + + return json.loads(response.text)['access_token'] diff --git a/maap_jupyter/maap_utils/maap_wpst.py b/maap_jupyter/maap_utils/maap_wpst.py new file mode 100644 index 0000000000000000000000000000000000000000..e9d832d6d76a2380437e2f01a746ec35a5795e4e --- /dev/null +++ b/maap_jupyter/maap_utils/maap_wpst.py @@ -0,0 +1,114 @@ +import requests +import json +import time +from typing import List + + +class MaapProcess(object): + + def __init__(self,id:str, title:str) -> None: + self.id = id + self.title = title + +class MaapJob(object): + + def __init__(self,p_id:str, job_id:str) -> None: + self.p_id = p_id + self.job_id = job_id + self.status = "NONE" + + + +class MaapWPST(object): + + def __init__(self,copa_backend_url: str,oauth_token: str) -> None: + + self.copa_backend_url = copa_backend_url + self.oauth_token = oauth_token + self.process_list = self.__load_process() + + + + def __load_process(self) -> List[MaapProcess]: + + response = requests.get(self.copa_backend_url+'wpst/processes',headers = {'Authorization': 'Bearer '+ self.oauth_token}) + response.raise_for_status() + + results = [] + for process_json in response.json()['processes']: + results.append(MaapProcess(process_json['id'],process_json['title'])) + + return results + + + def job_status(self,maap_job: MaapJob) -> str: + + response = requests.get(self.copa_backend_url+'wpst/processes/'+maap_job.p_id+'/jobs/'+maap_job.job_id,headers = {'Authorization': 'Bearer '+ self.oauth_token}) + response.raise_for_status() + + res_json = response.json() + if 'status' in res_json: + result = res_json['status'] + + return result + + + def launch_process(self,title,inputs) -> MaapJob: + + p_id = None + for process in self.process_list: + if title == process.title: + p_id = process.id + + job_id = None + + if p_id is not None: + payload = {'inputs':inputs,'outputs':[],'mode':'ASYNC','response':'RAW'} + response = requests.post(self.copa_backend_url+'wpst/processes/'+p_id+'/jobs',json=payload,headers = {'Authorization': 'Bearer '+ self.oauth_token}) + response.raise_for_status() + + res_json = response.json() + + if 'jobId' in res_json: + job_id = response.json()['jobId'] + + + else: + print("ERROR : Can not launch job for process :"+title+" !") + + return MaapJob(p_id,job_id) + + def wait_for_final_status(self, maap_job): + + job_status = 'RUNNING' + + while job_status not in ['SUCCEEDED','FAILED']: + + response = requests.get(self.copa_backend_url+'wpst/processes/{}/jobs/{}'.format(maap_job.p_id, maap_job.job_id), + headers = {'Authorization': 'Bearer '+ self.oauth_token}) + job_status = json.loads(response.content).get('status') + time.sleep(30) + + maap_job.status = job_status + + + def write_outputs(self,maap_job, out_dir): + + if maap_job.status == 'SUCCEEDED': + result_proc = requests.get('{}wpst/processes/{}/jobs/{}/result'.format(self.copa_backend_url, maap_job.p_id, maap_job.job_id), + headers = {'Authorization': 'Bearer '+ self.oauth_token}) + json_res = json.loads(result_proc.content) + + if 'outputs' in json_res : + + for out in json_res['outputs']: + name = out['href'].split('?')[0].split('/')[-1] + + r = requests.get(out['href']) + open(out_dir+'/'+name, 'wb').write(r.content) + + def delete_job(self,maap_job): + requests.delete('{}wpst/processes/{}/jobs/{}'.format(self.copa_backend_url, maap_job.p_id, maap_job.job_id), + headers = {'Authorization': 'Bearer '+ self.oauth_token}) + +