@@ -9186,3 +9186,198 @@ def test_delete_provider_updates_tenant_defaults(
91869186
91879187 # Unrelated entries should remain untouched
91889188 assert cfg .default_models .get ("other" ) == "model-x"
9189+
9190+ @pytest .mark .parametrize (
9191+ "credentials" ,
9192+ [
9193+ {}, # empty credentials
9194+ {
9195+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE"
9196+ }, # missing secret_access_key and region
9197+ {
9198+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
9199+ }, # missing access_key_id and region
9200+ {
9201+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9202+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9203+ }, # missing region
9204+ { # invalid access_key_id format (not starting with AKIA)
9205+ "access_key_id" : "ABCD0123456789ABCDEF" ,
9206+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9207+ "region" : "us-east-1" ,
9208+ },
9209+ { # invalid access_key_id format (wrong length)
9210+ "access_key_id" : "AKIAIOSFODNN7EXAMPL" ,
9211+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9212+ "region" : "us-east-1" ,
9213+ },
9214+ { # invalid secret_access_key format (wrong length)
9215+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9216+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEK" ,
9217+ "region" : "us-east-1" ,
9218+ },
9219+ { # invalid region format
9220+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9221+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9222+ "region" : "invalid-region" ,
9223+ },
9224+ { # invalid region format (uppercase)
9225+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9226+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9227+ "region" : "US-EAST-1" ,
9228+ },
9229+ ],
9230+ )
9231+ def test_bedrock_invalid_credentials (self , authenticated_client , credentials ):
9232+ """Bedrock provider with invalid credentials should error"""
9233+ payload = {
9234+ "data" : {
9235+ "type" : "lighthouse-providers" ,
9236+ "attributes" : {
9237+ "provider_type" : "bedrock" ,
9238+ "credentials" : credentials ,
9239+ },
9240+ }
9241+ }
9242+ resp = authenticated_client .post (
9243+ reverse ("lighthouse-providers-list" ),
9244+ data = payload ,
9245+ content_type = API_JSON_CONTENT_TYPE ,
9246+ )
9247+ assert resp .status_code == status .HTTP_400_BAD_REQUEST
9248+
9249+ def test_bedrock_valid_credentials_success (self , authenticated_client ):
9250+ """Bedrock provider with valid AWS credentials should succeed and mask credentials"""
9251+ valid_credentials = {
9252+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9253+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9254+ "region" : "us-east-1" ,
9255+ }
9256+ payload = {
9257+ "data" : {
9258+ "type" : "lighthouse-providers" ,
9259+ "attributes" : {
9260+ "provider_type" : "bedrock" ,
9261+ "credentials" : valid_credentials ,
9262+ },
9263+ }
9264+ }
9265+ resp = authenticated_client .post (
9266+ reverse ("lighthouse-providers-list" ),
9267+ data = payload ,
9268+ content_type = API_JSON_CONTENT_TYPE ,
9269+ )
9270+ assert resp .status_code == status .HTTP_201_CREATED
9271+ data = resp .json ()["data" ]
9272+
9273+ # Verify credentials are returned masked
9274+ masked_creds = data ["attributes" ].get ("credentials" )
9275+ assert masked_creds is not None
9276+ assert "access_key_id" in masked_creds
9277+ assert "secret_access_key" in masked_creds
9278+ assert "region" in masked_creds
9279+ # Verify all characters are masked with asterisks
9280+ assert all (c == "*" for c in masked_creds ["access_key_id" ])
9281+ assert all (c == "*" for c in masked_creds ["secret_access_key" ])
9282+
9283+ def test_bedrock_provider_duplicate_per_tenant (self , authenticated_client ):
9284+ """Creating a second Bedrock provider for same tenant should fail"""
9285+ valid_credentials = {
9286+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9287+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9288+ "region" : "us-west-2" ,
9289+ }
9290+ payload = {
9291+ "data" : {
9292+ "type" : "lighthouse-providers" ,
9293+ "attributes" : {
9294+ "provider_type" : "bedrock" ,
9295+ "credentials" : valid_credentials ,
9296+ },
9297+ }
9298+ }
9299+ # First creation succeeds
9300+ resp1 = authenticated_client .post (
9301+ reverse ("lighthouse-providers-list" ),
9302+ data = payload ,
9303+ content_type = API_JSON_CONTENT_TYPE ,
9304+ )
9305+ assert resp1 .status_code == status .HTTP_201_CREATED
9306+
9307+ # Second creation should fail with validation error
9308+ resp2 = authenticated_client .post (
9309+ reverse ("lighthouse-providers-list" ),
9310+ data = payload ,
9311+ content_type = API_JSON_CONTENT_TYPE ,
9312+ )
9313+ assert resp2 .status_code == status .HTTP_400_BAD_REQUEST
9314+ assert "already exists" in str (resp2 .json ()).lower ()
9315+
9316+ def test_bedrock_patch_credentials_and_fields_filter (self , authenticated_client ):
9317+ """PATCH credentials and verify fields filter returns decrypted values"""
9318+ valid_credentials = {
9319+ "access_key_id" : "AKIAIOSFODNN7EXAMPLE" ,
9320+ "secret_access_key" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ,
9321+ "region" : "eu-west-1" ,
9322+ }
9323+ create_payload = {
9324+ "data" : {
9325+ "type" : "lighthouse-providers" ,
9326+ "attributes" : {
9327+ "provider_type" : "bedrock" ,
9328+ "credentials" : valid_credentials ,
9329+ },
9330+ }
9331+ }
9332+ create_resp = authenticated_client .post (
9333+ reverse ("lighthouse-providers-list" ),
9334+ data = create_payload ,
9335+ content_type = API_JSON_CONTENT_TYPE ,
9336+ )
9337+ assert create_resp .status_code == status .HTTP_201_CREATED
9338+ provider_id = create_resp .json ()["data" ]["id" ]
9339+
9340+ # Update credentials with new valid ones
9341+ new_credentials = {
9342+ "access_key_id" : "AKIAZZZZZZZZZZZZZZZZ" ,
9343+ "secret_access_key" : "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789+/==" ,
9344+ "region" : "ap-south-1" ,
9345+ }
9346+ patch_payload = {
9347+ "data" : {
9348+ "type" : "lighthouse-providers" ,
9349+ "id" : provider_id ,
9350+ "attributes" : {
9351+ "credentials" : new_credentials ,
9352+ "is_active" : False ,
9353+ },
9354+ }
9355+ }
9356+ patch_resp = authenticated_client .patch (
9357+ reverse ("lighthouse-providers-detail" , kwargs = {"pk" : provider_id }),
9358+ data = patch_payload ,
9359+ content_type = API_JSON_CONTENT_TYPE ,
9360+ )
9361+ assert patch_resp .status_code == status .HTTP_200_OK
9362+ updated = patch_resp .json ()["data" ]["attributes" ]
9363+ assert updated ["is_active" ] is False
9364+
9365+ # Default GET should return masked credentials
9366+ get_resp = authenticated_client .get (
9367+ reverse ("lighthouse-providers-detail" , kwargs = {"pk" : provider_id })
9368+ )
9369+ assert get_resp .status_code == status .HTTP_200_OK
9370+ masked = get_resp .json ()["data" ]["attributes" ]["credentials" ]
9371+ assert all (c == "*" for c in masked ["access_key_id" ])
9372+ assert all (c == "*" for c in masked ["secret_access_key" ])
9373+
9374+ # Fields filter should return decrypted credentials
9375+ get_full = authenticated_client .get (
9376+ reverse ("lighthouse-providers-detail" , kwargs = {"pk" : provider_id })
9377+ + "?fields[lighthouse-providers]=credentials"
9378+ )
9379+ assert get_full .status_code == status .HTTP_200_OK
9380+ creds = get_full .json ()["data" ]["attributes" ]["credentials" ]
9381+ assert creds ["access_key_id" ] == new_credentials ["access_key_id" ]
9382+ assert creds ["secret_access_key" ] == new_credentials ["secret_access_key" ]
9383+ assert creds ["region" ] == new_credentials ["region" ]
0 commit comments