def backup_database(): """ Backups entire database contents to a YAML file which is encrypted using the password from a specified mapped password in the database. """ try: dir_mode = int(config.get("backups.dir_mode", "0700"), 8) file_mode = int(config.get("backups.file_mode", "0600"), 8) # Before we do anything, validate the configuration. if not os.path.exists(config["backups.path"]): # Attempt to make the directories. os.makedirs(config["backups.path"], mode=dir_mode) if not config.get("backups.encryption.password_id"): raise exc.ConfigurationError( "Cannot backup without configured password_id (backups.encryption.password_id)" ) try: pw = passwords.get(config["backups.encryption.password_id"]) except exc.NoSuchEntity as x: raise exc.ConfigurationError( "Configured backups.encryption.password_id does not exist in database: {0}".format(x) ) backup_fname = datetime.now().strftime("backup-%Y-%m-%d-%H-%M.gpg") msg = "Backing up database to {fname}, secured by password id={pw.id}, resource={resource.name}[{resource.id}]" log.info(msg.format(fname=backup_fname, pw=pw, resource=pw.resource)) exporter = GpgYamlExporter(passphrase=pw.password_decrypted, use_tags=True, include_key_metadata=True) encrypted_stream = BytesIO() exporter.export(stream=encrypted_stream) encrypted_stream.seek(0) # Just to ensure it's rewound backup_file = os.path.join(config["backups.path"], backup_fname) with open(backup_file, "w") as fp: fp.write(encrypted_stream.read()) os.chmod(backup_file, file_mode) except: log.critical("Error backing up database.", exc_info=True) raise
def export(self, group_id=None, **kwargs): form = ExportForm(request_params(), group_id=group_id) form.group_id.choices = [(g.id, g.name) for g in groups.list()] exporter_choices = [('yaml', 'YAML (GPG/PGP-encrypted)')] if config['export.keepass.enabled']: if not os.path.exists(config['export.keepass.exe_path']): log.error("KeePass export enabled, but specified converter script does not exist: {0}".format(config.get('export.keepass.exe_path'))) else: exporter_choices.append(('kdb', 'KeePass 1.x')) form.format.choices = exporter_choices if cherrypy.request.method == 'POST': if form.validate(): group = groups.get(form.group_id.data) if form.format.data == 'yaml': exporter = GpgYamlExporter(use_tags=False, passphrase=form.passphrase.data, resource_filters=[model.GroupResource.group_id==group.id]) # @UndefinedVariable encrypted_stream = BytesIO() exporter.export(stream=encrypted_stream) encrypted_stream.seek(0) # Just to ensure it's rewound return serve_fileobj(encrypted_stream, content_type='application/pgp-encrypted', disposition='attachment', name='group-{0}-export.pgp'.format(re.sub('[^\w\-\.]', '_', group.name))) elif form.format.data == 'kdb': exporter = KeepassExporter(passphrase=form.passphrase.data, resource_filters=[model.GroupResource.group_id==group.id]) # @UndefinedVariable encrypted_stream = BytesIO() exporter.export(stream=encrypted_stream) encrypted_stream.seek(0) # Just to ensure it's rewound return serve_fileobj(encrypted_stream, content_type='application/x-keepass-database', disposition='attachment', name='group-{0}-export.kdb'.format(re.sub('[^\w\-\.]', '_', group.name))) else: # I don't think we can get here in normal business. raise RuntimeError("Unhandled format specified: {0}".format(form.format.data)) else: # does not validate return render("group/export.html", {'form': form}) else: # request method is GET return render("group/export.html", {'form': form})
def export_data(options): """ Interactive target to export GPG-encrypted YAML file(s) for specified group(s). """ try: dirpath = options.export_data.dir except AttributeError: dirpath = None try: singlefile = options.export_data.file except AttributeError: singlefile = None if dirpath is None and singlefile is None: raise BuildFailure('One of --dir/-d or --file/-f is required.') elif dirpath is not None and singlefile is not None: raise BuildFailure('The --dir/-d or --file/-f are mutually exclusive') try: grouplist = options.export_data.groups except AttributeError: grouplist = None try: groupfile = options.export_data.groupfile except AttributeError: groupfile = None if groupfile is None and grouplist is None: raise BuildFailure("One of --groupfile/-G or --groups/-g options must be specified.") elif groupfile is not None and grouplist is not None: raise BuildFailure("The --groupfile/-G and --groups/-g options are mutually exclusive.") # First read in the groups by either comma-separating the list or by if grouplist: group_names = re.split(r'\s*,\s*', grouplist) else: with open(groupfile, 'r') as fp: group_names = [l.strip() for l in fp if l.strip() != '' and not l.strip().startswith('#')] # Check these against the database. Fail fast. groups = [groups_dao.get_by_name(gn, assert_exists=True) for gn in group_names] passphrase = raw_input("GPG Passphrase for export file(s): ") if singlefile: group_ids = [g.id for g in groups] print "Exporting groups {0!r} to file: {1}".format(group_names, singlefile) exporter = GpgYamlExporter(use_tags=False, passphrase=passphrase, resource_filters=[model.GroupResource.group_id.in_(group_ids)]) # @UndefinedVariable with open(singlefile, 'w') as output_file: exporter.export(stream=output_file) else: # Iterate over the groups. Export file per group. for g in groups: exporter = GpgYamlExporter(use_tags=False, passphrase=passphrase, resource_filters=[model.GroupResource.group_id==g.id]) # @UndefinedVariable fn='group-{0}-export.pgp'.format(re.sub('[^\w\-\.]', '_', g.name)) print "Exporting group '{0}' to file: {1}".format(g.name, fn) with open(os.path.join(dirpath, fn), 'w') as output_file: exporter.export(stream=output_file)