@@ -17,6 +17,7 @@ package controlapi
1717import (
1818 "context"
1919 "fmt"
20+ "strings"
2021 "testing"
2122
2223 "github.com/agent-substrate/substrate/cmd/ateapi/internal/defaults"
@@ -40,14 +41,24 @@ func validEgressPolicy() *ateapipb.EgressPolicy {
4041 ReplaceHeaders : []* ateapipb.CredentialHeader {{
4142 Header : "Authorization" ,
4243 Prefix : "Bearer " ,
43- CredentialUri : "ate-secret://kubernetes.io/provider/ns/name" ,
44+ CredentialUri : proto . String ( "ate-secret://kubernetes.io/provider/ns/name" ) ,
4445 }},
4546 },
4647 },
4748 }},
4849 }
4950}
5051
52+ // actorJWTHeader is an Authorization header replaced with an actor JWT for
53+ // audiences.
54+ func actorJWTHeader (audiences ... string ) * ateapipb.CredentialHeader {
55+ return & ateapipb.CredentialHeader {
56+ Header : "Authorization" ,
57+ Prefix : "Bearer " ,
58+ ActorJwt : & ateapipb.ActorJWTSource {Audiences : audiences },
59+ }
60+ }
61+
5162func TestValidateCreateActorEgressPolicyRequest (t * testing.T ) {
5263 validReq := func () * ateapipb.CreateActorEgressPolicyRequest {
5364 return & ateapipb.CreateActorEgressPolicyRequest {
@@ -757,7 +768,7 @@ func TestValidateEgressPolicyRules(t *testing.T) {
757768 mutate : func (p * ateapipb.EgressPolicy ) {
758769 p .Rules [0 ].Http .Effects .ReplaceHeaders = append (
759770 p .Rules [0 ].Http .Effects .ReplaceHeaders ,
760- & ateapipb.CredentialHeader {Header : "authorization" , CredentialUri : "ate-secret://example.com/provider/secret" },
771+ & ateapipb.CredentialHeader {Header : "authorization" , CredentialUri : proto . String ( "ate-secret://example.com/provider/secret" ) },
761772 )
762773 },
763774 want : field.ErrorList {
@@ -777,7 +788,7 @@ func TestValidateEgressPolicyRules(t *testing.T) {
777788 for i := range 16 {
778789 injections = append (injections , & ateapipb.CredentialHeader {
779790 Header : fmt .Sprintf ("X-Header-%d" , i ),
780- CredentialUri : "ate-secret://example.com/provider/secret" ,
791+ CredentialUri : proto . String ( "ate-secret://example.com/provider/secret" ) ,
781792 })
782793 }
783794 p .Rules [0 ].Http .Effects .ReplaceHeaders = injections
@@ -789,7 +800,7 @@ func TestValidateEgressPolicyRules(t *testing.T) {
789800 for i := range 17 {
790801 injections = append (injections , & ateapipb.CredentialHeader {
791802 Header : fmt .Sprintf ("X-Header-%d" , i ),
792- CredentialUri : "ate-secret://example.com/provider/secret" ,
803+ CredentialUri : proto . String ( "ate-secret://example.com/provider/secret" ) ,
793804 })
794805 }
795806 p .Rules [0 ].Http .Effects .ReplaceHeaders = injections
@@ -806,21 +817,127 @@ func TestValidateEgressPolicyRules(t *testing.T) {
806817 field .Invalid (staticHeader .Child ("prefix" ), "Bearer\r " , "must be a valid HTTP field value prefix" ),
807818 },
808819 }, {
809- name : "missing credential URI" ,
820+ name : "no credential source" ,
821+ mutate : func (p * ateapipb.EgressPolicy ) {
822+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].CredentialUri = nil
823+ },
824+ want : field.ErrorList {
825+ field .Invalid (staticHeader , nil , "one of" ).WithOrigin ("union" ),
826+ },
827+ }, {
828+ name : "empty credential URI" ,
810829 mutate : func (p * ateapipb.EgressPolicy ) {
811- p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].CredentialUri = ""
830+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].CredentialUri = proto . String ( "" )
812831 },
813832 want : field.ErrorList {
814- field .Required (staticHeader .Child ("credential_uri" ), "" ),
833+ field .Invalid (staticHeader .Child ("credential_uri" ), "" , "must be ate-secret://<provider-class>/<provider-name>/<provider-specific-tail> " ),
815834 },
816835 }, {
817836 name : "invalid credential URI" ,
818837 mutate : func (p * ateapipb.EgressPolicy ) {
819- p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].CredentialUri = "https://example.com/secret"
838+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].CredentialUri = proto . String ( "https://example.com/secret" )
820839 },
821840 want : field.ErrorList {
822841 field .Invalid (staticHeader .Child ("credential_uri" ), "https://example.com/secret" , "must be ate-secret://<provider-class>/<provider-name>/<provider-specific-tail>" ),
823842 },
843+ }, {
844+ name : "actor JWT on an http rule" ,
845+ mutate : func (p * ateapipb.EgressPolicy ) {
846+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader ("https://api.example.com" )
847+ },
848+ }, {
849+ name : "actor JWT on an https rule" ,
850+ mutate : func (p * ateapipb.EgressPolicy ) {
851+ p .Rules [0 ] = & ateapipb.EgressRule {Https : & ateapipb.HTTPSRule {
852+ Hostnames : []string {"api.example.com" },
853+ Effects : & ateapipb.HttpRuleEffects {ReplaceHeaders : []* ateapipb.CredentialHeader {actorJWTHeader ("https://api.example.com" )}},
854+ }}
855+ },
856+ }, {
857+ name : "actor JWT next to a credential URI" ,
858+ mutate : func (p * ateapipb.EgressPolicy ) {
859+ jwt := actorJWTHeader ("https://api.example.com" )
860+ jwt .Header = "X-Actor-Token"
861+ p .Rules [0 ].Http .Effects .ReplaceHeaders = append (p .Rules [0 ].Http .Effects .ReplaceHeaders , jwt )
862+ },
863+ }, {
864+ name : "credential URI and actor JWT in one header" ,
865+ mutate : func (p * ateapipb.EgressPolicy ) {
866+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].ActorJwt = & ateapipb.ActorJWTSource {Audiences : []string {"https://api.example.com" }}
867+ },
868+ want : field.ErrorList {
869+ field .Invalid (staticHeader , nil , "one of" ).WithOrigin ("union" ),
870+ },
871+ }, {
872+ name : "actor JWT without audiences" ,
873+ mutate : func (p * ateapipb.EgressPolicy ) {
874+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader ()
875+ },
876+ want : field.ErrorList {
877+ field .Required (staticHeader .Child ("actor_jwt" , "audiences" ), "" ),
878+ },
879+ }, {
880+ name : "actor JWT with an empty audience" ,
881+ mutate : func (p * ateapipb.EgressPolicy ) {
882+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader ("" )
883+ },
884+ want : field.ErrorList {
885+ field .TooShort (staticHeader .Child ("actor_jwt" , "audiences" ).Index (0 ), "" , 1 ).WithOrigin ("minLength" ),
886+ },
887+ }, {
888+ name : "actor JWT with a long audience" ,
889+ mutate : func (p * ateapipb.EgressPolicy ) {
890+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader (strings .Repeat ("a" , 513 ))
891+ },
892+ want : field.ErrorList {
893+ field .TooLong (staticHeader .Child ("actor_jwt" , "audiences" ).Index (0 ), "" , 512 ).WithOrigin ("maxLength" ),
894+ },
895+ }, {
896+ name : "actor JWT with a repeated audience" ,
897+ mutate : func (p * ateapipb.EgressPolicy ) {
898+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader ("a" , "a" )
899+ },
900+ want : field.ErrorList {
901+ field .Duplicate (staticHeader .Child ("actor_jwt" , "audiences" ).Index (1 ), "a" ),
902+ },
903+ }, {
904+ name : "actor JWT with too many audiences" ,
905+ mutate : func (p * ateapipb.EgressPolicy ) {
906+ var audiences []string
907+ for i := range 17 {
908+ audiences = append (audiences , fmt .Sprintf ("aud-%d" , i ))
909+ }
910+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader (audiences ... )
911+ },
912+ want : field.ErrorList {
913+ field .TooMany (staticHeader .Child ("actor_jwt" , "audiences" ), 17 , 16 ).WithOrigin ("maxItems" ),
914+ },
915+ }, {
916+ name : "actor JWT lifetime at its bounds" ,
917+ mutate : func (p * ateapipb.EgressPolicy ) {
918+ short , long := actorJWTHeader ("a" ), actorJWTHeader ("a" )
919+ short .ActorJwt .ExpirationSeconds = 300
920+ long .Header , long .ActorJwt .ExpirationSeconds = "X-Actor-Token" , 3600
921+ p .Rules [0 ].Http .Effects .ReplaceHeaders = []* ateapipb.CredentialHeader {short , long }
922+ },
923+ }, {
924+ name : "actor JWT lifetime too short" ,
925+ mutate : func (p * ateapipb.EgressPolicy ) {
926+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader ("a" )
927+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].ActorJwt .ExpirationSeconds = 299
928+ },
929+ want : field.ErrorList {
930+ field .Invalid (staticHeader .Child ("actor_jwt" , "expiration_seconds" ), 299 , "" ).WithOrigin ("minimum" ),
931+ },
932+ }, {
933+ name : "actor JWT lifetime too long" ,
934+ mutate : func (p * ateapipb.EgressPolicy ) {
935+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ] = actorJWTHeader ("a" )
936+ p .Rules [0 ].Http .Effects .ReplaceHeaders [0 ].ActorJwt .ExpirationSeconds = 3601
937+ },
938+ want : field.ErrorList {
939+ field .Invalid (staticHeader .Child ("actor_jwt" , "expiration_seconds" ), 3601 , "" ).WithOrigin ("maximum" ),
940+ },
824941 }, {
825942 name : "empty effects" ,
826943 mutate : func (p * ateapipb.EgressPolicy ) {
@@ -893,7 +1010,7 @@ func TestActorEgressPolicy(t *testing.T) {
8931010 ReplaceHeaders : []* ateapipb.CredentialHeader {{
8941011 Header : "Authorization" ,
8951012 Prefix : "Bearer " ,
896- CredentialUri : "ate-secret://kubernetes.io/provider/ns/name" ,
1013+ CredentialUri : proto . String ( "ate-secret://kubernetes.io/provider/ns/name" ) ,
8971014 }},
8981015 },
8991016 },
0 commit comments