@@ -564,7 +564,7 @@ def test_streaming_pull_max_messages(
564564
565565 @pytest .mark .skipif (
566566 "KOKORO_GFILE_DIR" not in os .environ ,
567- reason = "Requires Kokoro environment with a limited subscriber service account." ,
567+ reason = "Requires Kokoro environment with a service account with limited role ." ,
568568 )
569569 def test_streaming_pull_subscriber_permissions_sufficient (
570570 self , publisher , topic_path , subscriber , subscription_path , cleanup
@@ -601,6 +601,136 @@ def test_streaming_pull_subscriber_permissions_sufficient(
601601 finally :
602602 future .cancel ()
603603
604+ @pytest .mark .skipif (
605+ "KOKORO_GFILE_DIR" not in os .environ ,
606+ reason = "Requires Kokoro environment with a service account with limited role." ,
607+ )
608+ def test_publisher_role_can_publish_messages (
609+ self , publisher , topic_path , subscriber , subscription_path , cleanup
610+ ):
611+
612+ # Make sure the topic and subscription get deleted.
613+ cleanup .append ((publisher .delete_topic , topic_path ))
614+ cleanup .append ((subscriber .delete_subscription , subscription_path ))
615+
616+ # Create a topic and subscribe to it.
617+ publisher .create_topic (topic_path )
618+ subscriber .create_subscription (subscription_path , topic_path )
619+
620+ # Create a publisher client with only the publisher role only.
621+ filename = os .path .join (
622+ os .environ ["KOKORO_GFILE_DIR" ], "pubsub-publisher-service-account.json"
623+ )
624+ publisher_only_client = type (publisher ).from_service_account_file (filename )
625+
626+ self ._publish_messages (publisher_only_client , topic_path , batch_sizes = [2 ])
627+
628+ response = subscriber .pull (subscription_path , max_messages = 2 )
629+ assert len (response .received_messages ) == 2
630+
631+ @pytest .mark .skip (
632+ "Snapshot creation is not instant on the backend, causing test falkiness."
633+ )
634+ @pytest .mark .skipif (
635+ "KOKORO_GFILE_DIR" not in os .environ ,
636+ reason = "Requires Kokoro environment with a service account with limited role." ,
637+ )
638+ def test_snapshot_seek_subscriber_permissions_sufficient (
639+ self , project , publisher , topic_path , subscriber , subscription_path , cleanup
640+ ):
641+ snapshot_name = "snap" + unique_resource_id ("-" )
642+ snapshot_path = "projects/{}/snapshots/{}" .format (project , snapshot_name )
643+
644+ # Make sure the topic and subscription get deleted.
645+ cleanup .append ((publisher .delete_topic , topic_path ))
646+ cleanup .append ((subscriber .delete_subscription , subscription_path ))
647+ cleanup .append ((subscriber .delete_snapshot , snapshot_path ))
648+
649+ # Create a topic and subscribe to it.
650+ publisher .create_topic (topic_path )
651+ subscriber .create_subscription (
652+ subscription_path , topic_path , retain_acked_messages = True
653+ )
654+
655+ # A service account granting only the pubsub.subscriber role must be used.
656+ filename = os .path .join (
657+ os .environ ["KOKORO_GFILE_DIR" ], "pubsub-subscriber-service-account.json"
658+ )
659+ subscriber_only_client = type (subscriber ).from_service_account_file (filename )
660+
661+ # Publish two messages and create a snapshot inbetween.
662+ self ._publish_messages (publisher , topic_path , batch_sizes = [1 ])
663+ response = subscriber .pull (subscription_path , max_messages = 10 )
664+ assert len (response .received_messages ) == 1
665+
666+ subscriber .create_snapshot (snapshot_path , subscription_path )
667+
668+ self ._publish_messages (publisher , topic_path , batch_sizes = [1 ])
669+ response = subscriber .pull (subscription_path , max_messages = 10 )
670+ assert len (response .received_messages ) == 1
671+
672+ # A subscriber-only client should be allowed to seek to a snapshot.
673+ subscriber_only_client .seek (subscription_path , snapshot = snapshot_path )
674+
675+ # We should receive one message again, since we sought back to a snapshot.
676+ response = subscriber .pull (subscription_path , max_messages = 10 )
677+ assert len (response .received_messages ) == 1
678+
679+ @pytest .mark .skipif (
680+ "KOKORO_GFILE_DIR" not in os .environ ,
681+ reason = "Requires Kokoro environment with a service account with limited role." ,
682+ )
683+ def test_viewer_role_can_list_resources (
684+ self , project , publisher , topic_path , subscriber , cleanup
685+ ):
686+ project_path = "projects/" + project
687+
688+ # Make sure the created topic gets deleted.
689+ cleanup .append ((publisher .delete_topic , topic_path ))
690+
691+ publisher .create_topic (topic_path )
692+
693+ # A service account granting only the pubsub.viewer role must be used.
694+ filename = os .path .join (
695+ os .environ ["KOKORO_GFILE_DIR" ], "pubsub-viewer-service-account.json"
696+ )
697+ viewer_only_subscriber = type (subscriber ).from_service_account_file (filename )
698+ viewer_only_publisher = type (publisher ).from_service_account_file (filename )
699+
700+ # The following operations should not raise permission denied errors.
701+ # NOTE: At least one topic exists.
702+ topic = next (iter (viewer_only_publisher .list_topics (project_path )))
703+ next (iter (viewer_only_publisher .list_topic_subscriptions (topic .name )), None )
704+ next (iter (viewer_only_subscriber .list_subscriptions (project_path )), None )
705+ next (iter (viewer_only_subscriber .list_snapshots (project_path )), None )
706+
707+ @pytest .mark .skipif (
708+ "KOKORO_GFILE_DIR" not in os .environ ,
709+ reason = "Requires Kokoro environment with a service account with limited role." ,
710+ )
711+ def test_editor_role_can_create_resources (
712+ self , project , publisher , topic_path , subscriber , subscription_path , cleanup
713+ ):
714+ snapshot_name = "snap" + unique_resource_id ("-" )
715+ snapshot_path = "projects/{}/snapshots/{}" .format (project , snapshot_name )
716+
717+ # Make sure the created resources get deleted.
718+ cleanup .append ((subscriber .delete_snapshot , snapshot_path ))
719+ cleanup .append ((subscriber .delete_subscription , subscription_path ))
720+ cleanup .append ((publisher .delete_topic , topic_path ))
721+
722+ # A service account granting only the pubsub.editor role must be used.
723+ filename = os .path .join (
724+ os .environ ["KOKORO_GFILE_DIR" ], "pubsub-editor-service-account.json"
725+ )
726+ editor_subscriber = type (subscriber ).from_service_account_file (filename )
727+ editor_publisher = type (publisher ).from_service_account_file (filename )
728+
729+ # The following operations should not raise permission denied errors.
730+ editor_publisher .create_topic (topic_path )
731+ editor_subscriber .create_subscription (subscription_path , topic_path )
732+ editor_subscriber .create_snapshot (snapshot_path , subscription_path )
733+
604734 def _publish_messages (self , publisher , topic_path , batch_sizes ):
605735 """Publish ``count`` messages in batches and wait until completion."""
606736 publish_futures = []
0 commit comments