def test_update_phrase(self): config = opp_config.OppConfig(self.conf_filepath) # Add user utils.execute("opp-db --config_file %s add-user -uu -pp " "--phrase=123456" % self.conf_filepath) old = aescipher.AESCipher("123456") new = aescipher.AESCipher("654321") # Add category and item using old passphrase session = api.get_scoped_session(config) category = models.Category(name=old.encrypt("cat1")) item = models.Item(name=old.encrypt("item1"), url=old.encrypt("url1"), account=old.encrypt("account1"), username=old.encrypt("username1"), password=old.encrypt("password1"), blob=old.encrypt("blob1")) with session.begin(): user = api.user_get_by_username(session, 'u') category.user = user item.user = user api.category_create(session, [category]) api.item_create(session, [item]) # Update passphrase utils.execute("opp-db --config_file %s update-phrase -uu -pp " "--old_phrase=123456 --new_phrase=654321" % self.conf_filepath) # Check data using new passphrase session = api.get_scoped_session(config) with session.begin(): user = api.user_get_by_username(session, 'u') category = api.category_getall(session, user)[0] self.assertEqual(new.decrypt(category.name), "cat1") item = api.item_getall(session, user)[0] self.assertEqual(new.decrypt(item.name), "item1") self.assertEqual(new.decrypt(item.url), "url1") self.assertEqual(new.decrypt(item.account), "account1") self.assertEqual(new.decrypt(item.username), "username1") self.assertEqual(new.decrypt(item.password), "password1") self.assertEqual(new.decrypt(item.blob), "blob1") # Cleanup utils.execute("opp-db --config_file %s del-user -uu -pp" " --remove_data" % self.conf_filepath) self._assert_user_does_not_exist('u')
def _do_post(self, phrase): cat_list, error = self._check_payload(expect_list=True) if error: return error cipher = aescipher.AESCipher(phrase) categories = [] for cat in cat_list: # Make sure category id is parsed from request try: cat_id = cat['id'] except KeyError: return self.error("Missing category id in list!") if not cat_id: return self.error("Empty category id in list!") # Make sure category is parsed from request try: category = cat['name'] except KeyError: return self.error("Missing category name in list!") if not category: return self.error("Empty category name in list!") try: blob = cipher.encrypt(category) categories.append(models.Category(id=cat_id, name=blob)) except TypeError: return self.error("Invalid category name in list!") try: api.category_update(categories, session=self.session) return {'result': "success"} except Exception: return self.error("Unable to update categories in the database!")
def respond(self, require_phrase=True): """ This is the main function called by the request processing logic to generate a response for a particular endpoint call. """ # Validate passphrase if required if require_phrase: try: phrase = self.request.headers['x-opp-phrase'] except KeyError: raise OppError("Passphrase header missing!") cipher = aescipher.AESCipher(phrase) try: if cipher.decrypt(self.user.phrase_check) != "OK": raise OppError("Incorrect passphrase supplied!") except UnicodeDecodeError: raise OppError("Incorrect passphrase supplied!") else: phrase = None with self.session.begin(): if self.request.method == "GET": response = self._do_get(phrase) elif self.request.method == "PUT": response = self._do_put(phrase) elif self.request.method == "POST": response = self._do_post(phrase) elif self.request.method == "DELETE": response = self._do_delete() else: raise OppError("Method not supported!") return response
def _do_get(self, phrase): """ Fetch all categories and items data for a particular user. :param phrase: decryption passphrase :returns: success result along with categories and items arrays """ cat_array = [] item_array = [] cipher = aescipher.AESCipher(phrase) try: categories = api.category_getall(self.session, self.user) for category in categories: cat_array.append(category.extract(cipher, with_items=False)) items = api.item_getall(self.session, self.user) for item in items: item_array.append(item.extract(cipher, with_category=False)) except UnicodeDecodeError: raise bh.OppError("Unable to decrypt data!") except Exception: raise bh.OppError("Unable to fetch from the database!") return { 'result': 'success', 'categories': cat_array, 'items': item_array }
def _do_put(self, phrase): """ Create a list of items, given an array of item parameters, with option to specify auto-generation of common or unique passwords for each item. :param phrase: decryption passphrase :returns: success result along with array of newly created items """ payload_dicts = [{ 'name': "items", 'is_list': True, 'required': True }, { 'name': "auto_pass", 'is_list': False, 'required': False }, { 'name': "unique", 'is_list': False, 'required': False }, { 'name': "genopts", 'is_list': False, 'required': False }] item_list, auto_pass, unique, genopts = self._check_payload( payload_dicts) if auto_pass is True: # Retrieve words dictionary words = self._get_words(genopts) # Generate common password for all items if unique is not True: common_password = self._gen_pwd(words, genopts) cipher = aescipher.AESCipher(phrase) items = [] for row in item_list: if auto_pass is True: if unique is True: password = self._gen_pwd(words, genopts) else: password = common_password else: password = None items.append(self.make_item(row, cipher, password)) try: items = api.item_create(self.session, items) response = [] for item in items: response.append(item.extract(cipher)) return {'result': 'success', 'items': response} except Exception: raise bh.OppError("Unable to add new items to the database!")
def _do_get(self, phrase): response = [] cipher = aescipher.AESCipher(phrase) categories = api.category_getall(session=self.session) for category in categories: response.append(category.extract(cipher)) return {'result': "success", 'categories': response}
def _do_get(self, phrase): response = [] cipher = aescipher.AESCipher(phrase) items = api.item_getall(session=self.session) for item in items: response.append(item.extract(cipher)) return {'result': 'success', 'items': response}
def _do_post(self, phrase): item_list, error = self._check_payload(expect_list=True) if error: return error cipher = aescipher.AESCipher(phrase) items = [] for row in item_list: # Make sure item id is parsed from request try: item_id = row['id'] except KeyError: return self.error("Missing item id in list!") if not item_id: return self.error("Empty item id in list!") # Extract various item data into a list name = self._parse_or_set_empty(row, 'name') url = self._parse_or_set_empty(row, 'url') account = self._parse_or_set_empty(row, 'account') username = self._parse_or_set_empty(row, 'username') password = self._parse_or_set_empty(row, 'password') blob = self._parse_or_set_empty(row, 'blob') category_id = self._parse_or_set_empty(row, 'category_id') full_row = [name, url, account, username, password, blob] try: # TODO: (alex) deteremine if ok to insert completely empty item encoded_row = [ base64.b64encode(x.encode()).decode() for x in full_row ] encrypted_blob = cipher.encrypt("~".join(encoded_row)) [name, url, account, username, password, blob] = self._chunk6(encrypted_blob.decode()) items.append( models.Item(id=item_id, name=name, url=url, account=account, username=username, password=password, blob=blob, category_id=category_id)) except (AttributeError, TypeError): return self.error("Invalid item data in list!") try: api.item_update(items, session=self.session) return {'result': "success"} except Exception: return self.error("Unable to update items in the database!")
def update_phrase(config, u, p, old_phrase, new_phrase): if len(new_phrase) < 6: sys.exit("Error: passphrase must be at least 6 characters long!") try: old_cipher = aescipher.AESCipher(old_phrase) new_cipher = aescipher.AESCipher(new_phrase) s = api.get_scoped_session(config.conf) with s.begin(): user = api.user_get_by_username(s, u) if not user: sys.exit("Error: user does not exist!") if not utils.checkpw(p, user.password): sys.exit("Error: incorrect password!") try: if old_cipher.decrypt(user.phrase_check) != "OK": sys.exit("Error: incorrect old passphrase supplied!") except UnicodeDecodeError: sys.exit("Error: incorrect old passphrase supplied!") printv(config, "Updating user information") user.phrase_check = new_cipher.encrypt("OK") api.user_update(s, user) printv(config, "Updating user's categories") categories = api.category_getall(s, user) for category in categories: category.recrypt(old_cipher, new_cipher) api.category_update(s, categories) printv(config, "Updating user's items") items = api.item_getall(s, user) for item in items: item.recrypt(old_cipher, new_cipher) api.item_update(s, items) print("All of user's data has been successfuly " "re-encrypted with the new passphrase.") except Exception as e: sys.exit("Error: %s" % str(e))
def _do_put(self, phrase): """ Create a new user. :param phrase: not used :returns: success result """ payload_dicts = [{ 'name': "username", 'is_list': False, 'required': True }, { 'name': "password", 'is_list': False, 'required': True }, { 'name': "phrase", 'is_list': False }] payload_objects = self._check_payload(payload_dicts) u = self._validate(payload_objects[0], 'username') p = self._validate(payload_objects[1], 'password') phrase = self._validate(payload_objects[2], 'phrase') if len(phrase) < 6: raise bh.OppError("Passphrase must be at least 6 characters long!") try: cipher = aescipher.AESCipher(phrase) ok = cipher.encrypt("OK") user = api.user_get_by_username(self.session, u) if user: raise bh.OppError("User already exists!") hashed = utils.hashpw(p) user = models.User(username=u, password=hashed, phrase_check=ok) api.user_create(self.session, user) user = api.user_get_by_username(self.session, u) if user: return {'result': 'success'} else: raise bh.OppError("Unable to add user: '******'" % u) except bh.OppError as e: raise bh.OppError(e.error, e.desc, e.status, e.headers) except Exception: raise bh.OppError("Unable to add user: '******'" % u)
def _do_get(self, phrase): """ Fetch all user's items. :param phrase: decryption passphrase :returns: success result along with decrypted items array """ response = [] cipher = aescipher.AESCipher(phrase) try: items = api.item_getall(self.session, self.user) for item in items: response.append(item.extract(cipher)) except UnicodeDecodeError: raise bh.OppError("Unable to decrypt data!") except Exception: raise bh.OppError("Unable to fetch items from the database!") return {'result': 'success', 'items': response}
def add_user(config, u, p, phrase): if len(phrase) < 6: sys.exit("Error: passphrase must be at least 6 characters long!") try: cipher = aescipher.AESCipher(phrase) ok = cipher.encrypt("OK") s = api.get_scoped_session(config.conf) with s.begin(): user = api.user_get_by_username(s, u) if user: sys.exit("Error: user already exists!") hashed = utils.hashpw(p) user = models.User(username=u, password=hashed, phrase_check=ok) api.user_create(s, user) user = api.user_get_by_username(s, u) if user: print("Successfully added new user: '******'" % u) else: print("Error: unable to add user: '******'" % u) except Exception as e: sys.exit("Error: %s" % str(e))
def _do_put(self, phrase): cat_list, error = self._check_payload(expect_list=True) if error: return error cipher = aescipher.AESCipher(phrase) categories = [] for cat in cat_list: # Check for empty category name if not cat: return self.error("Empty category name in list!") try: blob = cipher.encrypt(cat) categories.append(models.Category(name=blob)) except TypeError: return self.error("Invalid category name in list!") try: api.category_create(categories, session=self.session) return {'result': "success"} except Exception: return self.error("Unable to add new categories to the database!")
def test_encrypt_decrypt_unicode_data(self): cipher = aescipher.AESCipher("secret passphrase") encrypted = cipher.encrypt(u"Привет Мир!") decrypted = cipher.decrypt(encrypted) self.assertEqual(decrypted, u"Привет Мир!")
def test_encrypt_decrypt(self): cipher = aescipher.AESCipher("secret passphrase") encrypted = cipher.encrypt("My Secret Message") decrypted = cipher.decrypt(encrypted) self.assertEqual(decrypted, "My Secret Message")
def _do_post(self, phrase): """ Update a list of items. Similar options to create except that item id is required for each item in the list. :param phrase: decryption passphrase :returns: success result """ payload_dicts = [{ 'name': "items", 'is_list': True, 'required': True }, { 'name': "auto_pass", 'is_list': False, 'required': False }, { 'name': "unique", 'is_list': False, 'required': False }, { 'name': "genopts", 'is_list': False, 'required': False }] item_list, auto_pass, unique, genopts = self._check_payload( payload_dicts) if auto_pass is True: # Retrieve words dictionary words = self._get_words(genopts) # Generate common password for all items if needed if unique is not True: common_password = self._gen_pwd(words, genopts) cipher = aescipher.AESCipher(phrase) items = [] for row in item_list: # Make sure item id is parsed from request try: item_id = row['id'] except KeyError: raise bh.OppError("Missing item id in list!") if not item_id: raise bh.OppError("Empty item id in list!") if auto_pass is True: if unique is True: password = self._gen_pwd(words, genopts) else: password = common_password else: password = None items.append(self.make_item(row, cipher, password, item_id)) try: api.item_update(self.session, items) response = [] for item in items: response.append(item.extract(cipher)) return {'result': 'success', 'items': response} except Exception: raise bh.OppError("Unable to update items in the database!")