From 502e4d5e3a25c7269551c0078a8b41b154c16c20 Mon Sep 17 00:00:00 2001 From: Khai Tran Date: Fri, 5 Jul 2024 17:29:28 +0700 Subject: [PATCH 1/4] chore: Update password history --- locker_server/api/v1_0/sync/serializers.py | 2 +- .../api_orm/abstracts/ciphers/ciphers.py | 6 +++ .../migrations/0028_auto_20240705_1726.py | 18 ++++++++ .../api_orm/model_parsers/cipher_parsers.py | 19 ++++---- .../api_orm/repositories/cipher_repository.py | 43 ++++++++++--------- locker_server/core/entities/cipher/cipher.py | 14 +++++- 6 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 locker_server/api_orm/migrations/0028_auto_20240705_1726.py diff --git a/locker_server/api/v1_0/sync/serializers.py b/locker_server/api/v1_0/sync/serializers.py index ca885c30..6d518164 100644 --- a/locker_server/api/v1_0/sync/serializers.py +++ b/locker_server/api/v1_0/sync/serializers.py @@ -133,7 +133,7 @@ def to_representation(self, instance): "notes": data.get("notes"), "organization_id": instance.team.team_id if instance.team else None, "organization_use_totp": True if login else False, - "password_history": instance.history, + "password_history": instance.password_history, "reprompt": instance.reprompt, "revision_date": convert_readable_date(instance.revision_date), "creation_date": convert_readable_date(instance.creation_date), diff --git a/locker_server/api_orm/abstracts/ciphers/ciphers.py b/locker_server/api_orm/abstracts/ciphers/ciphers.py index 088f446c..3dae5a7f 100644 --- a/locker_server/api_orm/abstracts/ciphers/ciphers.py +++ b/locker_server/api_orm/abstracts/ciphers/ciphers.py @@ -20,6 +20,7 @@ class AbstractCipherORM(models.Model): data = models.TextField(blank=True, null=True) favorites = models.TextField(blank=True, null=True) folders = models.TextField(blank=True, null=True, default="") + password_history = models.TextField(blank=True, null=True, default="") user = models.ForeignKey( locker_server_settings.LS_USER_MODEL, on_delete=models.CASCADE, related_name="ciphers", null=True @@ -60,3 +61,8 @@ def set_folder(self, user_id, folder_id=None): folders[user_id] = folder_id self.folders = folders self.save() + + def get_password_history(self): + if not self.password_history: + return [] + return ast.literal_eval(str(self.password_history)) diff --git a/locker_server/api_orm/migrations/0028_auto_20240705_1726.py b/locker_server/api_orm/migrations/0028_auto_20240705_1726.py new file mode 100644 index 00000000..1e90957a --- /dev/null +++ b/locker_server/api_orm/migrations/0028_auto_20240705_1726.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.23 on 2024-07-05 10:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api_orm', '0027_auto_20240704_1131'), + ] + + operations = [ + migrations.AddField( + model_name='cipherorm', + name='password_history', + field=models.TextField(blank=True, default='', null=True), + ) + ] diff --git a/locker_server/api_orm/model_parsers/cipher_parsers.py b/locker_server/api_orm/model_parsers/cipher_parsers.py index 259e4681..75fbb44a 100644 --- a/locker_server/api_orm/model_parsers/cipher_parsers.py +++ b/locker_server/api_orm/model_parsers/cipher_parsers.py @@ -33,6 +33,7 @@ def parse_cipher(cls, cipher_orm: CipherORM, parse_collection_ids=False, parse_h folders=cipher_orm.get_folders(), favorites=cipher_orm.get_favorites(), view_password=view_password, + password_history=cipher_orm.get_password_history(), user=user_parser.parse_user(user_orm=cipher_orm.user) if cipher_orm.user else None, created_by=user_parser.parse_user(user_orm=cipher_orm.created_by) if cipher_orm.created_by else None, team=team_parser.parse_team(team_orm=cipher_orm.team) if cipher_orm.team else None, @@ -46,15 +47,15 @@ def parse_cipher(cls, cipher_orm: CipherORM, parse_collection_ids=False, parse_h pass cipher.collection_ids = collection_ids - if parse_histories is True: - try: - show_history = getattr(cipher_orm, "show_history") - except AttributeError: - show_history = True - if show_history is False: - cipher.history = [] - else: - cipher.history = cls.parse_password_history(cipher_orm=cipher_orm, limit_history=limit_history) + # if parse_histories is True: + # try: + # show_history = getattr(cipher_orm, "show_history") + # except AttributeError: + # show_history = True + # if show_history is False: + # cipher.history = [] + # else: + # cipher.history = cls.parse_password_history(cipher_orm=cipher_orm, limit_history=limit_history) return cipher @classmethod diff --git a/locker_server/api_orm/repositories/cipher_repository.py b/locker_server/api_orm/repositories/cipher_repository.py index 45cd31eb..477cbc55 100644 --- a/locker_server/api_orm/repositories/cipher_repository.py +++ b/locker_server/api_orm/repositories/cipher_repository.py @@ -529,33 +529,34 @@ def update_cipher(self, cipher_id: str, cipher_data: Dict) -> Cipher: user_cipher_id = None # Create new cipher history - if cipher_orm.type in SAVE_HISTORY_CIPHER_TYPES: - password_history = cipher_data.get("password_history") or [] - # limit_history = cipher_data.get("limit_history") - # if limit_history and len(password_history) > limit_history: - # password_history = password_history[-limit_history:] - if cipher_orm.type == CIPHER_TYPE_LOGIN and len(password_history) > cipher_orm.cipher_histories.count(): - num = len(password_history)-cipher_orm.cipher_histories.count() - password_histories_data = password_history[:num] - c_data = cipher_orm.get_data() - cipher_histories_data = [] - for password_history_data in password_histories_data: - _cipher_data = c_data.copy() - _cipher_data.update({"password": password_history_data.get("password") or c_data.get("password")}) - cipher_histories_data.append({ - "last_use_date": password_history_data.get("last_used_date") or cipher_orm.last_use_date or now(), - "reprompt": cipher_orm.reprompt, - "score": cipher_orm.score, - "data": _cipher_data, - "cipher_id": cipher_orm.id, - }) - cipher_orm.cipher_histories.model.create_multiple(cipher_histories_data) + # if cipher_orm.type in SAVE_HISTORY_CIPHER_TYPES: + # password_history = cipher_data.get("password_history") or [] + # # limit_history = cipher_data.get("limit_history") + # # if limit_history and len(password_history) > limit_history: + # # password_history = password_history[-limit_history:] + # if cipher_orm.type == CIPHER_TYPE_LOGIN and len(password_history) > cipher_orm.cipher_histories.count(): + # num = len(password_history)-cipher_orm.cipher_histories.count() + # password_histories_data = password_history[:num] + # c_data = cipher_orm.get_data() + # cipher_histories_data = [] + # for password_history_data in password_histories_data: + # _cipher_data = c_data.copy() + # _cipher_data.update({"password": password_history_data.get("password") or c_data.get("password")}) + # cipher_histories_data.append({ + # "last_use_date": password_history_data.get("last_used_date") or cipher_orm.last_use_date or now(), + # "reprompt": cipher_orm.reprompt, + # "score": cipher_orm.score, + # "data": _cipher_data, + # "cipher_id": cipher_orm.id, + # }) + # cipher_orm.cipher_histories.model.create_multiple(cipher_histories_data) # Create new cipher object cipher_orm.revision_date = now() cipher_orm.reprompt = cipher_data.get("reprompt", cipher_orm.reprompt) or 0 cipher_orm.score = cipher_data.get("score", cipher_orm.score) cipher_orm.type = cipher_data.get("type", cipher_orm.type) cipher_orm.data = cipher_data.get("data", cipher_orm.get_data()) + cipher_orm.password_history = cipher_data.get("password_history", cipher_orm.get_password_history()) cipher_orm.user_id = user_cipher_id cipher_orm.team_id = team_id cipher_orm.save() diff --git a/locker_server/core/entities/cipher/cipher.py b/locker_server/core/entities/cipher/cipher.py index 151098f1..e7935620 100644 --- a/locker_server/core/entities/cipher/cipher.py +++ b/locker_server/core/entities/cipher/cipher.py @@ -1,3 +1,5 @@ +from typing import List + from locker_server.core.entities.team.team import Team from locker_server.core.entities.user.user import User from locker_server.shared.constants.ciphers import CIPHER_TYPE_LOGIN @@ -7,7 +9,8 @@ class Cipher(object): def __init__(self, cipher_id: str, creation_date: float = None, revision_date: float = None, deleted_date: float = None, last_use_date: float = None, num_use: int = 0, reprompt: int = 0, score: float = 0, cipher_type: int = None, data=None, favorites: str = "", folders: str = "", - view_password: bool = True, user: User = None, created_by: User = None, team: Team = None): + view_password: bool = True, password_history: List = None, + user: User = None, created_by: User = None, team: Team = None): self._cipher_id = cipher_id self._creation_date = creation_date self._revision_date = revision_date @@ -21,6 +24,7 @@ def __init__(self, cipher_id: str, creation_date: float = None, revision_date: f self._favorites = favorites self._folders = folders self._view_password = view_password + self._password_history = password_history self._history = [] self._collection_ids = [] self._user = user @@ -98,6 +102,14 @@ def view_password(self): def view_password(self, view_password_value): self._view_password = view_password_value + @property + def password_history(self): + return self._password_history or [] + + @password_history.setter + def password_history(self, password_history_value): + self._password_history = password_history_value + @property def collection_ids(self): return self._collection_ids From a110ac76d952bbceb2faa5c7906c6fa26a83501f Mon Sep 17 00:00:00 2001 From: Khai Tran Date: Fri, 5 Jul 2024 17:49:08 +0700 Subject: [PATCH 2/4] chore: Update password history when sharing --- locker_server/api/v1_0/sharing/serializers.py | 3 ++- locker_server/api_orm/repositories/sharing_repository.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/locker_server/api/v1_0/sharing/serializers.py b/locker_server/api/v1_0/sharing/serializers.py index b4b2ce80..12336234 100644 --- a/locker_server/api/v1_0/sharing/serializers.py +++ b/locker_server/api/v1_0/sharing/serializers.py @@ -4,7 +4,7 @@ from locker_server.shared.constants.members import MEMBER_ROLE_ADMIN, MEMBER_ROLE_MEMBER from locker_server.api.v1_0.ciphers.serializers import ItemFieldSerializer, LoginVaultSerializer, \ SecurityNoteVaultSerializer, CardVaultSerializer, IdentityVaultSerializer, CryptoAccountSerializer, \ - CryptoWalletSerializer + CryptoWalletSerializer, PasswordHistorySerializer from locker_server.shared.utils.app import get_cipher_detail_data @@ -88,6 +88,7 @@ class CipherShareSerializer(serializers.Serializer): identity = IdentityVaultSerializer(required=False, many=False, allow_null=True) cryptoAccount = CryptoAccountSerializer(required=False, many=False, allow_null=True) cryptoWallet = CryptoWalletSerializer(required=False, many=False, allow_null=True) + passwordHistory = PasswordHistorySerializer(many=True, required=False) class FolderShareSerializer(serializers.Serializer): diff --git a/locker_server/api_orm/repositories/sharing_repository.py b/locker_server/api_orm/repositories/sharing_repository.py index 44af99a1..941cc490 100644 --- a/locker_server/api_orm/repositories/sharing_repository.py +++ b/locker_server/api_orm/repositories/sharing_repository.py @@ -115,6 +115,7 @@ def _share_cipher_orm(cipher_orm: CipherORM, team_id: str, cipher_data) -> Ciphe cipher_orm.score = cipher_data.get("score", cipher_orm.score) cipher_orm.type = cipher_data.get("type", cipher_orm.type) cipher_orm.data = cipher_data.get("data", cipher_orm.get_data()) + cipher_orm.password_history = cipher_data.get("password_history", cipher_orm.get_password_history()) cipher_orm.user_id = None cipher_orm.team_id = team_id cipher_orm.save() @@ -128,6 +129,7 @@ def _stop_share_cipher_orm(cipher_orm: CipherORM, user_id: int, cipher_data): cipher_orm.score = cipher_data.get("score", cipher_orm.score) cipher_orm.type = cipher_data.get("type", cipher_orm.type) cipher_orm.data = cipher_data.get("data", cipher_orm.get_data()) + cipher_orm.password_history = cipher_data.get("password_history", cipher_orm.get_password_history()) cipher_orm.user_id = user_id cipher_orm.team_id = None cipher_orm.save() From 629091d8322ff3b75675be948a6096cdee4e525a Mon Sep 17 00:00:00 2001 From: Khai Tran Date: Fri, 5 Jul 2024 17:53:44 +0700 Subject: [PATCH 3/4] chore: Update password history when sharing --- locker_server/api/v1_0/sharing/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locker_server/api/v1_0/sharing/serializers.py b/locker_server/api/v1_0/sharing/serializers.py index 12336234..f7d7c356 100644 --- a/locker_server/api/v1_0/sharing/serializers.py +++ b/locker_server/api/v1_0/sharing/serializers.py @@ -129,7 +129,8 @@ def __get_shared_cipher_data(cipher): "attachments": None, "fields": cipher.get("fields"), "collection_ids": [], - "data": get_cipher_detail_data(cipher) + "data": get_cipher_detail_data(cipher), + "password_history": cipher.get("password_history") or cipher.get("passwordHistory") } return shared_cipher_data From 0c9a0323032ad0aaac7e2ce154e2423890abdf9b Mon Sep 17 00:00:00 2001 From: Khai Tran Date: Fri, 5 Jul 2024 18:28:58 +0700 Subject: [PATCH 4/4] fix: Fix password history when sharing --- locker_server/api/v1_0/sharing/serializers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/locker_server/api/v1_0/sharing/serializers.py b/locker_server/api/v1_0/sharing/serializers.py index f7d7c356..c12ec7e8 100644 --- a/locker_server/api/v1_0/sharing/serializers.py +++ b/locker_server/api/v1_0/sharing/serializers.py @@ -229,7 +229,8 @@ def __get_shared_cipher_data(cipher): "attachments": None, "fields": cipher.get("fields"), "collection_ids": [], - "data": get_cipher_detail_data(cipher) + "data": get_cipher_detail_data(cipher), + "password_history": cipher.get("password_history") or cipher.get("passwordHistory") } return shared_cipher_data @@ -295,7 +296,8 @@ def __get_personal_cipher_data(cipher): "score": cipher.get("score", 0), "reprompt": cipher.get("reprompt", 0), "fields": cipher.get("fields"), - "data": get_cipher_detail_data(cipher) + "data": get_cipher_detail_data(cipher), + "password_history": cipher.get("password_history") or cipher.get("passwordHistory") } return shared_cipher_data @@ -339,7 +341,8 @@ def __get_personal_cipher_data(cipher): "score": cipher.get("score", 0), "reprompt": cipher.get("reprompt", 0), "fields": cipher.get("fields"), - "data": get_cipher_detail_data(cipher) + "data": get_cipher_detail_data(cipher), + "password_history": cipher.get("password_history") or cipher.get("passwordHistory") } return shared_cipher_data @@ -367,7 +370,8 @@ def __get_share_cipher_data(cipher): "score": cipher.get("score", 0), "reprompt": cipher.get("reprompt", 0), "fields": cipher.get("fields"), - "data": get_cipher_detail_data(cipher) + "data": get_cipher_detail_data(cipher), + "password_history": cipher.get("password_history") or cipher.get("passwordHistory") } return shared_cipher_data