@@ -8971,3 +8971,198 @@ def test_delete_provider_updates_tenant_defaults(
89718971
89728972 # Unrelated entries should remain untouched
89738973 assert cfg .default_models .get ("other" ) == "model-x"
8974+
8975+ @pytest .mark .parametrize (
8976+ "credentials" ,
8977+ [
8978+ {}, # empty credentials
8979+ {
8980+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE"
8981+ }, # missing secret_access_key and region
8982+ {
8983+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
8984+ }, # missing access_key_id and region
8985+ {
8986+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
8987+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
8988+ }, # missing region
8989+ { # invalid access_key_id format (not starting with AKIA)
8990+ "access_key_id" : "ABCD0123456789ABCDEF" ,
8991+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
8992+ "region" : "us-east-1" ,
8993+ },
8994+ { # invalid access_key_id format (wrong length)
8995+ "access_key_id" : "AKIAIOSFODNN7EXAMPL" ,
8996+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
8997+ "region" : "us-east-1" ,
8998+ },
8999+ { # invalid secret_access_key format (wrong length)
9000+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9001+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEK" ,
9002+ "region" : "us-east-1" ,
9003+ },
9004+ { # invalid region format
9005+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9006+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9007+ "region" : "invalid-region" ,
9008+ },
9009+ { # invalid region format (uppercase)
9010+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9011+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9012+ "region" : "US-EAST-1" ,
9013+ },
9014+ ],
9015+ )
9016+ def test_bedrock_invalid_credentials (self , authenticated_client , credentials ):
9017+ """Bedrock provider with invalid credentials should error"""
9018+ payload = {
9019+ "data" : {
9020+ "type" : "lighthouse-providers" ,
9021+ "attributes" : {
9022+ "provider_type" : "bedrock" ,
9023+ "credentials" : credentials ,
9024+ },
9025+ }
9026+ }
9027+ resp = authenticated_client .post (
9028+ reverse ("lighthouse-providers-list" ),
9029+ data = payload ,
9030+ content_type = API_JSON_CONTENT_TYPE ,
9031+ )
9032+ assert resp .status_code == status .HTTP_400_BAD_REQUEST
9033+
9034+ def test_bedrock_valid_credentials_success (self , authenticated_client ):
9035+ """Bedrock provider with valid AWS credentials should succeed and mask credentials"""
9036+ valid_credentials = {
9037+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9038+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9039+ "region" : "us-east-1" ,
9040+ }
9041+ payload = {
9042+ "data" : {
9043+ "type" : "lighthouse-providers" ,
9044+ "attributes" : {
9045+ "provider_type" : "bedrock" ,
9046+ "credentials" : valid_credentials ,
9047+ },
9048+ }
9049+ }
9050+ resp = authenticated_client .post (
9051+ reverse ("lighthouse-providers-list" ),
9052+ data = payload ,
9053+ content_type = API_JSON_CONTENT_TYPE ,
9054+ )
9055+ assert resp .status_code == status .HTTP_201_CREATED
9056+ data = resp .json ()["data" ]
9057+
9058+ # Verify credentials are returned masked
9059+ masked_creds = data ["attributes" ].get ("credentials" )
9060+ assert masked_creds is not None
9061+ assert "access_key_id" in masked_creds
9062+ assert "secret_access_key" in masked_creds
9063+ assert "region" in masked_creds
9064+ # Verify all characters are masked with asterisks
9065+ assert all (c == "*" for c in masked_creds ["access_key_id" ])
9066+ assert all (c == "*" for c in masked_creds ["secret_access_key" ])
9067+
9068+ def test_bedrock_provider_duplicate_per_tenant (self , authenticated_client ):
9069+ """Creating a second Bedrock provider for same tenant should fail"""
9070+ valid_credentials = {
9071+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9072+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9073+ "region" : "us-west-2" ,
9074+ }
9075+ payload = {
9076+ "data" : {
9077+ "type" : "lighthouse-providers" ,
9078+ "attributes" : {
9079+ "provider_type" : "bedrock" ,
9080+ "credentials" : valid_credentials ,
9081+ },
9082+ }
9083+ }
9084+ # First creation succeeds
9085+ resp1 = authenticated_client .post (
9086+ reverse ("lighthouse-providers-list" ),
9087+ data = payload ,
9088+ content_type = API_JSON_CONTENT_TYPE ,
9089+ )
9090+ assert resp1 .status_code == status .HTTP_201_CREATED
9091+
9092+ # Second creation should fail with validation error
9093+ resp2 = authenticated_client .post (
9094+ reverse ("lighthouse-providers-list" ),
9095+ data = payload ,
9096+ content_type = API_JSON_CONTENT_TYPE ,
9097+ )
9098+ assert resp2 .status_code == status .HTTP_400_BAD_REQUEST
9099+ assert "already exists" in str (resp2 .json ()).lower ()
9100+
9101+ def test_bedrock_patch_credentials_and_fields_filter (self , authenticated_client ):
9102+ """PATCH credentials and verify fields filter returns decrypted values"""
9103+ valid_credentials = {
9104+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9105+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9106+ "region" : "eu-west-1" ,
9107+ }
9108+ create_payload = {
9109+ "data" : {
9110+ "type" : "lighthouse-providers" ,
9111+ "attributes" : {
9112+ "provider_type" : "bedrock" ,
9113+ "credentials" : valid_credentials ,
9114+ },
9115+ }
9116+ }
9117+ create_resp = authenticated_client .post (
9118+ reverse ("lighthouse-providers-list" ),
9119+ data = create_payload ,
9120+ content_type = API_JSON_CONTENT_TYPE ,
9121+ )
9122+ assert create_resp .status_code == status .HTTP_201_CREATED
9123+ provider_id = create_resp .json ()["data" ]["id" ]
9124+
9125+ # Update credentials with new valid ones
9126+ new_credentials = {
9127+ "access_key_id" : "AKIAZZZZZZZZZZZZZZZZ" ,
9128+ "secret_access_key" : "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789+/==" ,
9129+ "region" : "ap-south-1" ,
9130+ }
9131+ patch_payload = {
9132+ "data" : {
9133+ "type" : "lighthouse-providers" ,
9134+ "id" : provider_id ,
9135+ "attributes" : {
9136+ "credentials" : new_credentials ,
9137+ "is_active" : False ,
9138+ },
9139+ }
9140+ }
9141+ patch_resp = authenticated_client .patch (
9142+ reverse ("lighthouse-providers-detail" , kwargs = {"pk" : provider_id }),
9143+ data = patch_payload ,
9144+ content_type = API_JSON_CONTENT_TYPE ,
9145+ )
9146+ assert patch_resp .status_code == status .HTTP_200_OK
9147+ updated = patch_resp .json ()["data" ]["attributes" ]
9148+ assert updated ["is_active" ] is False
9149+
9150+ # Default GET should return masked credentials
9151+ get_resp = authenticated_client .get (
9152+ reverse ("lighthouse-providers-detail" , kwargs = {"pk" : provider_id })
9153+ )
9154+ assert get_resp .status_code == status .HTTP_200_OK
9155+ masked = get_resp .json ()["data" ]["attributes" ]["credentials" ]
9156+ assert all (c == "*" for c in masked ["access_key_id" ])
9157+ assert all (c == "*" for c in masked ["secret_access_key" ])
9158+
9159+ # Fields filter should return decrypted credentials
9160+ get_full = authenticated_client .get (
9161+ reverse ("lighthouse-providers-detail" , kwargs = {"pk" : provider_id })
9162+ + "?fields[lighthouse-providers]=credentials"
9163+ )
9164+ assert get_full .status_code == status .HTTP_200_OK
9165+ creds = get_full .json ()["data" ]["attributes" ]["credentials" ]
9166+ assert creds ["access_key_id" ] == new_credentials ["access_key_id" ]
9167+ assert creds ["secret_access_key" ] == new_credentials ["secret_access_key" ]
9168+ assert creds ["region" ] == new_credentials ["region" ]
0 commit comments