def test_get_and_set_DatabasePasswordByUser_simple(self): from django.conf import settings from psafefe.psafe.models import * from psafefe.psafe.functions import getUsersPersonalSafe, getDatabasePasswordByUser, setDatabasePasswordByUser # Create a user with read/write access to the test safe repo username, user = self.getUser( userClass = 'user', groups = [ self.groupsByRepo['testsafes']['writes'], self.groupsByRepo['testsafes']['reads'], ], ) userpass = self.getUserPass(user = user) mySafe = getUsersPersonalSafe( user = user, userPassword = userpass, wait = True, ) self.assertTrue(mySafe) # Get the DB we're working with db = PasswordSafe.objects.get( repo = self.groupsByRepo['testsafes']['repo'], filename__endswith = "simple.psafe3", ) # Save the DB password setDatabasePasswordByUser( user = user, userPassword = userpass, psafe = db, psafePassword = '******', wait = True, ) # Try with ppsafe set dbPassword = getDatabasePasswordByUser( user = user, userPassword = userpass, psafe = db, ppsafe = mySafe, wait = True, ) self.assertEqual(dbPassword, 'bogus12345', "Password for simple.psafe3 is wrong or has changed. Was trying without passing in a personal psafe to getDatabasePasswordByUser") # try without ppsafe set dbPassword = getDatabasePasswordByUser( user = user, userPassword = userpass, psafe = db, ppsafe = None, wait = True, ) self.assertEqual(dbPassword, 'bogus12345', "Password for simple.psafe3 is wrong or has changed. Was trying with passing in a personal psafe to getDatabasePasswordByUser")
def getCached(self, canLoad=False, user=None, userPassword=None): """ Return the RAM only cached data for this safe. @param canLoad: Indicates what to do if the entry doesn't exist. False: Error out. True: Load the safe then return the obj. """ self.log.debug("Getting cached copy") from psafefe.psafe.errors import EntryNotCached # Can't load without the password from django.contrib.auth.models import User if not isinstance(user, User): canLoad = False self.log.debug("Can't load due to lack of valid user (%r)" % user) if not userPassword: canLoad = False self.log.debug("Can't load due to lack of valid password for user (%r)" % userPassword) try: return self.mempsafe except MemPSafe.DoesNotExist, e: if canLoad: self.log.debug("Going to try loading") try: from psafefe.psafe.tasks.load import loadSafe from psafefe.psafe.functions import getDatabasePasswordByUser dbPassword = getDatabasePasswordByUser(user=user, userPassword=userPassword, psafe=self, wait=True) ls = loadSafe.delay(psafe_pk=self.pk, password=dbPassword, force=False) # @UndefinedVariable ls.wait() # Make sure to prevent inf. recursion if the load fails return self.getCached(canLoad=False) except Exception, e: raise EntryNotCached, "%r doesn't have a cached entry and loading failed with %r" % (self, e)
def updatePSafeCacheByPSafesPK(username, password, entPKsUnsafe, sync, **kw): """ Update the psafe cache for the given entities. If sync is true, then wait for the cache to update before returning. @note: Any safes that the user doesn't have a valid password for will be skipped. @note: Any PKs to which a safe doesn't exist or the user lacks at least read-only perms will raise an EntryDoesntExistError. @warning: If sync is not set the count of successes will NOT include any errors that occur during sync. It will only include ones where the safe password lookup and object lookup succeeded. @param username: Requesting user's login @type username: string @param password: Requesting user's login @type password: string @param entPKs: A list of safe PKs that should have their cache updated. @type entPKs: list of ints @param sync: If True, wait for the safes to be updated before returning. @type sync: boolean @return: If sync=False, the number of safes that have had a sync job successfully submitted. If sync=True, then the number of safes that had a sync job submitted andsuccessfullyy completed. @raise NoPermissionError: User doesn't have password safe sync permissions """ # Validate all safes and the users perms to them first ents = [] for pk in list(entPKsUnsafe): try: ent = PasswordSafe.objects.get(pk = pk) ents.append(ent) except PasswordSafe.DoesNotExist: raise EntryDoesntExistError, "Couldn't find a PasswordSafe where pk=%r" % pk if not ent.repo.user_can_access(user = kw['user'], mode = "R"): raise EntryDoesntExistError, "Couldn't find a PasswordSafe where pk=%r" % pk sync = bool(sync) if kw['user'].has_perm('psafe.can_sync_passwordsafe'): # user has perms waits = [] successes = 0 for psafe in ents: try: psafepass = getDatabasePasswordByUser(kw['user'], password, psafe) waits.append(loadSafe.delay(psafe_pk = entPK, password = psafepass)) # @UndefinedVariable successes += 1 except: # TODO: Add some sort of logging for this pass # Doing sync, wait for all results if sync: for i in waits: try: i.wait() except: # TODO: Add some sort of logging for this successes -= 1 return successes raise NoPermissionError, "User can't sync psafes"
def updatePSafeCacheByPSafesByUUID(username, password, entUUIDsUnsafe, sync, **kw): """ Update the psafe cache for the given entities. If sync is true, then wait for the cache to update before returning. @note: Any safes that the user doesn't have a valid password for will be skipped. @note: If the user lacks perms to any psafe an EntryDoesntExistError will be raised @note: If one of the UUIDs doesn't exist, then an EntryDoesntExistError will be raised. @note: If the user has perms to multiple psafes with the same UUID and that UUID is listed, a MultipleEntriesExistError will be raised @warning: If sync is not set the count of successes will NOT include any errors that occur during sync. It will only include ones where the safe password lookup and object lookup succeeded. @param username: Requesting user's login @type username: string @param password: Requesting user's login @type password: string @param entUUIDs: A list of safe UUIDs that should have their cache updated. @type entUUIDs: list of strings @param sync: If True, wait for the safes to be updated before returning. @type sync: boolean @return: The number of safes successfully updated @raise NoPermissionError: User doesn't have password safe sync permissions @raise EntryDoesntExistError: No psafe with the request UUID exists that the user as atleast read-only access to. @raise MultipleEntriesExistError: More than one psafe with the given UUID was found. """ # Validate input type entUUIDs = [] for pk in list(entUUIDsUnsafe): entUUIDs.append(str(pk)) sync = bool(sync) if kw['user'].has_perm('psafe.can_sync_passwordsafe'): # user has perms # Validate all safes BEFORE we start calling loadSafe validSafes = [] for entUUID in entUUIDs: psafes = PasswordSafe.objects.filter(uuid = entUUID) # Validate perms okPSafes = [] for ent in psafes: if ent.repo.user_can_access(user = kw['user'], mode = "R"): okPSafes.append(ent) # Only allow one UUID if len(psafes) > 1: raise MultipleEntriesExistError, "%r safes with a UUID of %r were found" % (psafes.count(), entUUID) elif len(psafes) == 0: raise EntryDoesntExistError, "Couldn't find a PasswordSafe where uuid=%r" % entUUID validSafes.append(psafes[0]) # All safes found are valid, so start the loadSafes waits = [] successes = 0 for psafe in validSafes: try: psafepass = getDatabasePasswordByUser(kw['user'], password, psafe) waits.append(loadSafe.delay(psafe_pk = entPK, password = psafepass)) # @UndefinedVariable successes += 1 except: # TODO: Add some sort of logging for this pass # Doing sync, wait for all results and watch for errors if sync: for i in waits: try: i.wait() except: # TODO: Add some sort of logging for this successes -= 1 return successes else: raise NoPermissionError, "User can't sync psafes"
try: safe = PasswordSafe.objects.get(pk=safeID) except PasswordSafe.DoesNotExist, e: log.warning("Got %r while trying to fetch Password Safe %r", e, safeID) raise EntryDoesntExistError("No safe with an ID of %r" % safeID) # Convert possible string into a list if isinstance(deviceID, list): log.debug("DeviceID is already a list, doing nothing to %r", deviceID) elif isinstance(deviceID, str): deviceID = deviceID.split('.') log.debug("DeviceID is a string, split into %r", deviceID) else: raise ValueError("Expected deviceID to be a string or a list, not %r" % deviceID) psafePassword = getDatabasePasswordByUser(kw['user'], password, safe, wait=True) actions = [] if 'shellLogins' in info: for shellUser, shellPassword in info['shellLogins'].items(): actions.append({ 'action':'add-update', 'refilters':{}, 'vfilters':{ 'Group':deviceID, 'Username':shellUser, 'Title':"Logins", }, 'changes':{ 'Password':shellPassword,
def _filterComplex(username, password, safeIDs, include, exclude, **kw): """ @param username: Requesting user's login @type username: string @param password: Requesting user's login @type password: string @param safeIDs: The PKs of all psafe objects that should be included in the search. @type safeIDs: A list of ints @param include: A dict indicating what fields must match and possible values to match against. @type include: A dict where keys are the fields names and the values are a list of possible values for matching entries. @param exclude: A dict indicating what fields/values must NOT match. @type exclude: A dict where keys are the fields names and the values are a list of possible values for entries that should be excluded. @return: A list of dicts containing all matching entries @raise EntryDoesntExistError: The requested entry doesn't exist or the user doesn't have permission to read it. @raise InvalidQueryError: One or more of the include/exclude field names or list of values is not valid. @note: Valid fields for filters: PK, UUID, Group, Title, Username, Notes, Password, Creation Time, Password Last Modification Time, Last Access Time, Password Expiry, Entry Last Modification Time, URL, AutoType, Run Command, Email. @warning: WHEN UPDATING THE ARG LIST OR THIS DOC STRING, MAKE SURE TO UPDATE THE RPC FUNCTIONS BELOW! """ extra = dict(username = username, safeIDs = safeIDs, user = kw['user'],) for safeID in safeIDs: if not isinstance(safeID, int): log.warn("The safeID/pk %r is not an int", safeID, extra = extra) raise InvalidQueryError("The safeID/pk %r is not an int" % safeID) if len(safeIDs) == 1: extra['safePK'] = safeID if not isinstance(safeIDs, list): log.warn("The list of safeIDs/safePKs is not a list. Got type %r, value %r. ", type(safeIDs), safeIDs, extra = extra) raise InvalidQueryError("The list of safeIDs/safePKs is not a list. Got %r." % type(safeIDs)) safes = PasswordSafe.objects.filter(pk__in = safeIDs).select_related() # Let the user know which safeIDs couldn't be found. if len(safes) != len(safeIDs): log.warning("The list of PasswordSafe objects returned (%d) is a different length than the list of safeIDs (%d). ", len(safes), len(safeIDs), extra = extra) for safeID in safeIDs: if safes.filter(pk = safeID).count() == 0: raise EntryDoesntExistError("No safe with an ID of %r" % safeID) # Validate the user's access to all of the safes for safe in safes: if safe.repo.user_can_access(user = kw['user'], mode = "R"): log.debug("User %r is ok to access %r", kw['user'], safe.repo, extra = extra) else: log.warning("User %r is NOT allowed to access %r", kw['user'], safe.repo, extra = extra) # raise NoPermissionError("User %r can't access this repo" % kw['user']) raise EntryDoesntExistError("No safe with an ID of %r" % safeID) safes.append(safe) extra['safes'] = safes log.debug("User %r is querying %d safes", kw['user'], len(safes), extra = extra) # Get the cached entry or load it if needed memSafes = {} for safe in safes: psafePassword = getDatabasePasswordByUser(kw['user'], password, safe, wait = True) memSafes[safe.pk] = safe.getCached(canLoad = True, user = kw['user'], userPassword = password) extra['memSafes'] = memSafes if len(memSafes) == 1: extra['memSafePK'] = memSafes.keys()[0] # Provide basic validation of the filters since the DB filter errors aren't passed on to the user for filterName, filterDict in [ ('include', include), ('exclude', exclude), ]: for field, values in filterDict.items(): if field not in FILTER_FIELDS_MAPPING: log.warning("User %r passed in field %r which isn't valid", kw['user'], field, extra = extra) raise InvalidQueryError("The field %r:%r in %r is not a valid field name" % (field, values, filterName)) if not isinstance(values, list): log.warning("User %r passed in a value of %r for field %r. The value needs to be a list. ", kw['user'], values, field, extra = extra) raise InvalidQueryError("The value list for field %r, which is %r, is not a list" % (field, values)) fieldType, modelFieldName = FILTER_FIELDS_MAPPING[field] for value in values: if not isinstance(value, fieldType): log.warning("User %r passed in value %r for field %r which isn't %r", kw['user'], value, fieldType, extra = extra) raise InvalidQueryError("The value %r from field %r in filter %r is not of type %r" % (value, field, filterName, fieldType)) # Passed sanity checks of data...now to build the query entryFilter = MemPsafeEntry.objects.filter(safe__in = memSafes.values()) try: for field, values in include.items(): if field == 'Old Passwords': for value in values: entryFilter = entryFilter.filter(mempasswordentryhistory__contains = value) else: fieldFilterName = "%s__in" % field entryFilter = entryFilter.filter(**{fieldFilterName:values}) except Exception, e: log.warn("Error processing include filter: %r User: %r", e, kw['user'], exc_info = sys.exc_info(), extra = extra) raise InvalidQueryError("Error in include filter")