class EndPoint_Security: rethinkdb_host = 'rethinkdb.galahad.com' ca_cert = '/var/private/ssl/rethinkdb_cert.pem' excalibur_key_file = '/var/private/ssl/excalibur_private_key.pem' virtue_key_dir = os.environ['HOME'] + '/user-keys' wait_for_ack = 30 # seconds def __init__(self, user, password): self.inst = LDAP(user, password) dn = 'cn=admin,dc=canvas,dc=virtue,dc=com' self.inst.get_ldap_connection() self.inst.conn.simple_bind_s(dn, 'Test123!') self.rdb_manager = RethinkDbManager() self.conn = None def transducer_list(self): ''' Lists all transducers currently available in the system Args: Returns: list: Transducer objects for known transducers ''' try: transducers_raw = self.inst.get_objs_of_type('OpenLDAPtransducer') if transducers_raw is None: return self.__error( 'unspecifiedError', details='Unable to get transducer objects from LDAP') transducers_ret = [] for transducer in transducers_raw: ldap_tools.parse_ldap(transducer[1]) transducers_ret.append(transducer[1]) return json.dumps(transducers_ret) except Exception as e: return self.__error('unspecifiedError', details='Unable to get transducer objects: ' + str(e)) def transducer_get(self, transducerId): ''' Gets information about the indicated Transducer. Does not include information about any instantiation in Virtues. Args: transducerId (str): The ID of the Transducer to get. Returns: Transducer: information about the indicated transducer ''' try: transducer = self.inst.get_obj('cid', transducerId, 'openLDAPtransducer', True) if transducer is None or transducer == (): return self.__error('invalidTransducerId') ldap_tools.parse_ldap(transducer) if DEBUG_PERMISSIONS: return json.dumps(transducer) return json.dumps(transducer) except Exception as e: return self.__error('unspecifiedError', details='Unable to get transducer object: ' + str(e)) def transducer_enable(self, transducerId, virtueId, configuration): ''' Enables the indicated Transducer in the indicated Virtue. Args: transducerId (str) : The ID of the Transducer to enable. virtueId (str) : The ID of the Virtue in which to enable the Transducer. configuration (object): The configuration to apply to the Transducer when it is enabled. Format is Transducer-specific. This overrides any existing configuration with the same keys. Returns: bool: True if the transducer was enabled, false otherwise ''' ret = self.__enable_disable(transducerId, virtueId, configuration, True) if type(ret) is bool and ret == True: return self.__error('success') return ret def transducer_disable(self, transducerId, virtueId): ''' Disables the indicated Transducer in the indicated Virtue Args: transducerId (str) : The ID of the Transducer to disable virtueId (str) : The ID of the Virtue in which to enable the Transducer. Returns: bool: True if the transducer was enabled, false otherwise ''' ret = self.__enable_disable(transducerId, virtueId, None, False) if type(ret) is bool and ret == True: return self.__error('success') return ret def transducer_get_enabled(self, transducerId, virtueId): ''' Gets the current enabled status for the indicated Transducer in the indicated Virtue. Args: transducerId (str) : The ID of the Transducer virtueId (str) : The ID of the Virtue in which to enable the Transducer. Returns: bool: True if the Transducer is enabled in the Virtue, false if it is not ''' if self.conn is None: self.__connect_rethinkdb() try: row = r.db('transducers').table('acks')\ .get([virtueId, transducerId]).run(self.conn) except r.ReqlError as e: return self.__error( 'unspecifiedError', details='Failed to get info about transducer: ' + str(e)) # Transducers are enabled by default if row is None: return json.dumps({'enabled': True}) verified = self.__verify_message(row) if (verified != True): return verified return json.dumps({'enabled': row['enabled']}) def transducer_get_configuration(self, transducerId, virtueId): ''' Gets the current configuration for the indicated Transducer in the indicated Virtue. Args: transducerId (str) : The ID of the Transducer virtueId (str) : The ID of the Virtue in which to enable the Transducer. Returns: TransducerConfig: Configuration information for the indicated Transducer in the indicated Virtue ''' if self.conn is None: self.__connect_rethinkdb() try: row = r.db('transducers').table('acks')\ .get([virtueId, transducerId]).run(self.conn) except r.ReqlError as e: return self.__error( 'unspecifiedError', details='Failed to get info about transducer: ' + str(e)) # If there's no row, there's no set config if row is None: return json.dumps(None) verified = self.__verify_message(row) if (verified != True): return verified # By definition, the configuration is a JSON object return row['configuration'] def transducer_list_enabled(self, virtueId): ''' Lists all Transducers currently that are currently enabled in the indicated Virtue. Args: virtueId (str) : The ID of the Virtue in which to enable the Transducer. Returns: list(Transducer): List of enabled transducers within the specified Virtue ''' enabled_transducers = [] if self.conn is None: self.__connect_rethinkdb() try: for row in r.db('transducers').table('acks')\ .filter({'virtue_id': virtueId}).run(self.conn): verified = self.__verify_message(row) if (verified != True): return verified if ('enabled' in row) and row['enabled']: enabled_transducers.append(row['transducer_id']) except r.ReqlError as e: return self.__error('unspecifiedError', details='Failed to get enabled transducers: ' + str(e)) return json.dumps(enabled_transducers) def transducer_all_virtues(self, transducerId, configuration, isEnabled): ''' Sets the given transducer to true for all running and stopped virtues. Updates roles and starting configuration to match, so additionally created or started virtues will share the same config. :param transducerId: :param isEnabled::o :return: ''' try: ret = self.__update_roles_transducer(transducerId, isEnabled) if ret != True: return ret ret = self.__update_transducer_starting_config( transducerId, configuration) if ret != True: return ret virtues_raw = self.inst.get_objs_of_type('OpenLDAPvirtue') if (virtues_raw == None): return json.dumps(ErrorCodes.user['unspecifiedError']) for virtue in virtues_raw: ldap_tools.parse_ldap(virtue[1]) ret = self.__enable_disable(transducerId, virtue[1]['id'], configuration, isEnabled) if (ret != True): return json.dumps(ret) return self.__error('success') except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def __connect_rethinkdb(self): # RethinkDB connection # This connection will fail if setup_rethinkdb.py hasn't been run, because # there won't be an excalibur user and it won't have the specified password. with open(self.excalibur_key_file, 'r') as f: key = f.read() self.excalibur_key = RSA.importKey(key) try: self.conn = r.connect(host=self.rethinkdb_host, user='******', password=key, ssl={'ca_certs': self.ca_cert}) except r.ReqlDriverError as e: return self.__error('unspecifiedError', details=\ 'Failed to connect to RethinkDB at host: ' + \ self.rethinkdb_host + ' because: ' + str(e)) return True def __enable_disable(self, transducerId, virtueId, configuration, isEnable): # Make sure transducer exists transducer = self.inst.get_obj('cid', transducerId, 'openLDAPtransducer', True) if transducer is None or transducer == (): return self.__error('invalidTransducerId') ldap_tools.parse_ldap(transducer) # Make sure virtue exists virtue = self.inst.get_obj('cid', virtueId, 'openLDAPvirtue', True) if virtue is None or virtue == (): return self.__error('invalidVirtueId') ldap_tools.parse_ldap(virtue) virtue_running = (virtue['state'] == 'RUNNING') # Change the ruleset ret = self.__change_ruleset(virtueId, transducerId, transducer['type'], isEnable, virtue_running, config=configuration) if ret != True: return ret if virtue_running: # Update the virtue's list of transducers # Call loads because transducer_list_enabled returns a string new_t_list = json.loads(self.transducer_list_enabled(virtueId)) if type(new_t_list) is dict and new_t_list['status'] == 'failed': # Couldn't retrieve new list of transducers return self.__error( 'unspecifiedError', details='Unable to update virtue\'s list of transducers') virtue['transducerIds'] = new_t_list ret = self.inst.modify_obj( 'cid', virtueId, ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'), 'OpenLDAPvirtue', True) else: # Update list of transducers in LDAP without syncing it # with rethink (Non-running virutes are not in rethink) new_t_list = virtue['transducerIds'] if isEnable: if transducerId not in new_t_list: new_t_list.append(transducerId) else: if transducerId in new_t_list: new_t_list.remove(transducerId) virtue['transducerIds'] = new_t_list ret = self.inst.modify_obj( 'cid', virtueId, ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'), 'OpenLDAPvirtue', True) if ret != 0: return self.__error( 'unspecifiedError', details='Unable to update virtue\'s list of transducers') return True def __sign_message(self, row): required_keys = [ 'virtue_id', 'transducer_id', 'type', 'configuration', 'enabled', 'timestamp' ] if not all([(key in row) for key in required_keys]): return (False, self.__error('unspecifiedError', details='Missing required keys in row: ' +\ str(filter((lambda key: key not in row), required_keys)))) message = '|'.join([ row['virtue_id'], row['transducer_id'], row['type'], str(row['configuration']), str(row['enabled']), str(row['timestamp']) ]) h = SHA.new(str(message)) signer = PKCS1_v1_5.new(self.excalibur_key) signature = signer.sign(h) return (True, signature) def __verify_message(self, row): if row is None: return self.__error('unspecifiedError', details='No match found in database') required_keys = [ 'virtue_id', 'transducer_id', 'type', 'configuration', 'enabled', 'timestamp', 'signature' ] if not all([(key in row) for key in required_keys]): return self.__error('unspecifiedError', details='Missing required keys in row: ' +\ str(filter((lambda key: key not in row), required_keys))) message = '|'.join([ row['virtue_id'], row['transducer_id'], row['type'], str(row['configuration']), str(row['enabled']), str(row['timestamp']) ]) virtue_public_key = os.path.join(self.virtue_key_dir, row['virtue_id'] + '.pem.pub') if not os.path.isfile(virtue_public_key): return self.__error('invalidOrMissingParameters', details=\ 'No file found for Virtue public key at: ' + \ virtue_public_key) with open(virtue_public_key) as f: virtue_key = RSA.importKey(f.read()) h = SHA.new(str(message)) verifier = PKCS1_v1_5.new(virtue_key) verified = verifier.verify(h, row['signature']) if not verified: printable_msg = deepcopy(row) del printable_msg['signature'] return self.__error('unspecifiedError', details=\ 'Unable to validate signature of ACK message: ' + \ json.dumps(printable_msg, indent=2)) return True def __change_ruleset(self, virtue_id, trans_id, transducer_type, enable, virtue_running, config=None): if self.conn is None: ret = self.__connect_rethinkdb() # Return if error if ret != True: return ret if type(transducer_type) is list: transducer_type = transducer_type[0] timestamp = int(time.time()) row = { 'id': [virtue_id, trans_id], 'virtue_id': virtue_id, 'transducer_id': trans_id, 'type': transducer_type, 'configuration': config, 'enabled': enable, 'timestamp': timestamp } (success, signature) = self.__sign_message(row) if not success: # Return error code return signature row['signature'] = r.binary(signature) # Send command to change ruleset try: res = r.db('transducers').table('commands')\ .insert(row, conflict='replace').run(self.conn) if res['errors'] > 0: return self.__error( 'unspecifiedError', details= 'Failed to insert into commands table; first error: ' + res['first_error']) except r.ReqlError as e: return self.__error( 'unspecifiedError', details='Failed to insert into commands table: ' + str(e)) # If the virtue isn't running yet, don't bother waiting for an ACK if not virtue_running: return True # Wait for ACK from the virtue that the ruleset has been changed # try: cursor = r.db('transducers').table('acks')\ .get([virtue_id, trans_id])\ .changes(squash=False).run(self.conn) # except r.ReqlError as e: # print 'ERROR: Failed to read from the ACKs table because:', e # return False retry = True while retry: try: retry = False # Wait max 30 seconds - if we miss the real ACK, hopefully # at least the next heartbeat will suffice print 'INFO: Waiting for ACK' change = cursor.next(wait=self.wait_for_ack) row = change['new_val'] verified = self.__verify_message(row) if (verified != True): return verified if row['timestamp'] >= timestamp: if row['enabled'] == enable: print 'INFO: ACK received!' return True else: return self.__error( 'unspecifiedError', details= 'Received ACK with incorrect value for enabled: ' + str(enable) + ' vs ' + str(row['enabled'])) else: print 'WARN: Timestamp incorrect:', timestamp, row[ 'timestamp'] # Retry once in case that was just a wayward ACK retry = True except (r.ReqlCursorEmpty, r.ReqlDriverError) as e: return self.__error( 'unspecifiedError', details='Failed to receive ACK before timeout') finally: cursor.close() return self.__error('unspecifiedError', details='Failed to receive ACK before timeout') def __error(self, key, details=None): if key not in ErrorCodes.security: print "Error type '" + key + "' not found! Using UnspecifiedError." key = 'unspecifiedError' e = deepcopy(ErrorCodes.security[key]) if details is not None: # e['details'] = details e['result'].append(details) return json.dumps(e) def __update_roles_transducer(self, transducerId, isEnabled): # Make sure transducer exists transducer = self.inst.get_obj('cid', transducerId, 'openLDAPtransducer', True) if transducer is None or transducer == (): return self.__error('invalidTransducerId') try: ldap_roles = self.inst.get_objs_of_type('OpenLDAProle') assert ldap_roles != None roles = ldap_tools.parse_ldap_list(ldap_roles) for role in roles: new_t_list = role['startingTransducerIds'] if isEnabled: if transducerId not in new_t_list: new_t_list.append(transducerId) else: if transducerId in new_t_list: new_t_list.remove(transducerId) role['startingTransducerIds'] = new_t_list ret = self.inst.modify_obj( 'cid', role['id'], ldap_tools.to_ldap(role, 'OpenLDAProle'), 'OpenLDAProle', True) if ret != 0: return self.__error( 'unspecifiedError', details='Unable to update virtue\'s list of transducers' ) return True except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def __update_transducer_starting_config(self, transducerId, configuration): transducer = self.inst.get_obj('cid', transducerId, 'openLDAPtransducer', True) if transducer is None or transducer == (): return self.__error('invalidTransducerId') ldap_tools.parse_ldap(transducer) transducer['startingConfiguration'] = configuration ret = self.inst.modify_obj( 'cid', transducer['id'], ldap_tools.to_ldap(transducer, 'openLDAPtransducer'), 'openLDAPtransducer', True) if ret != 0: return self.__error( 'unspecifiedError', details='Unable to update virtue\'s list of transducers') return True
class EndPoint(): def __init__(self, user, password): self.inst = LDAP(user, password) self.inst.bind_ldap() # Retrieve information about the specified application def application_get(self, username, applicationId): try: if (not DEBUG_PERMISSIONS): user = self.inst.get_obj('cusername', username, 'openLDAPuser') if (user == None or user == ()): # User was already validated, but can't be accessed now... return json.dumps(ErrorCodes.user['unspecifiedError']) ldap_tools.parse_ldap(user) app = self.inst.get_obj('cid', applicationId, 'openLDAPapplication', True) if (app == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(app) if (DEBUG_PERMISSIONS): return json.dumps(app) for roleId in user['authorizedRoleIds']: role = self.inst.get_obj('cid', roleId, 'openLDAProle') if (role == None or role == ()): # Error! continue ldap_tools.parse_ldap(role) if (applicationId in role['applicationIds']): # User is authorized to access this application. return json.dumps(app) return json.dumps(ErrorCodes.user['userNotAuthorized']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Retrieve data about the specified role def role_get(self, username, roleId): try: if (not DEBUG_PERMISSIONS): user = self.inst.get_obj('cusername', username, 'openLDAPuser') if (user == None or user == ()): return json.dumps(ErrorCodes.user['unspecifiedError']) ldap_tools.parse_ldap(user) role = self.inst.get_obj('cid', roleId, 'openLDAProle', True) if (role == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(role) if (DEBUG_PERMISSIONS or roleId in user['authorizedRoleIds']): # Now get the IP address of the virtue associated with this user/role virtue_ip = 'NULL' ldap_virtues = self.inst.get_objs_of_type('OpenLDAPvirtue') virtues = ldap_tools.parse_ldap_list(ldap_virtues) for v in virtues: if (v['username'] == username and v['roleId'] == roleId): virtue_ip = v['ipAddress'] break role['ipAddress'] = virtue_ip return json.dumps(role) return json.dumps(ErrorCodes.user['userNotAuthorized']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Retrieve a list of roles available to user def user_role_list(self, username): try: user = self.inst.get_obj('cusername', username, 'openLDAPuser') if (user == None or user == ()): return json.dumps(ErrorCodes.user['unspecifiedError']) ldap_tools.parse_ldap(user) roles = [] ldap_virtues = self.inst.get_objs_of_type('OpenLDAPvirtue') virtues = ldap_tools.parse_ldap_list(ldap_virtues) for roleId in user['authorizedRoleIds']: role = self.inst.get_obj('cid', roleId, 'openLDAProle') if (role == None or role == ()): continue ldap_tools.parse_ldap(role) # Now get the IP address of the virtue associated with this user/role virtue_ip = 'NULL' for v in virtues: if (v['username'] == username and v['roleId'] == roleId): virtue_ip = v['ipAddress'] break role['ipAddress'] = virtue_ip roles.append(role) return json.dumps(roles) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Retrieve a list of virtues available to user def user_virtue_list(self, username): try: virtues_raw = self.inst.get_objs_of_type('OpenLDAPvirtue') if (virtues_raw == None): return json.dumps(ErrorCodes.user['unspecifiedError']) virtues_ret = [] for virtue in virtues_raw: ldap_tools.parse_ldap(virtue[1]) if (virtue[1]['username'] == username): virtues_ret.append(virtue[1]) return json.dumps(virtues_ret) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Retrieve information about the specified virtue def virtue_get(self, username, virtueId): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(virtue) if (virtue['username'] == username or DEBUG_PERMISSIONS): return json.dumps(virtue) return json.dumps(ErrorCodes.user['userNotAuthorized']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def virtue_reload_state(self, username, virtueId): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(virtue) updated_virtue = copy.deepcopy(virtue) if (virtue['username'] != username): return json.dumps(ErrorCodes.user['userNotAuthorized']) rdb_manager = RethinkDbManager() rdb_virtue = rdb_manager.get_virtue(virtueId) if (rdb_virtue == None): updated_virtue['state'] = 'STOPPED' updated_virtue['ipAddress'] = 'NULL' else: updated_virtue['state'] = 'RUNNING' updated_virtue['ipAddress'] = rdb_virtue['guestnet'] if (updated_virtue != virtue): ldap_virtue = ldap_tools.to_ldap(updated_virtue, 'OpenLDAPvirtue') self.inst.modify_obj('cid', virtueId, ldap_virtue, 'OpenLDAPvirtue', True) return json.dumps(ErrorCodes.user['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Launch the specified virtue, which must have already been created def virtue_launch(self, username, virtueId, use_valor=True): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(virtue) if (virtue['username'] != username): return json.dumps(ErrorCodes.user['userNotAuthorized']) if ('RUNNING' in virtue['state'] or virtue['state'] == 'LAUNCHING'): return json.dumps(ErrorCodes.user['virtueAlreadyLaunched']) elif (virtue['state'] != 'STOPPED'): return json.dumps( ErrorCodes.user['virtueStateCannotBeLaunched']) if (use_valor): valor_manager = ValorManager() valor = valor_manager.get_empty_valor() try: virtue['ipAddress'] = valor_manager.add_virtue( valor['address'], valor['valor_id'], virtue['id'], 'images/provisioned_virtues/' + virtue['id'] + '.img') except AssertionError: return json.dumps(ErrorCodes.user['virtueAlreadyLaunched']) virtue['state'] = 'LAUNCHING' ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') self.inst.modify_obj('cid', virtue['id'], ldap_virtue, objectClass='OpenLDAPvirtue', throw_error=True) # wait until sshable success = False max_attempts = 5 # TODO: Remove this variable before merging into master see_no_evil = False if not use_valor: virtue['state'] = 'RUNNING' elif see_no_evil: virtue['state'] = 'RUNNING (Unverified)' else: for attempt_number in range(max_attempts): try: time.sleep(30) ssh = ssh_tool( 'virtue', virtue['ipAddress'], os.environ['HOME'] + '/user-keys/default-virtue-key.pem') ssh.ssh('echo test') print('Successfully connected to {}'.format( virtue['ipAddress'])) # KL --- add if resIDs not empty run: # Kerberos tgt setup for resource management if len(virtue['resourceIds']) is not 0: krb5cc_src = '/tmp/krb5cc_{}'.format(username) krb5cc_dest = '/tmp/krb5cc_0' ssh.scp_to(krb5cc_src, krb5cc_dest) role = self.inst.get_obj('cid', virtue['roleId'], 'openLDAProle') ldap_tools.parse_ldap(role) for res in virtue['resourceIds']: resource = self.inst.get_obj('cid', res, 'openLDAPresource') ldap_tools.parse_ldap(resource) resource_manager = ResourceManager(username, resource) getattr(resource_manager, resource['type'].lower())( virtue['ipAddress'], os.environ['HOME'] + '/user-keys/default-virtue-key.pem', role['applicationIds']) success = True break except Exception as e: print(e) print('Attempt {0} failed to connect').format(attempt_number+1) if (not success): valor_manager.rethinkdb_manager.remove_virtue(virtue['id']) virtue['state'] = 'STOPPED' ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') self.inst.modify_obj( 'cid', virtue['id'], ldap_virtue, objectClass='OpenLDAPvirtue', throw_error=True) return json.dumps(ErrorCodes.user['serverLaunchError']) virtue['state'] = 'RUNNING' ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') self.inst.modify_obj('cid', virtue['id'], ldap_virtue, objectClass='OpenLDAPvirtue', throw_error=True) return json.dumps(ErrorCodes.user['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Stop the specified virtue, but do not destroy it def virtue_stop(self, username, virtueId, use_valor=True): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(virtue) if (virtue['username'] != username): return json.dumps(ErrorCodes.user['userNotAuthorized']) if (virtue['state'] == 'STOPPED'): return json.dumps(ErrorCodes.user['virtueAlreadyStopped']) elif ('RUNNING' not in virtue['state']): return json.dumps( ErrorCodes.user['virtueStateCannotBeStopped']) try: if (use_valor): if len(virtue['resourceIds']) is not 0: role = self.inst.get_obj('cid', virtue['roleId'], 'openLDAProle') ldap_tools.parse_ldap(role) for res in virtue['resourceIds']: resource = self.inst.get_obj('cid', res, 'openLDAPresource') ldap_tools.parse_ldap(resource) resource_manager = ResourceManager(username, resource) call = 'remove_' + resource['type'].lower() getattr(resource_manager, call)( virtue['ipAddress'], os.environ['HOME'] + '/user-keys/default-virtue-key.pem', role['applicationIds']) ssh = ssh_tool('virtue', virtue['ipAddress'], os.environ['HOME'] + \ '/user-keys/default-virtue-key.pem') ssh.ssh('sudo rm /tmp/krb5cc_0') rdb_manager = RethinkDbManager() rdb_manager.remove_virtue(virtue['id']) except AssertionError: return json.dumps(ErrorCodes.user['serverStopError']) except: print('Error:\n{}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) else: virtue['state'] = 'STOPPED' ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') self.inst.modify_obj( 'cid', virtue['id'], ldap_virtue, objectClass='OpenLDAPvirtue', throw_error=True) return json.dumps(ErrorCodes.user['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Launch an application on the specified virtue def virtue_application_launch(self, username, virtueId, applicationId, use_ssh=True): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidVirtueId']) ldap_tools.parse_ldap(virtue) if (virtue['username'] != username): return json.dumps(ErrorCodes.user['userNotAuthorized']) if ('RUNNING' not in virtue['state'] and use_ssh): return json.dumps(ErrorCodes.user['virtueNotRunning']) app = self.inst.get_obj('cid', applicationId, 'OpenLDAPapplication', True) if (app == ()): return json.dumps(ErrorCodes.user['invalidApplicationId']) ldap_tools.parse_ldap(app) role = self.inst.get_obj('cid', virtue['roleId'], 'OpenLDAProle') if (role == None or role == ()): return json.dumps(ErrorCodes.user['unspecifiedError']) ldap_tools.parse_ldap(role) if (app['id'] not in role['applicationIds']): return json.dumps(ErrorCodes.user['applicationNotInVirtue']) if (app['id'] in virtue['applicationIds']): return json.dumps( ErrorCodes.user['applicationAlreadyLaunched']) if (use_ssh): ssh = ssh_tool('virtue', virtue['ipAddress'], '{0}/user-keys/{1}.pem'.format( os.environ['HOME'], username)) start_docker_container = ('sudo docker start $(sudo docker ps' + ' -af name="{0}" -q)').format( app['id']) # Copy the network Rules file. copy_network_rules = ( 'sudo docker cp /etc/networkRules $(sudo docker ps -af' + ' name="{0}" -q):/etc/networkRules').format(app['id']) ssh.ssh(copy_network_rules) # Copy the authorized keys file auth_keys_path = '/home/virtue/.ssh/authorized_keys' copy_authorized_keys_cmd = ( 'sudo docker cp {0}' ' {1}:{0}').format(auth_keys_path, app['id']) ssh.ssh(copy_authorized_keys_cmd) docker_exit = ssh.ssh(start_docker_container, test=False, silent=True) if (docker_exit != 0): # This is an issue with docker where if the docker daemon exits # uncleanly then a system file is locked and docker start fails # with the error: # Error response from daemon: id already in use # Error: failed to start containers: # The current workaround is to issue the docker start command # twice. Tne first time it fails with the above error and the # second time it succeeds. docker_exit = ssh.ssh(start_docker_container, test=False) if (docker_exit != 0): print( "Docker start command for launching application {} Failed".format( app['id'])) return json.dumps(ErrorCodes.user['serverLaunchError']) docker_chown_cmd = ( 'sudo docker exec {0}' ' chown virtue:virtue {1}').format( app['id'], auth_keys_path) ssh.ssh(docker_chown_cmd) docker_chmod_cmd = ( 'sudo docker exec {0}' ' which chmod 600 {1}').format(app['id'], auth_keys_path) ssh.ssh(docker_chmod_cmd) virtue['applicationIds'].append(applicationId) del virtue['state'] del virtue['ipAddress'] ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') assert 0 == self.inst.modify_obj('cid', virtue['id'], ldap_virtue, objectClass='OpenLDAPvirtue', throw_error=True) return json.dumps(ErrorCodes.user['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Stop an application on the specified virtue def virtue_application_stop(self, username, virtueId, applicationId, use_ssh=True): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidVirtueId']) ldap_tools.parse_ldap(virtue) if (virtue['username'] != username): return json.dumps(ErrorCodes.user['userNotAuthorized']) if ('RUNNING' not in virtue['state'] and use_ssh): return json.dumps(ErrorCodes.user['virtueNotRunning']) app = self.inst.get_obj('cid', applicationId, 'OpenLDAPapplication', True) if (app == ()): return json.dumps(ErrorCodes.user['invalidApplicationId']) ldap_tools.parse_ldap(app) role = self.inst.get_obj('cid', virtue['roleId'], 'OpenLDAProle') if (role == None or role == ()): return json.dumps(ErrorCodes.user['unspecifiedError']) ldap_tools.parse_ldap(role) if (app['id'] not in role['applicationIds']): return json.dumps(ErrorCodes.user['applicationNotInVirtue']) if (app['id'] not in virtue['applicationIds']): return json.dumps(ErrorCodes.user['applicationAlreadyStopped']) if (use_ssh): ssh = ssh_tool('virtue', virtue['ipAddress'], '{0}/user-keys/{1}.pem'.format( os.environ['HOME'], username)) docker_exit = ssh.ssh(('sudo docker stop $(sudo docker ps -af ' 'name="{0}" -q)').format(app['id']), test=False) if (docker_exit != 0): return json.dumps(ErrorCodes.user['serverStopError']) virtue['applicationIds'].remove(applicationId) del virtue['state'] del virtue['ipAddress'] ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') assert 0 == self.inst.modify_obj('cid', virtue['id'], ldap_virtue, objectClass='OpenLDAPvirtue', throw_error=True) return json.dumps(ErrorCodes.user['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def key_get(self, username): try: with open('{0}/user-keys/{1}.pem'.format( os.environ['HOME'], username), 'r') as keyfile: data = keyfile.read() return json.dumps(data) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError'])
class CreateVirtueThread(threading.Thread): def __init__(self, ldap_user, ldap_password, role_id, user, virtue_id, role=None): super(CreateVirtueThread, self).__init__() self.inst = LDAP(ldap_user, ldap_password) self.role_id = role_id self.role = role self.username = user self.virtue_id = virtue_id def run(self): thread_list.append(self) # Going to need to write to LDAP # self.inst.bind_ldap() dn = 'cn=admin,dc=canvas,dc=virtue,dc=com' self.inst.get_ldap_connection() self.inst.conn.simple_bind_s(dn, 'Test123!') if (self.role == None): role = self.inst.get_obj('cid', self.role_id, objectClass='OpenLDAProle') if (role == () or role == None): return ldap_tools.parse_ldap(role) else: role = self.role virtue = { 'id': self.virtue_id, 'username': self.username, 'roleId': self.role_id, 'applicationIds': [], 'resourceIds': role['startingResourceIds'], 'transducerIds': role['startingTransducerIds'], 'networkRules': role['networkRules'], 'state': 'CREATING', 'ipAddress': 'NULL' } ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') assert self.inst.add_obj(ldap_virtue, 'virtues', 'cid') == 0 virtue_path = 'images/provisioned_virtues/' + virtue['id'] + '.img' try: # For now generate keys and store in local dir subprocess.check_output( shlex.split(('ssh-keygen -t rsa -f {0}/{1}.pem -C' ' "Virtue Key for {1}" -N ""').format( USER_KEY_DIR, virtue['id']))) with open(USER_KEY_DIR + '/' + virtue['id'] + '.pem', 'r') as virtue_key_file: virtue_key = virtue_key_file.read().strip() with open(GALAHAD_KEY_DIR + '/excalibur_pub.pem', 'r') as excalibur_key_file: excalibur_key = excalibur_key_file.read().strip() user_key = subprocess.check_output([ 'ssh-keygen', '-y', '-f', '{0}/{1}.pem'.format(USER_KEY_DIR, self.username) ]) with open(GALAHAD_KEY_DIR + '/rethinkdb_cert.pem', 'r') as rdb_cert_file: rdb_cert = rdb_cert_file.read().strip() with open('/tmp/networkRules-' + virtue['id'], 'w+') as iprules_file: for rule in role['networkRules']: iprules_file.write(rule + '\n') # Create the virtue standby image files standby_virtues = StandbyVirtues(self.role_id) standby_virtues.create_virtue_image_file(self.virtue_id) subprocess.check_call(['sudo', 'python', os.environ['HOME'] + '/galahad/excalibur/' + \ 'call_provisioner.py', '-u', self.username, '-i', virtue['id'], '-n', '/tmp/networkRules-' + virtue['id'], '-b', '/mnt/efs/images/non_provisioned_virtues/' + role['id'] + '.img', '-o', '/mnt/efs/' + virtue_path, '-v', virtue_key, '-e', excalibur_key, '--user_key', user_key, '-r', rdb_cert]) # Enable/disable sensors as specified by the role transducers = {} for tid in role['startingTransducerIds']: transducer = self.inst.get_obj('cid', tid, 'OpenLDAPtransducer', True) if (transducer == ()): continue ldap_tools.parse_ldap(transducer) transducers[tid] = transducer eps = EndPoint_Security(self.inst.email, self.inst.password) all_transducers = json.loads(eps.transducer_list()) for transducer in all_transducers: if transducer['id'] in transducers: config = transducer['startingConfiguration'] ret = eps.transducer_enable(transducer['id'], self.virtue_id, config) else: ret = eps.transducer_disable(transducer['id'], self.virtue_id) virtue['state'] = 'STOPPED' ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') assert self.inst.modify_obj('cid', virtue['id'], ldap_virtue) == 0 except: print('Error while creating Virtue for role {0}:\n{1}'.format( role['id'], traceback.format_exc())) assert self.inst.del_obj('cid', virtue['id'], objectClass='OpenLDAPvirtue', throw_error=True) == 0 os.remove('{0}/{1}.pem'.format(USER_KEY_DIR, virtue['id'])) os.remove('{0}/{1}.pem.pub'.format(USER_KEY_DIR, virtue['id'])) os.remove('/mnt/efs/' + virtue_path)
class EndPoint_Admin(): def __init__(self, user, password): self.inst = LDAP(user, password) self.inst.bind_ldap() self.valor_api = ValorAPI() self.rdb_manager = RethinkDbManager() def application_list(self): try: ldap_applications = self.inst.get_objs_of_type( 'OpenLDAPapplication') assert ldap_applications != None applications = ldap_tools.parse_ldap_list(ldap_applications) return json.dumps(applications) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def resource_create(self, resource): try: resource_keys = ['credentials', 'type', 'unc'] if (set(resource.keys()) != set(resource_keys) and set(resource.keys()) != set(resource_keys + ['id'])): return json.dumps(ErrorCodes.admin['invalidFormat']) if (not isinstance(resource['credentials'], basestring) or not isinstance(resource['type'], basestring) or not isinstance(resource['unc'], basestring)): return json.dumps(ErrorCodes.admin['invalidFormat']) resource['id'] = 'Resource_{}_{}'.format(resource['type'], int(time.time())) ldap_resource = ldap_tools.to_ldap(resource, 'OpenLDAPresource') self.inst.add_obj(ldap_resource, 'resources', 'cid', throw_error=True) return json.dumps(resource) except Exception as e: return json.dumps(ErrorCodes.admin['unspecifiedError']) def resource_destroy(self, resourceId): try: resource = self.inst.get_obj('cid', resourceId, 'OpenLDAPresource', True) if (resource == ()): return json.dumps(ErrorCodes.admin['invalidId']) ldap_tools.parse_ldap(resource) # KL --- add check if in use ldap_virtues = self.inst.get_objs_of_type('OpenLDAPvirtue') virtues = ldap_tools.parse_ldap_list(ldap_virtues) if any(resourceId in virtue['resourceIds'] for virtue in virtues): return json.dumps(ErrorCodes.admin['virtueUsingResource']) self.inst.del_obj('cid', resource['id'], throw_error=True) except: print('Error while deleting {}:\n{}'.format( resource['id'], traceback.format_exc())) return json.dumps(ErrorCodes.user['serverDestroyError']) return json.dumps(ErrorCodes.admin['success']) def resource_get(self, resourceId): try: resource = self.inst.get_obj('cid', resourceId, objectClass='OpenLDAPresource', throw_error=True) if (resource == ()): return json.dumps(ErrorCodes.admin['invalidId']) ldap_tools.parse_ldap(resource) return json.dumps(resource) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def resource_list(self): try: ldap_resources = self.inst.get_objs_of_type('OpenLDAPresource') assert ldap_resources != None resources = ldap_tools.parse_ldap_list(ldap_resources) return json.dumps(resources) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def resource_attach(self, resourceId, virtueId): try: resource = self.inst.get_obj('cid', resourceId, objectClass='OpenLDAPresource', throw_error=True) if (resource == ()): return json.dumps(ErrorCodes.admin['invalidResourceId']) ldap_tools.parse_ldap(resource) virtue = self.inst.get_obj('cid', virtueId, objectClass='OpenLDAPvirtue', throw_error=True) if (virtue == ()): return json.dumps(ErrorCodes.admin['invalidVirtueId']) ldap_tools.parse_ldap(virtue) #if (virtue['state'] == 'DELETING'): # return json.dumps(ErrorCodes.admin['invalidVirtueState']) if (virtue['state'] != 'STOPPED'): return json.dumps(ErrorCodes.admin['invalidVirtueState']) if (resourceId in virtue['resourceIds']): return json.dumps(ErrorCodes.admin['cantAttach']) virtue['resourceIds'].append(resourceId) self.inst.modify_obj('cid', virtue['id'], ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'), objectClass='OpenLDAPvirtue', throw_error=True) #return json.dumps(ErrorCodes.admin['notImplemented']) return json.dumps(ErrorCodes.admin['success']) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def resource_detach(self, resourceId, virtueId): try: resource = self.inst.get_obj('cid', resourceId, objectClass='OpenLDAPresource', throw_error=True) if (resource == ()): return json.dumps(ErrorCodes.admin['invalidResourceId']) ldap_tools.parse_ldap(resource) virtue = self.inst.get_obj('cid', virtueId, objectClass='OpenLDAPvirtue', throw_error=True) if (virtue == ()): return json.dumps(ErrorCodes.admin['invalidVirtueId']) ldap_tools.parse_ldap(virtue) if (resourceId not in virtue['resourceIds']): return json.dumps(ErrorCodes.admin['cantDetach']) if (virtue['state'] != 'STOPPED'): return json.dumps(ErrorCodes.admin['invalidVirtueState']) virtue['resourceIds'].remove(resourceId) self.inst.modify_obj('cid', virtue['id'], ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'), objectClass='OpenLDAPvirtue', throw_error=True) #return json.dumps(ErrorCodes.admin['notImplemented']) return json.dumps(ErrorCodes.admin['success']) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def role_create(self, role, use_ssh=True, unity_img_name=None): try: role_keys = [ 'name', 'version', 'applicationIds', 'startingResourceIds', 'startingTransducerIds', 'networkRules' ] if (set(role.keys()) != set(role_keys) and set(role.keys()) != set(role_keys + ['id'])): return json.dumps(ErrorCodes.admin['invalidFormat']) if (not isinstance(role['name'], basestring) or not isinstance(role['version'], basestring) or type(role['applicationIds']) != list or type(role['startingResourceIds']) != list or type(role['networkRules']) != list or type(role['startingTransducerIds']) != list): return json.dumps(ErrorCodes.admin['invalidFormat']) if not role['applicationIds']: return json.dumps(ErrorCodes.admin['NoApplicationId']) default_unity_size = 3 # Measured in GB. for a in role['applicationIds']: app_test = self.inst.get_obj('cid', a, objectClass='OpenLDAPapplication', throw_error=True) if (app_test == ()): return json.dumps(ErrorCodes.admin['invalidApplicationId']) ldap_tools.parse_ldap(app_test) if (app_test['os'] == 'WINDOWS'): if (app_test['name'].startswith('Microsoft Office')): default_unity_size = default_unity_size + 7 else: default_unity_size = default_unity_size + 3 else: default_unity_size = default_unity_size + 2 for r in role['startingResourceIds']: res_test = self.inst.get_obj('cid', r, objectClass='OpenLDAPresource', throw_error=True) if (res_test == ()): return json.dumps(ErrorCodes.admin['invalidResourceId']) ldap_transducers = self.inst.get_objs_of_type('OpenLDAPtransducer') assert ldap_transducers != None all_transducers = ldap_tools.parse_ldap_list(ldap_transducers) for t in role['startingTransducerIds']: if (t not in [tr['id'] for tr in all_transducers]): return json.dumps(ErrorCodes.admin['invalidTransducerId']) new_role = copy.deepcopy(role) for t in all_transducers: if (t['startEnabled'] and t['id'] not in role['startingTransducerIds']): new_role['startingTransducerIds'].append(t['id']) new_role['id'] = '{0}{1}'.format( new_role['name'].lower().replace(' ', '_'), int(time.time())) if (unity_img_name == None): closest_fit = 0 for img in os.listdir('/mnt/efs/images/unities'): img_size = int(img.replace('GB.img', '')) if (img_size >= default_unity_size and (closest_fit == 0 or closest_fit > img_size)): closest_fit = img_size if (closest_fit != 0): unity_img_name = '{}GB'.format(closest_fit) else: return json.dumps({ 'status': 'failed', 'result': [ 17, ('Could not automatically find a usable unity' ' for this role. Please specify a unity' ' image.') ] }) if ((unity_img_name + '.img') not in os.listdir('/mnt/efs/images/unities')): return json.dumps({ 'status': 'failed', 'result': [ 17, ('Could not find a unity with the name' ' ({})').format(unity_img_name) ] }) try: # Call a controller thread to create and assemble the new image thr = AssembleRoleThread(self.inst.email, self.inst.password, new_role, unity_img_name, use_ssh=use_ssh) except AssertionError: return json.dumps(ErrorCodes.admin['storageError']) thr.start() return json.dumps({'id': new_role['id'], 'name': new_role['name']}) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) # Destroy the specified role def role_destroy(self, roleId, use_nfs=True): try: role = self.inst.get_obj('cid', roleId, 'OpenLDAProle', True) if (role == ()): return json.dumps(ErrorCodes.admin['invalidId']) ldap_tools.parse_ldap(role) # Check if any virtues exist with given role virtues = self.inst.get_objs_of_type('OpenLDAPvirtue') for virtue in virtues: ldap_tools.parse_ldap(virtue[1]) if (virtue[1]['roleId'] == roleId): # A Virtue exists for this role - Unable to destroy role virtueUsingRoleError = [] virtueUsingRoleError.append({ 'Virtue using the ' 'specified role exists': virtue[1]['id'] }) virtueUsingRoleError.append( ErrorCodes.admin['virtueUsingRole']) return json.dumps(virtueUsingRoleError) # Check if any user is authorized for the given role users = self.inst.get_objs_of_type('OpenLDAPuser') for user in users: ldap_tools.parse_ldap(user[1]) if (roleId in user[1]['authorizedRoleIds']): # A User exists with this role authorized - Unable to # destroy role userUsingRoleError = [] userUsingRoleError.append({ 'User authorized for the ' 'specified role exists': user[1]['username'] }) userUsingRoleError.append( ErrorCodes.admin['userUsingRole']) return json.dumps(userUsingRoleError) try: self.inst.del_obj('cid', roleId, throw_error=True) if (use_nfs): subprocess.check_call([ 'sudo', 'rm', '/mnt/efs/images/non_provisioned_virtues/' + role['id'] + '.img' ]) # Delete the Standby virtue role image files files = os.listdir('/mnt/efs/images/provisioned_virtues/') standby_files = (file for file in files if role['id'] + '_STANDBY_VIRTUE_' in file) for standby_file in standby_files: subprocess.check_call([ 'sudo', 'rm', '/mnt/efs/images/provisioned_virtues/' + standby_file ]) except: print('Error while deleting {0}:\n{1}'.format( role['id'], traceback.format_exc())) return json.dumps(ErrorCodes.admin['roleDestroyError']) return json.dumps(ErrorCodes.admin['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def role_list(self): try: ldap_roles = self.inst.get_objs_of_type('OpenLDAProle') assert ldap_roles != None roles = ldap_tools.parse_ldap_list(ldap_roles) return json.dumps(roles) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def system_export(self): return json.dumps(ErrorCodes.admin['notImplemented']) def system_import(self, data): return json.dumps(ErrorCodes.admin['notImplemented']) def test_import_user(self, which): return json.dumps(ErrorCodes.admin['notImplemented']) def test_import_application(self, which): return json.dumps(ErrorCodes.admin['notImplemented']) def test_import_role(self, which): return json.dumps(ErrorCodes.admin['notImplemented']) def user_list(self): try: # Check AD and update user list in ldap if required update_ldap_users_from_ad() ldap_users = self.inst.get_objs_of_type('OpenLDAPuser') assert ldap_users != None users = ldap_tools.parse_ldap_list(ldap_users) return json.dumps(users) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def user_get(self, username): try: # Check AD and update user list in ldap if required update_ldap_users_from_ad() user = self.inst.get_obj('cusername', username, objectClass='OpenLDAPuser', throw_error=True) if (user == ()): return json.dumps(ErrorCodes.admin['invalidUsername']) ldap_tools.parse_ldap(user) return json.dumps(user) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def user_virtue_list(self, username): try: # Check AD and update user list in ldap if required update_ldap_users_from_ad() user = self.inst.get_obj('cusername', username, objectClass='OpenLDAPuser') if (user == ()): return json.dumps(ErrorCodes.admin['invalidUsername']) elif (user == None): return json.dumps(ErrorCodes.admin['unspecifiedError']) ep = EndPoint(self.inst.email, self.inst.password) return ep.user_virtue_list(username) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def user_role_authorize(self, username, roleId): try: # Check AD and update user list in ldap if required update_ldap_users_from_ad() user = self.inst.get_obj('cusername', username, objectClass='OpenLDAPuser', throw_error=True) if (user == ()): return json.dumps(ErrorCodes.admin['invalidUsername']) ldap_tools.parse_ldap(user) role = self.inst.get_obj('cid', roleId, objectClass='OpenLDAProle', throw_error=True) if (role == ()): if (roleId in user['authorizedRoleIds']): # The user is authorized for a nonexistent role... # Remove it from their list? pass return json.dumps(ErrorCodes.admin['invalidRoleId']) ldap_tools.parse_ldap(role) if (roleId in user['authorizedRoleIds']): return json.dumps(ErrorCodes.admin['userAlreadyAuthorized']) user['authorizedRoleIds'].append(roleId) ldap_user = ldap_tools.to_ldap(user, 'OpenLDAPuser') self.inst.modify_obj('cusername', username, ldap_user, objectClass='OpenLDAPuser', throw_error=True) return json.dumps(ErrorCodes.admin['success']) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def user_role_unauthorize(self, username, roleId): try: user = self.inst.get_obj('cusername', username, objectClass='OpenLDAPuser', throw_error=True) if (user == ()): return json.dumps(ErrorCodes.admin['invalidUsername']) ldap_tools.parse_ldap(user) role = self.inst.get_obj('cid', roleId, objectClass='OpenLDAProle', throw_error=True) if (roleId not in user['authorizedRoleIds'] and role == ()): # If the role does not exist AND the user isn't 'authorized' # for it, return error. If the user is not authorized for a # real role, return error. If the user is authorized for a # nonexistent role, the admin may be trying to clean up an error return json.dumps(ErrorCodes.admin['invalidRoleId']) elif (roleId not in user['authorizedRoleIds']): return json.dumps(ErrorCodes.admin['userNotAlreadyAuthorized']) ldap_tools.parse_ldap(role) virtues = self.inst.get_objs_of_type('OpenLDAPvirtue') for v in virtues: ldap_tools.parse_ldap(v[1]) if (v[1]['username'] == username and v[1]['roleId'] == roleId): # Todo: Check if the user is logged on return json.dumps(ErrorCodes.admin['userUsingVirtue']) for r in user['authorizedRoleIds']: if (r == roleId): user['authorizedRoleIds'].remove(r) del r ldap_user = ldap_tools.to_ldap(user, 'OpenLDAPuser') self.inst.modify_obj('cusername', username, ldap_user, objectClass='OpenLDAPuser', throw_error=True) return json.dumps(ErrorCodes.admin['success']) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) # Create a virtue for the specified role, but do not launch it yet def virtue_create(self, username, roleId, use_nfs=True): try: user = None role = None resources = [] virtue_dict = {} user = self.inst.get_obj('cusername', username, 'OpenLDAPuser') if (user == None or user == ()): return json.dumps(ErrorCodes.admin['invalidUsername']) ldap_tools.parse_ldap(user) role = self.inst.get_obj('cid', roleId, 'OpenLDAProle', True) if (role == ()): return json.dumps(ErrorCodes.admin['invalidRoleId']) ldap_tools.parse_ldap(role) if (roleId not in user['authorizedRoleIds']): return json.dumps(ErrorCodes.admin['userNotAlreadyAuthorized']) if (role.get('state', 'CREATED') != 'CREATED'): return json.dumps(ErrorCodes.admin['invalidRoleState']) virtue = None curr_virtues = self.inst.get_objs_of_type('OpenLDAPvirtue') for v in curr_virtues: ldap_tools.parse_ldap(v[1]) if (v[1]['username'] == username and v[1]['roleId'] == roleId): return json.dumps( ErrorCodes.user['virtueAlreadyExistsForRole']) for rid in role['startingResourceIds']: resource = self.inst.get_obj('cid', rid, 'OpenLDAPresource', True) if (resource == ()): continue ldap_tools.parse_ldap(resource) resources.append(resource) virtue_id = 'Virtue_{0}_{1}'.format( role['name'].lower().replace(' ', '_'), int(time.time())) thr = CreateVirtueThread(self.inst.email, self.inst.password, role['id'], username, virtue_id, role=role) if (use_nfs): thr.start() else: virtue = { 'id': virtue_id, 'username': username, 'roleId': roleId, 'applicationIds': [], 'resourceIds': role['startingResourceIds'], 'transducerIds': role['startingTransducerIds'], 'networkRules': role['networkRules'], 'state': 'STOPPED', 'ipAddress': 'NULL' } ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue') self.inst.add_obj(ldap_virtue, 'virtues', 'cid') # Return the whole thing # return json.dumps( virtue ) # Return a json of the id and ip address return json.dumps({'ipAddress': 'NULL', 'id': virtue_id}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) # Destroy the specified stopped virtue def virtue_destroy(self, virtueId, use_nfs=True): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.admin['invalidId']) ldap_tools.parse_ldap(virtue) if (virtue['state'] != 'STOPPED'): return json.dumps(ErrorCodes.user['virtueNotStopped']) try: if (use_nfs): subprocess.check_call([ 'sudo', 'rm', '/mnt/efs/images/provisioned_virtues/' + virtue['id'] + '.img' ]) self.inst.del_obj('cid', virtue['id'], throw_error=True) except: print('Error while deleting {0}:\n{1}'.format( virtue['id'], traceback.format_exc())) return json.dumps(ErrorCodes.user['serverDestroyError']) self.rdb_manager.destroy_virtue(virtue['id']) return json.dumps(ErrorCodes.admin['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def virtue_reload_state(self, virtueId): try: virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if (virtue == ()): return json.dumps(ErrorCodes.user['invalidId']) ldap_tools.parse_ldap(virtue) updated_virtue = copy.deepcopy(virtue) rdb_manager = RethinkDbManager() rdb_virtue = rdb_manager.get_virtue(virtueId) if (rdb_virtue == None): updated_virtue['state'] = 'STOPPED' updated_virtue['ipAddress'] = 'NULL' else: updated_virtue['state'] = 'RUNNING' updated_virtue['ipAddress'] = rdb_virtue['guestnet'] if (updated_virtue != virtue): ldap_virtue = ldap_tools.to_ldap(updated_virtue, 'OpenLDAPvirtue') self.inst.modify_obj('cid', virtueId, ldap_virtue, 'OpenLDAPvirtue', True) return json.dumps(ErrorCodes.user['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def valor_create(self): try: valor_id = self.valor_api.valor_create() return json.dumps({'valor_id': valor_id}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def valor_launch(self, valor_id): try: valor_id = self.valor_api.valor_launch(valor_id) return json.dumps({'valor_id': valor_id}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def valor_stop(self, valor_id): try: valor_id = self.valor_api.valor_stop(valor_id) return json.dumps({'valor_id': valor_id}) except Exception as exception: print('Error:\n{0}'.format(traceback.format_exc())) # Virtue/s exists on this valor - Unable to stop valor return json.dumps({ 'status': 'failed', 'result': [11, exception.message] }) def valor_destroy(self, valor_id): try: valor_id = self.valor_api.valor_destroy(valor_id) return json.dumps({'valor_id': valor_id}) except Exception as exception: print('Error:\n{0}'.format(traceback.format_exc())) # Virtue/s exists on this valor - Unable to destroy valor return json.dumps({ 'status': 'failed', 'result': [11, exception.message] }) def valor_list(self): try: valors = self.valor_api.valor_list() return json.dumps(valors) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def valor_create_pool(self, number_of_valors): try: valor_ids = self.valor_api.valor_create_pool(number_of_valors) return json.dumps({'valor_ids': valor_ids}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def valor_migrate_virtue(self, virtue_id, destination_valor_id): try: valor_id = self.valor_api.valor_migrate_virtue( virtue_id, destination_valor_id) return json.dumps({'valor_id': valor_id}) except Exception as exception: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps({ 'status': 'failed', 'result': [11, exception.message] }) def galahad_get_id(self): try: instance_data = AWS.get_instance_info() return json.dumps(instance_data['instance-id']) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def application_add(self, application): try: app = copy.copy(application) if (set(app.keys()) != set(['id', 'name', 'version', 'os']) and set(app.keys()) != set( ['id', 'name', 'version', 'os', 'port'])): return json.dumps(ErrorCodes.admin['invalidFormat']) if (isinstance(app.get('port'), basestring)): app['port'] = int(app['port']) if (not isinstance(app['id'], basestring) or not isinstance(app['name'], basestring) or not isinstance(app['version'], basestring) or type(app.get('port', 0)) != int): return json.dumps(ErrorCodes.admin['invalidFormat']) if (app['os'] != 'LINUX' and app['os'] != 'WINDOWS'): return json.dumps(ErrorCodes.admin['invalidFormat']) if (self.inst.get_obj('cid', app['id'], throw_error=True) != ()): return json.dumps(ErrorCodes.admin['invalidId']) ecr_auth_json = subprocess.check_output([ 'aws', 'ecr', 'get-authorization-token', '--registry-ids', '703915126451', '--output', 'json', '--region', 'us-east-2' ]) ecr_auth = json.loads(ecr_auth_json.decode()) docker_registry = ecr_auth['authorizationData'][0]['proxyEndpoint'] docker_token = ecr_auth['authorizationData'][0][ 'authorizationToken'] # Since the user is only adding the app, not creating it, make sure # the image is already in the docker repo. response = requests.get( '{0}/v2/starlab-virtue/tags/list'.format(docker_registry), headers={'Authorization': 'Basic ' + docker_token}) if ('virtue-' + app['id'] not in response.json()['tags']): return json.dumps(ErrorCodes.admin['imageNotFound']) ldap_app = ldap_tools.to_ldap(app, 'OpenLDAPapplication') ret = self.inst.add_obj(ldap_app, 'virtues', 'cid') assert ret == 0 return json.dumps(ErrorCodes.admin['success']) except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError']) def auto_migration_start(self, migration_interval=None): try: self.valor_api.auto_migration_start(migration_interval) return json.dumps(ErrorCodes.admin['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def auto_migration_stop(self): try: self.valor_api.auto_migration_stop() return json.dumps(ErrorCodes.admin['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def auto_migration_status(self): try: status, interval = self.valor_api.auto_migration_status() if status: migration_status = 'ON' return json.dumps({ 'auto_migration_status': migration_status, 'auto_migration_interval': interval }) else: migration_status = 'OFF' return json.dumps({'auto_migration_status': migration_status}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def virtue_introspect_start(self, virtue_id, interval=None, modules=None): try: self.rdb_manager.introspect_virtue_start(virtue_id, interval, modules) return json.dumps({'virtue_id': virtue_id}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def virtue_introspect_stop(self, virtue_id): try: self.rdb_manager.introspect_virtue_stop(virtue_id) return json.dumps({'virtue_id': virtue_id}) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def virtue_introspect_start_all(self, interval=None, modules=None): try: virtues_raw = self.inst.get_objs_of_type('OpenLDAPvirtue') if (virtues_raw == None): return json.dumps(ErrorCodes.user['unspecifiedError']) for virtue in virtues_raw: ldap_tools.parse_ldap(virtue[1]) virtue_running = (virtue[1]['state'] == 'RUNNING') if virtue_running: self.rdb_manager.introspect_virtue_start( virtue[1]['id'], interval, modules) return json.dumps(ErrorCodes.admin['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def virtue_introspect_stop_all(self): try: virtues_raw = self.inst.get_objs_of_type('OpenLDAPvirtue') if (virtues_raw == None): return json.dumps(ErrorCodes.user['unspecifiedError']) for virtue in virtues_raw: ldap_tools.parse_ldap(virtue[1]) virtue_running = (virtue[1]['state'] == 'RUNNING') if virtue_running: self.rdb_manager.introspect_virtue_stop(virtue[1]['id']) return json.dumps(ErrorCodes.admin['success']) except: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.user['unspecifiedError']) def _set_introspection_ldap(self, virtueId, isEnabled): introsection_id = 'introspection' virtue = self.inst.get_obj('cid', virtueId, 'OpenLDAPvirtue', True) if virtue is None or virtue == (): return json.dumps(ErrorCodes.admin['invalidVirtueId']) ldap_tools.parse_ldap(virtue) new_t_list = json.loads(virtue['transducerIds']) if isEnabled: if introsection_id not in new_t_list: new_t_list.append(introsection_id) else: if introsection_id in new_t_list: new_t_list.remove(introsection_id) virtue['transducerIds'] = json.dumps(new_t_list) ret = self.inst.modify_obj( 'cid', virtueId, ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'), 'OpenLDAPvirtue', True) if ret != 0: return json.dumps(ErrorCodes.user['unspecifiedError']) return json.dumps(ErrorCodes.admin['success']) def _set_introspection_role(self, isEnabled): introspection_id = 'introspection' try: ldap_roles = self.inst.get_objs_of_type('OpenLDAProle') assert ldap_roles != None roles = ldap_tools.parse_ldap_list(ldap_roles) for role in roles: print(role) print(role['startingTransducerIds']) new_t_list = role['startingTransducerIds'] if isEnabled: if introspection_id not in new_t_list: new_t_list.append(introspection_id) else: if introspection_id in new_t_list: new_t_list.remove(introspection_id) role['startingTransducerIds'] = new_t_list ret = self.inst.modify_obj( 'cid', role['id'], ldap_tools.to_ldap(role, 'OpenLDAProle'), 'OpenLDAProle', True) if ret != 0: return json.dumps(ErrorCodes.user['unspecifiedError']) return True except Exception as e: print('Error:\n{0}'.format(traceback.format_exc())) return json.dumps(ErrorCodes.admin['unspecifiedError'])