def forwarder(request, volttron_instances): #print "Fixture forwarder" global volttron_instance1, volttron_instance2 global forwarder_uuid, forwarder_config # 1. Update destination address in forwarder configuration if volttron_instance1.encrypt: tf = tempfile.NamedTemporaryFile() ks = KeyStore(tf.name) # generate public private key pair for instance1 ks.generate() # add public key of instance1 to instance2 auth file authfile = AuthFile(volttron_instance2.volttron_home + "/auth.json") entry = AuthEntry(credentials=ks.public) authfile.add(entry) # setup destination address to include keys forwarder_config["destination-vip"] =\ "{}?serverkey={}&publickey={}&secretkey={}".format( volttron_instance2.vip_address, volttron_instance2.serverkey, ks.public, ks.secret) else: forwarder_config["destination-vip"] = volttron_instance2.vip_address # 1: Install historian agent # Install and start sqlhistorian agent in instance2 forwarder_uuid = volttron_instance1.install_agent( agent_dir="services/core/ForwardHistorian", config_file=forwarder_config, start=True) print("forwarder agent id: ", forwarder_uuid)
def _append_allow_curve_key(self, publickey): entry = AuthEntry(credentials=publickey) authfile = AuthFile(self.volttron_home + "/auth.json") try: authfile.add(entry) except AuthFileEntryAlreadyExists: pass
def forwarder(request, volttron_instances): #print "Fixture forwarder" global volttron_instance1, volttron_instance2 global forwarder_uuid, forwarder_config # 1. Update destination address in forwarder configuration if volttron_instance1.encrypt: tf = tempfile.NamedTemporaryFile() ks = KeyStore(tf.name) # generate public private key pair for instance1 ks.generate() # add public key of instance1 to instance2 auth file authfile = AuthFile(volttron_instance2.volttron_home + "/auth.json") entry = AuthEntry(credentials=ks.public()) authfile.add(entry) # setup destination address to include keys forwarder_config["destination-vip"] =\ "{}?serverkey={}&publickey={}&secretkey={}".format( volttron_instance2.vip_address, volttron_instance2.serverkey, ks.public(), ks.secret()) else: forwarder_config["destination-vip"] = volttron_instance2.vip_address # 1: Install historian agent # Install and start sqlhistorian agent in instance2 forwarder_uuid = volttron_instance1.install_agent( agent_dir="services/core/ForwardHistorian", config_file=forwarder_config, start=True) print("forwarder agent id: ", forwarder_uuid)
def build_connection(self, peer=None, address=None, identity=None, publickey=None, secretkey=None, serverkey=None, capabilities=[], **kwargs): self.logit('Building connection to {}'.format(peer)) self.allow_all_connections() if address is None: self.logit( 'Default address was None so setting to current instances') address = self.vip_address serverkey = self.serverkey if serverkey is None: self.logit("serverkey wasn't set but the address was.") raise Exception("Invalid state.") if publickey is None or secretkey is None: self.logit('generating new public secret key pair') keyfile = tempfile.mktemp(".keys", "agent", self.volttron_home) keys = KeyStore(keyfile) keys.generate() publickey = keys.public secretkey = keys.secret entry = AuthEntry(capabilities=capabilities, comments="Added by test", credentials=keys.public) file = AuthFile(self.volttron_home + "/auth.json") file.add(entry) conn = Connection(address=address, peer=peer, publickey=publickey, secretkey=secretkey, serverkey=serverkey, volttron_home=self.volttron_home) return conn
def auth_file_platform_tuple(volttron_instance_encrypt): platform = volttron_instance_encrypt auth_file = AuthFile(os.path.join(platform.volttron_home, 'auth.json')) allow_entries, groups, roles = auth_file.read() gevent.sleep(0.5) return auth_file, platform
def test_upgrade_file_verison_0_to_1_1_minimum_entries(tmpdir_factory): """The only required field in 'version 0' was credentials""" mechanism = "CURVE" publickey = "A" * 43 version0 = { "allow": [{"credentials": mechanism + ":" + publickey}], } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(json.dumps(version0, indent=2)) upgraded = AuthFile(filename) entries = upgraded.read()[0] assert len(entries) == 1 assert entries[0].user_id is not None expected = version0['allow'][0] expected["credentials"] = publickey expected["mechanism"] = mechanism expected["domain"] = None expected["address"] = None expected["user_id"] = entries[0].user_id # this will be a UUID expected["enabled"] = True expected["comments"] = None expected["capabilities"] = [] expected["roles"] = [] expected["groups"] = [] assert_auth_entries_same(expected, vars(entries[0]))
def test_upgrade_file_verison_0_to_1_1_minimum_entries(tmpdir_factory): """The only required field in 'version 0' was credentials""" mechanism = "CURVE" publickey = "A" * 43 version0 = { "allow": [{ "credentials": mechanism + ":" + publickey }], } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(json.dumps(version0, indent=2)) upgraded = AuthFile(filename) entries = upgraded.read()[0] assert len(entries) == 1 assert entries[0].user_id is not None expected = version0['allow'][0] expected["credentials"] = publickey expected["mechanism"] = mechanism expected["domain"] = None expected["address"] = None expected["user_id"] = entries[0].user_id # this will be a UUID expected["enabled"] = True expected["comments"] = None expected["capabilities"] = [] expected["roles"] = [] expected["groups"] = [] assert_auth_entries_same(expected, vars(entries[0]))
def build_agents_with_capability_args(volttron_instance): """Returns two agents for testing authorization where one agent has rpc call with capability and argument restriction The first agent is the "RPC callee." The second agent is the unauthorized "RPC caller." """ # Can't call the fixture directly so build our own agent here. agent1 = volttron_instance.build_agent(identity='agent1') gevent.sleep(1) agent2 = volttron_instance.build_agent(identity='agent2') gevent.sleep(1) agent1.foo = lambda x: x agent1.foo.__name__ = 'foo' agent2.boo = lambda x, y: (x, y) agent2.boo.__name__ = 'boo' agent1.vip.rpc.export(method=agent1.foo) agent2.vip.rpc.export(method=agent2.boo) agent1.vip.rpc.allow(agent1.foo, 'can_call_foo') agent2.vip.rpc.allow(agent2.boo, 'can_call_boo') yield agent1, agent2 agent1.core.stop() agent2.core.stop() auth_file = AuthFile(os.path.join(volttron_instance.volttron_home, 'auth.json')) allow_entries = auth_file.read_allow_entries() auth_file.remove_by_indices(list(range(3, len(allow_entries)))) gevent.sleep(0.5)
def build_two_test_agents(volttron_instance): """Returns two agents for testing authorization The first agent is the "RPC callee." The second agent is the unauthorized "RPC caller." """ agent1 = volttron_instance.build_agent(identity='agent1') gevent.sleep(1) agent2 = volttron_instance.build_agent(identity='agent2') gevent.sleep(1) agent1.foo = lambda x: x agent1.foo.__name__ = 'foo' agent1.vip.rpc.export(method=agent1.foo) agent1.vip.rpc.allow(agent1.foo, 'can_call_foo') try: yield agent1, agent2 finally: agent1.core.stop() agent2.core.stop() auth_file = AuthFile(os.path.join(volttron_instance.volttron_home, 'auth.json')) allow_entries = auth_file.read_allow_entries() auth_file.remove_by_indices(list(range(3, len(allow_entries)))) # TODO if we have to wait for auth propagation anyways why do we create new agents for each test case # we should just update capabilities, at least we will save on agent creation and tear down time gevent.sleep(1)
def _allow(self, environ, start_response, data=None): _log.info('Allowing new vc instance to connect to server.') jsondata = jsonapi.loads(data) json_validate_request(jsondata) assert jsondata.get('method') == 'allowvc' assert jsondata.get('params') params = jsondata.get('params') if isinstance(params, list): vcpublickey = params[0] else: vcpublickey = params.get('vcpublickey') assert vcpublickey assert len(vcpublickey) == 43 authfile = AuthFile() authentry = AuthEntry(credentials=vcpublickey) try: authfile.add(authentry) except AuthFileEntryAlreadyExists: pass start_response('200 OK', [('Content-Type', 'application/json')]) return jsonapi.dumps(json_result(jsondata['id'], "Added"))
def build_two_test_agents(volttron_instance): """Returns two agents for testing authorization The first agent is the "RPC callee." The second agent is the unauthorized "RPC caller." """ agent1 = build_agent(volttron_instance, 'agent1') agent2 = build_agent(volttron_instance, 'agent2') gevent.sleep(1) agent1.foo = lambda x: x agent1.foo.__name__ = 'foo' agent1.vip.rpc.export(method=agent1.foo) agent1.vip.rpc.allow(agent1.foo, 'can_call_foo') yield agent1, agent2 agent1.core.stop() agent2.core.stop() auth_file = AuthFile( os.path.join(volttron_instance.volttron_home, 'auth.json')) allow_entries = auth_file.read_allow_entries() auth_file.remove_by_indices(list(range(3, len(allow_entries)))) gevent.sleep(0.5)
def allow_all_connections(self): """ Add a /.*/ entry to the auth.json file. """ entry = AuthEntry(credentials="/.*/") authfile = AuthFile(self.volttron_home + "/auth.json") try: authfile.add(entry) except AuthFileEntryAlreadyExists: pass
def auth_file_platform_tuple(volttron_instance): platform = volttron_instance auth_file = AuthFile(os.path.join(platform.volttron_home, 'auth.json')) gevent.sleep(0.5) yield auth_file, platform allow_entries = auth_file.read_allow_entries() auth_file.remove_by_indices(list(range(3, len(allow_entries)))) gevent.sleep(0.5)
def _disable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error disable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to disable setup mode") auth_file = AuthFile() auth_file.remove_by_credentials("/.*/") return "SUCCESS"
def auth_file_platform_tuple(): with get_test_volttron_home('zmq') as vhome: auth_file = AuthFile(os.path.join(vhome, 'auth.json')) gevent.sleep(0.5) yield auth_file allow_entries = auth_file.read_allow_entries() auth_file.remove_by_indices(list(range(3, len(allow_entries)))) gevent.sleep(0.5)
def set_auth_identities(agent_credential_map): """Updates auth entries' identity field in auth file based on existing agents""" auth_file = AuthFile() entries, deny_entries, groups, roles = auth_file.read() for entry in entries: for credential in agent_credential_map: if entry.credentials == credential: entry.identity = agent_credential_map[credential] auth_file._write(entries, deny_entries, groups, roles) return
def test_forwarding(volttron_instance1_encrypt, volttron_instance2_encrypt): global FORWARDER_CONFIG tf = tempfile.NamedTemporaryFile() tf2 = tempfile.NamedTemporaryFile() tf3 = tempfile.NamedTemporaryFile() ks = KeyStore(tf.name) ks.generate() ks2 = KeyStore(tf2.name) ks2.generate() ks3 = KeyStore(tf2.name) ks3.generate() wrap1 = volttron_instance1_encrypt wrap2 = volttron_instance2_encrypt authfile1 = AuthFile(wrap1.volttron_home + "/auth.json") entry1 = AuthEntry(credentials="CURVE:{}".format(ks3.public())) authfile1.add(entry1) authfile = AuthFile(wrap2.volttron_home + "/auth.json") entry = AuthEntry(credentials="CURVE:{}".format(ks.public())) authfile.add(entry) entry = AuthEntry(credentials="CURVE:{}".format(ks2.public())) authfile.add(entry) forward_to_vip = "{}?serverkey={}&publickey={}&secretkey={}".format( wrap2.vip_address, wrap2.publickey, ks.public(), ks.secret()) FORWARDER_CONFIG["destination-vip"] = forward_to_vip forwarder_config = FORWARDER_CONFIG print("THE CONFIG = {}".format(forwarder_config)) wrap1.install_agent(agent_dir="services/core/ForwardHistorian", config_file=forwarder_config) connect_to_wrap2 = "{}?serverkey={}&publickey={}&secretkey={}".format( wrap2.vip_address, wrap2.publickey, ks2.public(), ks2.secret()) connect_to_wrap1 = "{}?serverkey={}&publickey={}&secretkey={}".format( wrap1.vip_address, wrap1.publickey, ks3.public(), ks3.secret()) agent_connected1 = wrap1.build_agent(address=connect_to_wrap1) agent_connected2 = wrap2.build_agent(address=connect_to_wrap2) message = '' agent_connected2.vip.pubsub.subscribe('pubsub', '', callback=onmessage) gevent.sleep(0.2) do_publish(agent1=agent_connected1) gevent.sleep(1) assert allforwardedmessage
def test_forwarding(volttron_instance1_encrypt, volttron_instance2_encrypt): global FORWARDER_CONFIG tf = tempfile.NamedTemporaryFile() tf2 = tempfile.NamedTemporaryFile() tf3 = tempfile.NamedTemporaryFile() ks = KeyStore(tf.name) ks.generate() ks2 = KeyStore(tf2.name) ks2.generate() ks3 = KeyStore(tf2.name) ks3.generate() wrap1 = volttron_instance1_encrypt wrap2 = volttron_instance2_encrypt authfile1 = AuthFile(wrap1.volttron_home+"/auth.json") entry1 = AuthEntry( credentials="CURVE:{}".format(ks3.public()) ) authfile1.add(entry1) authfile = AuthFile(wrap2.volttron_home+"/auth.json") entry = AuthEntry( credentials="CURVE:{}".format(ks.public())) authfile.add(entry) entry = AuthEntry( credentials="CURVE:{}".format(ks2.public())) authfile.add(entry) forward_to_vip = "{}?serverkey={}&publickey={}&secretkey={}".format( wrap2.vip_address, wrap2.publickey, ks.public(), ks.secret() ) FORWARDER_CONFIG["destination-vip"] = forward_to_vip forwarder_config = FORWARDER_CONFIG print("THE CONFIG = {}".format(forwarder_config)) wrap1.install_agent( agent_dir="services/core/ForwardHistorian", config_file=forwarder_config ) connect_to_wrap2 = "{}?serverkey={}&publickey={}&secretkey={}".format( wrap2.vip_address, wrap2.publickey, ks2.public(), ks2.secret() ) connect_to_wrap1 = "{}?serverkey={}&publickey={}&secretkey={}".format( wrap1.vip_address, wrap1.publickey, ks3.public(), ks3.secret() ) agent_connected1 = wrap1.build_agent(address=connect_to_wrap1) agent_connected2 = wrap2.build_agent(address=connect_to_wrap2) message = '' agent_connected2.vip.pubsub.subscribe('pubsub', '', callback=onmessage) gevent.sleep(0.2) do_publish(agent1=agent_connected1) gevent.sleep(1) assert allforwardedmessage
def test_upgrade_file_verison_0_to_latest(tmpdir_factory): mechanism = "CURVE" publickey = "A" * 43 version0 = { "allow": [ { "domain": "vip", "address": "127.0.0.1", "user_id": "user123", "enabled": True, "comments": "This is a test entry", "capabilities": ["can_publish_temperature"], "roles": [], "groups": [], "credentials": mechanism + ":" + publickey } ], "roles": { "manager": ["can_managed_platform"] }, "groups": { "admin": ["reader", "writer"] }, "version": { "major": 0, "minor": 0 }, } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(jsonapi.dumps(version0, indent=2)) upgraded = AuthFile(filename) entries, denied_entries, groups, roles = upgraded.read() assert groups == version0['groups'] assert roles == version0['roles'] assert len(entries) == 1 expected = version0['allow'][0] expected["credentials"] = publickey expected["mechanism"] = mechanism expected["capabilities"] = {'can_publish_temperature': None, 'edit_config_store': {'identity': entries[0].user_id}} expected["rpc_method_authorizations"] = {} assert_auth_entries_same(expected, vars(entries[0])) # RPC Method Authorizations added with 1.3 for entry in upgraded.auth_data["allow_list"]: assert entry["rpc_method_authorizations"] == {}
def manage(self, address, vcserverkey=None, vcpublickey=None): """ Allows the `VolttronCentralPlatform` to be managed. From the web perspective this should be after the user has specified that a user has blessed an agent to be able to be managed. When the user enters a discovery address in `VolttronCentral` it is implied that the user wants to manage a platform. :returns publickey of the `VolttronCentralPlatform` """ _log.info('Manage request from address: {} serverkey: {}'.format( address, vcserverkey)) self._was_unmanaged = False parsed = urlparse.urlparse(address) same_address = False if parsed.scheme == 'ipc': if self._volttron_central_ipc_address == address: same_address = True self._volttron_central_ipc_address = address elif parsed.scheme == 'tcp': _log.debug('Found tcp scheme adding AuthEntry') if self._volttron_central_tcp_address == address: same_address = True self._volttron_central_tcp_address = address self._volttron_central_serverkey = vcserverkey # Add the vcpublickey to the auth file. entry = AuthEntry( credentials=vcpublickey, capabilities=['manager']) # , address=parsedaddress.hostname) authfile = AuthFile() authfile.add(entry) else: raise AttributeError('Invalid scheme in address') if self._vc_connection() is not None: self._is_registered = True self._is_registering = False _log.debug("Returning publickey from manage function.") return self.core.publickey raise NotManagedError( "Could not connect to specified volttron central")
def _enable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error enable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to enable setup mode") auth_file = AuthFile() entries = auth_file.find_by_credentials(".*") if len(entries) > 0: return "SUCCESS" entry = AuthEntry(credentials="/.*/", comments="Un-Authenticated connections allowed here", user_id="unknown") auth_file.add(entry) return "SUCCESS"
def manage(self, address, vcserverkey, vcpublickey): """ Allows the `VolttronCentralPlatform` to be managed. From the web perspective this should be after the user has specified that a user has blessed an agent to be able to be managed. When the user enters a discovery address in `VolttronCentral` it is implied that the user wants to manage a platform. :returns publickey of the `VolttronCentralPlatform` """ _log.info('Manage request from address: {} serverkey: {}'.format( address, vcserverkey)) if self._managed: raise AlreadyManagedError() parsedaddress = urlparse.urlparse(address) if 'ipc://' == address[:6].lower(): self._agent_connected_to_vc = self else: # Attempt to connect to the passed address and serverkey. self._agent_connected_to_vc = build_agent( address=address, serverkey=vcserverkey, publickey=self.core.publickey, secretkey=self.core.secretkey) version, peer, identity = self._agent_connected_to_vc.vip.hello().get( timeout=30) if not self == self._agent_connected_to_vc: # Add the vcpublickey to the auth file. entry = AuthEntry( credentials="CURVE:{}".format(vcpublickey), capabilities=['manager']) # , address=parsedaddress.hostname) authfile = AuthFile() authfile.add(entry) self._managed = True self.core.spawn_later(2, self._publish_agent_list_to_vc) self.core.spawn_later(2, self._publish_stats) return self.core.publickey
def test_upgrade_file_verison_0_to_1_1(tmpdir_factory): mechanism = "CURVE" publickey = "A" * 43 version0 = { "allow": [ { "domain": "vip", "address": "127.0.0.1", "user_id": "user123", "enabled": True, "comments": "This is a test entry", "capabilities": ["can_publish_temperature"], "roles": [], "groups": [], "credentials": mechanism + ":" + publickey } ], "roles": { "manager": ["can_managed_platform"] }, "groups": { "admin": ["reader", "writer"] } } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(json.dumps(version0, indent=2)) upgraded = AuthFile(filename) entries, groups, roles = upgraded.read() assert groups == version0['groups'] assert roles == version0['roles'] assert len(entries) == 1 expected = version0['allow'][0] expected["credentials"] = publickey expected["mechanism"] = mechanism assert_auth_entries_same(expected, vars(entries[0]))
def test_upgrade_file_verison_0_to_latest_minimum_entries(tmpdir_factory): """The only required field in 'version 0' was credentials""" mechanism = "CURVE" publickey = "A" * 43 version0 = { "allow": [{"credentials": mechanism + ":" + publickey}], "version": { "major": 0, "minor": 0 }, } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(jsonapi.dumps(version0, indent=2)) upgraded = AuthFile(filename) entries = upgraded.read()[0] assert len(entries) == 1 assert entries[0].user_id is not None expected = version0['allow'][0] expected["credentials"] = publickey expected["mechanism"] = mechanism expected["domain"] = None expected["address"] = None expected["user_id"] = entries[0].user_id #this will be a UUID expected["enabled"] = True expected["comments"] = None expected["capabilities"] = {'edit_config_store': {'identity': entries[0].user_id}} expected["rpc_method_authorizations"] = {} expected["roles"] = [] expected["groups"] = [] assert_auth_entries_same(expected, vars(entries[0])) # RPC Method Authorizations added with 1.3 for entry in upgraded.auth_data["allow_list"]: assert entry["rpc_method_authorizations"] == {}
def upgrade_old_agents(aip): """ Moves any keystore.json from agent-data to dist-info. Only applies to agents in auth file. """ vhome = Path(aip.env.volttron_home) agent_map = aip.get_agent_identity_to_uuid_mapping() auth_file = AuthFile() install_dir = vhome.joinpath("agents") for agent in agent_map: agent_path = install_dir.joinpath(agent_map[agent]) try: agent_data = get_agent_path(agent_path, 'agent-data') # Skip if no agent-data exists except KeyError as err: print(f"agent-data not found for {err}") continue keystore_path = agent_data.joinpath('keystore.json') try: dist_info = get_agent_path(agent_path, 'dist-info') # Skip if no dist-info exists except KeyError as err: print(f"dist-info not found for {err}") continue keystore_dest_path = dist_info.joinpath('keystore.json') if keystore_path.exists(): agent_keystore = KeyStore(keystore_path) for entry in auth_file.read()[0]: # Only move if agent exists in auth file if entry.credentials == agent_keystore.public: shutil.move(str(keystore_path), str(keystore_dest_path)) break return
def test_upgrade_file_verison_0_to_1_1(tmpdir_factory): mechanism = "CURVE" publickey = "A" * 43 version0 = { "allow": [{ "domain": "vip", "address": "127.0.0.1", "user_id": "user123", "enabled": True, "comments": "This is a test entry", "capabilities": ["can_publish_temperature"], "roles": [], "groups": [], "credentials": mechanism + ":" + publickey }], "roles": { "manager": ["can_managed_platform"] }, "groups": { "admin": ["reader", "writer"] } } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(json.dumps(version0, indent=2)) upgraded = AuthFile(filename) entries, groups, roles = upgraded.read() assert groups == version0['groups'] assert roles == version0['roles'] assert len(entries) == 1 expected = version0['allow'][0] expected["credentials"] = publickey expected["mechanism"] = mechanism assert_auth_entries_same(expected, vars(entries[0]))
def _register_instance(self, discovery_address, display_name=None, provisional=False): """ Register an instance with VOLTTRON Central based on jsonrpc. NOTE: This method is meant to be called from the jsonrpc method. The registration of the instance will fail in the following cases: - no discoverable instance at the passed uri - no platform.agent installed at the discoverable instance - is a different volttron central managing the discoverable instance. If the display name is not set then the display name becomes the same as the discovery_address. This will be used in the volttron central ui. :param discovery_address: A ip:port for an instance of volttron discovery. :param display_name: :return: dictionary: The dictionary will hold either an error object or a result object. """ _log.info( 'Attempting to register name: {} with address: {}'.format( display_name, discovery_address)) try: discovery_response = DiscoveryInfo.request_discovery_info( discovery_address) except DiscoveryError as e: return { 'error': { 'code': DISCOVERY_ERROR, 'message': e.message }} pa_instance_serverkey = discovery_response.serverkey pa_vip_address = discovery_response.vip_address assert pa_instance_serverkey _log.debug('connecting to pa_instance') try: connected_to_pa = ConnectedPlatform( address=pa_vip_address, serverkey=pa_instance_serverkey, secretkey=self.core.secretkey, publickey=self.core.publickey ) connected_to_pa.connect() if not connected_to_pa.is_connected(): return { 'error': { 'code': UNABLE_TO_REGISTER_INSTANCE, 'message': 'Could not connect to {}' .format(pa_vip_address) }} except gevent.Timeout: return { 'error': { 'code': UNABLE_TO_REGISTER_INSTANCE, 'message': 'Could not connect to {}' .format(pa_vip_address) }} except Exception as ex: return {'error': {'code': UNHANDLED_EXCEPTION, 'message': ex.message }} _log.debug('Connected to address') peers = connected_to_pa.agent.vip.peerlist().get(timeout=30) if VOLTTRON_CENTRAL_PLATFORM not in peers: connected_to_pa.core.stop() return {'error': {'code': UNABLE_TO_REGISTER_INSTANCE, 'message': '{} not present.'.format( VOLTTRON_CENTRAL_PLATFORM) }} # The call to manage should return a public key for that agent result = connected_to_pa.agent.vip.rpc.call( VOLTTRON_CENTRAL_PLATFORM, 'manage', self._web_info.vip_address, self._web_info.serverkey, self.core.publickey).get(timeout=30) # Magic number 43 is the length of a encoded public key. if len(result) != 43: return {'error': {'code': UNABLE_TO_REGISTER_INSTANCE, 'message': 'Invalid publickey returned from {}' .format(VOLTTRON_CENTRAL_PLATFORM) }} # Add the pa's public key so it can connect back to us. auth_file = AuthFile() auth_entry = AuthEntry(credentials="CURVE:{}".format(result), capabilities=['managing'] ) auth_file.add(auth_entry) # TODO: figure out if we are local or not entry = PlatformRegistry.build_entry( pa_vip_address, pa_instance_serverkey, discovery_address, display_name, False) self._registry.register(entry) self._pa_agents[entry.platform_uuid] = connected_to_pa _log.debug("Adding {}".format(entry.platform_uuid)) instance_name = display_name if display_name else discovery_address context = 'Registered instance {}'.format(instance_name) connected_to_pa.agent.vip.rpc.call( VOLTTRON_CENTRAL_PLATFORM, 'reconfigure', platform_uuid=entry.platform_uuid).get(timeout=30) return {'status': 'SUCCESS', 'context': context}
def test_upgrade_file_version_1_2_to_1_3(tmpdir_factory): """The only required field in 'version 0' was credentials""" version1_2 = { "roles":{ "manager":[ "can_managed_platform" ] }, "version":{ "major":1, "minor":2 }, "groups":{ "admin":[ "reader", "writer" ] }, "allow":[ { "domain":"vip", "user_id":"user1", "roles":[], "enabled":True, "mechanism":"CURVE", "capabilities":{'can_publish_temperature': None, 'edit_config_store': {'identity': 'user1'}}, "groups":[], "address":"127.0.0.1", "credentials":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "comments":"This is a test entry" }, { "domain": "vip", "user_id": "user2", "roles": [], "enabled": True, "mechanism": "CURVE", "capabilities": {'blah': None, 'foo': None, 'edit_config_store': {'identity': 'user2'}}, "groups": [], "address": "127.0.0.1", "credentials": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "comments": "This is a test entry" }, { "domain": "vip", "user_id": CONTROL, "roles": [], "enabled": True, "mechanism": "CURVE", "capabilities": {'edit_config_store': {'identity': '/.*/'}}, "groups": [], "address": "127.0.0.1", "credentials": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "comments": "This is a test entry" }, { "domain": "vip", "user_id": VOLTTRON_CENTRAL_PLATFORM, "roles": [], "enabled": True, "mechanism": "CURVE", "capabilities": {'edit_config_store': {'identity': '/.*/'}}, "groups": [], "address": "127.0.0.1", "credentials": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "comments": "This is a test entry" } ] } filename = str(tmpdir_factory.mktemp('auth_test').join('auth.json')) with open(filename, 'w') as fp: fp.write(jsonapi.dumps(version1_2, indent=2)) upgraded = AuthFile(filename) entries = upgraded.read()[0] assert len(entries) == 4 for entry in entries: assert entry.rpc_method_authorizations == {}
def allow_all_connections(self): """ Add a CURVE:.* entry to the auth.json file. """ entry = AuthEntry(credentials="/CURVE:.*/") authfile = AuthFile(self.volttron_home + "/auth.json") authfile.add(entry)
def add_to_auth(volttron_home, publickey, capabilities=None): authfile = AuthFile(os.path.join(volttron_home, 'auth.json')) entry = AuthEntry( credentials="CURVE:{}".format(publickey), capabilities=capabilities ) authfile.add(entry)
def _vc_connection(self): """ Attempt to connect to volttron central management console. The attempts will be done in the following order. 1. if peer is vc register with it. 2. volttron-central-tcp and serverkey 2. volttron-central-http (looks up tcp and serverkey) 3. volttron-central-ipc :param sender: :param kwargs: :return: """ assert self._agent_started, "cannot be called before onstart signal" if self._volttron_central_connection: # if connected return the connection. if self._volttron_central_connection.is_connected(5): _log.debug('Returning connection') return self._volttron_central_connection _log.debug("Resetting connection as the peer wasn't responding.") # reset the connection so we can try it again below. self._volttron_central_connection.kill() self._volttron_central_connection = None # First check to see if there is a peer with a volttron.central # identity, if there is use it as the manager of the platform. peers = self.vip.peerlist().get(timeout=5) if VOLTTRON_CENTRAL in peers: _log.debug('VC is a local peer.') self._volttron_central_connection = Connection( self.core.address, VOLTTRON_CENTRAL) if self._volttron_central_connection.is_connected() and \ self._volttron_central_connection.is_peer_connected(): _log.debug("Connection has been established to local peer.") return self._volttron_central_connection # If we have an http address for volttron central, but haven't # looked up the address yet, then look up and set the address from # volttron central discovery. if self._volttron_central_http_address is not None and \ self._volttron_central_tcp_address is None and \ self._volttron_central_serverkey is None: _log.debug('Using discovery to lookup tcp connection') response = requests.get("{}/discovery/".format( self._volttron_central_http_address)) if response.ok: jsonresp = response.json() entry = AuthEntry(credentials="/.*/", capabilities=['manager'] #, #address=jsonresp['vip-address'] ) authfile = AuthFile(get_home() + "/auth.json") authfile.add(entry) self._volttron_central_tcp_address = jsonresp['vip-address'] self._volttron_central_serverkey = jsonresp['serverkey'] # First see if we are able to connect via tcp with the serverkey. if self._volttron_central_tcp_address is not None and \ self._volttron_central_serverkey is not None: _log.debug('Connecting to volttron central using tcp.') vc_conn = Connection(address=self._volttron_central_tcp_address, peer=VOLTTRON_CENTRAL, serverkey=self._volttron_central_serverkey, publickey=self.core.publickey, secretkey=self.core.secretkey) if not vc_conn.is_connected(5): raise ValueError("Unable to connect to remote platform") if not vc_conn.is_peer_connected(5): raise ValueError( "Peer: {} unavailable on remote platform.".format( VOLTTRON_CENTRAL)) #TODO Only add a single time for this address. if self._volttron_central_publickey: # Add the vcpublickey to the auth file. entry = AuthEntry(credentials=self._volttron_central_publickey, capabilities=['manager']) authfile = AuthFile() authfile.add(entry) self._volttron_central_connection = vc_conn return self._volttron_central_connection # Next see if we have a valid ipc address (Not Local though) if self._volttron_central_ipc_address is not None: self._volttron_central_connection = Connection( address=self._volttron_central_ipc_address, peer=VOLTTRON_CENTRAL) return self._volttron_central_connection
def _append_allow_curve_key(self, publickey): entry = AuthEntry(credentials="CURVE:{}".format(publickey)) authfile = AuthFile(self.volttron_home+"/auth.json") authfile.add(entry)
VIRGINIA TECH – ADVANCED RESEARCH INSTITUTE under Contract DE-EE0006352 #__author__ = "BEMOSS Team" #__credits__ = "" #__version__ = "2.0" #__maintainer__ = "BEMOSS Team" #__email__ = "*****@*****.**" #__website__ = "www.bemoss.org" #__created__ = "2014-09-12 12:04:50" #__lastUpdated__ = "2016-03-14 11:23:33" ''' import os path = os.environ.get('VOLTTRON_HOME') from volttron.platform.auth import AuthEntry, AuthFile if __name__ == "__main__": print "Adding new Auth entry" auth_file = AuthFile(path + '/auth.json') entry = AuthEntry(credentials='NULL', comments='second', user_id="BEMOSSAGENT", capabilities=["BEMOSS_BASIC_AGENT"]) auth_file.add(entry) print "NEW AUTH IS ADDED BY BEMOSS" # p = Popen([ "volttron-ctl auth-update 0 ;"], stdin=PIPE, shell=True) # p.communicate("\n".join(["", "BEMOSSAGENT", "", "", "", "", "", "", "", "", ""])) # print "finished adding userid"
def _append_allow_curve_key(self, publickey): entry = AuthEntry(credentials="CURVE:{}".format(publickey)) authfile = AuthFile(self.volttron_home + "/auth.json") authfile.add(entry)
def _main(): """ Routine for configuring an insalled volttron instance. The function interactively sets up the instance for working with volttron central and the discovery service. """ volttron_home = _os.path.normpath(expandall( _os.environ.get('VOLTTRON_HOME', '~/.volttron'))) _os.environ['VOLTTRON_HOME'] = volttron_home if not _os.path.exists(volttron_home): _os.makedirs(volttron_home, 0o755) y_or_n = ('Y', 'N', 'y', 'n') y = ('Y', 'y') n = ('N', 'n') print('\nYour VOLTTRON_HOME currently set to: {}'.format(volttron_home)) t = ('\nIs this the volttron you are attempting to setup? [Y]', y_or_n, 'Y') if not prompt_response(t) in y: print( '\nPlease execute with VOLTRON_HOME=/your/path volttron-cfg to ' 'modify VOLTTRON_HOME.\n') return t = ('\nIs this instance discoverable (Y/N)? [N] ', y_or_n, 'N') _explain_discoverable() is_discoverable = prompt_response(t) in y if is_discoverable: t = ('\nWhat is the external ipv4 address for this instance? ' '[127.0.0.1]: ', None, '127.0.0.1') external_ip = prompt_response(t) t = ('What is the vip port this instance? [22916] ',) vip_port = prompt_response(t) if not vip_port: vip_port = 22916 t = ('\nWhat is the port for discovery? [8080] ',) external_port = prompt_response(t) if not external_port: external_port = 8080 t = ( '\nWhich IP addresses are allowed to discover this instance? ' '[/127.*/] ', None, '/127.*/') ip_allowed_to_discover = prompt_response(t) AuthFile().add(AuthEntry(address=ip_allowed_to_discover, credentials='/CURVE:.*/')) t = ('\nIs this instance a volttron central (Y/N)? [N] ', y_or_n, 'N') do_install_vc = prompt_response(t) in y do_vc_autostart = True do_platform_autostart = True if do_install_vc: t = ('\nShould volttron central autostart(Y/N)? [Y] ', y_or_n, 'Y') do_vc_autostart = prompt_response(t) in y t = ('\nInclude volttron central platform agent on ' 'volttron central? [Y]', y_or_n, 'Y') do_install_platform = prompt_response(t) in y else: do_install_platform = True t = ('\nAddress of volttron central? [127.0.0.1]: ', None, '127.0.0.1') vc_ipaddress = prompt_response(t) should_resolve = True first = True t = ('Port of volttron central? [8080] ',) vc_port = prompt_response(t) if not vc_port: vc_port = 8080 while not _resolvable(vc_ipaddress, vc_port) and should_resolve: print("Couldn't resolve {}:{}".format(vc_ipaddress, vc_port)) t2 = ( '\nShould volttron central be resolvable now? [Y] ', y_or_n, 'Y') if first: should_resolve = prompt_response(t2) in ('y', 'Y') first = False if should_resolve: t = ('\nAddress of volttron central? ',) vc_ipaddress = prompt_response(t) t = ('\nPort of volttron central? ',) vc_port = prompt_response(t) if do_install_platform: t = ('\nShould platform agent autostart(Y/N)? [Y] ', y_or_n, 'Y') do_platform_autostart = prompt_response(t) in y external_uri = "tcp://{}:{}".format(external_ip, vip_port) bind_web_address = "http://{}:{}".format(external_ip, external_port) try: vc_web_address = "http://{}:{}".format(vc_ipaddress, vc_port) _make_configuration(external_uri, bind_web_address, vc_web_address) # if vc_ipaddres isn't defined # only happens on volttron central. except UnboundLocalError: _make_configuration(external_uri, bind_web_address) t = ('\nShould install sqlite platform historian? [N]', y_or_n, n) do_install_platform_historian = prompt_response(t) in y do_historian_autostart = True if do_install_platform_historian: t = ('\nShould historian agent autostart(Y/N)? [Y] ', y_or_n, 'Y') do_historian_autostart = prompt_response(t) in y # in order to install agents we need to start the platform. _start_platform() _install_agents((do_install_vc, do_vc_autostart), (do_install_platform, do_platform_autostart), (do_install_platform_historian, do_historian_autostart)) _shutdown_platform() print('Finished configuration\n') print('You can now start you volttron instance.\n') print('If you need to change the instance configuration you can edit') print('the config file at {}/{}\n'.format(volttron_home, 'config'))
def _periodic_attempt_registration(self): _log.debug("periodic attempt to register.") if self._scheduled_connection_event is not None: # This won't hurt anything if we are canceling ourselves. self._scheduled_connection_event.cancel() if not self.enable_registration: _log.debug('Registration of vcp is not enabled.') next_update_time = self._next_update_time() self._scheduled_connection_event = self.core.schedule( next_update_time, self._periodic_attempt_registration) return try: vc = self.get_vc_connection() if vc is None: _log.debug("vc not connected") return local_address = self.current_config.get( 'local_external_addresses')[0] if not vc.call("is_registered", address=local_address): _log.debug("platform agent is not registered.") self.registration_state = RegistrationStates.NotRegistered if self.registration_state == RegistrationStates.NotRegistered: vc_agent_publickey = vc.call("get_publickey") _log.debug( 'vc agent publickey is {}'.format(vc_agent_publickey)) assert vc_agent_publickey and len(vc_agent_publickey) == 43 authfile = AuthFile() # find_by_credentials returns a list. entries = authfile.find_by_credentials(vc_agent_publickey) if entries is not None and len(entries) > 0: entry = entries[0] if "manage" not in entry.capabilities: _log.debug("Updating vc capability.") entry.add_capabilities("manager") authfile.add(entry, overwrite=True) else: _log.debug('Adding vc publickey to auth') entry = AuthEntry(credentials=vc_agent_publickey, capabilities=['manager'], comments="Added by VCP", user_id="vc") authfile = AuthFile() authfile.add(entry) local_address = self.current_config.get( 'local_external_addresses')[0] local_name = self.current_config.get('local_instance_name', local_address) local_serverkey = self.current_config.get('local_serverkey') vc_address = self.current_config.get( 'volttron_central_address') _log.debug("Registering with vc from vcp.") _log.debug("Instance is named: {}".format(local_name)) _log.debug("Local Address is: {}".format(local_address)) _log.debug("VC Address is: {}".format(vc_address)) vc.call('register_instance', address=local_address, display_name=local_name, vcpserverkey=local_serverkey, vcpagentkey=self.core.publickey) else: _log.debug("Current platform registration state: {}".format( self.registration_state)) except Unreachable as e: _log.error("Couldn't connect to volttron.central. {}".format( self.current_config.get('volttron_central_address'))) except ValueError as e: _log.error(e.message) except Exception as e: _log.error("Error: {}".format(e.args)) except gevent.Timeout as e: _log.error("timout occured connecting to remote platform.") finally: _log.debug('Scheduling next periodic call') next_update_time = self._next_update_time() self._scheduled_connection_event = self.core.schedule( next_update_time, self._periodic_attempt_registration)
def allow_all_connections(self): """ Add a CURVE:.* entry to the auth.json file. """ entry = AuthEntry(credentials="/CURVE:.*/") authfile = AuthFile(self.volttron_home+"/auth.json") authfile.add(entry)
def add_to_auth(volttron_home, publickey, capabilities=None): authfile = AuthFile(os.path.join(volttron_home, 'auth.json')) entry = AuthEntry(credentials=publickey, mechanism="CURVE", capabilities=capabilities) authfile.add(entry, overwrite=True)
def add_to_auth(volttron_home, publickey, capabilities=None): authfile = AuthFile(os.path.join(volttron_home, 'auth.json')) entry = AuthEntry( credentials=publickey, mechanism="CURVE", capabilities=capabilities ) authfile.add(entry, overwrite=True)