diff --git a/.github/workflows/staging-deploy.yml b/.github/workflows/staging-deploy.yml index 230d686..cc189df 100644 --- a/.github/workflows/staging-deploy.yml +++ b/.github/workflows/staging-deploy.yml @@ -17,7 +17,7 @@ jobs: with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} + key: ${{ secrets.KEY }} script: | set -e diff --git a/protos/v1/vault.proto b/protos/v1/vault.proto index 9672ba2..6bc8eb6 100644 --- a/protos/v1/vault.proto +++ b/protos/v1/vault.proto @@ -4,241 +4,241 @@ package vault.v1; // Request message for creating an entity. message CreateEntityRequest { - // The ISO 3166-1 alpha-2 country code of the entity. - string country_code = 1; - // The phone number of the entity. - string phone_number = 2; - // The password of the entity. - string password = 3; - // The client's public key for publishing. - string client_publish_pub_key = 4; - // The client's public key for device identification. - string client_device_id_pub_key = 5; - // The ownership proof response from the client. - string ownership_proof_response = 6; + // The ISO 3166-1 alpha-2 country code of the entity. + string country_code = 1; + // The phone number of the entity. + string phone_number = 2; + // The password of the entity. + string password = 3; + // The client's public key for publishing. + string client_publish_pub_key = 4; + // The client's public key for device identification. + string client_device_id_pub_key = 5; + // The ownership proof response from the client. + string ownership_proof_response = 6; } // Response message for creating an entity. message CreateEntityResponse { - // Indicates if ownership proof is required. - bool requires_ownership_proof = 1; - // A long-lived token for the authenticated entity. - string long_lived_token = 2; - // The server's public key for publishing. - string server_publish_pub_key = 3; - // The server's public key for device identification. - string server_device_id_pub_key = 4; - // A response message. - string message = 5; - // The next available time to request another proof of ownership. - int32 next_attempt_timestamp = 6; + // Indicates if ownership proof is required. + bool requires_ownership_proof = 1; + // A long-lived token for the authenticated entity. + string long_lived_token = 2; + // The server's public key for publishing. + string server_publish_pub_key = 3; + // The server's public key for device identification. + string server_device_id_pub_key = 4; + // A response message. + string message = 5; + // The next available time to request another proof of ownership. + int32 next_attempt_timestamp = 6; } // Request message for authenticating an entity. message AuthenticateEntityRequest { - // The phone number of the entity. - string phone_number = 1; - // The password of the entity. - string password = 2; - // The client's public key for publishing. - string client_publish_pub_key = 3; - // The client's public key for device identification. - string client_device_id_pub_key = 4; - // The ownership proof response from the client. - string ownership_proof_response = 5; + // The phone number of the entity. + string phone_number = 1; + // The password of the entity. + string password = 2; + // The client's public key for publishing. + string client_publish_pub_key = 3; + // The client's public key for device identification. + string client_device_id_pub_key = 4; + // The ownership proof response from the client. + string ownership_proof_response = 5; } // Response message for authenticating an entity. message AuthenticateEntityResponse { - // Indicates if ownership proof is required. - bool requires_ownership_proof = 1; - // A long-lived token for the authenticated entity. - string long_lived_token = 2; - // The server's public key for publishing. - string server_publish_pub_key = 3; - // The server's public key for device identification. - string server_device_id_pub_key = 4; - // A response message. - string message = 5; - // The next available time to request another proof of ownership. - int32 next_attempt_timestamp = 6; + // Indicates if ownership proof is required. + bool requires_ownership_proof = 1; + // A long-lived token for the authenticated entity. + string long_lived_token = 2; + // The server's public key for publishing. + string server_publish_pub_key = 3; + // The server's public key for device identification. + string server_device_id_pub_key = 4; + // A response message. + string message = 5; + // The next available time to request another proof of ownership. + int32 next_attempt_timestamp = 6; } // Request message for listing entity's stored tokens. message ListEntityStoredTokenRequest { - // The long-lived token of the authenticated entity. - string long_lived_token = 1; + // The long-lived token of the authenticated entity. + string long_lived_token = 1; } // Response message for listing entity's stored tokens. message ListEntityStoredTokenResponse { - // The list of stored tokens. - repeated Token stored_tokens = 1; - // A response message. - string message = 2; + // The list of stored tokens. + repeated Token stored_tokens = 1; + // A response message. + string message = 2; } // Represents a token. message Token { - // The platform associated with the token. - string platform = 1; - // The unique identifier of the account associated with the token. - string account_identifier = 2; + // The platform associated with the token. + string platform = 1; + // The unique identifier of the account associated with the token. + string account_identifier = 2; } // Request message for storing an entity's token. message StoreEntityTokenRequest { - // The long-lived token of the authenticated entity. - string long_lived_token = 1; - // The OAuth2 token to be stored (JSON string). - string token = 2; - // The platform associated with the token. - string platform = 3; - // The identifier of the account associated with the token. - string account_identifier = 4; + // The long-lived token of the authenticated entity. + string long_lived_token = 1; + // The OAuth2 token to be stored (JSON string). + string token = 2; + // The platform associated with the token. + string platform = 3; + // The identifier of the account associated with the token. + string account_identifier = 4; } // Response message for storing an entity's token. message StoreEntityTokenResponse { - // A response message. - string message = 1; - // Indicates whether the operation was successful. - bool success = 2; + // A response message. + string message = 1; + // Indicates whether the operation was successful. + bool success = 2; } // Request message for getting entity access token. message GetEntityAccessTokenRequest { - // Device ID for identifying the requesting device. - string device_id = 1; - // The long-lived token of the authenticated entity. - string long_lived_token = 2; - // The platform associated with the token. - string platform = 3; - // The identifier of the account associated with the token. - string account_identifier = 4; + // Device ID for identifying the requesting device. + string device_id = 1; + // The long-lived token of the authenticated entity. + string long_lived_token = 2; + // The platform associated with the token. + string platform = 3; + // The identifier of the account associated with the token. + string account_identifier = 4; } // Response message for getting entity access token. message GetEntityAccessTokenResponse { - // Entity access token (JSON string). - string token = 1; - // A response message. - string message = 2; - // Indicates whether the operation was successful. - bool success = 3; + // Entity access token (JSON string). + string token = 1; + // A response message. + string message = 2; + // Indicates whether the operation was successful. + bool success = 3; } // Request message for decrypting payload. message DecryptPayloadRequest { - // Device ID for identifying the requesting device. - string device_id = 1; - // Encrypted payload that needs to be decrypted. - string payload_ciphertext = 2; + // Device ID for identifying the requesting device. + string device_id = 1; + // Encrypted payload that needs to be decrypted. + string payload_ciphertext = 2; } message DecryptPayloadResponse { - // Decrypted plaintext payload. - string payload_plaintext = 1; - // A response message. - string message = 2; - // Indicates whether the operation was successful. - bool success = 3; + // Decrypted plaintext payload. + string payload_plaintext = 1; + // A response message. + string message = 2; + // Indicates whether the operation was successful. + bool success = 3; } // Request message for encrypting payload. message EncryptPayloadRequest { - // Device ID for identifying the requesting device. - string device_id = 1; - // Plaintext payload to be encrypted. - string payload_plaintext = 2; + // Device ID for identifying the requesting device. + string device_id = 1; + // Plaintext payload to be encrypted. + string payload_plaintext = 2; } // Response message for encrypting payload. message EncryptPayloadResponse { - // Encrypted payload. - string payload_ciphertext = 1; - // A response message. - string message = 2; - // Indicates whether the operation was successful. - bool success = 3; + // Encrypted payload. + string payload_ciphertext = 1; + // A response message. + string message = 2; + // Indicates whether the operation was successful. + bool success = 3; } // Request message for updating an entity's token. message UpdateEntityTokenRequest { - // Device ID for identifying the requesting device. - string device_id = 1; - // The OAuth2 token to be stored (JSON string). - string token = 2; - // The platform associated with the token. - string platform = 3; - // The identifier of the account associated with the token. - string account_identifier = 4; + // Device ID for identifying the requesting device. + string device_id = 1; + // The OAuth2 token to be stored (JSON string). + string token = 2; + // The platform associated with the token. + string platform = 3; + // The identifier of the account associated with the token. + string account_identifier = 4; } // Response message for updating an entity's token. message UpdateEntityTokenResponse { - // A response message. - string message = 1; - // Indicates whether the operation was successful. - bool success = 2; + // A response message. + string message = 1; + // Indicates whether the operation was successful. + bool success = 2; } // Request message for deleting an entity's token. message DeleteEntityTokenRequest { - // The long-lived token of the authenticated entity. - string long_lived_token = 1; - // The platform associated with the token. - string platform = 2; - // The identifier of the account associated with the token. - string account_identifier = 3; + // The long-lived token of the authenticated entity. + string long_lived_token = 1; + // The platform associated with the token. + string platform = 2; + // The identifier of the account associated with the token. + string account_identifier = 3; } // Response message for deleting an entity's token. message DeleteEntityTokenResponse { - // A response message. - string message = 1; - // Indicates whether the operation was successful. - bool success = 2; + // A response message. + string message = 1; + // Indicates whether the operation was successful. + bool success = 2; } // Request message for deleting an entity. message DeleteEntityRequest { - // The long-lived token of the authenticated entity. - string long_lived_token = 1; + // The long-lived token of the authenticated entity. + string long_lived_token = 1; } // Response message for deleting an entity. message DeleteEntityResponse { - // A response message. - string message = 1; - // Indicates whether the operation was successful. - bool success = 2; + // A response message. + string message = 1; + // Indicates whether the operation was successful. + bool success = 2; } // Service for managing entities. service Entity { - // Creates an entity. - rpc CreateEntity (CreateEntityRequest) returns (CreateEntityResponse); - // Authenticates an entity. - rpc AuthenticateEntity (AuthenticateEntityRequest) returns (AuthenticateEntityResponse); - // Lists all stored access tokens for an entity. - rpc ListEntityStoredTokens (ListEntityStoredTokenRequest) returns (ListEntityStoredTokenResponse); - // Deletes an entity. - rpc DeleteEntity (DeleteEntityRequest) returns (DeleteEntityResponse); + // Creates an entity. + rpc CreateEntity(CreateEntityRequest) returns (CreateEntityResponse); + // Authenticates an entity. + rpc AuthenticateEntity(AuthenticateEntityRequest) returns (AuthenticateEntityResponse); + // Lists all stored access tokens for an entity. + rpc ListEntityStoredTokens(ListEntityStoredTokenRequest) returns (ListEntityStoredTokenResponse); + // Deletes an entity. + rpc DeleteEntity(DeleteEntityRequest) returns (DeleteEntityResponse); } // Service for managing entities internally. service EntityInternal { - // Stores a token for an entity. - rpc StoreEntityToken (StoreEntityTokenRequest) returns (StoreEntityTokenResponse); - // Get an entity's access token. - rpc GetEntityAccessToken (GetEntityAccessTokenRequest) returns (GetEntityAccessTokenResponse); - // Decrypt payload. - rpc DecryptPayload (DecryptPayloadRequest) returns (DecryptPayloadResponse); - // Encrypt payload. - rpc EncryptPayload (EncryptPayloadRequest) returns (EncryptPayloadResponse); - // Updates an entity's access token. - rpc UpdateEntityToken (UpdateEntityTokenRequest) returns (UpdateEntityTokenResponse); - // Deletes an entity's access token. - rpc DeleteEntityToken (DeleteEntityTokenRequest) returns (DeleteEntityTokenResponse); + // Stores a token for an entity. + rpc StoreEntityToken(StoreEntityTokenRequest) returns (StoreEntityTokenResponse); + // Get an entity's access token. + rpc GetEntityAccessToken(GetEntityAccessTokenRequest) returns (GetEntityAccessTokenResponse); + // Decrypt payload. + rpc DecryptPayload(DecryptPayloadRequest) returns (DecryptPayloadResponse); + // Encrypt payload. + rpc EncryptPayload(EncryptPayloadRequest) returns (EncryptPayloadResponse); + // Updates an entity's access token. + rpc UpdateEntityToken(UpdateEntityTokenRequest) returns (UpdateEntityTokenResponse); + // Deletes an entity's access token. + rpc DeleteEntityToken(DeleteEntityTokenRequest) returns (DeleteEntityTokenResponse); } diff --git a/src/db_models.py b/src/db_models.py index 1bab7a6..b22cd48 100644 --- a/src/db_models.py +++ b/src/db_models.py @@ -39,14 +39,8 @@ class Meta: database = database table_name = "entities" indexes = ( - ( - ("phone_number_hash",), - True, - ), - ( - ("device_id",), - True, - ), + (("phone_number_hash",), True), + (("device_id",), True), ) @@ -81,7 +75,10 @@ class Meta: database = database table_name = "tokens" - indexes = ((("platform", "eid", "account_identifier_hash"), True),) + indexes = ( + (("platform", "eid", "account_identifier_hash"), True), + (("account_identifier_hash",), True), + ) if Configurations.MODE in ("production", "development"): diff --git a/src/entity.py b/src/entity.py index 86cc2bc..8b16ac6 100644 --- a/src/entity.py +++ b/src/entity.py @@ -43,7 +43,8 @@ def find_entity(**search_criteria): Returns: Entity or None: The found entity if exists, else None. """ - try: - return Entity.get(**search_criteria) - except DoesNotExist: - return None + with database.connection_context(): + try: + return Entity.get(**search_criteria) + except DoesNotExist: + return None diff --git a/src/grpc_entity_internal_service.py b/src/grpc_entity_internal_service.py index 89c22eb..d3c0e42 100644 --- a/src/grpc_entity_internal_service.py +++ b/src/grpc_entity_internal_service.py @@ -9,7 +9,7 @@ import vault_pb2_grpc from src.entity import find_entity -from src.tokens import fetch_entity_tokens, create_entity_token +from src.tokens import fetch_entity_tokens, create_entity_token, find_token from src.crypto import generate_hmac from src.utils import ( load_key, @@ -162,6 +162,33 @@ def StoreEntityToken(self, request, context): response = vault_pb2.StoreEntityTokenResponse + def check_existing_token(entity_obj, account_identifier_hash): + existing_tokens = fetch_entity_tokens( + entity=entity_obj, + account_identifier_hash=account_identifier_hash, + platform=request.platform, + ) + + if existing_tokens: + return error_response( + context, + response, + "Entity already has a token associated with account " + f"identifier {request.account_identifier} for {request.platform}", + grpc.StatusCode.ALREADY_EXISTS, + ) + + if find_token(account_identifier_hash=account_identifier_hash): + return error_response( + context, + response, + "An entity already has a token associated with the account " + f"identifier '{request.account_identifier}'.", + grpc.StatusCode.ALREADY_EXISTS, + ) + + return None + try: invalid_fields_response = validate_request_fields( context, @@ -189,20 +216,10 @@ def StoreEntityToken(self, request, context): HASHING_KEY, request.account_identifier ) - existing_tokens = fetch_entity_tokens( - entity=entity_obj, - account_identifier_hash=account_identifier_hash, - platform=request.platform, - ) + existing_token = check_existing_token(entity_obj, account_identifier_hash) - if existing_tokens: - return error_response( - context, - response, - "Entity already has a token associated with account " - f"identifier {request.account_identifier} for {request.platform}", - grpc.StatusCode.ALREADY_EXISTS, - ) + if existing_token: + return existing_token new_token = { "entity": entity_obj, diff --git a/src/tokens.py b/src/tokens.py index 5bd5fd1..d4ba573 100644 --- a/src/tokens.py +++ b/src/tokens.py @@ -3,6 +3,7 @@ """ from playhouse.shortcuts import model_to_dict +from peewee import DoesNotExist from src.db_models import Token from src.utils import remove_none_values @@ -91,3 +92,21 @@ def fetch_entity_tokens( return remove_none_values(results) return tokens + + +def find_token(**search_criteria): + """ + Find a single token based on search criteria. + + Args: + **search_criteria: Additional keyword arguments representing the fields + and their values to search for. + + Returns: + Token or None: The token object if found, otherwise None. + """ + with database.connection_context(): + try: + return Token.get(**search_criteria) + except DoesNotExist: + return None