Example #1
0
def feature(self, node="clickhouse1"):
    """Check grant query syntax.

    ```sql
    GRANT ON CLUSTER [cluster_name] role [,...] TO {user | another_role | CURRENT_USER} [,...] [WITH ADMIN OPTION]
    ```
    """
    node = self.context.cluster.node(node)

    @contextmanager
    def setup(users=0, roles=0):
        try:
            with Given("I have some users and roles"):
                for i in range(users):
                    node.query(f"CREATE USER OR REPLACE user{i}")
                for j in range(roles):
                    node.query(f"CREATE ROLE OR REPLACE role{j}")
            yield
        finally:
            with Finally("I drop the users and roles"):
                for i in range(users):
                    node.query(f"DROP USER IF EXISTS user{i}")
                for j in range(roles):
                    node.query(f"DROP ROLE IF EXISTS role{j}")

    with Scenario("I grant a role to a user",
                  requirements=[RQ_SRS_006_RBAC_Grant_Role("1.0")]):
        with setup(1, 1):
            with When("I grant a role"):
                node.query("GRANT role0 TO user0")

    with Scenario(
            "I grant a nonexistent role to user",
            requirements=[RQ_SRS_006_RBAC_Grant_Role("1.0")],
    ):
        with setup(1, 0):
            with When("I grant nonexistent role to a user"):
                exitcode, message = errors.role_not_found_in_disk(name="role0")
                node.query("GRANT role0 TO user0",
                           exitcode=exitcode,
                           message=message)

    # with nonexistent object name, GRANT assumes type role (treats user0 as role)
    with Scenario(
            "I grant a role to a nonexistent user",
            requirements=[RQ_SRS_006_RBAC_Grant_Role("1.0")],
    ):
        with setup(0, 1):
            with When("I grant role to a nonexistent user"):
                exitcode, message = errors.role_not_found_in_disk(name="user0")
                node.query("GRANT role0 TO user0",
                           exitcode=exitcode,
                           message=message)

    with Scenario(
            "I grant a nonexistent role to a nonexistent user",
            requirements=[RQ_SRS_006_RBAC_Grant_Role("1.0")],
    ):
        with setup(0, 0):
            with When("I grant nonexistent role to a nonexistent user"):
                exitcode, message = (errors.role_not_found_in_disk(
                    name="user0") if check_clickhouse_version(">=21.09")(self)
                                     else errors.role_not_found_in_disk(
                                         name="role0"))
                node.query("GRANT role0 TO user0",
                           exitcode=exitcode,
                           message=message)

    with Scenario(
            "I grant a role to multiple users",
            requirements=[RQ_SRS_006_RBAC_Grant_Role("1.0")],
    ):
        with setup(2, 1):
            with When("I grant role to a multiple users"):
                node.query("GRANT role0 TO user0, user1")

    with Scenario(
            "I grant multiple roles to multiple users",
            requirements=[RQ_SRS_006_RBAC_Grant_Role("1.0")],
    ):
        with setup(2, 2):
            with When("I grant multiple roles to multiple users"):
                node.query("GRANT role0, role1 TO user0, user1")

    with Scenario(
            "I grant role to current user",
            requirements=[RQ_SRS_006_RBAC_Grant_Role_CurrentUser("1.0")],
    ):
        with setup(1, 1):
            with Given("I have a user with access management privilege"):
                node.query("GRANT ACCESS MANAGEMENT ON *.* TO user0")
            with When("I grant role to current user"):
                node.query("GRANT role0 TO CURRENT_USER",
                           settings=[("user", "user0")])

    with Scenario(
            "I grant role to default user, throws exception",
            requirements=[RQ_SRS_006_RBAC_Grant_Role_CurrentUser("1.0")],
    ):
        with setup(1, 1):
            with When("I grant role to default user"):
                exitcode, message = errors.cannot_update_default()
                node.query("GRANT role0 TO CURRENT_USER",
                           exitcode=exitcode,
                           message=message)

    with Scenario(
            "I grant role to user with admin option",
            requirements=[RQ_SRS_006_RBAC_Grant_Role_AdminOption("1.0")],
    ):
        with setup(1, 1):
            with When("I grant role to a user with admin option"):
                node.query("GRANT role0 TO user0 WITH ADMIN OPTION")

    with Scenario(
            "I grant role to user on cluster",
            requirements=[RQ_SRS_006_RBAC_Grant_Role_OnCluster("1.0")],
    ):
        try:
            with Given("I have a user and a role on a cluster"):
                node.query(
                    "CREATE USER OR REPLACE user0 ON CLUSTER sharded_cluster")
                node.query(
                    "CREATE ROLE OR REPLACE role0 ON CLUSTER sharded_cluster")
            with When("I grant the role to the user"):
                node.query("GRANT ON CLUSTER sharded_cluster role0 TO user0")
        finally:
            with Finally("I drop the user and role"):
                node.query(
                    "DROP USER IF EXISTS user0 ON CLUSTER sharded_cluster")
                node.query(
                    "DROP ROLE IF EXISTS role0 ON CLUSTER sharded_cluster")

    with Scenario(
            "I grant role to user on fake cluster, throws exception",
            requirements=[RQ_SRS_006_RBAC_Grant_Role_OnCluster("1.0")],
    ):
        with setup(1, 1):
            with When("I grant the role to the user"):
                exitcode, message = errors.cluster_not_found("fake_cluster")
                node.query(
                    "GRANT ON CLUSTER fake_cluster role0 TO user0",
                    exitcode=exitcode,
                    message=message,
                )
Example #2
0
}

xflags = {
    "privileges/alter index/table_type='ReplicatedVersionedCollapsingMergeTree-sharded_cluster'/role with privileges from role with grant option/granted=:/I try to ALTER INDEX with given privileges/I check order by when privilege is granted":
    (
        SKIP,
        0,
    )
}

ffails = {
    "/clickhouse/rbac/privileges/:/table_type='ReplicatedReplacingMergeTree-sharded_cluster":
    (
        Skip,
        "Causes clickhouse timeout on 21.10",
        (lambda test: check_clickhouse_version(">=21.10")
         (test) and check_clickhouse_version("<21.11")(test)),
    ),
    "/clickhouse/rbac/views": (
        Skip,
        "Does not work on clickhouse 21.09",
        (lambda test: check_clickhouse_version(">=21.9")
         (test) and check_clickhouse_version("<21.10")(test)),
    ),
}


@TestModule
@ArgumentParser(argparser)
@XFails(xfails)
@XFlags(xflags)
@FFails(ffails)
Example #3
0
# Cross-outs of known fails
xfails = {
    "mapping/roles removed and added in parallel": [(Fail, "known bug")],
    "user dn detection/mapping/roles removed and added in parallel":
    [(Fail, "known bug")],
    "cluster secret/external user directory/:/:/cluster with secret/ldap user/:mapped True/select using mapped role/with privilege on source and distributed":
    [(Fail, "https://github.com/ClickHouse/ClickHouse/issues/34130")],
}

# Force results without running the test
ffails = {
    "cluster secret": (
        Skip,
        "feature available on 20.10+",
        check_clickhouse_version("<20.10"),
    )
}


@TestFeature
@Name("role mapping")
@ArgumentParser(argparser)
@Specifications(SRS_014_ClickHouse_LDAP_Role_Mapping)
@Requirements(RQ_SRS_014_LDAP_RoleMapping("1.0"))
@XFails(xfails)
@FFails(ffails)
def regression(self,
               local,
               clickhouse_binary_path,
               clickhouse_version=None,
Example #4
0
from helpers.common import check_clickhouse_version

# Cross-outs of known fails
xfails = {
    "mapping/roles removed and added in parallel":
        [(Fail, "known bug")],
    "user dn detection/mapping/roles removed and added in parallel":
        [(Fail, "known bug")],
    "cluster secret/external user directory/:/:/cluster with secret/ldap user/:mapped True/select using mapped role/with privilege on source and distributed":
        [(Fail, "https://github.com/ClickHouse/ClickHouse/issues/34130")]
}

# Force results without running the test
ffails={
    "cluster secret": 
        (Skip, "feature available on 20.10+", check_clickhouse_version("<20.10"))
}

@TestFeature
@Name("role mapping")
@ArgumentParser(argparser)
@Specifications(
    SRS_014_ClickHouse_LDAP_Role_Mapping
)
@Requirements(
    RQ_SRS_014_LDAP_RoleMapping("1.0")
)
@XFails(xfails)
@FFails(ffails)
def regression(self, local, clickhouse_binary_path, clickhouse_version=None, stress=None):
    """ClickHouse LDAP role mapping regression module.
Example #5
0
        (Fail, "can't be tested with self-signed certificates")
    ],
    "connection protocols/tls require cert default demand": [
        (Fail, "can't be tested with self-signed certificates")
    ],
    "connection protocols/starttls with custom port":
    [(Fail,
      "it seems that starttls is not enabled by default on custom plain-text ports in LDAP server"
      )],
    "connection protocols/tls cipher suite": [(Fail, "can't get it to work")]
}

ffails = {
    "user authentications/verification cooldown performance/:":
    (Skip, "causes timeout on 21.8",
     (lambda test: check_clickhouse_version(">=21.8")
      (test) and check_clickhouse_version("<21.9")(test)))
}


@TestFeature
@Name("external user directory")
@ArgumentParser(argparser)
@Specifications(SRS_009_ClickHouse_LDAP_External_User_Directory)
@Requirements(RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication("1.0"))
@XFails(xfails)
@FFails(ffails)
def regression(self,
               local,
               clickhouse_binary_path,
               clickhouse_version=None,
               stress=None):
Example #6
0
def feature(self, node="clickhouse1"):
    """Check revoke query syntax.

    ```sql
    REVOKE [ON CLUSTER cluster_name] [ADMIN OPTION FOR]
        role [,...] FROM {user | role | CURRENT_USER} [,...]
             | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...]
    ```
    """
    node = self.context.cluster.node(node)

    @contextmanager
    def setup(users=2, roles=2):
        try:
            with Given("I have some users"):
                for i in range(users):
                    node.query(f"CREATE USER OR REPLACE user{i}")
            with And("I have some roles"):
                for i in range(roles):
                    node.query(f"CREATE ROLE OR REPLACE role{i}")
            yield
        finally:
            with Finally("I drop the users"):
                for i in range(users):
                    node.query(f"DROP USER IF EXISTS user{i}")
            with And("I drop the roles"):
                for i in range(roles):
                    node.query(f"DROP ROLE IF EXISTS role{i}")

    with Scenario("I revoke a role from a user",
                  requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
        with setup():
            with When("I revoke a role"):
                node.query("REVOKE role0 FROM user0")

    with Scenario(
            "I revoke a nonexistent role from user",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup(1, 0):
            with When("I revoke nonexistent role from a user"):
                exitcode, message = errors.role_not_found_in_disk(name="role0")
                node.query("REVOKE role0 FROM user0",
                           exitcode=exitcode,
                           message=message)

    # with nonexistent object name, REVOKE assumes type role (treats user0 as role)
    with Scenario(
            "I revoke a role from a nonexistent user",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup(0, 1):
            with When("I revoke role from a nonexistent user"):
                exitcode, message = errors.role_not_found_in_disk(name="user0")
                node.query("REVOKE role0 FROM user0",
                           exitcode=exitcode,
                           message=message)

    # with nonexistent object name, REVOKE assumes type role (treats user0 as role)
    with Scenario(
            "I revoke a role from ALL EXCEPT nonexistent user",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup(0, 1):
            with When("I revoke role from a nonexistent user"):
                exitcode, message = errors.role_not_found_in_disk(name="user0")
                node.query(
                    "REVOKE role0 FROM ALL EXCEPT user0",
                    exitcode=exitcode,
                    message=message,
                )

    with Scenario(
            "I revoke a nonexistent role from a nonexistent user",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup(0, 0):
            with When("I revoke nonexistent role from a nonexistent user"):
                exitcode, message = (errors.role_not_found_in_disk(
                    name="user0") if check_clickhouse_version(">=21.09")(self)
                                     else errors.role_not_found_in_disk(
                                         name="role0"))
                node.query("REVOKE role0 FROM user0",
                           exitcode=exitcode,
                           message=message)

    with Scenario(
            "I revoke a role from multiple users",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup():
            with When("I revoke a role from multiple users"):
                node.query("REVOKE role0 FROM user0, user1")

    with Scenario(
            "I revoke multiple roles from multiple users",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup():
            node.query("REVOKE role0, role1 FROM user0, user1")

    # user is default, expect exception
    with Scenario(
            "I revoke a role from default user",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke a role from default user"):
                exitcode, message = errors.cannot_update_default()
                node.query("REVOKE role0 FROM CURRENT_USER",
                           exitcode=exitcode,
                           message=message)

    # user is user0
    with Scenario(
            "I revoke a role from current user",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke a role from current user"):
                node.query("REVOKE role0 FROM CURRENT_USER",
                           settings=[("user", "user0")])

    # user is default, expect exception
    with Scenario(
            "I revoke a role from all",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke a role from all"):
                exitcode, message = errors.cannot_update_default()
                node.query("REVOKE role0 FROM ALL",
                           exitcode=exitcode,
                           message=message)

    # user is default, expect exception
    with Scenario(
            "I revoke multiple roles from all",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke multiple roles from all"):
                exitcode, message = errors.cannot_update_default()
                node.query("REVOKE role0, role1 FROM ALL",
                           exitcode=exitcode,
                           message=message)

    with Scenario(
            "I revoke a role from all but current user",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke a role from all except current"):
                node.query("REVOKE role0 FROM ALL EXCEPT CURRENT_USER")

    with Scenario(
            "I revoke a role from all but default user",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke a role from all except default"):
                node.query("REVOKE role0 FROM ALL EXCEPT default",
                           settings=[("user", "user0")])

    with Scenario(
            "I revoke multiple roles from all but default user",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0"),
            ],
    ):
        with setup():
            with When("I revoke multiple roles from all except default"):
                node.query(
                    "REVOKE role0, role1 FROM ALL EXCEPT default",
                    settings=[("user", "user0")],
                )

    with Scenario("I revoke a role from a role",
                  requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
        with setup():
            with When("I revoke a role from a role"):
                node.query("REVOKE role0 FROM role1")

    with Scenario(
            "I revoke a role from a role and a user",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role("1.0")],
    ):
        with setup():
            with When("I revoke a role from multiple roles"):
                node.query("REVOKE role0 FROM role1, user0")

    with Scenario(
            "I revoke a role from a user on cluster",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0")],
    ):
        with Given("I have a role and a user on a cluster"):
            node.query(
                "CREATE USER OR REPLACE user0 ON CLUSTER sharded_cluster")
            node.query(
                "CREATE ROLE OR REPLACE role0 ON CLUSTER sharded_cluster")
        with When("I revoke a role from user on a cluster"):
            node.query("REVOKE ON CLUSTER sharded_cluster role0 FROM user0")
        with Finally("I drop the user and role"):
            node.query("DROP USER IF EXISTS user0 ON CLUSTER sharded_cluster")
            node.query("DROP ROLE IF EXISTS role0 ON CLUSTER sharded_cluster")

    with Scenario(
            "I revoke a role on fake cluster, throws exception",
            requirements=[RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0")],
    ):
        with Given("I have a role and a user on a cluster"):
            node.query("CREATE USER OR REPLACE user0")
            node.query("CREATE ROLE OR REPLACE role0")
        with When("I revoke a role from user on a cluster"):
            exitcode, message = errors.cluster_not_found("fake_cluster")
            node.query(
                "REVOKE ON CLUSTER fake_cluster role0 FROM user0",
                exitcode=exitcode,
                message=message,
            )
        with Finally("I drop the user and role"):
            node.query("DROP USER IF EXISTS user0")
            node.query("DROP ROLE IF EXISTS role0")

    with Scenario(
            "I revoke multiple roles from multiple users on cluster",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0"),
            ],
    ):
        with Given("I have multiple roles and multiple users on a cluster"):
            for i in range(2):
                node.query(
                    f"CREATE USER OR REPLACE user{i} ON CLUSTER sharded_cluster"
                )
                node.query(
                    f"CREATE ROLE OR REPLACE role{i} ON CLUSTER sharded_cluster"
                )
        with When("I revoke multiple roles from multiple users on cluster"):
            node.query(
                "REVOKE ON CLUSTER sharded_cluster role0, role1 FROM user0, user1"
            )
        with Finally("I drop the roles and users"):
            for i in range(2):
                node.query(
                    f"DROP USER IF EXISTS user{i} ON CLUSTER sharded_cluster")
                node.query(
                    f"DROP ROLE IF EXISTS role{i} ON CLUSTER sharded_cluster")

    with Scenario(
            "I revoke admin option for role from a user",
            requirements=[RQ_SRS_006_RBAC_Revoke_AdminOption("1.0")],
    ):
        with setup():
            with When("I revoke admin option for role from a user"):
                node.query("REVOKE ADMIN OPTION FOR role0 FROM user0")

    with Scenario(
            "I revoke admin option for multiple roles from multiple users",
            requirements=[
                RQ_SRS_006_RBAC_Revoke_Role("1.0"),
                RQ_SRS_006_RBAC_Revoke_AdminOption("1.0"),
            ],
    ):
        with setup():
            with When(
                    "I revoke admin option for multiple roles from multiple users"
            ):
                node.query(
                    "REVOKE ADMIN OPTION FOR role0, role1 FROM user0, user1")