44using SimpleIdServer . Scim . Parser . Operators ;
55using System ;
66using System . Collections . Generic ;
7+ using System . ComponentModel ;
78using System . Linq ;
89using System . Linq . Expressions ;
910
@@ -81,7 +82,7 @@ public static Expression EvaluateAttributes(this SCIMLogicalExpression expressio
8182 }
8283 }
8384
84- public static Expression EvaluateAttributes ( this SCIMComparisonExpression expression , ParameterExpression parameterExpression , string propertyName = "CachedChildren" )
85+ public static Expression EvaluateAttributes ( this SCIMComparisonExpression expression , ParameterExpression parameterExpression , string propertyName = "CachedChildren" , bool useRegex = false )
8586 {
8687 var schemaAttributeId = Expression . Property ( parameterExpression , "SchemaAttributeId" ) ;
8788 var propertyValueString = Expression . Property ( parameterExpression , "ValueString" ) ;
@@ -97,7 +98,8 @@ public static Expression EvaluateAttributes(this SCIMComparisonExpression expres
9798 propertyValueBoolean ,
9899 propertyValueDecimal ,
99100 propertyValueBinary ,
100- parameterExpression ) ;
101+ parameterExpression ,
102+ useRegex ) ;
101103 return Expression . And ( Expression . Equal ( schemaAttributeId , Expression . Constant ( expression . LeftExpression . SchemaAttribute . Id ) ) , comparison ) ;
102104 }
103105
@@ -252,7 +254,8 @@ public static Expression BuildComparisonExpression(
252254 MemberExpression propertyValueBoolean = null ,
253255 MemberExpression propertyValueDecimal = null ,
254256 MemberExpression propertyValueBinary = null ,
255- ParameterExpression representationParameter = null )
257+ ParameterExpression representationParameter = null ,
258+ bool useRegex = false )
256259 {
257260 representationParameter = representationParameter ?? Expression . Parameter ( typeof ( SCIMRepresentationAttribute ) , "ra" ) ;
258261 propertyValueString = propertyValueString ?? Expression . Property ( representationParameter , "ValueString" ) ;
@@ -268,22 +271,22 @@ public static Expression BuildComparisonExpression(
268271 switch ( schemaAttr . Type )
269272 {
270273 case SCIMSchemaAttributeTypes . STRING :
271- comparison = NotEqual ( propertyValueString , Expression . Constant ( comparisonExpression . Value ) , schemaAttr . CaseExact ) ;
274+ comparison = NotEqual ( propertyValueString , Expression . Constant ( comparisonExpression . Value ) , schemaAttr . CaseExact , useRegex ) ;
272275 break ;
273276 case SCIMSchemaAttributeTypes . INTEGER :
274- comparison = NotEqual ( propertyValueInteger , Expression . Constant ( ParseInt ( comparisonExpression . Value ) ) ) ;
277+ comparison = NotEqual ( propertyValueInteger , Expression . Constant ( ParseInt ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
275278 break ;
276279 case SCIMSchemaAttributeTypes . DATETIME :
277- comparison = NotEqual ( propertyValueDatetime , Expression . Constant ( ParseDateTime ( comparisonExpression . Value ) ) ) ;
280+ comparison = NotEqual ( propertyValueDatetime , Expression . Constant ( ParseDateTime ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
278281 break ;
279282 case SCIMSchemaAttributeTypes . BOOLEAN :
280- comparison = NotEqual ( propertyValueBoolean , Expression . Constant ( ParseBoolean ( comparisonExpression . Value ) ) ) ;
283+ comparison = NotEqual ( propertyValueBoolean , Expression . Constant ( ParseBoolean ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
281284 break ;
282285 case SCIMSchemaAttributeTypes . DECIMAL :
283- comparison = NotEqual ( propertyValueDecimal , Expression . Constant ( ParseDecimal ( comparisonExpression . Value ) ) ) ;
286+ comparison = NotEqual ( propertyValueDecimal , Expression . Constant ( ParseDecimal ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
284287 break ;
285288 case SCIMSchemaAttributeTypes . BINARY :
286- comparison = NotEqual ( propertyValueBinary , Expression . Constant ( comparisonExpression . Value ) ) ;
289+ comparison = NotEqual ( propertyValueBinary , Expression . Constant ( comparisonExpression . Value ) , useRegex : useRegex ) ;
287290 break ;
288291 }
289292 break ;
@@ -347,22 +350,22 @@ public static Expression BuildComparisonExpression(
347350 switch ( schemaAttr . Type )
348351 {
349352 case SCIMSchemaAttributeTypes . STRING :
350- comparison = Equal ( propertyValueString , Expression . Constant ( comparisonExpression . Value ) , schemaAttr . CaseExact ) ;
353+ comparison = Equal ( propertyValueString , Expression . Constant ( comparisonExpression . Value ) , schemaAttr . CaseExact , useRegex ) ;
351354 break ;
352355 case SCIMSchemaAttributeTypes . INTEGER :
353- comparison = Equal ( propertyValueInteger , Expression . Constant ( ParseInt ( comparisonExpression . Value ) ) ) ;
356+ comparison = Equal ( propertyValueInteger , Expression . Constant ( ParseInt ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
354357 break ;
355358 case SCIMSchemaAttributeTypes . DATETIME :
356- comparison = Equal ( propertyValueDatetime , Expression . Constant ( ParseDateTime ( comparisonExpression . Value ) ) ) ;
359+ comparison = Equal ( propertyValueDatetime , Expression . Constant ( ParseDateTime ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
357360 break ;
358361 case SCIMSchemaAttributeTypes . BOOLEAN :
359- comparison = Equal ( propertyValueBoolean , Expression . Constant ( ParseBoolean ( comparisonExpression . Value ) ) ) ;
362+ comparison = Equal ( propertyValueBoolean , Expression . Constant ( ParseBoolean ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
360363 break ;
361364 case SCIMSchemaAttributeTypes . DECIMAL :
362- comparison = Equal ( propertyValueDecimal , Expression . Constant ( ParseDecimal ( comparisonExpression . Value ) ) ) ;
365+ comparison = Equal ( propertyValueDecimal , Expression . Constant ( ParseDecimal ( comparisonExpression . Value ) ) , useRegex : useRegex ) ;
363366 break ;
364367 case SCIMSchemaAttributeTypes . BINARY :
365- comparison = Equal ( propertyValueBinary , Expression . Constant ( comparisonExpression . Value ) ) ;
368+ comparison = Equal ( propertyValueBinary , Expression . Constant ( comparisonExpression . Value ) , useRegex : useRegex ) ;
366369 break ;
367370 }
368371 break ;
@@ -373,10 +376,29 @@ public static Expression BuildComparisonExpression(
373376 var startWith = typeof ( string ) . GetMethod ( "StartsWith" , new Type [ ] { typeof ( string ) } ) ;
374377 if ( ! schemaAttr . CaseExact )
375378 {
376- comparison = Expression . Call (
377- Expression . Call ( Expression . Coalesce ( propertyValueString , Expression . Constant ( string . Empty ) ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ,
378- startWith ,
379- Expression . Call ( Expression . Constant ( comparisonExpression . Value ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ) ;
379+ if ( useRegex )
380+ {
381+ var regexMethod = typeof ( System . Text . RegularExpressions . Regex ) . GetMethod (
382+ "IsMatch" ,
383+ new Type [ ] { typeof ( string ) , typeof ( string ) , typeof ( System . Text . RegularExpressions . RegexOptions ) }
384+ ) ;
385+ var escapedValue = System . Text . RegularExpressions . Regex . Escape ( comparisonExpression . Value ) ;
386+ var pattern = $ "^{ escapedValue } ";
387+ comparison = Expression . Call (
388+ null ,
389+ regexMethod ,
390+ propertyValueString ,
391+ Expression . Constant ( pattern ) ,
392+ Expression . Constant ( System . Text . RegularExpressions . RegexOptions . IgnoreCase )
393+ ) ;
394+ }
395+ else
396+ {
397+ comparison = Expression . Call (
398+ Expression . Call ( Expression . Coalesce ( propertyValueString , Expression . Constant ( string . Empty ) ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ,
399+ startWith ,
400+ Expression . Call ( Expression . Constant ( comparisonExpression . Value ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ) ;
401+ }
380402 }
381403 else
382404 {
@@ -390,12 +412,32 @@ public static Expression BuildComparisonExpression(
390412 {
391413 case SCIMSchemaAttributeTypes . STRING :
392414 var endWith = typeof ( string ) . GetMethod ( "EndsWith" , new Type [ ] { typeof ( string ) } ) ;
393- if ( ! schemaAttr . CaseExact )
415+ if ( ! schemaAttr . CaseExact )
394416 {
395- comparison = Expression . Call (
396- Expression . Call ( Expression . Coalesce ( propertyValueString , Expression . Constant ( string . Empty ) ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ,
397- endWith ,
398- Expression . Call ( Expression . Constant ( comparisonExpression . Value ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ) ;
417+ if ( useRegex )
418+ {
419+ var regexMethod = typeof ( System . Text . RegularExpressions . Regex ) . GetMethod (
420+ "IsMatch" ,
421+ new Type [ ] { typeof ( string ) , typeof ( string ) , typeof ( System . Text . RegularExpressions . RegexOptions ) }
422+ ) ;
423+ var escapedValue = System . Text . RegularExpressions . Regex . Escape ( comparisonExpression . Value ) ;
424+ var pattern = $ "{ escapedValue } $";
425+
426+ comparison = Expression . Call (
427+ null ,
428+ regexMethod ,
429+ propertyValueString ,
430+ Expression . Constant ( pattern ) ,
431+ Expression . Constant ( System . Text . RegularExpressions . RegexOptions . IgnoreCase )
432+ ) ;
433+ }
434+ else
435+ {
436+ comparison = Expression . Call (
437+ Expression . Call ( Expression . Coalesce ( propertyValueString , Expression . Constant ( string . Empty ) ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ,
438+ endWith ,
439+ Expression . Call ( Expression . Constant ( comparisonExpression . Value ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ) ;
440+ }
399441 }
400442 else
401443 {
@@ -405,21 +447,37 @@ public static Expression BuildComparisonExpression(
405447 }
406448 break ;
407449 case SCIMComparisonOperators . CO :
408- if ( schemaAttr . Type == SCIMSchemaAttributeTypes . STRING )
450+ var contains = typeof ( string ) . GetMethod ( "Contains" , new [ ] { typeof ( string ) } ) ;
451+ if ( ! schemaAttr . CaseExact )
409452 {
410- var contains = typeof ( string ) . GetMethod ( "Contains" , new [ ] { typeof ( string ) } ) ;
411- if ( ! schemaAttr . CaseExact )
453+ if ( useRegex )
412454 {
455+ var regexMethod = typeof ( System . Text . RegularExpressions . Regex ) . GetMethod (
456+ "IsMatch" ,
457+ new Type [ ] { typeof ( string ) , typeof ( string ) , typeof ( System . Text . RegularExpressions . RegexOptions ) }
458+ ) ;
459+ var escapedValue = System . Text . RegularExpressions . Regex . Escape ( comparisonExpression . Value ) ;
460+
413461 comparison = Expression . Call (
414- Expression . Call ( Expression . Coalesce ( propertyValueString , Expression . Constant ( string . Empty ) ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ,
415- contains ,
416- Expression . Call ( Expression . Constant ( comparisonExpression . Value ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ) ;
462+ null ,
463+ regexMethod ,
464+ propertyValueString ,
465+ Expression . Constant ( escapedValue ) ,
466+ Expression . Constant ( System . Text . RegularExpressions . RegexOptions . IgnoreCase )
467+ ) ;
417468 }
418469 else
419470 {
420- comparison = Expression . Call ( propertyValueString , contains , Expression . Constant ( comparisonExpression . Value ) ) ;
471+ comparison = Expression . Call (
472+ Expression . Call ( Expression . Coalesce ( propertyValueString , Expression . Constant ( string . Empty ) ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ,
473+ contains ,
474+ Expression . Call ( Expression . Constant ( comparisonExpression . Value ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ) ;
421475 }
422476 }
477+ else
478+ {
479+ comparison = Expression . Call ( propertyValueString , contains , Expression . Constant ( comparisonExpression . Value ) ) ;
480+ }
423481 break ;
424482 }
425483
@@ -454,7 +512,7 @@ private static Expression LessThanOrEqual(Expression e1, Expression e2)
454512 return Expression . LessThanOrEqual ( e1 , e2 ) ;
455513 }
456514
457- private static Expression Equal ( Expression e1 , Expression e2 , bool caseSensitive = true )
515+ private static Expression Equal ( Expression e1 , Expression e2 , bool caseSensitive = true , bool useRegex = false )
458516 {
459517 if ( IsNullableType ( e1 . Type ) && ! IsNullableType ( e2 . Type ) )
460518 {
@@ -465,35 +523,63 @@ private static Expression Equal(Expression e1, Expression e2, bool caseSensitive
465523 e1 = Expression . Convert ( e1 , e2 . Type ) ;
466524 }
467525
468- if ( ! caseSensitive )
526+ var checkCaseNotsensitive = ! caseSensitive && e1 . Type == typeof ( string ) ;
527+ if ( ! checkCaseNotsensitive )
528+ {
529+ return Expression . Equal ( e1 , e2 ) ;
530+ }
531+
532+ if ( ! useRegex )
469533 {
470534 e1 = Expression . Coalesce ( e1 , Expression . Constant ( string . Empty ) ) ;
471535 e1 = Expression . Call ( e1 , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ;
472536 e2 = Expression . Call ( e2 , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ;
537+ return Expression . Equal ( e1 , e2 ) ;
473538 }
474539
475- return Expression . Equal ( e1 , e2 ) ;
476- }
477-
478- private static Expression NotEqual ( Expression e1 , Expression e2 , bool caseSensitive = true )
479- {
480- if ( IsNullableType ( e1 . Type ) && ! IsNullableType ( e2 . Type ) )
481- {
482- e2 = Expression . Convert ( e2 , e1 . Type ) ;
483- }
484- else if ( ! IsNullableType ( e1 . Type ) && IsNullableType ( e2 . Type ) )
540+ var regexMethod = typeof ( System . Text . RegularExpressions . Regex ) . GetMethod (
541+ "IsMatch" ,
542+ new Type [ ] { typeof ( string ) , typeof ( string ) , typeof ( System . Text . RegularExpressions . RegexOptions ) }
543+ ) ;
544+ var escapeMethod = typeof ( System . Text . RegularExpressions . Regex ) . GetMethod ( "Escape" , new Type [ ] { typeof ( string ) } ) ;
545+ if ( e2 is ConstantExpression constantExpr && constantExpr . Value is string strValue )
485546 {
486- e1 = Expression . Convert ( e1 , e2 . Type ) ;
547+ var escapedValue = System . Text . RegularExpressions . Regex . Escape ( strValue ) ;
548+ var pattern = $ "^{ escapedValue } $";
549+ return Expression . Call (
550+ null ,
551+ regexMethod ,
552+ e1 ,
553+ Expression . Constant ( pattern ) ,
554+ Expression . Constant ( System . Text . RegularExpressions . RegexOptions . IgnoreCase )
555+ ) ;
487556 }
488-
489- if ( ! caseSensitive )
557+ else
490558 {
491- e1 = Expression . Coalesce ( e1 , Expression . Constant ( string . Empty ) ) ;
492- e1 = Expression . Call ( e1 , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ;
493- e2 = Expression . Call ( e2 , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ;
559+ var escapedE2 = Expression . Call ( null , escapeMethod , e2 ) ;
560+ var concatMethod = typeof ( string ) . GetMethod ( "Concat" , new Type [ ] { typeof ( string ) , typeof ( string ) , typeof ( string ) } ) ;
561+ var pattern = Expression . Call (
562+ null ,
563+ concatMethod ,
564+ Expression . Constant ( "^" ) ,
565+ escapedE2 ,
566+ Expression . Constant ( "$" )
567+ ) ;
568+
569+ return Expression . Call (
570+ null ,
571+ regexMethod ,
572+ e1 ,
573+ pattern ,
574+ Expression . Constant ( System . Text . RegularExpressions . RegexOptions . IgnoreCase )
575+ ) ;
494576 }
577+ }
495578
496- return Expression . NotEqual ( e1 , e2 ) ;
579+ private static Expression NotEqual ( Expression e1 , Expression e2 , bool caseSensitive = true , bool useRegex = false )
580+ {
581+ var equalExpr = Equal ( e1 , e2 , caseSensitive , useRegex ) ;
582+ return Expression . Not ( equalExpr ) ;
497583 }
498584
499585 private static Expression GreaterThanOrEqual ( Expression e1 , Expression e2 )
0 commit comments