示例#1
0
def grant_users_access(users, channels, sg_admin_url, sg_db):
    sg_client = MobileRestClient()
    for username in users:
        sg_client.update_user(url=sg_admin_url,
                              db=sg_db,
                              name=username,
                              channels=channels)
def test_backfill_channels_looping_longpoll_changes(params_from_base_test_setup, sg_conf_name, grant_type):

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A"], roles=[])
    user_b_user_info = userinfo.UserInfo("USER_B", "pass", channels=["B"], roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url, db=sg_db,
                       name=admin_user_info.name, password=admin_user_info.password, channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url, db=sg_db,
                       name=user_b_user_info.name, password=user_b_user_info.password, channels=user_b_user_info.channels)

    admin_session = client.create_session(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url, db=sg_db, name=user_b_user_info.name, password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url, db=sg_db, number=50, id_prefix=None, auth=admin_session, channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="b_doc", auth=user_b_session, channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=b_docs, auth=user_b_session, strict=True)

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=user_b_session, feed="normal")

    with concurrent.futures.ProcessPoolExecutor() as ex:

        # Start long poll changes feed.
        changes_task = ex.submit(client.get_changes,
                                 url=sg_url, db=sg_db, since=user_b_changes["last_seq"], auth=user_b_session, timeout=10, limit=20)

        # Grant access to channel "A"
        if grant_type == "CHANNEL-REST":
            log_info("Granting user access to channel A via Admin REST user update")
            # Grant via update to user in Admin API
            client.update_user(url=sg_admin_url, db=sg_db,
                               name=user_b_user_info.name, channels=["A", "B"])

        elif grant_type == "CHANNEL-SYNC":
            log_info("Granting user access to channel A sync function access()")
            # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
            access_doc = document.create_doc("channel_access", channels=["A"])
            access_doc["users"] = ["USER_B"]
            client.add_doc(url=sg_url, db=sg_db, doc=access_doc, auth=admin_session)

        elif grant_type == "ROLE-REST":
            log_info("Granting user access to channel A via Admin REST role grant")
            # Create role with channel A
            client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])
            client.update_user(url=sg_admin_url, db=sg_db, name="USER_B", roles=["channel-A-role"])

        elif grant_type == "ROLE-SYNC":
            log_info("Granting user access to channel A via sync function role() grant")
            # Create role with channel A
            client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])

            # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
            role_access_doc = document.create_doc("role_access")
            role_access_doc["users"] = ["USER_B"]
            role_access_doc["roles"] = ["role:channel-A-role"]
            client.add_doc(sg_url, db=sg_db, doc=role_access_doc, auth=admin_session)

        else:
            pytest.fail("Unsupported grant_type!!!!")

        # Block on return of longpoll changes, feed should wake up and return 20 results
        changes = changes_task.result()

    assert len(changes["results"]) == 20
    num_requests = 1

    # Cross the results off from the 'a_docs' dictionary
    for doc in changes["results"]:
        del ids_and_revs_from_a_docs[doc["id"]]

    # Start looping longpoll changes with limit, cross off changes from dictionary each time one is found
    # Since 20 changes should be crossed off already, this should execute 2x.
    log_info("Starting looping longpoll changes with limit!")
    last_seq = changes["last_seq"]
    while True:

        if len(ids_and_revs_from_a_docs.keys()) == 0:
            log_info("All docs were found! Exiting polling loop")
            break

        changes = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=user_b_session, limit=20, timeout=10)
        num_requests += 1

        # There are more than 2 requests, throw an exception.
        if num_requests == 2:
            assert len(changes["results"]) == 20
        elif num_requests == 3:
            # This will be 10 or 11 depending on if the _user/ doc is returned
            assert 10 <= len(changes["results"]) <= 11
        else:
            raise exceptions.ChangesError("Looping longpoll should only have to perform 3 requests to get all the changes!!")

        # Cross the results off from the 'a_docs' dictionary.
        # This will blow up in docs duplicate docs are sent to changes
        for doc in changes["results"]:
            if doc["id"] != "_user/USER_B":
                del ids_and_revs_from_a_docs[doc["id"]]

        last_seq = changes["last_seq"]

    # Shanges after longpoll
    zero_results = client.get_changes(url=sg_url, db=sg_db,
                                      since=last_seq,
                                      auth=user_b_session, feed="normal")

    # Changes should be caught up and there should be no results
    assert len(zero_results["results"]) == 0
def test_backfill_channels_oneshot_changes(params_from_base_test_setup, sg_conf_name, grant_type):

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A"], roles=[])
    user_b_user_info = userinfo.UserInfo("USER_B", "pass", channels=["B"], roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url, db=sg_db,
                       name=admin_user_info.name, password=admin_user_info.password, channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url, db=sg_db,
                       name=user_b_user_info.name, password=user_b_user_info.password, channels=user_b_user_info.channels)

    admin_session = client.create_session(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url, db=sg_db, name=user_b_user_info.name, password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url, db=sg_db, number=50, id_prefix=None, auth=admin_session, channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="b_doc", auth=user_b_session, channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=b_docs, auth=user_b_session, strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=user_b_session, feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info("Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url, db=sg_db,
                           name=user_b_user_info.name, channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url, db=sg_db, doc=access_doc, auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])
        client.update_user(url=sg_admin_url, db=sg_db, name="USER_B", roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info("Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url, db=sg_db, doc=role_access_doc, auth=admin_session)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    user_b_changes_after_grant = client.get_changes(url=sg_url, db=sg_db,
                                                    since=user_b_changes["last_seq"], auth=user_b_session, feed="normal")

    # User B shoud have recieved 51 docs (a_docs + 1 _user/USER_B doc) if a REST grant or 50 changes if the grant
    # is via the sync function
    changes_results = user_b_changes_after_grant["results"]
    assert 50 <= len(changes_results) <= 51

    # Create a dictionary of id rev pair of all the docs that are not "_user/" docs from changes
    ids_and_revs_from_user_changes = {
        change["id"]: change["changes"][0]["rev"]
        for change in changes_results if not change["id"].startswith("_user/")
    }

    assert len(ids_and_revs_from_user_changes) == 50

    # Create a list of id rev pair of all of the channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}

    assert len(ids_and_revs_from_a_docs) == 50

    # Check that the changes and the a_docs are identical in id and rev
    assert ids_and_revs_from_user_changes == ids_and_revs_from_a_docs

    # Get changes from last_seq of the changes request after the grant. There should be no new changes
    user_b_changes = client.get_changes(url=sg_url, db=sg_db,
                                        since=user_b_changes_after_grant["last_seq"], auth=user_b_session, feed="normal")
    assert len(user_b_changes["results"]) == 0
def test_backfill_channels_oneshot_limit_changes(params_from_base_test_setup, sg_conf_name, grant_type):

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A"], roles=[])
    user_b_user_info = userinfo.UserInfo("USER_B", "pass", channels=["B"], roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url, db=sg_db,
                       name=admin_user_info.name, password=admin_user_info.password, channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url, db=sg_db,
                       name=user_b_user_info.name, password=user_b_user_info.password, channels=user_b_user_info.channels)

    admin_session = client.create_session(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url, db=sg_db, name=user_b_user_info.name, password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url, db=sg_db, number=50, id_prefix=None, auth=admin_session, channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="b_doc", auth=user_b_session, channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=b_docs, auth=user_b_session, strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=user_b_session, feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info("Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url, db=sg_db,
                           name=user_b_user_info.name, channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url, db=sg_db, doc=access_doc, auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])
        client.update_user(url=sg_admin_url, db=sg_db, name="USER_B", roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info("Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url, db=sg_db, doc=role_access_doc, auth=admin_session)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    log_info("Doing 3, 1 shot changes with limit and last seq!")
    # Issue 3 oneshot changes with a limit of 20

    #################
    # Changes Req #1
    #################
    user_b_changes_after_grant_one = client.get_changes(url=sg_url, db=sg_db,
                                                        since=user_b_changes["last_seq"], auth=user_b_session, feed="normal", limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_one["results"]) == 20

    for doc in user_b_changes_after_grant_one["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #2
    #################
    user_b_changes_after_grant_two = client.get_changes(url=sg_url, db=sg_db,
                                                        since=user_b_changes_after_grant_one["last_seq"],
                                                        auth=user_b_session, feed="normal", limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_two["results"]) == 20

    for doc in user_b_changes_after_grant_two["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #3
    #################
    user_b_changes_after_grant_three = client.get_changes(url=sg_url, db=sg_db,
                                                          since=user_b_changes_after_grant_two["last_seq"],
                                                          auth=user_b_session, feed="normal", limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_three["results"]) == 10

    for doc in user_b_changes_after_grant_three["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    # Make sure all the docs have been crossed out
    assert len(ids_and_revs_from_a_docs) == 0

    #################
    # Changes Req #4
    #################
    user_b_changes_after_grant_four = client.get_changes(url=sg_url, db=sg_db,
                                                         since=user_b_changes_after_grant_three["last_seq"],
                                                         auth=user_b_session, feed="normal", limit=20)

    # Changes should be caught up and there should be no results
    assert len(user_b_changes_after_grant_four["results"]) == 0
def test_longpoll_awaken_roles(params_from_base_test_setup, sg_conf_name):

    cluster_conf = params_from_base_test_setup["cluster_config"]
    cluster_topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)
    sg_admin_url = cluster_topology["sync_gateways"][0]["admin"]
    sg_url = cluster_topology["sync_gateways"][0]["public"]

    log_info("sg_conf: {}".format(sg_conf))
    log_info("sg_admin_url: {}".format(sg_admin_url))
    log_info("sg_url: {}".format(sg_url))

    cluster = Cluster(config=cluster_conf)
    cluster.reset(sg_config_path=sg_conf)

    admin_role = "admin_role"
    admin_channel = "admin_channel"

    admin_user_info = userinfo.UserInfo(name="admin", password="******", channels=[], roles=[admin_role])
    adam_user_info = userinfo.UserInfo(name="adam", password="******", channels=[], roles=[])
    traun_user_info = userinfo.UserInfo(name="traun", password="******", channels=[], roles=[])
    andy_user_info = userinfo.UserInfo(name="andy", password="******", channels=[], roles=[])
    sg_db = "db"

    client = MobileRestClient()

    # Create a role on sync_gateway
    client.create_role(url=sg_admin_url, db=sg_db, name=admin_role, channels=[admin_channel])

    # Create users with no channels or roles
    admin_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                    name=admin_user_info.name, password=admin_user_info.password, roles=[admin_role])

    adam_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                   name=adam_user_info.name, password=adam_user_info.password)

    traun_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                    name=traun_user_info.name, password=traun_user_info.password)

    andy_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                   name=andy_user_info.name, password=andy_user_info.password)

    ################################
    # change feed wakes for role add
    ################################

    # Get starting sequence of docs, use the last seq to progress past any _user docs.
    adam_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=adam_auth)
    traun_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=traun_auth)
    andy_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=andy_auth)

    # Add doc with channel associated with the admin role
    admin_doc = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="admin_doc", auth=admin_auth, channels=[admin_channel])
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=admin_auth)

    with concurrent.futures.ProcessPoolExecutor() as ex:
        # Start changes feed for 3 users from latest last_seq
        adam_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=adam_changes["last_seq"], timeout=10, auth=adam_auth)
        traun_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=traun_changes["last_seq"], timeout=10, auth=traun_auth)
        andy_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=andy_changes["last_seq"], timeout=10, auth=andy_auth)

        # Wait for changes feed to notice there are no changes and enter wait. 2 seconds should be more than enough
        time.sleep(2)

        # Make sure the changes future is still running and has not exited due to any new changes, the feed should be caught up
        # and waiting
        assert not adam_changes_task.done()
        assert not traun_changes_task.done()
        assert not andy_changes_task.done()

        adam_auth = client.update_user(url=sg_admin_url, db=sg_db,
                                       name=adam_user_info.name, password=adam_user_info.password, roles=[admin_role])

        traun_auth = client.update_user(url=sg_admin_url, db=sg_db,
                                        name=traun_user_info.name, password=traun_user_info.password, roles=[admin_role])

        andy_auth = client.update_user(url=sg_admin_url, db=sg_db,
                                       name=andy_user_info.name, password=andy_user_info.password, roles=[admin_role])

        adam_changes = adam_changes_task.result()
        assert 1 <= len(adam_changes["results"]) <= 2
        assert adam_changes["results"][0]["id"] == "admin_doc_0" or adam_changes["results"][0]["id"] == "_user/adam"

        traun_changes = traun_changes_task.result()
        assert 1 <= len(traun_changes["results"]) <= 2
        assert traun_changes["results"][0]["id"] == "admin_doc_0" or traun_changes["results"][0]["id"] == "_user/traun"

        andy_changes = andy_changes_task.result()
        assert 1 <= len(andy_changes["results"]) <= 2
        assert andy_changes["results"][0]["id"] == "admin_doc_0" or andy_changes["results"][0]["id"] == "_user/andy"

    # Check that the user docs all show up in changes feed
    client.verify_doc_id_in_changes(url=sg_url, db=sg_db, expected_doc_id="_user/adam", auth=adam_auth)
    client.verify_doc_id_in_changes(url=sg_url, db=sg_db, expected_doc_id="_user/traun", auth=traun_auth)
    client.verify_doc_id_in_changes(url=sg_url, db=sg_db, expected_doc_id="_user/andy", auth=andy_auth)

    # Check that the admin doc made it to all the changes feeds
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=adam_auth)
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=traun_auth)
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=andy_auth)

    # At this point, each user should have a changes feed that is caught up for the next section

    ###########################################
    # change feed wakes for channel add to role
    ###########################################

    abc_channel = "ABC"
    abc_pusher_info = userinfo.UserInfo(name="abc_pusher", password="******", channels=[abc_channel], roles=[])

    abc_pusher_auth = client.create_user(url=sg_admin_url, db=sg_db, name=abc_pusher_info.name,
                                         password=abc_pusher_info.password, channels=abc_pusher_info.channels)

    # Add doc with ABC channel
    client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="abc_doc", auth=abc_pusher_auth, channels=[abc_channel])

    # Get latest last_seq for next test section
    adam_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=adam_auth)
    traun_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=traun_auth)
    andy_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=andy_auth)

    with concurrent.futures.ProcessPoolExecutor() as ex:
        # Start changes feed for 3 users from latest last_seq
        adam_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=adam_changes["last_seq"], timeout=10, auth=adam_auth)
        traun_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=traun_changes["last_seq"], timeout=10, auth=traun_auth)
        andy_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=andy_changes["last_seq"], timeout=10, auth=andy_auth)

        # Wait for changes feed to notice there are no changes and enter wait. 2 seconds should be more than enough
        time.sleep(2)

        # Make sure the changes future is still running and has not exited due to any new changes, the feed should be caught up
        # and waiting
        assert not adam_changes_task.done()
        assert not traun_changes_task.done()
        assert not andy_changes_task.done()

        # Update admin role to include ABC channel
        # Since adam, traun, and andy are assigned to that role, they should wake up and get the 'abc_pusher_0' doc
        client.update_role(url=sg_admin_url, db=sg_db, name=admin_role, channels=[admin_channel, abc_channel])

        adam_changes = adam_changes_task.result()
        assert len(adam_changes["results"]) == 1
        assert adam_changes["results"][0]["id"] == "abc_doc_0"

        traun_changes = traun_changes_task.result()
        assert len(traun_changes["results"]) == 1
        assert traun_changes["results"][0]["id"] == "abc_doc_0"

        andy_changes = adam_changes_task.result()
        assert len(andy_changes["results"]) == 1
        assert andy_changes["results"][0]["id"] == "abc_doc_0"
示例#6
0
def test_awaken_backfill_channels_longpoll_changes_with_limit(
        params_from_base_test_setup, sg_conf_name, grant_type):
    """
    Test that checks that docs are backfilled for logpoll changes with limit for a access grant (via REST or SYNC)

    CHANNEL-REST = Channel is granted to user via REST
    CHANNEL-SYNC = Channel is granted to user via sync function access()
    ROLE-REST = Role is granted to user via REST
    ROLE-SYNC = Role is granted to user via sync function role()
    CHANNEL-TO-ROLE-REST = Channel is added to existing role via REST
    CHANNEL-TO-ROLE-SYNC = Channel is added to existing role via sync access()
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin",
                                        "pass",
                                        channels=["A"],
                                        roles=[])

    if grant_type == "CHANNEL-TO-ROLE-REST" or grant_type == "CHANNEL-TO-ROLE-SYNC":
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=[])
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=["empty_role"])
    else:
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=[])

    # Create users / sessions
    client.create_user(
        url=sg_admin_url,
        db=sg_db,
        name=admin_user_info.name,
        password=admin_user_info.password,
        channels=admin_user_info.channels,
    )

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=user_b_user_info.name,
                       password=user_b_user_info.password,
                       channels=user_b_user_info.channels,
                       roles=user_b_user_info.roles)

    admin_session = client.create_session(url=sg_admin_url,
                                          db=sg_db,
                                          name=admin_user_info.name,
                                          password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=user_b_user_info.name,
                                           password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=50,
                             id_prefix=None,
                             auth=admin_session,
                             channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=1,
                             id_prefix="b_doc",
                             auth=user_b_session,
                             channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=b_docs,
                                  auth=user_b_session,
                                  strict=True)

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=user_b_session,
                                        feed="normal")

    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as ex:

        # Start long poll changes feed.
        changes_task = ex.submit(client.get_changes,
                                 url=sg_url,
                                 db=sg_db,
                                 since=user_b_changes["last_seq"],
                                 auth=user_b_session,
                                 timeout=10,
                                 limit=20)

        # Grant access to channel "A"
        if grant_type == "CHANNEL-REST":
            log_info(
                "Granting user access to channel A via Admin REST user update")
            # Grant via update to user in Admin API
            client.update_user(url=sg_admin_url,
                               db=sg_db,
                               name=user_b_user_info.name,
                               channels=["A", "B"])

        elif grant_type == "CHANNEL-SYNC":
            log_info(
                "Granting user access to channel A sync function access()")
            # Grant via access() in sync_function,
            # then id 'channel_access' will trigger an access(doc.users, doc.channels)
            access_doc = document.create_doc("channel_access", channels=["A"])
            access_doc["users"] = ["USER_B"]
            client.add_doc(url=sg_url,
                           db=sg_db,
                           doc=access_doc,
                           auth=admin_session)

        elif grant_type == "ROLE-REST":
            log_info(
                "Granting user access to channel A via Admin REST role grant")
            # Create role with channel A
            client.create_role(url=sg_admin_url,
                               db=sg_db,
                               name="channel-A-role",
                               channels=["A"])
            client.update_user(url=sg_admin_url,
                               db=sg_db,
                               name=user_b_user_info.name,
                               roles=["channel-A-role"])

        elif grant_type == "ROLE-SYNC":
            log_info(
                "Granting user access to channel A via sync function role() grant"
            )
            # Create role with channel A
            client.create_role(url=sg_admin_url,
                               db=sg_db,
                               name="channel-A-role",
                               channels=["A"])

            # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
            role_access_doc = document.create_doc("role_access")
            role_access_doc["users"] = ["USER_B"]
            role_access_doc["roles"] = ["role:channel-A-role"]
            client.add_doc(sg_url,
                           db=sg_db,
                           doc=role_access_doc,
                           auth=admin_session)

        elif grant_type == "CHANNEL-TO-ROLE-REST":
            # Update the empty_role to have channel "A"
            client.update_role(url=sg_admin_url,
                               db=sg_db,
                               name="empty_role",
                               channels=["A"])

        elif grant_type == "CHANNEL-TO-ROLE-SYNC":
            # Grant empty_role access to channel "A" via sync function
            # Grant channel access to role via sync function
            access_doc = document.create_doc("channel_grant_to_role")
            access_doc["roles"] = ["role:empty_role"]
            access_doc["channels"] = ["A"]
            client.add_doc(url=sg_url,
                           db=sg_db,
                           doc=access_doc,
                           auth=admin_session,
                           use_post=True)

        else:
            pytest.fail("Unsupported grant_type!!!!")

        # Block on return of longpoll changes, feed should wake up and return 20 results
        changes = changes_task.result()

    assert len(changes["results"]) == 20
    num_requests = 1

    # append _user/doc to the doc scratch pad if a REST grant
    if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
        ids_and_revs_from_a_docs["_user/USER_B"] = None

    # Cross the results off from the 'a_docs' dictionary
    for doc in changes["results"]:
        del ids_and_revs_from_a_docs[doc["id"]]

    # Start looping longpoll changes with limit, cross off changes from dictionary each time one is found
    # Since 20 changes should be crossed off already, this should execute 2x.
    log_info("Starting looping longpoll changes with limit!")
    last_seq = changes["last_seq"]
    while True:

        if len(ids_and_revs_from_a_docs.keys()) == 0:
            log_info("All docs were found! Exiting polling loop")
            break

        changes = client.get_changes(url=sg_url,
                                     db=sg_db,
                                     since=last_seq,
                                     auth=user_b_session,
                                     limit=20,
                                     timeout=10)
        num_requests += 1

        # There are more than 2 requests, throw an exception.
        if num_requests == 2:
            assert len(changes["results"]) == 20
        elif num_requests == 3:
            # This will be 10 or 11 depending on if the _user/ doc is returned
            if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
                assert len(changes["results"]) == 11
            else:
                assert len(changes["results"]) == 10
        else:
            raise exceptions.ChangesError(
                "Looping longpoll should only have to perform 3 requests to get all the changes!!"
            )

        # Cross the results off from the 'a_docs' dictionary.
        # This will blow up in docs duplicate docs are sent to changes
        for doc in changes["results"]:
            del ids_and_revs_from_a_docs[doc["id"]]

        last_seq = changes["last_seq"]

    # Shanges after longpoll
    zero_results = client.get_changes(url=sg_url,
                                      db=sg_db,
                                      since=last_seq,
                                      auth=user_b_session,
                                      feed="normal")

    # Changes should be caught up and there should be no results
    assert len(zero_results["results"]) == 0
示例#7
0
def test_backfill_channels_oneshot_changes(params_from_base_test_setup,
                                           sg_conf_name, grant_type):
    """
    Test that checks that docs are backfilled for one shot changes for a access grant (via REST or SYNC)

    CHANNEL-REST = Channel is granted to user via REST
    CHANNEL-SYNC = Channel is granted to user via sync function access()
    ROLE-REST = Role is granted to user via REST
    ROLE-SYNC = Role is granted to user via sync function role()
    CHANNEL-TO-ROLE-REST = Channel is added to existing role via REST
    CHANNEL-TO-ROLE-SYNC = Channel is added to existing role via sync access()
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin",
                                        "pass",
                                        channels=["A"],
                                        roles=[])

    if grant_type == "CHANNEL-TO-ROLE-REST" or grant_type == "CHANNEL-TO-ROLE-SYNC":
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=[])
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=["empty_role"])
    else:
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=admin_user_info.name,
                       password=admin_user_info.password,
                       channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=user_b_user_info.name,
                       password=user_b_user_info.password,
                       channels=user_b_user_info.channels,
                       roles=user_b_user_info.roles)

    admin_session = client.create_session(url=sg_admin_url,
                                          db=sg_db,
                                          name=admin_user_info.name,
                                          password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=user_b_user_info.name,
                                           password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=50,
                             id_prefix=None,
                             auth=admin_session,
                             channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=1,
                             id_prefix="b_doc",
                             auth=user_b_session,
                             channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until admin user sees docs in changes
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=a_docs,
                                  auth=admin_session)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=b_docs,
                                  auth=user_b_session,
                                  strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=user_b_session,
                                        feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info(
            "Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name=user_b_user_info.name,
                           channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name="USER_B",
                           channels=["B"],
                           roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info(
            "Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url,
                       db=sg_db,
                       doc=role_access_doc,
                       auth=admin_session)

    elif grant_type == "CHANNEL-TO-ROLE-REST":
        # Update the empty_role to have channel "A"
        client.update_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=["A"])

    elif grant_type == "CHANNEL-TO-ROLE-SYNC":
        # Grant empty_role access to channel "A" via sync function
        # Grant channel access to role via sync function
        access_doc = document.create_doc("channel_grant_to_role")
        access_doc["roles"] = ["role:empty_role"]
        access_doc["channels"] = ["A"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session,
                       use_post=True)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    # Issue one shot changes to make sure access grant is successful, the change may not propagate immediately so retry.
    num_retries = 3
    count = 0
    while True:
        if count == num_retries:
            raise exceptions.ChangesError(
                "Didn't get all expected changes before timing out!")

        user_b_changes_after_grant = client.get_changes(
            url=sg_url,
            db=sg_db,
            since=user_b_changes["last_seq"],
            auth=user_b_session,
            feed="normal")

        if len(user_b_changes_after_grant["results"]) > 0:
            # Found changes, break out an validate changes!
            break

        time.sleep(1)
        count += 1

    # User B shoud have recieved 51 docs (a_docs + 1 _user/USER_B doc) if a REST grant or 50 changes if the grant
    # is via the sync function
    changes_results = user_b_changes_after_grant["results"]
    assert 50 <= len(changes_results) <= 51

    # Create a dictionary of id rev pair of all the docs that are not "_user/" docs from changes
    ids_and_revs_from_user_changes = {
        change["id"]: change["changes"][0]["rev"]
        for change in changes_results if not change["id"].startswith("_user/")
    }

    assert len(ids_and_revs_from_user_changes) == 50

    # Create a list of id rev pair of all of the channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}

    assert len(ids_and_revs_from_a_docs) == 50

    # Check that the changes and the a_docs are identical in id and rev
    assert ids_and_revs_from_user_changes == ids_and_revs_from_a_docs

    # Get changes from last_seq of the changes request after the grant. There should be no new changes
    user_b_changes = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant["last_seq"],
        auth=user_b_session,
        feed="normal")
    assert len(user_b_changes["results"]) == 0
示例#8
0
def test_backfill_channels_oneshot_limit_changes(params_from_base_test_setup,
                                                 sg_conf_name, grant_type):
    """
    Test that checks that docs are backfilled for one shot changes with limit for a access grant (via REST or SYNC)

    CHANNEL-REST = Channel is granted to user via REST
    CHANNEL-SYNC = Channel is granted to user via sync function access()
    ROLE-REST = Role is granted to user via REST
    ROLE-SYNC = Role is granted to user via sync function role()
    CHANNEL-TO-ROLE-REST = Channel is added to existing role via REST
    CHANNEL-TO-ROLE-SYNC = Channel is added to existing role via sync access()
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin",
                                        "pass",
                                        channels=["A"],
                                        roles=[])

    if grant_type == "CHANNEL-TO-ROLE-REST" or grant_type == "CHANNEL-TO-ROLE-SYNC":
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=[])
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=["empty_role"])
    else:
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=admin_user_info.name,
                       password=admin_user_info.password,
                       channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=user_b_user_info.name,
                       password=user_b_user_info.password,
                       channels=user_b_user_info.channels,
                       roles=user_b_user_info.roles)

    admin_session = client.create_session(url=sg_admin_url,
                                          db=sg_db,
                                          name=admin_user_info.name,
                                          password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=user_b_user_info.name,
                                           password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=50,
                             id_prefix=None,
                             auth=admin_session,
                             channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=1,
                             id_prefix="b_doc",
                             auth=user_b_session,
                             channels=["B"])
    assert len(b_docs) == 1

    # Loop until admin user sees docs in changes
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=a_docs,
                                  auth=admin_session)

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=b_docs,
                                  auth=user_b_session,
                                  strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=user_b_session,
                                        feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info(
            "Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name=user_b_user_info.name,
                           channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name="USER_B",
                           roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info(
            "Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url,
                       db=sg_db,
                       doc=role_access_doc,
                       auth=admin_session)

    elif grant_type == "CHANNEL-TO-ROLE-REST":
        # Update the empty_role to have channel "A"
        client.update_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=["A"])

    elif grant_type == "CHANNEL-TO-ROLE-SYNC":
        # Grant empty_role access to channel "A" via sync function
        # Grant channel access to role via sync function
        access_doc = document.create_doc("channel_grant_to_role")
        access_doc["roles"] = ["role:empty_role"]
        access_doc["channels"] = ["A"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session,
                       use_post=True)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    log_info("Doing 3, 1 shot changes with limit and last seq!")
    # Issue 3 oneshot changes with a limit of 20

    # Issue one shot changes to make sure access grant is successful, the change may not propagate immediately so retry.
    num_retries = 3
    count = 0
    while True:
        if count == num_retries:
            raise exceptions.ChangesError(
                "Didn't get all expected changes before timing out!")

        user_b_changes_after_grant_one = client.get_changes(
            url=sg_url,
            db=sg_db,
            since=user_b_changes["last_seq"],
            auth=user_b_session,
            feed="normal",
            limit=20)

        if len(user_b_changes_after_grant_one["results"]) > 0:
            # Found changes, break out an validate changes!
            break

        time.sleep(1)
        count += 1

    #################
    # Changes Req #1
    #################

    # Expect a user doc in the changes
    if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
        ids_and_revs_from_a_docs["_user/USER_B"] = None

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_one["results"]) == 20

    for doc in user_b_changes_after_grant_one["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #2
    #################
    user_b_changes_after_grant_two = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant_one["last_seq"],
        auth=user_b_session,
        feed="normal",
        limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_two["results"]) == 20

    for doc in user_b_changes_after_grant_two["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #3
    #################
    user_b_changes_after_grant_three = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant_two["last_seq"],
        auth=user_b_session,
        feed="normal",
        limit=20)

    # User B should have recieved 10 docs due to limit or 11 docs with with a terminating _user doc
    # The terminating user doc only happens with a grant via REST
    if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
        assert len(user_b_changes_after_grant_three["results"]) == 11
    else:
        assert len(user_b_changes_after_grant_three["results"]) == 10

    for doc in user_b_changes_after_grant_three["results"]:
        # cross off non user doc keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    # Make sure all the docs have been crossed out
    assert len(ids_and_revs_from_a_docs) == 0

    #################
    # Changes Req #4
    #################
    user_b_changes_after_grant_four = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant_three["last_seq"],
        auth=user_b_session,
        feed="normal",
        limit=20)

    # Changes should be caught up and there should be no results
    assert len(user_b_changes_after_grant_four["results"]) == 0