コード例 #1
0
ファイル: util.py プロジェクト: EliAndrewC/ensconce
def initialize_key_metadata(key, salt, force_overwrite=False, nested_transaction=False):
    """
    Called when key is first specified to set some database encrypted contents.
    
    This must be run before the crypto engine has been initialized with the secret
    key.
    
    :param key: The new encryption and signing key set.
    :type key: ensconce.crypto.MasterKey
    
    :param salt: The salt to use for the KDF function.  IMPORTANT: This cannot change w/o re-encrypting database.
    :type salt: str
    
    :param force_overwrite: Whether to delete any existing metadata first (dangerous!)
    :type force_overwrite: bool
    
    :param nested_transaction: Whether this is being run within an existing transaction (i.e. do not commit).
    :type nested_transaction: bool
    
    :raise ensconce.exc.CryptoAlreadyInitialized: If the engine has already been initialized we bail out.
    :raise ensconce.exc.UnconfiguredModel: If we can't create an SA session.
    :raise ensconce.exc.ExistingKeyMetadata: If there is already key metadata (and `force_overwrite` param is not `True`).
    """
    assert isinstance(key, MasterKey)
    assert isinstance(salt, str)

    if state.initialized:
        raise exc.CryptoAlreadyInitialized()

    if meta.Session is None:
        raise exc.UnconfiguredModel()

    session = meta.Session()
    try:
        existing_keys = session.query(model.KeyMetadata).all()
        if len(existing_keys) > 0:
            if force_overwrite:
                for ek in existing_keys:
                    session.delete(ek)
                    log.warning("Forcibly removing existing metadata: {0}".format(ek))
                session.flush()
            else:
                raise exc.ExistingKeyMetadata()

        km = model.KeyMetadata()
        km.id = 0  # Chosen to be obviously out of auto-increment "range"
        km.validation = create_key_validation_payload(key=key)
        km.kdf_salt = salt
        session.add(km)
        if not nested_transaction:
            session.commit()  # We are deliberately committing early here
        else:
            session.flush()
    except:
        if not nested_transaction:
            # This conditional probably has little effect, since the connection will be in err state anyway
            # until a rollback is issued.
            session.rollback()
        log.exception("Error initializing key metadata")
        raise
コード例 #2
0
ファイル: tasks.py プロジェクト: EliAndrewC/ensconce
 def stop(self):
     self.stopped.set()
     for i in range(50):
         self.threads[:] = [t for t in self.threads if t.is_alive()]
         if self.threads:
             time.sleep(0.1)
         else:
             break
     else:
         log.warning("not all daemons have been joined: %r" % (self.threads,))
コード例 #3
0
ファイル: password.py プロジェクト: EliAndrewC/ensconce
 def process_edit(self, **kwargs):
     form = PasswordEditForm(request_params())
     if form.validate():
         (pw, modified) = passwords.modify(form.password_id.data, 
                                           username=form.username.data, 
                                           password=form.password_decrypted.data, 
                                           description=form.description.data,
                                           tags=form.tags.data)
 
         auditlog.log(auditlog.CODE_CONTENT_MOD, target=pw,
                      attributes_modified=modified)
         notify_entity_activity(pw, 'updated')
         raise cherrypy.HTTPRedirect('/resource/view/{0}'.format(pw.resource_id))
     else:
         log.warning("Form failed validation: {0}".format(form.errors))
         return render('password/edit.html', {'form': form})
コード例 #4
0
ファイル: resource.py プロジェクト: EliAndrewC/ensconce
 def process_edit(self, **kwargs):
     form = ResourceEditForm(request_params())
     form.group_ids.choices = [(g.id, g.label) for g in groups.list()]
     if form.validate():
         (resource, modified) = resources.modify(form.resource_id.data,
                                                 name=form.name.data,
                                                 addr=form.addr.data,
                                                 group_ids=form.group_ids.data,
                                                 notes=form.notes_decrypted.data,
                                                 description=form.description.data,
                                                 tags=form.tags.data) # XXX: process
         auditlog.log(auditlog.CODE_CONTENT_MOD, target=resource, attributes_modified=modified)
         notify_entity_activity(resource, 'updated')
         raise cherrypy.HTTPRedirect('/resource/view/{0}'.format(resource.id))
     else:
         log.warning("Form validation failed.")
         log.warning(form.errors)
         return render('resource/edit.html', {'form': form})
コード例 #5
0
ファイル: export.py プロジェクト: EliAndrewC/ensconce
 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