@@ -111,7 +111,7 @@ def expr(self):
111111 def calculate (self , left , right ):
112112 pass
113113
114- def _eval_positive (self , attr , attr_is_array , value , value_is_array ): # NOQA
114+ def _eval_positive (self , object_attr , is_object_attr_array , policy_value ): # NOQA
115115 """
116116 positive:
117117 - 1 hit: return True
@@ -121,65 +121,22 @@ def _eval_positive(self, attr, attr_is_array, value, value_is_array): # NOQA
121121 op = eq => one of attr equals one of value
122122
123123 attr = 1; value = 1; True
124- attr = 1; value = [1, 2]; True
125124 attr = [1, 2]; value = 2; True
126- attr = [1, 2]; value = [5, 1]; True
127-
128- attr = [1, 2]; value = [3, 4]; False
129125 """
130- if self .op == OP .ANY :
131- return self .calculate (attr , value )
132-
133- # 1. IN/NOT_IN value is a list, just only check attr
134- if self .op in (OP .IN ,):
135- if attr_is_array :
136- for a in attr :
137- if self .calculate (a , value ):
138- return True
139- return False
140-
141- return self .calculate (attr , value )
142-
143- # 2. CONTAINS/NOT_CONTAINS attr is a list, just check value
144- if self .op in (OP .CONTAINS ,):
145- if value_is_array :
146- for v in value :
147- if self .calculate (attr , v ):
148- return True
149- return False
150-
151- return self .calculate (attr , value )
152-
153- # 3. Others, check both attr and value
154- # 3.1 both not array, the most common situation
155- if not (value_is_array or attr_is_array ):
156- return self .calculate (attr , value )
157-
158- # 3.2 only value is array, the second common situation
159- if value_is_array and (not attr_is_array ):
160- for v in value :
161- # return early if hit
162- if self .calculate (attr , v ):
126+ # if self.op == OP.ANY:
127+ # return self.calculate(object_attr, policy_value)
128+
129+ # NOTE: here, the policyValue should not be array!
130+ # It's single value (except: the NotIn op policyValue is an array)
131+ if is_object_attr_array :
132+ for a in object_attr :
133+ if self .calculate (a , policy_value ):
163134 return True
164135 return False
165136
166- # 3.3 only attr value is array
167- if (not value_is_array ) and attr_is_array :
168- for a in attr :
169- # return early if hit
170- if self .calculate (a , value ):
171- return True
172- return False
137+ return self .calculate (object_attr , policy_value )
173138
174- # 4. both array
175- for a in attr :
176- for v in value :
177- # return early if hit
178- if self .calculate (a , v ):
179- return True
180- return False
181-
182- def _eval_negative (self , attr , attr_is_array , value , value_is_array ): # NOQA
139+ def _eval_negative (self , object_attr , is_object_attr_array , policy_value ): # NOQA
183140 """
184141 negative:
185142 - 1 miss: return False
@@ -189,58 +146,18 @@ def _eval_negative(self, attr, attr_is_array, value, value_is_array): # NOQA
189146 op = not_eq => all of attr should not_eq to all of the value
190147
191148 attr = 1; value = 2; True
192- attr = 1; value = [2]; True
193- attr = [1, 2]; value = [3, 4]; True
194149 attr = [1, 2]; value = 3; True
195-
196- attr = [1, 2]; value = [2, 3]; False
197150 """
198- # 1. IN/NOT_IN value is a list, just only check attr
199- if self .op in (OP .NOT_IN ,):
200- if attr_is_array :
201- for a in attr :
202- if not self .calculate (a , value ):
203- return False
204- return True
205-
206- return self .calculate (attr , value )
207-
208- # 2. CONTAINS/NOT_CONTAINS attr is a list, just check value
209- if self .op in (OP .NOT_CONTAINS ,):
210- if value_is_array :
211- for v in value :
212- if not self .calculate (attr , v ):
213- return False
214- return True
215-
216- return self .calculate (attr , value )
217-
218- # 3. Others, check both attr and value
219- # 3.1 both not array, the most common situation
220- if not (value_is_array or attr_is_array ):
221- return self .calculate (attr , value )
222151
223- # 3.2 only value is array, the second common situation
224- if value_is_array and (not attr_is_array ):
225- for v in value :
226- if not self .calculate (attr , v ):
152+ # NOTE: here, the policyValue should not be array!
153+ # It's single value (except: the NotIn op policyValue is an array)
154+ if is_object_attr_array :
155+ for a in object_attr :
156+ if not self .calculate (a , policy_value ):
227157 return False
228158 return True
229159
230- # 3.3 only attr value is array
231- if (not value_is_array ) and attr_is_array :
232- for a in attr :
233- if not self .calculate (a , value ):
234- return False
235- return True
236-
237- # 4. both array
238- for a in attr :
239- for v in value :
240- # return early if hit
241- if not self .calculate (a , v ):
242- return False
243- return True
160+ return self .calculate (object_attr , policy_value )
244161
245162 def eval (self , obj_set ):
246163 """
@@ -251,19 +168,64 @@ def eval(self, obj_set):
251168 if one of them is array, or both array
252169 calculate each item in array
253170 """
254- attr = obj_set .get (self .field )
255- value = self .value
171+ object_attr = obj_set .get (self .field )
172+ policy_value = self .value
256173
257- attr_is_array = isinstance (attr , (list , tuple ))
258- value_is_array = isinstance (value , (list , tuple ))
174+ is_object_attr_array = isinstance (object_attr , (list , tuple ))
175+ is_policy_value_array = isinstance (policy_value , (list , tuple ))
259176
260- # positive and negative operator
261- # == 命中一个即返回
262- # != 需要全部遍历完, 确认全部不等于才返回?
263- if self .op .startswith ("not_" ):
264- return self ._eval_negative (attr , attr_is_array , value , value_is_array )
265- else :
266- return self ._eval_positive (attr , attr_is_array , value , value_is_array )
177+ # any
178+ if self .op == OP .ANY :
179+ return True
180+
181+ # if you add new operator, please read this first: https://github.com/TencentBlueKing/bk-iam-saas/issues/1293
182+ # valid the attr and value first
183+ if self .op in (OP .IN , OP .NOT_IN ):
184+ # a in b, a not_in b
185+ # b should be an array, while a can be a single or an array
186+ # so we should make the in expression b always be an array
187+ if not is_policy_value_array :
188+ return False
189+
190+ if self .op == OP .IN :
191+ return self ._eval_positive (object_attr , is_object_attr_array , policy_value )
192+ else :
193+ return self ._eval_negative (object_attr , is_object_attr_array , policy_value )
194+
195+ if self .op in (OP .CONTAINS , OP .NOT_CONTAINS ):
196+ # a contains b, a not_contains b
197+ # a should be an array, b should be a single value
198+ # so, we should make the contains expression b always be a single string,
199+ # while a can be a single value or an array
200+ if not is_object_attr_array or is_policy_value_array :
201+ return False
202+ return self .calculate (object_attr , policy_value )
203+
204+ if self .op in (
205+ OP .EQ ,
206+ OP .NOT_EQ ,
207+ OP .LT ,
208+ OP .LTE ,
209+ OP .GT ,
210+ OP .GTE ,
211+ OP .STARTS_WITH ,
212+ OP .NOT_STARTS_WITH ,
213+ OP .ENDS_WITH ,
214+ OP .NOT_ENDS_WITH ,
215+ OP .STRING_CONTAINS ,
216+ ):
217+ # a starts_with b, a not_starts_with, a ends_with b, a not_ends_with b
218+ # b should be a single value, while a can be a single value or an array
219+ if is_policy_value_array :
220+ return False
221+
222+ # positive and negative operator
223+ # == 命中一个即返回
224+ # != 需要全部遍历完, 确认全部不等于才返回?
225+ if self .op .startswith ("not_" ):
226+ return self ._eval_negative (object_attr , is_object_attr_array , policy_value )
227+ else :
228+ return self ._eval_positive (object_attr , is_object_attr_array , policy_value )
267229
268230
269231class EqualOperator (BinaryOperator ):
@@ -318,7 +280,6 @@ def calculate(self, left, right):
318280
319281class StartsWithOperator (BinaryOperator ):
320282 def __init__ (self , field , value ):
321- # TODO: value should be string?
322283 super (StartsWithOperator , self ).__init__ (OP .STARTS_WITH , field , value )
323284
324285 def calculate (self , left , right ):
@@ -327,7 +288,6 @@ def calculate(self, left, right):
327288
328289class NotStartsWithOperator (BinaryOperator ):
329290 def __init__ (self , field , value ):
330- # TODO: value should be string?
331291 super (NotStartsWithOperator , self ).__init__ (OP .NOT_STARTS_WITH , field , value )
332292
333293 def calculate (self , left , right ):
@@ -336,7 +296,6 @@ def calculate(self, left, right):
336296
337297class EndsWithOperator (BinaryOperator ):
338298 def __init__ (self , field , value ):
339- # TODO: value should be string?
340299 super (EndsWithOperator , self ).__init__ (OP .ENDS_WITH , field , value )
341300
342301 def calculate (self , left , right ):
@@ -345,13 +304,20 @@ def calculate(self, left, right):
345304
346305class NotEndsWithOperator (BinaryOperator ):
347306 def __init__ (self , field , value ):
348- # TODO: value should be string?
349307 super (NotEndsWithOperator , self ).__init__ (OP .NOT_ENDS_WITH , field , value )
350308
351309 def calculate (self , left , right ):
352310 return not left .endswith (right )
353311
354312
313+ class StringContainsOperator (BinaryOperator ):
314+ def __init__ (self , field , value ):
315+ super (StringContainsOperator , self ).__init__ (OP .STRING_CONTAINS , field , value )
316+
317+ def calculate (self , left , right ):
318+ return right in left
319+
320+
355321class LTOperator (BinaryOperator ):
356322 def __init__ (self , field , value ):
357323 # TODO: field / value should be numberic
@@ -407,6 +373,7 @@ def calculate(self, left, right):
407373 OP .NOT_STARTS_WITH : NotStartsWithOperator ,
408374 OP .ENDS_WITH : EndsWithOperator ,
409375 OP .NOT_ENDS_WITH : NotEndsWithOperator ,
376+ OP .STRING_CONTAINS : StringContainsOperator ,
410377 OP .LT : LTOperator ,
411378 OP .LTE : LTEOperator ,
412379 OP .GT : GTOperator ,
0 commit comments