예제 #1
0
 def getPasswordForResource(self, username, resource_id, include_history=False):
     """
     Looks up a password matching specified username @ specified resource name (e.g. hostname).
     
     :param username: The username associated with the password.
     :type username: str
     :param resource_id: The resource ID or name that we are looking up.
     :type resource_id: int or str
     :param include_history: Whether to include history (previous passwords) for this password.
     :type include_history: bool
     :return: The matching password, or None if none found.
     """
     try:
         try:
             resource_id = int(resource_id)
         except ValueError:
             resource = resources.get_by_name(resource_id, assert_single=True)
         else:
             resource = resources.get(resource_id)
         pw = passwords.get_for_resource(username=username, resource_id=resource.id, assert_exists=True)
         auditlog.log(auditlog.CODE_CONTENT_VIEW, target=pw)
         return pw.to_dict(decrypt=True, include_history=include_history)
     except exc.NoSuchEntity:
         log.info("Unable to find password matching user@resource: {0}@{1}".format(username, resource_id))
         raise
     except:
         log.exception("Unable to find password for resource.")
         raise RuntimeError("Unhandled error trying to lookup password for user@resource: {0}@{1}".format(username, resource_id))
예제 #2
0
 def getResource(self, resource_id):
     try:
         resource_id = int(resource_id)
     except ValueError:
         resource = resources.get_by_name(resource_id, assert_single=True)
     else:
         resource = resources.get(resource_id)
     auditlog.log(auditlog.CODE_CONTENT_VIEW, target=resource)
     return resource.to_dict(decrypt=True, include_passwords=True)
예제 #3
0
 def view(self, resource_id):
     try:
         resource_id = int(resource_id)
     except ValueError:
         resource = resources.get_by_name(resource_id, assert_single=True)
     else:
         resource = resources.get(resource_id)
     
     auditlog.log(auditlog.CODE_CONTENT_VIEW, target=resource)
     return render('resource/view.html', {'resource': resource})
예제 #4
0
 def test_edit_link(self):
     """ Test clicking the edit link (prompt) """
     r = resources.get_by_name("BoA")
     
     self.open_url('/resource/list')
     
     editlink = self.wd.find_element(By.ID, "edit-link-{0}".format(r.id))
     editlink.click()
     time.sleep(0.5) # FIXME: Need to figure out how to wait on page loads; this is supposed to happen automatically ...
     
     self.assertEquals('Edit Resource', self.wd.title)
     
     namefield = self.wd.find_element(By.ID, "name")
     self.assertEquals("BoA", namefield.get_attribute('value'))
예제 #5
0
 def test_edit_duplicate(self):
     """ Test editing a resource and specifying duplicate name. """
     
     name = "Bikeshed PIN"
     new_name = "BoA"  # A name we know to exist in First Group
     r1 = resources.get_by_name(name)
     
     self.open_url('/resource/edit/{0}'.format(r1.id))
     
     el = self.wd.find_element(By.ID, "name")
     el.clear()
     el.send_keys(new_name)
     
     self.submit_form("resource_form")
     self.assert_form_error("Resource \"{0}\" already exists in group \"First Group\".".format(new_name))
예제 #6
0
 def test_delete_link_no_passwords(self):
     """ Test clicking the delete link when no passwords. (prompt) """
     r = resources.get_by_name("Bikeshed PIN")
     
     self.open_url('/resource/list')
     
     deletelink = self.wd.find_element(By.ID, "delete-link-{0}".format(r.id))
     deletelink.click()
     
     alert = self.wd.switch_to_alert()
     self.assertEqual("Are you sure you want to remove resource {0} (id={1})".format(r.name, r.id), alert.text)
     alert.accept()
     
     self.assert_notification("Resource deleted: {0} (id={1})".format(r.name, r.id))
     self.assert_not_in_list_table(r.name)
예제 #7
0
 def test_delete_link_passwords(self):
     """ Test clicking the delete link with passwords (confirm page) """
     r = resources.get_by_name("BoA")
     
     self.open_url('/resource/list')
     
     deletelink = self.wd.find_element(By.ID, "delete-link-{0}".format(r.id))
     deletelink.click()
     
     self.assertEquals('Delete Resource', self.wd.title)
     
     self.submit_form("delete_form")
     
     alert = self.wd.switch_to_alert()
     self.assertEqual("Are you sure you wish to permanently delete this resource and passwords?", alert.text)
     alert.accept()
     
     self.assert_notification("Resource deleted: {0} (id={1})".format(r.name, r.id))
     self.assert_not_in_list_table(r.name)
예제 #8
0
    def test_add_duplicate(self):
        """ Test adding a duplicate password. """

        rsc = resources.get_by_name("host1.example.com")
        self.open_url('/resource/view/{0}'.format(rsc.id))
        self.submit_form("add_password_form")
        
        self.assertEqual("Add a Password", self.wd.title)
        
        el = self.wd.find_element(By.ID, "username")
        el.send_keys('user4')
        
        el = self.wd.find_element(By.ID, "password_decrypted")
        el.send_keys('1234')
        
        self.submit_form("password_form")
        
        self.assertEqual("View Resource", self.wd.title)
        count = rsc.passwords.filter_by(username='******').count()
        self.assertEquals(2, count)
예제 #9
0
 def test_view_pw(self):
     """ Test viewing password contents; ensure matches db. """
     rsc = resources.get_by_name("host1.example.com")
     self.open_url('/resource/view/{0}'.format(rsc.id))
     
     user0 = rsc.passwords.filter_by(username='******').one()
     
     el = self.wd.find_element(By.ID, "pw{0}".format(user0.id))
     self.assertFalse(el.is_displayed())
     
     link = self.wd.find_element(By.ID, "lnk{0}".format(user0.id))
     
         
     link.click()
     
     def is_displayed(el):
         if el.is_displayed():
             return el
     
     found_el = WebDriverWait(self.wd, 10).until(lambda d: is_displayed(d.find_element(By.ID, "pw{0}".format(user0.id))))
     
     self.assertEqual(user0.password_decrypted, el.get_attribute("value"))
예제 #10
0
 def test_add_gen(self):
     """ Test adding a new password and generating pw """
     rsc = resources.get_by_name("host1.example.com")
     self.open_url('/resource/view/{0}'.format(rsc.id))
     self.submit_form("add_password_form")
     
     self.assertEqual("Add a Password", self.wd.title)
     
     el = self.wd.find_element(By.ID, "username")
     el.send_keys('user5')
     
     # Generate a password
     self.wd.find_element(By.ID, "generate-pw-button").click()
     
     def has_value(element):
         if element.get_attribute("value") != "":
             return element
         
     genpw_el = WebDriverWait(self.wd, 10).until(lambda d: has_value(d.find_element(By.ID, "mypassword")))
     generated_password = genpw_el.get_attribute('value')
     
     # Copy it in
     self.wd.find_element(By.ID, "copy-pw-button").click()
         
     self.assertEquals(generated_password, self.wd.find_element(By.ID, "password_decrypted").get_attribute('value'))
     
     self.submit_form("password_form")
     
     self.assertEqual("View Resource", self.wd.title)
     
     user5 = rsc.passwords.filter_by(username='******').one()
     
     self.assert_notification("Password created: user5 (id={0})".format(user5.id))
     self.assert_in_list_table("user5", table=2, is_link=False)
     
     self.assertEqual(generated_password, user5.password_decrypted)
예제 #11
0
 def from_structure(self, structure):
     """
     Populates the SQLAlchemy model from a python dictionary of the database structure.
     """
     session = meta.Session()
     
     try:
         for resource_s in structure['resources']:
             log.debug("Importing: {0!r}".format(resource_s))
             
             # First build up a list of group_ids for this resource that will correspond to groups
             # in *this* database.
             group_ids = []
             for gname in resource_s['groups']:
                 group = groups.get_by_name(gname, assert_exists=False)
                 if not group:
                     group = groups.create(gname)
                     log.info("Created group: {0!r}".format(group))
                 else:
                     log.info("Found existing group: {0!r}".format(group))
                     
                 group_ids.append(group.id)
             
             # First we should see if there is a match for the id and name; we can't rely on name alone since
             # there is no guarantee of name uniqueness (even with a group)
             resource = None
             resource_candidate = resources.get(resource_s['id'], assert_exists=False)
             if resource_candidate and resource_candidate.name == resource_s['name']:
                 resource = resource_candidate 
             else:
                 # If we find a matching resource (by name) and there is only one then we'll use that.
                 try:
                     resource = resources.get_by_name(resource_s['name'], assert_single=True, assert_exists=True)
                 except MultipleResultsFound:
                     log.info("Multiple resource matched name {0!r}, will create a new one.".format(resource_s['name']))
                 except exc.NoSuchEntity:
                     log.debug("No resource found matching name: {0!r}".format(resource_s['name']))
                     pass
                 
             resource_attribs = ('name', 'addr', 'description', 'notes', 'tags')
             resource_attribs_update = dict([(k,v) for (k,v) in resource_s.items() if k in resource_attribs])
             
             if resource:
                 (resource, modified) = resources.modify(resource.id, group_ids=group_ids, **resource_attribs_update)
                 # (yes, we are overwriting 'resource' var with new copy returned from this method)
                 log.info("Updating existing resource: {0!r} (modified: {1!r})".format(resource, modified))
                 if modified and modified != ['group_ids']:
                     if not self.force:
                         raise RuntimeError("Refusing to modify existing resource attributes {0!r} on {1!r} (use 'force' to override this).".format(modified, resource))
                     else:
                         log.warning("Overwriting resource attributes {0!r} on {1!r}".format(modified, resource))
             else:
                 # We will just assume that we need to create the resource.  Yes, it's possible it'll match an existing
                 # one, but better to build a merge tool than end up silently merging things that are not the same.
                 resource = resources.create(group_ids=group_ids, **resource_attribs_update)
                 log.info("Created new resource: {0!r}".format(resource))
             
             # Add the passwords
             for password_s in resource_s['passwords']:
                 
                 password_attribs = ('username', 'description', 'password', 'tags')
                 password_attribs_update = dict([(k,v) for (k,v) in password_s.items() if k in password_attribs])
             
                 # Look for a matching password.  We do know that this is unique.
                 password = passwords.get_for_resource(password_s['username'], password_s['resource_id'], assert_exists=False)
                 if password:
                     (password, modified) = passwords.modify(password_id=password.id, **password_attribs_update)
                     # (Yeah, we overwrite password object.)
                     log.info("Updating existing password: {0!r} (modified: {1!r})".format(password, modified))
                     
                     non_pw_modified = set(modified) - set(['password'])
                     if not modified:
                         log.debug("Password row not modified.")
                     else:
                         log.debug("Password modified: {0!r}".format(modified))
                      
                     # If anything changed other than password, we need to ensure that force=true
                     if non_pw_modified:
                         if not self.force:
                             raise RuntimeError("Refusing to modify existing password attributes {0!r} on {1!r} (use 'force' to override this).".format(non_pw_modified, password))
                         else:
                             log.warning("Overwriting password attributes {0!r} on {1!r}".format(non_pw_modified, password))
                 else:
                     password = passwords.create(resource_id=resource.id, **password_attribs_update)
                     log.info("Creating new password: {0!r}".format(password))
             
             
             # This probably isn't necessary as all the DAO methods should also flush session, but might as well.
             session.flush()
             
     except:
         session.rollback()
         raise