3333
3434import static com .google .auth .oauth2 .OAuth2Utils .TOKEN_EXCHANGE_URL_FORMAT ;
3535import static org .junit .Assert .assertEquals ;
36+ import static org .junit .Assert .assertFalse ;
3637import static org .junit .Assert .assertNotNull ;
3738import static org .junit .Assert .assertThrows ;
3839import static org .junit .Assert .assertTrue ;
@@ -589,6 +590,40 @@ private static void triggerConcurrentRefresh(
589590 }
590591 }
591592
593+ private static class CabToken {
594+ String intermediateToken ;
595+ String encryptedRestriction ;
596+
597+ CabToken (String intermediateToken , String encryptedRestriction ) {
598+ this .intermediateToken = intermediateToken ;
599+ this .encryptedRestriction = encryptedRestriction ;
600+ }
601+ }
602+
603+ private static CabToken parseCabToken (AccessToken token ) throws Exception {
604+ String [] parts = token .getTokenValue ().split ("\\ ." );
605+ assertEquals (parts .length , 2 );
606+
607+ return new CabToken (parts [0 ], parts [1 ]);
608+ }
609+
610+ private static ClientSideAccessBoundary decryptRestriction (String restriction ,
611+ String sessionKey )
612+ throws Exception {
613+ byte [] rawKey = Base64 .getDecoder ().decode (sessionKey );
614+
615+ KeysetHandle keysetHandle = TinkProtoKeysetFormat .parseKeyset (
616+ rawKey , InsecureSecretKeyAccess .get ());
617+
618+ Aead aead =
619+ keysetHandle .getPrimitive (RegistryConfiguration .get (), Aead .class );
620+ byte [] rawRestrictions =
621+ aead .decrypt (Base64 .getUrlDecoder ().decode (restriction ),
622+ /*associatedData=*/ new byte [0 ]);
623+
624+ return ClientSideAccessBoundary .parseFrom (rawRestrictions );
625+ }
626+
592627 @ Test
593628 public void generateToken_withAvailablityCondition_success () throws Exception {
594629 MockStsTransportFactory transportFactory = new MockStsTransportFactory ();
@@ -610,56 +645,42 @@ public void generateToken_withAvailablityCondition_success() throws Exception {
610645 cabBuilder
611646 .addRule (
612647 CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
613- .setAvailableResource ("//storage.googleapis.com/projects/"
614- + "_/buckets/example-bucket" )
615- .setAvailablePermissions (
616- ImmutableList .of ("inRole:roles/storage.objectViewer" ))
648+ .setAvailableResource ("resource" )
649+ .setAvailablePermissions (ImmutableList .of ("role1" , "role2" ))
617650 .setAvailabilityCondition (
618651 CredentialAccessBoundary .AccessBoundaryRule
619652 .AvailabilityCondition .newBuilder ()
620- .setExpression (
621- "resource.name.startsWith('projects/_/"
622- + "buckets/example-bucket/objects/customer-a')" )
653+ .setExpression ("a == b" )
623654 .build ())
624655 .build ())
625656 .build ();
626657
627658 AccessToken token = factory .generateToken (accessBoundary );
628659
629- String [] parts = token .getTokenValue ().split ("\\ ." );
630- assertEquals (parts .length , 2 );
631- assertEquals (parts [0 ], "accessToken" );
660+ CabToken cabToken = parseCabToken (token );
661+ assertEquals (cabToken .intermediateToken , "accessToken" );
632662
633- byte [] rawKey = Base64 .getDecoder ().decode (
663+ // Checks the encrypted restriction is the correct proto format of the
664+ // CredentialAccessBoundary
665+ ClientSideAccessBoundary clientSideAccessBoundary = decryptRestriction (
666+ cabToken .encryptedRestriction ,
634667 transportFactory .transport .getAccessBoundarySessionKey ());
635-
636- KeysetHandle keysetHandle = TinkProtoKeysetFormat .parseKeyset (
637- rawKey , InsecureSecretKeyAccess .get ());
638-
639- Aead aead =
640- keysetHandle .getPrimitive (RegistryConfiguration .get (), Aead .class );
641- byte [] rawRestrictions =
642- aead .decrypt (Base64 .getUrlDecoder ().decode (parts [1 ]), new byte [0 ]);
643- ClientSideAccessBoundary clientSideAccessBoundary =
644- ClientSideAccessBoundary .parseFrom (rawRestrictions );
645668 assertEquals (clientSideAccessBoundary .getAccessBoundaryRulesCount (), 1 );
669+
646670 ClientSideAccessBoundaryRule rule =
647671 clientSideAccessBoundary .getAccessBoundaryRules (0 );
648- assertEquals (rule .getAvailableResource (),
649- "//storage.googleapis.com/projects/_/buckets/example-bucket" );
650- assertEquals (rule .getAvailablePermissions (0 ),
651- "inRole:roles/storage.objectViewer" );
672+
673+ // Available resource and available permission should be the exact same as
674+ // in original format
675+ assertEquals (rule .getAvailableResource (), "resource" );
676+ assertEquals (rule .getAvailablePermissionsList (),
677+ ImmutableList .of ("role1" , "role2" ));
678+
679+ // Availablity condition should be in the correct compiled proto format
652680 Expr expr = rule .getCompiledAvailabilityCondition ();
653- assertEquals (expr .getCallExpr ()
654- .getTarget ()
655- .getSelectExpr ()
656- .getOperand ()
657- .getIdentExpr ()
658- .getName (),
659- "resource" );
660- assertEquals (expr .getCallExpr ().getFunction (), "startsWith" );
661- assertEquals (expr .getCallExpr ().getArgs (0 ).getConstExpr ().getStringValue (),
662- "projects/_/buckets/example-bucket/objects/customer-a" );
681+ assertEquals (expr .getCallExpr ().getFunction (), "_==_" );
682+ assertEquals (expr .getCallExpr ().getArgs (0 ).getIdentExpr ().getName (), "a" );
683+ assertEquals (expr .getCallExpr ().getArgs (1 ).getIdentExpr ().getName (), "b" );
663684 }
664685
665686 @ Test
@@ -681,42 +702,102 @@ public void generateToken_withoutAvailabilityCondition_success() throws Exceptio
681702 CredentialAccessBoundary .newBuilder ();
682703 CredentialAccessBoundary accessBoundary =
683704 cabBuilder
684- .addRule (
685- CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
686- .setAvailableResource ("//storage.googleapis.com/projects/"
687- + "_/buckets/example-bucket" )
688- .setAvailablePermissions (
689- ImmutableList .of ("inRole:roles/storage.objectViewer" ))
690- .build ())
705+ .addRule (CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
706+ .setAvailableResource ("resource" )
707+ .setAvailablePermissions (ImmutableList .of ("role" ))
708+ .build ())
691709 .build ();
692710
693711 AccessToken token = factory .generateToken (accessBoundary );
694712
695- String [] parts = token .getTokenValue ().split ("\\ ." );
696- assertEquals (parts .length , 2 );
697- assertEquals (parts [0 ], "accessToken" );
713+ CabToken cabToken = parseCabToken (token );
714+ assertEquals (cabToken .intermediateToken , "accessToken" );
698715
699- byte [] rawKey = Base64 .getDecoder ().decode (
716+ // Checks the encrypted restriction is the correct proto format of the
717+ // CredentialAccessBoundary
718+ ClientSideAccessBoundary clientSideAccessBoundary = decryptRestriction (
719+ cabToken .encryptedRestriction ,
700720 transportFactory .transport .getAccessBoundarySessionKey ());
701-
702- KeysetHandle keysetHandle = TinkProtoKeysetFormat .parseKeyset (
703- rawKey , InsecureSecretKeyAccess .get ());
704-
705- Aead aead =
706- keysetHandle .getPrimitive (RegistryConfiguration .get (), Aead .class );
707- byte [] rawRestrictions =
708- aead .decrypt (Base64 .getUrlDecoder ().decode (parts [1 ]), new byte [0 ]);
709- ClientSideAccessBoundary clientSideAccessBoundary =
710- ClientSideAccessBoundary .parseFrom (rawRestrictions );
711721 assertEquals (clientSideAccessBoundary .getAccessBoundaryRulesCount (), 1 );
722+
712723 ClientSideAccessBoundaryRule rule =
713724 clientSideAccessBoundary .getAccessBoundaryRules (0 );
714- assertEquals (rule .getAvailableResource (),
715- "//storage.googleapis.com/projects/_/buckets/example-bucket" );
716- assertEquals (rule .getAvailablePermissions (0 ),
717- "inRole:roles/storage.objectViewer" );
718- assertTrue (rule .getCompiledAvailabilityCondition ().equals (
719- Expr .getDefaultInstance ()));
725+
726+ // Available resource and available permission should be the exact same as
727+ // in original format
728+ assertEquals (rule .getAvailableResource (), "resource" );
729+ assertEquals (rule .getAvailablePermissionsList (), ImmutableList .of ("role" ));
730+
731+ // Availablity condition should be empty since it's not provided
732+ assertFalse (rule .hasCompiledAvailabilityCondition ());
733+ }
734+
735+ @ Test
736+ public void generateToken_withMultipleRules_success () throws Exception {
737+ MockStsTransportFactory transportFactory = new MockStsTransportFactory ();
738+ transportFactory .transport .setReturnAccessBoundarySessionKey (true );
739+
740+ ClientSideCredentialAccessBoundaryFactory .Builder builder =
741+ ClientSideCredentialAccessBoundaryFactory .newBuilder ();
742+
743+ ClientSideCredentialAccessBoundaryFactory factory =
744+ builder
745+ .setSourceCredential (getServiceAccountSourceCredentials (
746+ mockTokenServerTransportFactory ))
747+ .setHttpTransportFactory (transportFactory )
748+ .build ();
749+
750+ CredentialAccessBoundary .Builder cabBuilder =
751+ CredentialAccessBoundary .newBuilder ();
752+ CredentialAccessBoundary accessBoundary =
753+ cabBuilder
754+ .addRule (CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
755+ .setAvailableResource ("resource1" )
756+ .setAvailablePermissions (
757+ ImmutableList .of ("role1-1" , "role1-2" ))
758+ .setAvailabilityCondition (
759+ CredentialAccessBoundary .AccessBoundaryRule
760+ .AvailabilityCondition .newBuilder ()
761+ .setExpression ("a == b" )
762+ .build ())
763+ .build ())
764+ .addRule (CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
765+ .setAvailableResource ("resource" )
766+ .setAvailablePermissions (ImmutableList .of ("role2" ))
767+ .build ())
768+ .build ();
769+
770+ AccessToken token = factory .generateToken (accessBoundary );
771+
772+ CabToken cabToken = parseCabToken (token );
773+ assertEquals (cabToken .intermediateToken , "accessToken" );
774+
775+ // Checks the encrypted restriction is the correct proto format of the
776+ // CredentialAccessBoundary
777+ ClientSideAccessBoundary clientSideAccessBoundary = decryptRestriction (
778+ cabToken .encryptedRestriction ,
779+ transportFactory .transport .getAccessBoundarySessionKey ());
780+ assertEquals (clientSideAccessBoundary .getAccessBoundaryRulesCount (), 2 );
781+
782+ // Checks the first rule
783+ ClientSideAccessBoundaryRule rule1 =
784+ clientSideAccessBoundary .getAccessBoundaryRules (0 );
785+ assertEquals (rule1 .getAvailableResource (), "resource1" );
786+ assertEquals (rule1 .getAvailablePermissionsList (),
787+ ImmutableList .of ("role1-1" , "role1-2" ));
788+
789+ Expr expr = rule1 .getCompiledAvailabilityCondition ();
790+ assertEquals (expr .getCallExpr ().getFunction (), "_==_" );
791+ assertEquals (expr .getCallExpr ().getArgs (0 ).getIdentExpr ().getName (), "a" );
792+ assertEquals (expr .getCallExpr ().getArgs (1 ).getIdentExpr ().getName (), "b" );
793+
794+ // Checks the second rule
795+ ClientSideAccessBoundaryRule rule2 =
796+ clientSideAccessBoundary .getAccessBoundaryRules (1 );
797+ assertEquals (rule2 .getAvailableResource (), "resource" );
798+ assertEquals (rule2 .getAvailablePermissionsList (),
799+ ImmutableList .of ("role2" ));
800+ assertFalse (rule2 .hasCompiledAvailabilityCondition ());
720801 }
721802
722803 @ Test
@@ -738,20 +819,21 @@ public void generateToken_withInvalidAvailabilityCondition_failure() throws Exce
738819 CredentialAccessBoundary .newBuilder ();
739820 CredentialAccessBoundary accessBoundary =
740821 cabBuilder
741- .addRule (
742- CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
743- .setAvailableResource ("//storage.googleapis.com/projects/"
744- + "_/buckets/example-bucket" )
745- .setAvailablePermissions (
746- ImmutableList .of ("inRole:roles/storage.objectViewer" ))
747- .setAvailabilityCondition (
748- CredentialAccessBoundary .AccessBoundaryRule
749- .AvailabilityCondition .newBuilder ()
750- .setExpression (
751- "resource.name.startsWith('projects/_/"
752- + "buckets/example-bucket/objects/customer-a'" )
753- .build ())
754- .build ())
822+ .addRule (CredentialAccessBoundary .AccessBoundaryRule .newBuilder ()
823+ .setAvailableResource (
824+ "//storage.googleapis.com/projects/"
825+ + "_/buckets/example-bucket" )
826+ .setAvailablePermissions (ImmutableList .of (
827+ "inRole:roles/storage.objectViewer" ))
828+ .setAvailabilityCondition (
829+ CredentialAccessBoundary .AccessBoundaryRule
830+ .AvailabilityCondition .newBuilder ()
831+ .setExpression (
832+ "resource.name.startsWith('projects/_/"
833+ + "buckets/example-bucket/objects/" +
834+ "customer-a'" ) // No closing bracket
835+ .build ())
836+ .build ())
755837 .build ();
756838
757839 assertThrows (CelValidationException .class ,
0 commit comments