Skip to content

Commit a7d832e

Browse files
add rbac test case (milvus-io#18787)
Signed-off-by: huangjincheng2022 <[email protected]> Signed-off-by: huangjincheng2022 <[email protected]>
1 parent 1436b7a commit a7d832e

File tree

5 files changed

+473
-11
lines changed

5 files changed

+473
-11
lines changed

tests/python_client/base/utility_wrapper.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from check.func_check import ResponseChecker
88
from utils.api_request import api_request
99
from common.common_type import BulkLoadStates
10+
from pymilvus.orm.role import Role
1011

1112

1213
TIMEOUT = 20
@@ -16,6 +17,7 @@ class ApiUtilityWrapper:
1617
""" Method of encapsulating utility files """
1718

1819
ut = utility
20+
role = None
1921

2022
def bulk_load(self, collection_name, partition_name="", row_based=True, files="", timeout=None,
2123
using="default", check_task=None, check_items=None, **kwargs):
@@ -251,3 +253,95 @@ def delete_user(self, user, using="default", check_task=None, check_items=None):
251253
check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ,
252254
using=using).run()
253255
return res, check_result
256+
257+
def init_role(self, name, using="default", check_task=None, check_items=None, **kwargs):
258+
func_name = sys._getframe().f_code.co_name
259+
res, is_succ = api_request([Role, name, using], **kwargs)
260+
self.role = res if is_succ else None
261+
check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ,
262+
name=name, **kwargs).run()
263+
return res, check_result
264+
265+
def create_role(self, using="default", check_task=None, check_items=None, **kwargs):
266+
func_name = sys._getframe().f_code.co_name
267+
res, is_succ = api_request([self.role.create], **kwargs)
268+
res = res if is_succ else None
269+
check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ,
270+
**kwargs).run()
271+
return res, check_result
272+
273+
def list_roles(self, include_user_info: bool, using="default", check_task=None, check_items=None):
274+
func_name = sys._getframe().f_code.co_name
275+
res, is_succ = api_request([self.ut.list_roles, include_user_info, using])
276+
check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ, using=using).run()
277+
return res, check_result
278+
279+
def list_user(self, username: str, include_role_info: bool, using="default", check_task=None, check_items=None):
280+
func_name = sys._getframe().f_code.co_name
281+
res, is_succ = api_request([self.ut.list_user, username, include_role_info, using])
282+
check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ, using=using).run()
283+
return res, check_result
284+
285+
def list_users(self, include_role_info: bool, using="default", check_task=None, check_items=None):
286+
func_name = sys._getframe().f_code.co_name
287+
res, is_succ = api_request([self.ut.list_users, include_role_info, using])
288+
check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ, using=using).run()
289+
return res, check_result
290+
291+
def role_drop(self, check_task=None, check_items=None, **kwargs):
292+
func_name = sys._getframe().f_code.co_name
293+
res, check = api_request([self.role.drop], **kwargs)
294+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
295+
return res, check_result
296+
297+
def role_is_exist(self, check_task=None, check_items=None, **kwargs):
298+
func_name = sys._getframe().f_code.co_name
299+
res, check = api_request([self.role.is_exist], **kwargs)
300+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
301+
return res, check_result
302+
303+
def role_add_user(self, username: str, check_task=None, check_items=None, **kwargs):
304+
func_name = sys._getframe().f_code.co_name
305+
res, check = api_request([self.role.add_user, username], **kwargs)
306+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
307+
return res, check_result
308+
309+
def role_remove_user(self, username: str, check_task=None, check_items=None, **kwargs):
310+
func_name = sys._getframe().f_code.co_name
311+
res, check = api_request([self.role.remove_user, username], **kwargs)
312+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
313+
return res, check_result
314+
315+
def role_get_users(self, check_task=None, check_items=None, **kwargs):
316+
func_name = sys._getframe().f_code.co_name
317+
res, check = api_request([self.role.get_users], **kwargs)
318+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
319+
return res, check_result
320+
321+
@property
322+
def role_name(self):
323+
return self.role.name
324+
325+
def role_grant(self, object: str, object_name: str, privilege: str, check_task=None, check_items=None, **kwargs):
326+
func_name = sys._getframe().f_code.co_name
327+
res, check = api_request([self.role.grant, object, object_name, privilege], **kwargs)
328+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
329+
return res, check_result
330+
331+
def role_revoke(self, object: str, object_name: str, privilege: str, check_task=None, check_items=None, **kwargs):
332+
func_name = sys._getframe().f_code.co_name
333+
res, check = api_request([self.role.revoke, object, object_name, privilege], **kwargs)
334+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
335+
return res, check_result
336+
337+
def role_list_grant(self, object: str, object_name: str, check_task=None, check_items=None, **kwargs):
338+
func_name = sys._getframe().f_code.co_name
339+
res, check = api_request([self.role.list_grant, object, object_name], **kwargs)
340+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
341+
return res, check_result
342+
343+
def role_list_grants(self, check_task=None, check_items=None, **kwargs):
344+
func_name = sys._getframe().f_code.co_name
345+
res, check = api_request([self.role.list_grants], **kwargs)
346+
check_result = ResponseChecker(res, func_name, check_task, check_items, check, **kwargs).run()
347+
return res, check_result

tests/python_client/check/func_check.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pymilvus.client.types import CompactionPlans
2+
from pymilvus.orm.role import Role
23

34
from utils.util_log import test_log as log
45
from common import common_type as ct
@@ -75,6 +76,10 @@ def run(self):
7576
elif self.check_task == CheckTasks.check_merge_compact:
7677
result = self.check_merge_compact_plan(self.response, self.func_name, self.check_items)
7778

79+
elif self.check_task == CheckTasks.check_role_property:
80+
# Collection interface response check
81+
result = self.check_role_property(self.response, self.func_name, self.check_items)
82+
7883
# Add check_items here if something new need verify
7984

8085
return result
@@ -373,3 +378,15 @@ def check_merge_compact_plan(compaction_plans, func_name, check_items):
373378
assert len(compaction_plans.plans[0].sources) == segment_num
374379
assert compaction_plans.plans[0].target not in compaction_plans.plans[0].sources
375380

381+
@staticmethod
382+
def check_role_property(role, func_name, check_items):
383+
exp_func_name = "create_role"
384+
if func_name != exp_func_name:
385+
log.warning("The function name is {} rather than {}".format(func_name, exp_func_name))
386+
if not isinstance(role, Role):
387+
raise Exception("The result to check isn't role type object")
388+
if check_items is None:
389+
raise Exception("No expect values found in the check task")
390+
if check_items.get("name", None):
391+
assert role.name == check_items["name"]
392+
return True

tests/python_client/common/common_func.py

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
"""" Methods of processing data """
1515

16+
1617
class ParamInfo:
1718
def __init__(self):
1819
self.param_host = ""
@@ -54,11 +55,15 @@ def gen_bool_field(name=ct.default_bool_field_name, description=ct.default_desc,
5455
is_primary=is_primary, **kwargs)
5556
return bool_field
5657

57-
def gen_string_field(name=ct.default_string_field_name, description=ct.default_desc, is_primary=False, max_length=ct.default_length, **kwargs):
58-
string_field, _ = ApiFieldSchemaWrapper().init_field_schema(name=name, dtype=DataType.VARCHAR, description=description, max_length=max_length,
59-
is_primary=is_primary, **kwargs)
58+
59+
def gen_string_field(name=ct.default_string_field_name, description=ct.default_desc, is_primary=False,
60+
max_length=ct.default_length, **kwargs):
61+
string_field, _ = ApiFieldSchemaWrapper().init_field_schema(name=name, dtype=DataType.VARCHAR,
62+
description=description, max_length=max_length,
63+
is_primary=is_primary, **kwargs)
6064
return string_field
6165

66+
6267
def gen_int8_field(name=ct.default_int8_field_name, description=ct.default_desc, is_primary=False, **kwargs):
6368
int8_field, _ = ApiFieldSchemaWrapper().init_field_schema(name=name, dtype=DataType.INT8, description=description,
6469
is_primary=is_primary, **kwargs)
@@ -118,6 +123,7 @@ def gen_default_collection_schema(description=ct.default_desc, primary_field=ct.
118123
primary_field=primary_field, auto_id=auto_id)
119124
return schema
120125

126+
121127
def gen_general_collection_schema(description=ct.default_desc, primary_field=ct.default_int64_field_name,
122128
auto_id=False, is_binary=False, dim=ct.default_dim):
123129
if is_binary:
@@ -128,8 +134,9 @@ def gen_general_collection_schema(description=ct.default_desc, primary_field=ct.
128134
primary_field=primary_field, auto_id=auto_id)
129135
return schema
130136

137+
131138
def gen_string_pk_default_collection_schema(description=ct.default_desc, primary_field=ct.default_string_field_name,
132-
auto_id=False, dim=ct.default_dim):
139+
auto_id=False, dim=ct.default_dim):
133140
fields = [gen_int64_field(), gen_float_field(), gen_string_field(), gen_float_vec_field(dim=dim)]
134141
schema, _ = ApiCollectionSchemaWrapper().init_collection_schema(fields=fields, description=description,
135142
primary_field=primary_field, auto_id=auto_id)
@@ -162,32 +169,33 @@ def gen_default_binary_collection_schema(description=ct.default_desc, primary_fi
162169

163170

164171
def gen_schema_multi_vector_fields(vec_fields):
165-
fields = [gen_int64_field(), gen_float_field(),gen_string_field(), gen_float_vec_field()]
172+
fields = [gen_int64_field(), gen_float_field(), gen_string_field(), gen_float_vec_field()]
166173
fields.extend(vec_fields)
167174
primary_field = ct.default_int64_field_name
168175
schema, _ = ApiCollectionSchemaWrapper().init_collection_schema(fields=fields, description=ct.default_desc,
169176
primary_field=primary_field, auto_id=False)
170177
return schema
171178

179+
172180
def gen_schema_multi_string_fields(string_fields):
173-
fields =[gen_int64_field(), gen_float_field(),gen_string_field(),gen_float_vec_field()]
181+
fields = [gen_int64_field(), gen_float_field(), gen_string_field(), gen_float_vec_field()]
174182
fields.extend(string_fields)
175183
primary_field = ct.default_int64_field_name
176184
schema, _ = ApiCollectionSchemaWrapper().init_collection_schema(fields=fields, description=ct.default_desc,
177185
primary_field=primary_field, auto_id=False)
178186
return schema
179187

180188

181-
182189
def gen_vectors(nb, dim):
183190
vectors = [[random.random() for _ in range(dim)] for _ in range(nb)]
184191
vectors = preprocessing.normalize(vectors, axis=1, norm='l2')
185192
return vectors.tolist()
186193

194+
187195
def gen_string(nb):
188196
string_values = [str(random.random()) for _ in range(nb)]
189197
return string_values
190-
198+
191199

192200
def gen_binary_vectors(num, dim):
193201
raw_vectors = []
@@ -239,6 +247,7 @@ def gen_dataframe_multi_vec_fields(vec_fields, nb=ct.default_nb):
239247
df[field.name] = vec_values
240248
return df
241249

250+
242251
def gen_dataframe_multi_string_fields(string_fields, nb=ct.default_nb):
243252
"""
244253
gen dataframe data for fields: int64, float, float_vec and vec_fields
@@ -495,17 +504,18 @@ def gen_normal_string_expressions(field):
495504
f"{field} == \"0\"|| {field} == \"1\"|| {field} ==\"2\"",
496505
f"{field} != \"0\"",
497506
f"{field} not in [\"0\", \"1\", \"2\"]",
498-
f"{field} in [\"0\", \"1\", \"2\"]"
507+
f"{field} in [\"0\", \"1\", \"2\"]"
499508
]
500509
return expressions
501510

511+
502512
def gen_invaild_string_expressions():
503513
expressions = [
504514
"varchar in [0, \"1\"]",
505-
"varchar not in [\"0\", 1, 2]"
515+
"varchar not in [\"0\", 1, 2]"
506516
]
507517
return expressions
508-
518+
509519

510520
def gen_normal_expressions_field(field):
511521
expressions = [
@@ -705,6 +715,7 @@ def get_segment_distribution(res):
705715

706716
return segment_distribution
707717

718+
708719
def percent_to_int(string):
709720
"""
710721
transform percent(0%--100%) to int
@@ -720,3 +731,31 @@ def percent_to_int(string):
720731
new_int = int(string.strip("%"))
721732

722733
return new_int
734+
735+
736+
def gen_grant_list(collection_name):
737+
grant_list = [{"object": "Collection", "object_name": collection_name, "privilege": "Load"},
738+
{"object": "Collection", "object_name": collection_name, "privilege": "Release"},
739+
{"object": "Collection", "object_name": collection_name, "privilege": "Compaction"},
740+
{"object": "Collection", "object_name": collection_name, "privilege": "Delete"},
741+
{"object": "Collection", "object_name": collection_name, "privilege": "GetStatistics"},
742+
{"object": "Collection", "object_name": collection_name, "privilege": "CreateIndex"},
743+
{"object": "Collection", "object_name": collection_name, "privilege": "IndexDetail"},
744+
{"object": "Collection", "object_name": collection_name, "privilege": "DropIndex"},
745+
{"object": "Collection", "object_name": collection_name, "privilege": "Search"},
746+
{"object": "Collection", "object_name": collection_name, "privilege": "Flush"},
747+
{"object": "Collection", "object_name": collection_name, "privilege": "Query"},
748+
{"object": "Collection", "object_name": collection_name, "privilege": "LoadBalance"},
749+
{"object": "Collection", "object_name": collection_name, "privilege": "Import"},
750+
{"object": "Global", "object_name": "*", "privilege": "All"},
751+
{"object": "Global", "object_name": "*", "privilege": "CreateCollection"},
752+
{"object": "Global", "object_name": "*", "privilege": "DropCollection"},
753+
{"object": "Global", "object_name": "*", "privilege": "DescribeCollection"},
754+
{"object": "Global", "object_name": "*", "privilege": "ShowCollections"},
755+
{"object": "Global", "object_name": "*", "privilege": "CreateOwnership"},
756+
{"object": "Global", "object_name": "*", "privilege": "DropOwnership"},
757+
{"object": "Global", "object_name": "*", "privilege": "SelectOwnership"},
758+
{"object": "Global", "object_name": "*", "privilege": "ManageOwnership"},
759+
{"object": "User", "object_name": "*", "privilege": "UpdateUser"},
760+
{"object": "User", "object_name": "*", "privilege": "SelectUser"}]
761+
return grant_list

tests/python_client/common/common_type.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class CheckTasks:
194194
check_distance = "check_distance"
195195
check_delete_compact = "check_delete_compact"
196196
check_merge_compact = "check_merge_compact"
197+
check_role_property = "check_role_property"
197198

198199

199200
class BulkLoadStates:

0 commit comments

Comments
 (0)