diff --git a/hbp_nrp_virtual_coach/doc/source/tutorials/launching_exp.rst b/hbp_nrp_virtual_coach/doc/source/tutorials/launching_exp.rst index 1de0fe99bd8f9a26aa43d976a18d7d963fe91b51..099965eaa75c8131cc77a42214c6c2f6a12a3fbc 100644 --- a/hbp_nrp_virtual_coach/doc/source/tutorials/launching_exp.rst +++ b/hbp_nrp_virtual_coach/doc/source/tutorials/launching_exp.rst @@ -41,7 +41,9 @@ Here we chose the local machine as the NRP server. The Virtual Coach can connect vc = VirtualCoach('http://148.187.97.37', oidc_username='<your-hbp-username>', oidc_password='<your-hbp-password>') -Notice that parameters *storage_username* and *storage_password* are replaced with *oidc_username* and *oidc_password*. The latter must be used when :term:`OIDC` authentication is required, the former are used otherwise. +Notice that parameters *storage_username* and *storage_password* are replaced with *oidc_username* and *oidc_password*. The latter must be used when EBRAINS :term:`OIDC` authentication is required, the former are used otherwise. + +.. note:: Prior using Virtual Coach with the online NRP servers, you should login at least once to that online server with your EBRAINS account in order to give the user consent. Once we created a VirtualCoach instance, we can use it to check out the current state of our environment. We can see a list of the available experiments to run, a list of the available servers to run experiments on, and a list of the currently running experiments. diff --git a/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py b/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py index f16e95d3fc27c60b4613531cddf30cccf429b8a1..e49621c07ed15b4a44c783264e9703df0b59dc68 100644 --- a/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py +++ b/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py @@ -129,9 +129,6 @@ class TestVirtualCoach(unittest.TestCase): def test_init_asserts_no_password(self): - # invalid oidc token - self.assertRaises(AssertionError, VirtualCoach, oidc_token=123) - # invalid environment self.assertRaises(AssertionError, VirtualCoach, environment=True) diff --git a/hbp_nrp_virtual_coach/pynrp/virtual_coach.py b/hbp_nrp_virtual_coach/pynrp/virtual_coach.py index 9880c0e53f81e12e040c6975aa9d035b9b87dd0b..7b8e38831e850d0419e6c361340105dd641bae63 100644 --- a/hbp_nrp_virtual_coach/pynrp/virtual_coach.py +++ b/hbp_nrp_virtual_coach/pynrp/virtual_coach.py @@ -43,6 +43,8 @@ import tempfile import requests from urllib import parse as urlparse +import secrets + from copy import copy from datetime import datetime, timedelta @@ -72,7 +74,7 @@ class VirtualCoach(object): """ def __init__(self, environment='http://localhost:9000', oidc_username=None, oidc_password=None, - oidc_token=None, storage_username=None, storage_password=None): + storage_username=None, storage_password=None): """ Instantiates the Virtual Coach by loading the configuration file and logging into OIDC for the given user. This will only fail if the config file is invalid or if the user @@ -86,9 +88,6 @@ class VirtualCoach(object): authentication and no token is provided. :param oidc_password: (optional) A string representing the OIDC Server password. If not supplied, the user will be prompted to enter a password. - :param oidc_token: (optional) A string representing the OIDC token for the current - user, required if the selected environment requires OIDC - and the username is not provided. :param storage_username: (optional) A string representing the Storage Server username. It is required if the user wants to have access to the storage server to clone experiments and launch cloned experiments. @@ -97,7 +96,6 @@ class VirtualCoach(object): """ assert isinstance(environment, (string_types, type(None))) assert isinstance(oidc_username, (string_types, type(None))) - assert isinstance(oidc_token, (string_types, type(None))) assert isinstance(storage_username, (string_types, type(None))) # parse and load the config file before any OIDC actions @@ -107,19 +105,12 @@ class VirtualCoach(object): # authorize client into oidc or storage server token = '' - if oidc_username or oidc_token: - if oidc_username: - logger.info('Logging into OIDC as: %s', oidc_username) - if not oidc_password: - oidc_password = getpass.getpass(prompt='input your OIDC password: ') - token = self.__get_oidc_token(oidc_username, oidc_password) - else: - if self.__oidc_token_is_valid(token): - logger.info('Using provided token for OIDC server authorization') - token = oidc_token - else: - raise Exception('Provided OIDC token is invalid, VirtualCoach cannot be ' - 'instantiated.') + if oidc_username: + logger.info('Logging into OIDC as: %s', oidc_username) + if not oidc_password: + oidc_password = getpass.getpass(prompt='input your OIDC password: ') + token = self.__get_oidc_token(oidc_username, oidc_password) + elif storage_username: logger.warning('No OIDC username supplied, simulation services will fail if OIDC is ' 'enabled in this environment (%s).', environment) @@ -141,31 +132,6 @@ class VirtualCoach(object): # if the config is valid and the login doesn't fail, we're ready logger.info('Ready.') - def __oidc_token_is_valid(self, token): - ''' - check if a given oidc token is valid - - :param token: token to validate as a string - ''' - - response = requests.post('https://services.humanbrainproject.eu/oidc/tokeninfo', - data=urlparse.urlencode({'access_token': token}), - headers={'Content-Type': 'application/x-www-form-urlencoded'}) - - if response.status_code != 200: - raise Exception('OIDC token validation failed, Status Code: %d' - % response.status_code) - - d = json.loads(response.content) - if 'valid' in d and not d['valid']: - logger.info('OIDC token not valid') - return False - elif 'expires_in' in d and int(d['expires_in']) < 1: - logger.info('OIDC token expired') - return False - - logger.info('OIDC token valid') - return True def __get_oidc_token(self, user_name, password): """ @@ -176,18 +142,19 @@ class VirtualCoach(object): """ # hbp oidc request - client_id = 'kamaji-python-client' - oauth_url = 'https://services.humanbrainproject.eu/oidc/' + client_id = 'nrp-frontend' + oauth_url = 'https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/' # construct request query = { 'response_type': 'token', 'client_id': client_id, - 'redirect_uri': oauth_url + 'resources/oauth_code.html', - 'prompt': 'consent', + 'redirect_uri': 'https://neurorobotics.net', + 'nonce': secrets.token_urlsafe(), + 'scope': 'openid profile email group' } - parts = list(urlparse.urlparse(oauth_url + 'authorize')) + parts = list(urlparse.urlparse(oauth_url + 'auth')) query.update(dict(urlparse.parse_qsl(parts[4]))) # 4 is the index of the query part parts[4] = urlparse.urlencode(query) authorize_url = urlparse.urlunparse(parts) @@ -198,12 +165,12 @@ class VirtualCoach(object): br.set_handle_robots(False) for _ in range(3): br.open(authorize_url) - br.select_form(name='j_spring_security_check') + br.select_form(nr=0) # fill form # pylint: disable=unsupported-assignment-operation - br['j_username'] = user_name - br['j_password'] = password + br['username'] = user_name + br['password'] = password # pylint: disable=assignment-from-none res = br.submit() @@ -215,9 +182,7 @@ class VirtualCoach(object): # the user is forwarded to the approve page if not approved yet if 'access_token' not in urlparse.urlparse(res.geturl()).fragment.lower(): - br.select_form(name='confirmationForm') - # pylint: disable=assignment-from-none - res = br.submit() + raise Exception('Login to NRP online and give your consent. Then return to VC.') url_with_fragment = res.geturl()