def delete_tokens(self): """Delete all tokens for a Duo user account.""" for token in self.token_ids: try: self.admin_api.delete_user_token(self.user_id, token) except RuntimeError as err: raise PluginError('Duo API error - %s' % err.message) # verify deletion self._get_user_result() if self.token_ids: raise PluginError('Deleting tokens - Verification failed')
def delete_phones(self): """Delete all phones for a Duo user account.""" for phone in self.phone_ids: try: self.admin_api.delete_user_phone(self.user_id, phone) except RuntimeError as err: raise PluginError('Duo API error - %s' % err.message) # verify deletion self._get_user_result() if self.phone_ids: raise PluginError('Deleting phones - Verification failed')
def __init__(self, username, reason): self._setup_plugins_config() self.username = username self.reason = reason # Load GAM try: import sys sys.path.append(self.gam_location) import gam self.gam = gam.ProcessGAMCommand except IOError: raise PluginError('Failed locate GAM.') except ImportError: raise PluginError('Failed to import GAM.')
def _get(self, username): """Get one or more Active Directory user objects.""" try: result = self.ldapl.search_name(username) except ldap.LDAPError: raise PluginError('LDAP error - Invalid Base DN') if len(result) == 0: raise PluginError('User "%s" does not exist.' % username) elif len(result) > 1: raise PluginError( 'More than one result found for "%s".' % username) elif len(result) == 1: return result[0][0][1]
def disable(self): """Disables an Active Directory user account.""" new_uac = str(int(self.aduser['userAccountControl'][0]) | 0x00000002) try: self.ldapl.conn.modify_s( self.aduser['distinguishedName'][0], [(ldap.MOD_REPLACE, 'userAccountControl', [new_uac])]) self.aduser = self._get(self.aduser['sAMAccountName'][0]) if not int(self.aduser['userAccountControl'][0]) & 0x00000002: raise PluginError( 'Disabling account - Verification failed.') except ldap.LDAPError as err: raise PluginError( 'LDAP error while disabling AD account: %s' % self._get_ldap_errors(err))
def _remove_group(self, group): """Remove Google user from group.""" out = self._run_gam('gam update group %s remove user %s' % ( group, self.username))[1] if out != ' removing %s@%s\n' % (self.username, self.domain): raise PluginError( 'Removing user from group - Verification failed.')
def _add_group(self, group_dn): """Add a user object to an Active Directory group.""" try: self.ldapl.conn.modify_s( group_dn, # distinguishedName [(ldap.MOD_ADD, 'member', self.aduser['distinguishedName'])] ) self.aduser = self._get(self.aduser['sAMAccountName'][0]) if ('memberOf' not in self.aduser or group_dn not in self.aduser['memberOf']): raise PluginError( 'Adding to group "%s" - Verification failed.' % group_dn) except ldap.LDAPError as err: raise PluginError( 'LDAP error while adding user to group: %s' % self._get_ldap_errors(err))
def _update_file_state(self, fstate): """Updates file state by hash for all Bit9 policies.""" # disable warnings when not verifying certificate if not self.strong_cert: requests.packages.urllib3.disable_warnings() auth_json = { 'X-Auth-Token': self.token, 'content-type': 'application/json', } data = { 'hash': self.hashcode, 'name': self.reason, 'fileState': fstate, # 1 means 'unapproved', 3 means 'banned' } # make request to Bit9 try: bit9_request = requests.post( self.url, json.dumps(data), headers=auth_json, verify=self.strong_cert, timeout=60, ) bit9_request.raise_for_status() except requests.exceptions.RequestException as err: raise PluginError('Bit9 server failed to respond - %s.' % err)
def _move(self, newsuperior): """Move an Active Directory user to another organizational unit.""" try: self.ldapl.conn.rename_s( self.aduser['distinguishedName'][0], 'cn=%s' % self.aduser['cn'][0], newsuperior) self.aduser = self._get(self.aduser['sAMAccountName'][0]) superior = self.aduser['distinguishedName'][0].split( ',', 1)[1] if newsuperior.lower() != superior.lower(): raise PluginError( 'Moving to "%s" - Verification failed.' % newsuperior) except ldap.LDAPError as err: raise PluginError( 'LDAP error while moving AD object: %s' % self._get_ldap_errors(err))
def get_plugin_config_options(section): """Get configuration options from plugins.ini.""" config = SafeConfigParser() config.read('plugins.ini') try: return dict(config.items(section)) except NoSectionError: raise PluginError('No "%s" section in plugins.ini' % section)
def deactivate_user(self): """Deactivate LastPass user. Blocks logins but retains data and enterprise membership. """ num_users_disabled = 0 users_unverified = [] for domain in self.domains: email = '%s@%s' % (self.username, domain) # get user try: user = self._get_user_api(email) except PluginError as err: # user not found, go to next domain in list if err.message == '%s is not a valid user.' % email: continue else: raise # validate data if 'Users' not in user or len(user['Users']) < 1: continue try: uid = user['Users'].keys()[0] if not user['Users'][uid]['disabled']: # disable user self._delete_user_api(email, 0) # verification user = self._get_user_api(email) if user['Users'][uid]['disabled']: num_users_disabled += 1 else: users_unverified.append(email) # user already disabled else: num_users_disabled += 1 except KeyError: raise PluginError('Received unexpected response from server.') if users_unverified: users = ' and '.join(users_unverified) raise PluginError( 'Deactivating user - Verification failed for %s.' % users) if not num_users_disabled: domains = ' or '.join(self.domains) raise PluginError('Deactivating user - no users found for %s.' % domains)
def _setup_plugins_config(self): """Setup values from plugins.ini configuration.""" option = get_plugin_config_options('lastpass') try: self.cid = option['cid'] self.provhash = option['provhash'] self.domains = convert_str_tolist(option['domains']) except KeyError as err: raise PluginError('No "%s" option in plugins.ini' % err.message)
def _get_user_result(self): """"Lookup Duo user by name and store result.""" self.user_result = None try: result = self.admin_api.get_users_by_name(self.username) except RuntimeError as err: raise PluginError('Duo API error - %s' % err.message) if not result: raise PluginError('User could not be found.') # check that response contains everything expected if (isinstance(result, list) and len(result) == 1 and isinstance(result[0], dict) and set(['user_id', 'phones', 'tokens']) <= set(result[0]) and isinstance(result[0]['phones'], list) and isinstance(result[0]['tokens'], list)): self.user_result = result[0] else: raise PluginError('Received unexpected response from server.')
def randomize_password(self): """Changes a Google user's password to a random value.""" out = self.info new_pwd = generate_random_string( self.reset_password_length) out = self._update('password \'%s\'' % new_pwd) if out != 'updating user %s@%s...\n' % (self.username, self.domain): raise PluginError( 'Randomizing password - Verification failed.')
def _run_gam(self, gam_cmd): """Take a GAM command string and run it.""" with capture_stdout() as out: self.gam(gam_cmd.split()) if out[1]: if '403' in out[1]: raise PluginError( 'GAM error - Not authorized to access this resource/API.') return out
def _setup_plugins_config(self): """Setup values from plugins.ini configuration.""" option = get_plugin_config_options('google') try: self.domain = option['domain'] self.reset_password_length = int(option['reset_password_length']) self.gam_location = option['gam_location'] self.backup_dir = option['backup_dir'] except KeyError as err: raise PluginError('No "%s" option in plugins.ini' % err.message)
def suspend(self): """Suspend a Google user account.""" self._update('suspended on') out = self.info for line in out.split('\n'): if line.startswith('Account Suspended:'): result = line.split(':')[1].strip() if result == 'True': return raise PluginError('Suspending account - Verification failed.')
def remove_from_gal(self): """Remove a Google user from the Global Address List.""" self._update('gal off') out = self.info for line in out.split('\n'): if line.startswith('Included in GAL:'): result = line.split(':')[1].strip() if result == 'False': return raise PluginError('Removing from GAL - Verification failed.')
def _setup_plugins_config(self): """Setup values from plugins.ini configuration.""" option = plugins.utils.get_plugin_config_options('bit9') try: self.token = option['token'] self.url = option['url'] self.strong_cert = True if option['strong_cert'].lower() == 'false': self.strong_cert = False except KeyError as err: raise PluginError('No "%s" option in plugins.ini' % err.message)
def _setup_plugins_config(self): """Setup values from plugins.ini configuration.""" option = get_plugin_config_options('duo') try: self.admin_api = duo_client.Admin( ikey=option['integration_key'], skey=option['secret_key'], host=option['api_hostname'], ) except KeyError as err: raise PluginError('No "%s" option in plugins.ini' % err.message)
def _call_api(self, cmd, data): """Make a call to the LastPass Provisioning API.""" api_url = 'https://lastpass.com/enterpriseapi.php' post_data = { 'cid': self.cid, 'provhash': self.provhash, 'cmd': cmd, 'data': data, } response = requests.post(api_url, data=json.dumps(post_data)) if response.status_code == requests.codes.ok: try: json_response = response.json() except ValueError: raise PluginError('API is not configured correctly.') if 'error' in json_response: raise PluginError(json_response['error'][0]) return json_response else: raise PluginError('HTTP %s' % response.status_code)
def randomize_password(self): """Change an Active Directory user's password to a random value.""" new_pwd = generate_random_string(self.reset_password_length) new_pwd = unicode('\"' + new_pwd + '\"').encode('utf-16-le') try: self.ldapl.conn.modify_s( self.aduser['distinguishedName'][0], [(ldap.MOD_REPLACE, 'unicodePwd', [new_pwd])]) except ldap.LDAPError as err: raise PluginError( 'LDAP error while randomizing AD password: %s' % self._get_ldap_errors(err))
def remove_group_memberships(self): """Remove an Active Directory user from all groups.""" if 'memberOf' not in self.aduser: return self._backup_group_memberships() try: for group in self.aduser['memberOf']: self.ldapl.conn.modify_s( group, [( ldap.MOD_DELETE, 'member', self.aduser['distinguishedName'])]) self.aduser = self._get(self.aduser['sAMAccountName'][0]) if 'memberOf' in self.aduser: raise PluginError( 'Removing group memberships - Verification failed.') except ldap.LDAPError as err: raise PluginError( 'LDAP error while removing from groups: %s' % self._get_ldap_errors(err))
def __init__(self, username, reason): # create LDAP connection self._setup_plugins_config() self.ldapl = PyLDAPLite( server='ldaps://%s' % self.server, base_dn=self.base_dn) try: self.ldapl.connect(self.admin, self.password) except ldap.LDAPError as err: raise PluginError( 'LDAP error while creating connection: %s' % self._get_ldap_errors(err)) self.aduser = self._get(username) self.reason = reason
def _setup_plugins_config(self): """Setup values from plugins.ini configuration.""" option = get_plugin_config_options('activedirectory') try: self.server = option['server'] self.base_dn = option['base_dn'] self.admin = option['admin'] self.password = option['password'] self.reset_password_length = int(option['reset_password_length']) self.backup_dir = option['backup_dir'] self.disabled_group = option['disabled_group'] self.disabled_ou = option['disabled_ou'] except KeyError as err: raise PluginError('No "%s" option in plugins.ini' % err.message)
def _backup_group_memberships(self): """Backup a user's group memberships.""" groups = self.groups if not groups: return backup_file = '%s_googlegroups_%s.txt' % ( self.username, datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) try: with open('%s/%s' % (self.backup_dir, backup_file), 'w') as fil: for grp in groups: fil.write("%s\n" % grp) except IOError: raise PluginError( 'Error writing group memberships to backup file.')
def info(self): """Get Google user account information.""" out = self._run_gam('gam info user %s' % self.username)[0] if not out: raise PluginError('GAM error - User could not be found.') return out