def test_jwt_encode(encryption_type): with get_test_volttron_home(messagebus='zmq') as vhome: if encryption_type == "private_key": algorithm = "HS256" encoded_key = get_random_key().encode("utf-8") else: with certs_profile_1(vhome) as certs: algorithm = "RS256" encoded_key = CertWrapper.get_private_key( certs.server_certs[0].key_file) claims = { "woot": ["bah"], "all I want": 3210, "do it next": { "foo": "billy" } } token = jwt.encode(claims, encoded_key, algorithm) if encryption_type == 'tls': decode_key = CertWrapper.get_cert_public_key( certs.server_certs[0].cert_file) new_claimes = jwt.decode(token, decode_key, algorithm) else: new_claimes = jwt.decode(token, encoded_key, algorithm) assert not DeepDiff(claims, new_claimes)
def test_get_random_key(): with get_test_volttron_home(messagebus='zmq'): key = get_random_key() # According to docs the default length is 65 # # Note 2x default is what we should get due to hex encoding. assert 130 == len(key) with pytest.raises(ValueError) as err: key = get_random_key(0) with pytest.raises(ValueError) as err: key = get_random_key(-1) key = get_random_key(20) # note 2x the passed random key assert 40 == len(key)
def set_test_admin(): authorize_ep = MockAuthenticateEndpoints(web_secret_key=get_random_key()) authorize_ep.set_access_token_timeout(0.1) authorize_ep.set_refresh_token_timeout(0.2) AdminEndpoints().add_user("test_admin", "Pass123", groups=['admin']) test_user = {"username": "******", "password": "******"} gevent.sleep(1) return authorize_ep, test_user
def test_authenticate_must_use_post_request(): with get_test_volttron_home(messagebus='zmq'): env = get_test_web_env('/authenticate', method='GET') authorize_ep = AuthenticateEndpoints(web_secret_key=get_random_key()) response = authorize_ep.get_auth_token(env, {}) assert ('Content-Type', 'text/html') in response.headers.items() assert '401 Unauthorized' in response.status
def test_discovery(scheme): vhome = create_volttron_home() # creates a vhome level key store keystore = KeyStore() serverkey = decode_key(keystore.public) # Depending upon scheme we enable/disable password jwt and certificate based jwt. if scheme == 'https': with certs_profile_1('/'.join([vhome, 'certs'])) as certs: config_params = dict(web_ssl_key=certs.server_certs[0].key_file, web_ssl_cert=certs.server_certs[0].cert_file) else: config_params = dict(web_secret_key=get_random_key()) with get_test_volttron_home(messagebus='zmq', config_params=config_params): instance_name = "booballoon" host, port = get_hostname_and_random_port() # this is the vip address address = f"tcp://{host}:{port}" def _construct_query_mock(core): """ Internal function that creates a concrete response for the data. when query('instance-name').get() is called the passed instance name is returned """ nonlocal instance_name, address kv = {"instance-name": instance_name, "addresses": [address]} return MockQuery(**kv) with mock.patch('volttron.platform.vip.agent.subsystems.query.Query', _construct_query_mock): host, port = get_hostname_and_random_port() bind_web_address = f"{scheme}://{host}:{port}" serverkey = decode_key(keystore.public) mws = MasterWebService(serverkey=serverkey, identity=MASTER_WEB, address=address, bind_web_address=bind_web_address, **config_params) mws.startupagent(sender='testweb') env = get_test_web_env("/discovery/") mock_start_response = mock.Mock() # A closingiterator is returned from the response object so we use the next # on the returned response. Then we can do json responses. response = mws.app_routing(env, mock_start_response).__next__() # load json into a dict for testing responses. response = jsonapi.loads(response.decode('utf-8')) assert response.get('instance-name') is not None assert instance_name == response.get('instance-name') assert keystore.public == response.get('serverkey') assert address == response.get('vip-address')
def test_both_private_key_and_passphrase(): with pytest.raises( ValueError, match="Must use either ssl_private_key or web_secret_key not both!" ): with get_test_volttron_home(messagebus='zmq') as vhome: with certs_profile_1(vhome) as certs: authorizeep = AuthenticateEndpoints( web_secret_key=get_random_key(), tls_private_key=certs.server_certs[0].key)
def test_authenticate_endpoint(scheme): kwargs = {} # Note this is not a context wrapper, it just does the creation for us vhome = create_volttron_home() if scheme == 'https': with certs_profile_1(vhome) as certs: kwargs['web_ssl_key'] = certs.server_certs[0].key_file kwargs['web_ssl_cert'] = certs.server_certs[0].cert_file else: kwargs['web_secret_key'] = get_random_key() # We are specifying the volttron_home here so we don't create an additional one. with get_test_volttron_home(messagebus='zmq', config_params=kwargs, volttron_home=vhome): user = '******' passwd = 'cat' adminep = AdminEndpoints() adminep.add_user(user, passwd) env = get_test_web_env('/authenticate', method='POST') if scheme == 'http': authorizeep = AuthenticateEndpoints( web_secret_key=kwargs.get('web_secret_key')) else: authorizeep = AuthenticateEndpoints( tls_private_key=CertWrapper.load_key(kwargs.get( 'web_ssl_key'))) invalid_login_username_params = dict(username='******', password=passwd) response = authorizeep.get_auth_tokens(env, invalid_login_username_params) # assert '401 Unauthorized' in response.content assert '401 UNAUTHORIZED' == response.status invalid_login_password_params = dict(username=user, password='******') response = authorizeep.get_auth_tokens(env, invalid_login_password_params) assert '401 UNAUTHORIZED' == response.status valid_login_params = urlencode(dict(username=user, password=passwd)) response = authorizeep.get_auth_tokens(env, valid_login_params) assert '200 OK' == response.status assert "application/json" in response.content_type response_data = json.loads(response.data.decode('utf-8')) assert 3 == len(response_data["refresh_token"].split('.')) assert 3 == len(response_data["access_token"].split('.'))
def test_admin_unauthorized(): config_params = {"web-secret-key": get_random_key()} with get_test_volttron_home(messagebus='zmq', config_params=config_params): myuser = '******' mypass = '******' adminep = AdminEndpoints() adminep.add_user(myuser, mypass) # User hasn't logged in so this should be not authorized. env = get_test_web_env('/admin/api/boo') response = adminep.admin(env, {}) assert '401 Unauthorized' == response.status assert b'Unauthorized User' in response.response[0]
def get_test_volttron_home(messagebus: str, web_https=False, web_http=False, has_vip=True, volttron_home: str = None, config_params: dict = None, env_options: dict = None): """ Create a full volttronn_home test environment with all of the options available in the environment (os.environ) and configuration file (volttron_home/config) in order to test from. @param messagebus: Currently supports rmq and zmq strings @param web_https: Determines if https should be used and enabled. If this is specified then the cert_fixtures.certs_profile_1 function will be used to generate certificates for the server and signed ca. Either web_https or web_http may be specified not both. @param has_vip: Allows the rmq message bus to not specify a vip address if backward compatibility is not needed. @param config_params: Configuration parameters that should go into the volttron configuration file, note if the basic ones are set via the previous arguments (i.e. web_https) then it is an error to specify bind-web-address (or other) duplicate. @param env_options: Other options that should be specified in the os.environ during the setup of this environment. """ # Make these not None so that we can use set operations on them to see if we have any overlap between # common configuration params and environment. if config_params is None: config_params = {} if env_options is None: env_options = {} # make a copy so we can restore in cleanup env_cpy = os.environ.copy() # start validating input assert messagebus in ( 'rmq', 'zmq'), 'Invalid messagebus specified, must be rmq or zmq.' if web_http and web_https: raise ValueError( "Incompatabile tyeps web_https and web_Http cannot both be specified as True" ) default_env_options = ('VOLTTRON_HOME', 'MESSAGEBUS') for v in default_env_options: if v in env_options: raise ValueError( f"Cannot specify {v} in env_options as it is set already.") # All is well.Create vhome if volttron_home: os.makedirs(volttron_home, exist_ok=True) else: volttron_home = create_volttron_home() # Create env envs = dict(VOLTTRON_HOME=volttron_home, MESSAGEBUS=messagebus) os.environ.update(envs) os.environ.update(env_options) # make the top level dirs os.mkdir(os.path.join(volttron_home, "agents")) os.mkdir(os.path.join(volttron_home, "configuration_store")) os.mkdir(os.path.join(volttron_home, "keystores")) os.mkdir(os.path.join(volttron_home, "run")) # create the certs. This will create the certs dirs web_certs_dir = os.path.join(volttron_home, "web_certs") web_certs = None if web_https: web_certs = certs_profile_1(web_certs_dir) vip_address = None bind_web_address = None web_ssl_cert = None web_ssl_key = None web_secret_key = None config_file = {} if messagebus == 'rmq': if has_vip: ip, port = get_rand_ip_and_port() vip_address = f"tcp://{ip}:{port}" web_https = True elif messagebus == 'zmq': if web_http or web_https: ip, port = get_rand_ip_and_port() vip_address = f"tcp://{ip}:{port}" if web_https: hostname, port = get_hostname_and_random_port() bind_web_address = f"https://{hostname}:{port}" web_ssl_cert = web_certs.server_certs[0].cert_file web_ssl_key = web_certs.server_certs[0].key_file elif web_http: hostname, port = get_hostname_and_random_port() bind_web_address = f"http://{hostname}:{port}" web_secret_key = get_random_key() if vip_address: config_file['vip-address'] = vip_address if bind_web_address: config_file['bind-web-address'] = bind_web_address if web_ssl_cert: config_file['web-ssl-cert'] = web_ssl_cert if web_ssl_key: config_file['web-ssl-key'] = web_ssl_key if web_secret_key: config_file['web-secret-key'] = web_secret_key config_intersect = set(config_file).intersection(set(config_params)) if len(config_intersect) > 0: raise ValueError( f"passed configuration params {list(config_intersect)} are built internally" ) config_file.update(config_params) update_platform_config(config_file) try: yield volttron_home finally: os.environ.clear() os.environ.update(env_cpy) if not os.environ.get("DEBUG", 0) != 1 and not os.environ.get( "DEBUG_MODE", 0): shutil.rmtree(volttron_home, ignore_errors=True)