okta-expression-parser 0.3.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: okta-expression-parser
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: Returns a boolean by passing Okta profile data to an Okta Expression using Okta Expression Language
5
5
  Author: Mathew Moon
6
6
  Author-email: me@mathewmoon.net
@@ -10,9 +10,9 @@ class ExpressionLexer(Lexer):
10
10
  # reflags = re.IGNORECASE
11
11
  # Set of token names.
12
12
  tokens = {
13
+ NAME,
13
14
  STRING,
14
15
  INT,
15
- NAME,
16
16
  NE,
17
17
  GTE,
18
18
  LTE,
@@ -28,6 +28,10 @@ class ExpressionLexer(Lexer):
28
28
  QUESTION_MARK,
29
29
  MEMBEROF,
30
30
  MEMBEROFANY,
31
+ MEMBEROFNAME,
32
+ MEMBEROFGROUPSTARTSWITH,
33
+ MEMBEROFGROUPCONTAINS,
34
+ MEMBEROFGROUPNAMEREGEX,
31
35
  USER,
32
36
  CLASS,
33
37
  }
@@ -48,7 +52,11 @@ class ExpressionLexer(Lexer):
48
52
  OR = r"or|OR|Or"
49
53
  EQ = r"(==|eq|EQ|Eq)"
50
54
  BOOL = r"(true|false|True|False|TRUE|FALSE)"
55
+ MEMBEROFGROUPCONTAINS = "isMemberOfGroupNameContains"
56
+ MEMBEROFGROUPNAMEREGEX = "isMemberOfGroupNameRegex"
57
+ MEMBEROFGROUPSTARTSWITH = "isMemberOfGroupNameStartsWith"
51
58
  MEMBEROFANY = "isMemberOfAnyGroup"
59
+ MEMBEROFNAME = "isMemberOfGroupName"
52
60
  MEMBEROF = "isMemberOfGroup"
53
61
  USER = r"user\b"
54
62
  COLON = r"\:"
@@ -57,7 +65,8 @@ class ExpressionLexer(Lexer):
57
65
  NULL = r"null|NULL|Null"
58
66
  CLASS = r"String|Arrays|Convert|Iso3166Convert|Groups"
59
67
  INT = r"\d+"
60
- STRING = r""""([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'|''|\"\""""
68
+ # STRING = r""""([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'|''|\"\""""
69
+ STRING = r'''"(?:\\.|[^"\\])*"'''
61
70
  NAME = r"[a-zA-Z_][a-zA-Z0-9\-_]*"
62
71
 
63
72
  # Line number tracking
@@ -1,6 +1,7 @@
1
1
  # pyright: reportUndefinedVariable=false
2
2
  import logging
3
3
  from os import environ
4
+ from re import match
4
5
  from sys import stdout
5
6
  from typing import Any, Dict, List, Sequence
6
7
  from types import ModuleType
@@ -13,6 +14,19 @@ from .expression_classes.arrays import _ArrayType
13
14
  from .lexer import ExpressionLexer
14
15
 
15
16
 
17
+ # Sly doesn't really facilitate configuring logging from the caller, so we're going to
18
+ # suppress all logging below the level specified in the LOG_LEVEL environment variable.
19
+ log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "EXCEPTION"]
20
+
21
+ log_level = environ.get("LOG_LEVEL", "ERROR")
22
+
23
+ log_rank = log_levels.index(log_level)
24
+
25
+ for level_name in list(reversed(log_levels))[:log_rank +1]:
26
+ log_attr = level_name.lower()
27
+ setattr(sly.yacc.SlyLogger, log_attr, lambda self, msg, *args, **kwargs: None)
28
+
29
+
16
30
  class ExpressionParser(sly.Parser):
17
31
  #: Writes grammar rules to a file for debugging
18
32
  debugfile = environ.get("OKTA_EXPRESSION_PARSER_LOG_FILE")
@@ -33,7 +47,7 @@ class ExpressionParser(sly.Parser):
33
47
  group_ids: List[str] = [],
34
48
  user_profile: Dict[str, Any] = {},
35
49
  log_to_stdout: bool = False,
36
- log_level: str = "INFO",
50
+ log_level: str = environ.get("LOG_LEVEL", "ERROR"),
37
51
  expression_classes: ModuleType = _default_expression_classes,
38
52
  group_data: dict = {},
39
53
  ) -> None:
@@ -49,6 +63,7 @@ class ExpressionParser(sly.Parser):
49
63
  ):
50
64
  self._expression_classes.Groups.group_data = group_data
51
65
 
66
+ #: A dict, with Group ID's as the keys and group data as the values. Used for operations that require more context than just the group ID.
52
67
  self.group_data = group_data
53
68
 
54
69
  self.__group_ids: str = group_ids
@@ -325,15 +340,47 @@ class ExpressionParser(sly.Parser):
325
340
 
326
341
  return p.operand in self.__group_ids
327
342
 
328
- #@_("turnary") # noqa: 821
329
- #def operand(self, p: YaccProduction) -> Any:
330
- # condition = p.turnary[0]
331
- # if_true = p.turnary[1]
332
- # if_false = p.turnary[2]
333
- # if condition:
334
- # return if_true
335
- # else:
336
- # return if_false
343
+ @_('MEMBEROFNAME "(" operand ")"')
344
+ def condition(self, p: YaccProduction):
345
+ """Tests if a user is a member of a group with a specific name"""
346
+
347
+ matches = [
348
+ v for k, v in self.group_data.items() if v["profile"]["name"] == p.operand
349
+ ]
350
+
351
+ return bool(matches)
352
+
353
+ @_('MEMBEROFGROUPSTARTSWITH "(" operand ")"')
354
+ def condition(self, p: YaccProduction):
355
+ """Tests if a user is a member of a group with a specific name"""
356
+
357
+ matches = [
358
+ v
359
+ for k, v in self.group_data.items()
360
+ if v["profile"]["name"].startswith(p.operand)
361
+ ]
362
+
363
+ return bool(matches)
364
+
365
+ @_('MEMBEROFGROUPCONTAINS "(" operand ")"')
366
+ def condition(self, p: YaccProduction):
367
+ """Tests if a user is a member of a group with a specific name"""
368
+
369
+ matches = [
370
+ v for k, v in self.group_data.items() if p.operand in v["profile"]["name"]
371
+ ]
372
+
373
+ return bool(matches)
374
+
375
+ @_('MEMBEROFGROUPNAMEREGEX "(" operand ")"')
376
+ def condition(self, p: YaccProduction):
377
+ """Tests if a user is a member of a group with a specific name"""
378
+
379
+ for _, group in self.group_data.items():
380
+ if match(p.operand, group["profile"]["name"]):
381
+ return True
382
+
383
+ return False
337
384
 
338
385
  @_("operand QUESTION_MARK operand COLON operand") # noqa: 821
339
386
  @_("condition QUESTION_MARK operand COLON operand") # noqa: 821
@@ -343,7 +390,6 @@ class ExpressionParser(sly.Parser):
343
390
  if_false = p.operand1 if hasattr(p, "condition") else p.operand2
344
391
 
345
392
  return if_true if condition else if_false
346
- #return (condition, p.operand0, p.operand1)
347
393
 
348
394
  @_("condition error") # noqa: 821
349
395
  def operand(self, x): # noqa: 811
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "okta-expression-parser"
3
- version = "0.3.0"
3
+ version = "0.4.1"
4
4
  description = "Returns a boolean by passing Okta profile data to an Okta Expression using Okta Expression Language"
5
5
  authors = ["Mathew Moon <me@mathewmoon.net>"]
6
6
  readme = "README.md"