def _sync_pws(datapath, force, dec_gpg, enc_gpg): accounts = get_all_passwords(datapath) git = Git(datapath) with GitTransaction(git): num = 0 if git.has_origin(): git.rebase_origin_master() for (host, user, path) in accounts.iterate(): # There are three cases where the file needs to be reencrypted: # 1. The force flag is set # 2. The file is encrypted to a key that is no longer available, # or expected as a recipient # 3. The list of recipients do not match the expected recipient list (rec_fps, rec_not_found) = enc_gpg.get_file_recipients(path) if force or rec_not_found or sorted(rec_fps) != sorted( enc_gpg.get_recipient_fps()): debug('Need to reencrypt {}'.format(path)) encpw = enc_gpg.encrypt(dec_gpg.decrypt_file(path)) write_and_add(git, path, encpw, True) num += 1 if num > 0: uids = ''.join( [' - {}\n'.format(x) for x in enc_gpg.get_recipient_uids()]) git.commit( "Synchronized and reencrypted {} passwords to {} recipient{}{}\n\n{}\n\n{}" .format(num, enc_gpg.get_num_recipients(), 's' if enc_gpg.get_num_recipients() > 1 else '', ' (forced)' if force else '', uids, get_version())) if git.has_origin(): git.push_master() return num
def do_add(datapath, path, host, user, encpw, exist_ok): git = Git(datapath) with GitTransaction(git): if git.has_origin(): git.rebase_origin_master() write_and_add(git, path, encpw, exist_ok) git.commit("{}/{}: {}\n\n{}".format(host, user, 'replace' if exist_ok else 'add', get_version())) if git.has_origin(): git.push_master()
def init_git(cfg, args): datapath = cfg['global']['datapath'] if os.path.isdir(os.path.join(datapath, ".git")): sys.exit( '{} is already a git repo! Not reinitializing to avoid losing data.' .format(datapath)) os.makedirs(datapath, exist_ok=True) Git.create_repo(datapath, bare=False) fn = os.path.join(datapath, '.gitignore') with open(fn, 'x') as f: f.write('lock\n') git = Git(datapath) git.add('.gitignore') git.commit('Initial') print('Password storage git repo initialized in {}'.format(datapath))
def update_repo(cfg, args): datapath = cfg['global']['datapath'] git = Git(datapath) with GitTransaction(git): orig_head = git.get_head() if git.has_origin(): git.rebase_origin_master() if git.get_head() != orig_head: print('Password database updated from origin.') else: print( 'Password database already in sync with origin, nothing updated.' ) else: print('Cannot update: no git origin configured.')
def setUp(self): debug.set_debug(False) self.tempdir = tempfile.mkdtemp() self.orig_cwd = os.getcwd() os.chdir(self.tempdir) self.repopath = os.path.join(self.tempdir, "origin") Git.create_repo(self.repopath, bare=True) self.git = Git(self.repopath) self.clone_path = os.path.join(self.tempdir, "clone") self.git.clone_to(self.clone_path) self.gitclone = Git(self.clone_path) self.create_file(os.path.join(self.clone_path, "initial")) self.gitclone.add("initial") self.gitclone.commit("message") self.gitclone.push_master()
def test_transaction_fail(self): new_path = os.path.join(self.tempdir, "new") self.git.clone_to(new_path) ngit = Git(new_path, silent=True) with self.assertRaises(subprocess.CalledProcessError): with GitTransaction(ngit): self.create_file(os.path.join(self.clone_path, "one")) self.create_file(os.path.join(new_path, "two")) self.gitclone.add("one") ngit.add("two") self.gitclone.commit("message1") ngit.commit("message2") # gitclone.push_master() should succeed but ngit.push_master() should # fail because their histories have diverged. When it fails, # the ngit commit should be reset to what it was self.assertTrue( os.path.isfile(os.path.join(self.clone_path, "one"))) self.assertTrue(os.path.isfile(os.path.join(new_path, "two"))) self.gitclone.push_master() self.assertTrue( os.path.isfile(os.path.join(self.clone_path, "one"))) self.assertTrue(os.path.isfile(os.path.join(new_path, "two"))) ngit.push_master() # This line should never be execued as git2.push_master() # should break the with block self.assertTrue(False) self.assertTrue(os.path.isfile(os.path.join(self.clone_path, "one"))) self.assertFalse(os.path.isfile(os.path.join(new_path, "two"))) # There should be no tags left since the transaction was aborted self.assertEqual(self.git.run_git(["tag"]), '') self.assertEqual(self.gitclone.run_git(["tag"]), '')
def test_rebase(self): new_path = os.path.join(self.tempdir, "new") self.git.clone_to(new_path) git1 = Git(new_path) self.create_file(os.path.join(new_path, "one")) git1.add("one") git1.commit("message1") git1.push_master() self.assertTrue(os.path.isfile(os.path.join(new_path, "one"))) newnew_path = os.path.join(self.tempdir, "newnew") self.git.clone_to(newnew_path) git2 = Git(newnew_path) self.create_file(os.path.join(newnew_path, "two")) git2.add("two") git2.commit("message2") git2.push_master() self.assertTrue(os.path.isfile(os.path.join(new_path, "one"))) self.assertFalse(os.path.isfile(os.path.join(new_path, "two"))) git1.rebase_origin_master() self.assertTrue(os.path.isfile(os.path.join(new_path, "one"))) self.assertTrue(os.path.isfile(os.path.join(new_path, "two")))
class TestGit(unittest.TestCase): def setUp(self): debug.set_debug(False) self.tempdir = tempfile.mkdtemp() self.orig_cwd = os.getcwd() os.chdir(self.tempdir) self.repopath = os.path.join(self.tempdir, "origin") Git.create_repo(self.repopath, bare=True) self.git = Git(self.repopath) self.clone_path = os.path.join(self.tempdir, "clone") self.git.clone_to(self.clone_path) self.gitclone = Git(self.clone_path) self.create_file(os.path.join(self.clone_path, "initial")) self.gitclone.add("initial") self.gitclone.commit("message") self.gitclone.push_master() def tearDown(self): os.chdir(self.orig_cwd) shutil.rmtree(self.tempdir) def create_file(self, path): with open(path, 'xb') as f: f.write(os.urandom(1024)) def test_get_head(self): # get_head() validates the output so we only need to make sure no # exception is raised when running it. It would be better to have a git # repo with a previously known hash here but that's probably overkill. self.git.get_head() def test_rebase(self): new_path = os.path.join(self.tempdir, "new") self.git.clone_to(new_path) git1 = Git(new_path) self.create_file(os.path.join(new_path, "one")) git1.add("one") git1.commit("message1") git1.push_master() self.assertTrue(os.path.isfile(os.path.join(new_path, "one"))) newnew_path = os.path.join(self.tempdir, "newnew") self.git.clone_to(newnew_path) git2 = Git(newnew_path) self.create_file(os.path.join(newnew_path, "two")) git2.add("two") git2.commit("message2") git2.push_master() self.assertTrue(os.path.isfile(os.path.join(new_path, "one"))) self.assertFalse(os.path.isfile(os.path.join(new_path, "two"))) git1.rebase_origin_master() self.assertTrue(os.path.isfile(os.path.join(new_path, "one"))) self.assertTrue(os.path.isfile(os.path.join(new_path, "two"))) def test_transaction_success(self): fpath = os.path.join(self.clone_path, "one") with GitTransaction(self.gitclone): self.create_file(fpath) self.gitclone.add("one") self.gitclone.commit("message1") self.assertTrue(os.path.isfile(fpath)) self.gitclone.push_master() self.assertTrue(os.path.isfile(fpath)) # There should be no tags left since the transaction was successful self.assertEqual(self.git.run_git(["tag"]), '') self.assertEqual(self.gitclone.run_git(["tag"]), '') def test_transaction_fail(self): new_path = os.path.join(self.tempdir, "new") self.git.clone_to(new_path) ngit = Git(new_path, silent=True) with self.assertRaises(subprocess.CalledProcessError): with GitTransaction(ngit): self.create_file(os.path.join(self.clone_path, "one")) self.create_file(os.path.join(new_path, "two")) self.gitclone.add("one") ngit.add("two") self.gitclone.commit("message1") ngit.commit("message2") # gitclone.push_master() should succeed but ngit.push_master() should # fail because their histories have diverged. When it fails, # the ngit commit should be reset to what it was self.assertTrue( os.path.isfile(os.path.join(self.clone_path, "one"))) self.assertTrue(os.path.isfile(os.path.join(new_path, "two"))) self.gitclone.push_master() self.assertTrue( os.path.isfile(os.path.join(self.clone_path, "one"))) self.assertTrue(os.path.isfile(os.path.join(new_path, "two"))) ngit.push_master() # This line should never be execued as git2.push_master() # should break the with block self.assertTrue(False) self.assertTrue(os.path.isfile(os.path.join(self.clone_path, "one"))) self.assertFalse(os.path.isfile(os.path.join(new_path, "two"))) # There should be no tags left since the transaction was aborted self.assertEqual(self.git.run_git(["tag"]), '') self.assertEqual(self.gitclone.run_git(["tag"]), '') def test_has_origin(self): self.assertFalse(self.git.has_origin()) self.assertTrue(self.gitclone.has_origin())
def do_rm(pwfile, host, user): git = Git(datapath) with GitTransaction(git): if git.has_origin(): git.rebase_origin_master() git.rm(pwfile) git.commit("{}/{}: remove\n\n{}".format(host, user, get_version())) if git.has_origin(): git.push_master()