Example #1
0
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)
Example #2
0
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')
Example #3
0
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('.'))
Example #5
0
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'] = binascii.hexlify(
            os.urandom(65)).decode('utf-8')
    host, port = get_hostname_and_random_port()
    kwargs['bind_web_address'] = f"{scheme}://{host}:{port}"

    # 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):

        # add a user so that we can actually log in.
        user = '******'
        passwd = 'cat'
        adminep = AdminEndpoints()
        adminep.add_user(user, passwd, groups=['foo', 'read-only'])
        expected_claims = dict(groups=['foo', 'read-only'])

        with get_master_web(**kwargs) as mw:

            data = urlencode(dict(username=user,
                                  password=passwd)).encode('utf-8')
            assert len(data) > 0
            # test not sending any parameters.
            env = get_test_web_env("/authenticate",
                                   input_data=data,
                                   method='POST')
            mocked_start_response, response = get_server_response(env, mw)
            assert 3 == len(response.split("."))

            claims = mw.get_user_claims(response)
            assert claims
            assert not DeepDiff(expected_claims, claims)
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)