|
35 | 35 | import static org.junit.Assert.assertEquals; |
36 | 36 | import static org.junit.Assert.assertNotNull; |
37 | 37 | import static org.junit.Assert.assertThrows; |
| 38 | +import static org.junit.Assert.assertTrue; |
38 | 39 | import static org.mockito.Mockito.mock; |
39 | 40 | import static org.mockito.Mockito.when; |
40 | 41 |
|
|
44 | 45 | import com.google.auth.TestUtils; |
45 | 46 | import com.google.auth.credentialaccessboundary.ClientSideCredentialAccessBoundaryFactory.IntermediateCredentials; |
46 | 47 | import com.google.auth.credentialaccessboundary.ClientSideCredentialAccessBoundaryFactory.RefreshType; |
| 48 | +import com.google.auth.credentialaccessboundary.protobuf.ClientSideAccessBoundaryProto.ClientSideAccessBoundary; |
| 49 | +import com.google.auth.credentialaccessboundary.protobuf.ClientSideAccessBoundaryProto.ClientSideAccessBoundaryRule; |
47 | 50 | import com.google.auth.http.HttpTransportFactory; |
48 | 51 | import com.google.auth.oauth2.AccessToken; |
| 52 | +import com.google.auth.oauth2.CredentialAccessBoundary; |
49 | 53 | import com.google.auth.oauth2.GoogleCredentials; |
50 | 54 | import com.google.auth.oauth2.MockStsTransport; |
51 | 55 | import com.google.auth.oauth2.MockTokenServerTransportFactory; |
52 | 56 | import com.google.auth.oauth2.OAuth2Utils; |
53 | 57 | import com.google.auth.oauth2.ServiceAccountCredentials; |
| 58 | +import com.google.common.collect.ImmutableList; |
| 59 | +import com.google.crypto.tink.Aead; |
| 60 | +import com.google.crypto.tink.InsecureSecretKeyAccess; |
| 61 | +import com.google.crypto.tink.KeysetHandle; |
| 62 | +import com.google.crypto.tink.RegistryConfiguration; |
| 63 | +import com.google.crypto.tink.TinkProtoKeysetFormat; |
| 64 | + |
| 65 | +import dev.cel.expr.Expr; |
| 66 | + |
54 | 67 | import java.io.IOException; |
55 | 68 | import java.time.Duration; |
| 69 | +import java.util.Base64; |
56 | 70 | import java.util.Map; |
57 | 71 | import java.util.concurrent.CountDownLatch; |
58 | 72 | import org.junit.Before; |
@@ -572,4 +586,173 @@ private static void triggerConcurrentRefresh( |
572 | 586 | } |
573 | 587 | } |
574 | 588 | } |
| 589 | + |
| 590 | + @Test |
| 591 | + public void generateToken() throws Exception { |
| 592 | + MockStsTransportFactory transportFactory = new MockStsTransportFactory(); |
| 593 | + transportFactory.transport.setReturnAccessBoundarySessionKey(true); |
| 594 | + |
| 595 | + ClientSideCredentialAccessBoundaryFactory.Builder builder = |
| 596 | + ClientSideCredentialAccessBoundaryFactory.newBuilder(); |
| 597 | + |
| 598 | + ClientSideCredentialAccessBoundaryFactory factory = |
| 599 | + builder |
| 600 | + .setSourceCredential(getServiceAccountSourceCredentials( |
| 601 | + mockTokenServerTransportFactory)) |
| 602 | + .setHttpTransportFactory(transportFactory) |
| 603 | + .build(); |
| 604 | + |
| 605 | + CredentialAccessBoundary.Builder cabBuilder = |
| 606 | + CredentialAccessBoundary.newBuilder(); |
| 607 | + CredentialAccessBoundary accessBoundary = |
| 608 | + cabBuilder |
| 609 | + .addRule( |
| 610 | + CredentialAccessBoundary.AccessBoundaryRule.newBuilder() |
| 611 | + .setAvailableResource("//storage.googleapis.com/projects/" |
| 612 | + + "_/buckets/example-bucket") |
| 613 | + .setAvailablePermissions( |
| 614 | + ImmutableList.of("inRole:roles/storage.objectViewer")) |
| 615 | + .setAvailabilityCondition( |
| 616 | + CredentialAccessBoundary.AccessBoundaryRule |
| 617 | + .AvailabilityCondition.newBuilder() |
| 618 | + .setExpression( |
| 619 | + "resource.name.startsWith('projects/_/" |
| 620 | + + "buckets/example-bucket/objects/customer-a')") |
| 621 | + .build()) |
| 622 | + .build()) |
| 623 | + .build(); |
| 624 | + |
| 625 | + AccessToken token = factory.generateToken(accessBoundary); |
| 626 | + |
| 627 | + String[] parts = token.getTokenValue().split("\\."); |
| 628 | + assertEquals(parts.length, 2); |
| 629 | + assertEquals(parts[0], "accessToken"); |
| 630 | + |
| 631 | + byte[] rawKey = Base64.getDecoder().decode( |
| 632 | + transportFactory.transport.getAccessBoundarySessionKey()); |
| 633 | + |
| 634 | + KeysetHandle keysetHandle = TinkProtoKeysetFormat.parseKeyset( |
| 635 | + rawKey, InsecureSecretKeyAccess.get()); |
| 636 | + |
| 637 | + Aead aead = |
| 638 | + keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); |
| 639 | + byte[] rawRestrictions = |
| 640 | + aead.decrypt(Base64.getUrlDecoder().decode(parts[1]), new byte[0]); |
| 641 | + ClientSideAccessBoundary clientSideAccessBoundary = |
| 642 | + ClientSideAccessBoundary.parseFrom(rawRestrictions); |
| 643 | + assertEquals(clientSideAccessBoundary.getAccessBoundaryRulesCount(), 1); |
| 644 | + ClientSideAccessBoundaryRule rule = |
| 645 | + clientSideAccessBoundary.getAccessBoundaryRules(0); |
| 646 | + assertEquals(rule.getAvailableResource(), |
| 647 | + "//storage.googleapis.com/projects/_/buckets/example-bucket"); |
| 648 | + assertEquals(rule.getAvailablePermissions(0), |
| 649 | + "inRole:roles/storage.objectViewer"); |
| 650 | + Expr expr = rule.getCompiledAvailabilityCondition(); |
| 651 | + assertEquals(expr.getCallExpr() |
| 652 | + .getTarget() |
| 653 | + .getSelectExpr() |
| 654 | + .getOperand() |
| 655 | + .getIdentExpr() |
| 656 | + .getName(), |
| 657 | + "resource"); |
| 658 | + assertEquals(expr.getCallExpr().getFunction(), "startsWith"); |
| 659 | + assertEquals(expr.getCallExpr().getArgs(0).getConstExpr().getStringValue(), |
| 660 | + "projects/_/buckets/example-bucket/objects/customer-a"); |
| 661 | + } |
| 662 | + |
| 663 | + @Test |
| 664 | + public void generateToken_withoutAvailabilityCondition() throws Exception { |
| 665 | + MockStsTransportFactory transportFactory = new MockStsTransportFactory(); |
| 666 | + transportFactory.transport.setReturnAccessBoundarySessionKey(true); |
| 667 | + |
| 668 | + ClientSideCredentialAccessBoundaryFactory.Builder builder = |
| 669 | + ClientSideCredentialAccessBoundaryFactory.newBuilder(); |
| 670 | + |
| 671 | + ClientSideCredentialAccessBoundaryFactory factory = |
| 672 | + builder |
| 673 | + .setSourceCredential(getServiceAccountSourceCredentials( |
| 674 | + mockTokenServerTransportFactory)) |
| 675 | + .setHttpTransportFactory(transportFactory) |
| 676 | + .build(); |
| 677 | + |
| 678 | + CredentialAccessBoundary.Builder cabBuilder = |
| 679 | + CredentialAccessBoundary.newBuilder(); |
| 680 | + CredentialAccessBoundary accessBoundary = |
| 681 | + cabBuilder |
| 682 | + .addRule( |
| 683 | + CredentialAccessBoundary.AccessBoundaryRule.newBuilder() |
| 684 | + .setAvailableResource("//storage.googleapis.com/projects/" |
| 685 | + + "_/buckets/example-bucket") |
| 686 | + .setAvailablePermissions( |
| 687 | + ImmutableList.of("inRole:roles/storage.objectViewer")) |
| 688 | + .build()) |
| 689 | + .build(); |
| 690 | + |
| 691 | + AccessToken token = factory.generateToken(accessBoundary); |
| 692 | + |
| 693 | + String[] parts = token.getTokenValue().split("\\."); |
| 694 | + assertEquals(parts.length, 2); |
| 695 | + assertEquals(parts[0], "accessToken"); |
| 696 | + |
| 697 | + byte[] rawKey = Base64.getDecoder().decode( |
| 698 | + transportFactory.transport.getAccessBoundarySessionKey()); |
| 699 | + |
| 700 | + KeysetHandle keysetHandle = TinkProtoKeysetFormat.parseKeyset( |
| 701 | + rawKey, InsecureSecretKeyAccess.get()); |
| 702 | + |
| 703 | + Aead aead = |
| 704 | + keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); |
| 705 | + byte[] rawRestrictions = |
| 706 | + aead.decrypt(Base64.getUrlDecoder().decode(parts[1]), new byte[0]); |
| 707 | + ClientSideAccessBoundary clientSideAccessBoundary = |
| 708 | + ClientSideAccessBoundary.parseFrom(rawRestrictions); |
| 709 | + assertEquals(clientSideAccessBoundary.getAccessBoundaryRulesCount(), 1); |
| 710 | + ClientSideAccessBoundaryRule rule = |
| 711 | + clientSideAccessBoundary.getAccessBoundaryRules(0); |
| 712 | + assertEquals(rule.getAvailableResource(), |
| 713 | + "//storage.googleapis.com/projects/_/buckets/example-bucket"); |
| 714 | + assertEquals(rule.getAvailablePermissions(0), |
| 715 | + "inRole:roles/storage.objectViewer"); |
| 716 | + assertTrue(rule.getCompiledAvailabilityCondition().equals( |
| 717 | + Expr.getDefaultInstance())); |
| 718 | + } |
| 719 | + |
| 720 | + @Test |
| 721 | + public void generateToken_withInvalidCelExpression() throws Exception { |
| 722 | + MockStsTransportFactory transportFactory = new MockStsTransportFactory(); |
| 723 | + transportFactory.transport.setReturnAccessBoundarySessionKey(true); |
| 724 | + |
| 725 | + ClientSideCredentialAccessBoundaryFactory.Builder builder = |
| 726 | + ClientSideCredentialAccessBoundaryFactory.newBuilder(); |
| 727 | + |
| 728 | + ClientSideCredentialAccessBoundaryFactory factory = |
| 729 | + builder |
| 730 | + .setSourceCredential(getServiceAccountSourceCredentials( |
| 731 | + mockTokenServerTransportFactory)) |
| 732 | + .setHttpTransportFactory(transportFactory) |
| 733 | + .build(); |
| 734 | + |
| 735 | + CredentialAccessBoundary.Builder cabBuilder = |
| 736 | + CredentialAccessBoundary.newBuilder(); |
| 737 | + CredentialAccessBoundary accessBoundary = |
| 738 | + cabBuilder |
| 739 | + .addRule( |
| 740 | + CredentialAccessBoundary.AccessBoundaryRule.newBuilder() |
| 741 | + .setAvailableResource("//storage.googleapis.com/projects/" |
| 742 | + + "_/buckets/example-bucket") |
| 743 | + .setAvailablePermissions( |
| 744 | + ImmutableList.of("inRole:roles/storage.objectViewer")) |
| 745 | + .setAvailabilityCondition( |
| 746 | + CredentialAccessBoundary.AccessBoundaryRule |
| 747 | + .AvailabilityCondition.newBuilder() |
| 748 | + .setExpression( |
| 749 | + "resource.name.startsWith('projects/_/" |
| 750 | + + "buckets/example-bucket/objects/customer-a'") |
| 751 | + .build()) |
| 752 | + .build()) |
| 753 | + .build(); |
| 754 | + |
| 755 | + assertThrows(IOException.class, |
| 756 | + () -> { factory.generateToken(accessBoundary); }); |
| 757 | + } |
575 | 758 | } |
0 commit comments