synapse 2.152.0__py311-none-any.whl → 2.154.0__py311-none-any.whl

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.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (87) hide show
  1. synapse/axon.py +19 -16
  2. synapse/cortex.py +203 -15
  3. synapse/exc.py +0 -2
  4. synapse/lib/ast.py +42 -23
  5. synapse/lib/autodoc.py +2 -2
  6. synapse/lib/cache.py +16 -1
  7. synapse/lib/cell.py +5 -5
  8. synapse/lib/httpapi.py +198 -2
  9. synapse/lib/layer.py +5 -2
  10. synapse/lib/modelrev.py +36 -3
  11. synapse/lib/node.py +2 -5
  12. synapse/lib/parser.py +1 -1
  13. synapse/lib/schemas.py +51 -0
  14. synapse/lib/snap.py +10 -0
  15. synapse/lib/storm.lark +24 -4
  16. synapse/lib/storm.py +98 -19
  17. synapse/lib/storm_format.py +1 -1
  18. synapse/lib/stormhttp.py +11 -4
  19. synapse/lib/stormlib/auth.py +16 -2
  20. synapse/lib/stormlib/backup.py +1 -0
  21. synapse/lib/stormlib/basex.py +2 -0
  22. synapse/lib/stormlib/cell.py +7 -0
  23. synapse/lib/stormlib/compression.py +3 -0
  24. synapse/lib/stormlib/cortex.py +1168 -0
  25. synapse/lib/stormlib/ethereum.py +1 -0
  26. synapse/lib/stormlib/graph.py +2 -0
  27. synapse/lib/stormlib/hashes.py +5 -0
  28. synapse/lib/stormlib/hex.py +6 -0
  29. synapse/lib/stormlib/infosec.py +6 -1
  30. synapse/lib/stormlib/ipv6.py +1 -0
  31. synapse/lib/stormlib/iters.py +58 -1
  32. synapse/lib/stormlib/json.py +5 -0
  33. synapse/lib/stormlib/mime.py +1 -0
  34. synapse/lib/stormlib/model.py +19 -3
  35. synapse/lib/stormlib/modelext.py +1 -0
  36. synapse/lib/stormlib/notifications.py +2 -0
  37. synapse/lib/stormlib/pack.py +2 -0
  38. synapse/lib/stormlib/random.py +1 -0
  39. synapse/lib/stormlib/smtp.py +0 -7
  40. synapse/lib/stormlib/stats.py +223 -0
  41. synapse/lib/stormlib/stix.py +8 -0
  42. synapse/lib/stormlib/storm.py +1 -0
  43. synapse/lib/stormlib/version.py +3 -0
  44. synapse/lib/stormlib/xml.py +3 -0
  45. synapse/lib/stormlib/yaml.py +2 -0
  46. synapse/lib/stormtypes.py +250 -170
  47. synapse/lib/trigger.py +180 -4
  48. synapse/lib/types.py +1 -1
  49. synapse/lib/version.py +2 -2
  50. synapse/lib/view.py +55 -6
  51. synapse/models/inet.py +21 -6
  52. synapse/models/orgs.py +48 -2
  53. synapse/models/risk.py +126 -2
  54. synapse/models/syn.py +6 -0
  55. synapse/tests/files/stormpkg/badapidef.yaml +13 -0
  56. synapse/tests/files/stormpkg/storm/modules/apimod +10 -0
  57. synapse/tests/files/stormpkg/testpkg.yaml +23 -0
  58. synapse/tests/test_axon.py +7 -2
  59. synapse/tests/test_cortex.py +231 -35
  60. synapse/tests/test_lib_ast.py +138 -43
  61. synapse/tests/test_lib_autodoc.py +1 -1
  62. synapse/tests/test_lib_modelrev.py +9 -0
  63. synapse/tests/test_lib_node.py +55 -0
  64. synapse/tests/test_lib_storm.py +14 -1
  65. synapse/tests/test_lib_stormhttp.py +65 -6
  66. synapse/tests/test_lib_stormlib_auth.py +12 -3
  67. synapse/tests/test_lib_stormlib_cortex.py +1327 -0
  68. synapse/tests/test_lib_stormlib_iters.py +116 -0
  69. synapse/tests/test_lib_stormlib_stats.py +187 -0
  70. synapse/tests/test_lib_stormlib_storm.py +8 -0
  71. synapse/tests/test_lib_stormsvc.py +24 -1
  72. synapse/tests/test_lib_stormtypes.py +124 -69
  73. synapse/tests/test_lib_trigger.py +315 -0
  74. synapse/tests/test_lib_view.py +1 -2
  75. synapse/tests/test_model_base.py +26 -0
  76. synapse/tests/test_model_inet.py +22 -0
  77. synapse/tests/test_model_orgs.py +28 -0
  78. synapse/tests/test_model_risk.py +73 -0
  79. synapse/tests/test_tools_autodoc.py +25 -0
  80. synapse/tests/test_tools_genpkg.py +9 -3
  81. synapse/tests/utils.py +39 -0
  82. synapse/tools/autodoc.py +42 -2
  83. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/METADATA +2 -2
  84. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/RECORD +87 -79
  85. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/WHEEL +1 -1
  86. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/LICENSE +0 -0
  87. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/top_level.txt +0 -0
synapse/lib/trigger.py CHANGED
@@ -21,6 +21,8 @@ Conditions = set((
21
21
  'node:add',
22
22
  'node:del',
23
23
  'prop:set',
24
+ 'edge:add',
25
+ 'edge:del'
24
26
  ))
25
27
 
26
28
  RecursionDepth = contextvars.ContextVar('RecursionDepth', default=0)
@@ -37,11 +39,13 @@ TrigSchema = {
37
39
  'user': {'type': 'string', 'pattern': s_config.re_iden},
38
40
  'view': {'type': 'string', 'pattern': s_config.re_iden},
39
41
  'form': {'type': 'string', 'pattern': _formre},
42
+ 'n2form': {'type': 'string', 'pattern': _formre},
40
43
  'tag': {'type': 'string', 'pattern': _tagre},
41
44
  'prop': {'type': 'string', 'pattern': _propre},
45
+ 'verb': {'type': 'string', },
42
46
  'name': {'type': 'string', },
43
47
  'doc': {'type': 'string', },
44
- 'cond': {'enum': ['node:add', 'node:del', 'tag:add', 'tag:del', 'prop:set']},
48
+ 'cond': {'enum': ['node:add', 'node:del', 'tag:add', 'tag:del', 'prop:set', 'edge:add', 'edge:del']},
45
49
  'storm': {'type': 'string'},
46
50
  'async': {'type': 'boolean'},
47
51
  'enabled': {'type': 'boolean'},
@@ -69,6 +73,14 @@ TrigSchema = {
69
73
  'if': {'properties': {'cond': {'const': 'prop:set'}}},
70
74
  'then': {'required': ['prop']},
71
75
  },
76
+ {
77
+ 'if': {'properties': {'cond': {'const': 'edge:add'}}},
78
+ 'then': {'required': ['verb']},
79
+ },
80
+ {
81
+ 'if': {'properties': {'cond': {'const': 'edge:del'}}},
82
+ 'then': {'required': ['verb']},
83
+ },
72
84
  ],
73
85
  }
74
86
  TrigSchemaValidator = s_config.getJsValidator(TrigSchema)
@@ -89,9 +101,9 @@ class Triggers:
89
101
  self.view = view
90
102
  self.triggers = {}
91
103
 
92
- self.tagadd = collections.defaultdict(list) # (form, tag): [ Triger ... ]
93
- self.tagset = collections.defaultdict(list) # (form, tag): [ Triger ... ]
94
- self.tagdel = collections.defaultdict(list) # (form, tag): [ Triger ... ]
104
+ self.tagadd = collections.defaultdict(list) # (form, tag): [ Trigger ... ]
105
+ self.tagset = collections.defaultdict(list) # (form, tag): [ Trigger ... ]
106
+ self.tagdel = collections.defaultdict(list) # (form, tag): [ Trigger ... ]
95
107
 
96
108
  self.tagaddglobs = collections.defaultdict(s_cache.TagGlobs) # form: TagGlobs
97
109
  self.tagsetglobs = collections.defaultdict(s_cache.TagGlobs) # form: TagGlobs
@@ -101,6 +113,15 @@ class Triggers:
101
113
  self.nodedel = collections.defaultdict(list) # form: [ Trigger ... ]
102
114
  self.propset = collections.defaultdict(list) # prop: [ Trigger ... ]
103
115
 
116
+ self.edgeadd = collections.defaultdict(list) # (n1form, verb, n2form: [ Trigger ... ]
117
+ self.edgedel = collections.defaultdict(list) # (n1form, verb, n2form: [ Trigger ... ]
118
+
119
+ self.edgeaddglobs = collections.defaultdict(s_cache.EdgeGlobs) # (n1form, n2form: [ EdgeGlobs ... ]
120
+ self.edgedelglobs = collections.defaultdict(s_cache.EdgeGlobs) # (n1form, n2form: [ EdgeGlobs ... ]
121
+
122
+ self.edgeaddcache = s_cache.LruDict()
123
+ self.edgedelcache = s_cache.LruDict()
124
+
104
125
  @contextlib.contextmanager
105
126
  def _recursion_check(self):
106
127
 
@@ -182,6 +203,106 @@ class Triggers:
182
203
  for _, trig in globs.get(tag):
183
204
  await trig.execute(node, vars=vars, view=view)
184
205
 
206
+ async def runEdgeAdd(self, n1, verb, n2, view=None):
207
+ n1form = n1.form.name if n1 else None
208
+ n2form = n2.form.name if n2 else None
209
+ n2iden = n2.iden() if n2 else None
210
+ varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden}}}
211
+ with self._recursion_check():
212
+ cachekey = (n1form, verb, n2form)
213
+ cached = self.edgeaddcache.get(cachekey)
214
+ if cached is None:
215
+ cached = []
216
+ for trig in self.edgeadd.get((None, verb, None), ()):
217
+ cached.append(trig)
218
+
219
+ globs = self.edgeaddglobs.get((None, None))
220
+ if globs:
221
+ for _, trig in globs.get(verb):
222
+ cached.append(trig)
223
+
224
+ if n1:
225
+ for trig in self.edgeadd.get((n1form, verb, None), ()):
226
+ cached.append(trig)
227
+
228
+ globs = self.edgeaddglobs.get((n1form, None))
229
+ if globs:
230
+ for _, trig in globs.get(verb):
231
+ cached.append(trig)
232
+
233
+ if n2:
234
+ for trig in self.edgeadd.get((None, verb, n2form), ()):
235
+ cached.append(trig)
236
+
237
+ globs = self.edgeaddglobs.get((None, n2form))
238
+ if globs:
239
+ for _, trig in globs.get(verb):
240
+ cached.append(trig)
241
+
242
+ if n1 and n2:
243
+ for trig in self.edgeadd.get((n1form, verb, n2form), ()):
244
+ cached.append(trig)
245
+
246
+ globs = self.edgeaddglobs.get((n1form, n2form))
247
+ if globs:
248
+ for _, trig in globs.get(verb):
249
+ cached.append(trig)
250
+
251
+ self.edgeaddcache[cachekey] = cached
252
+
253
+ for trig in cached:
254
+ await trig.execute(n1, vars=varz, view=view)
255
+
256
+ async def runEdgeDel(self, n1, verb, n2, view=None):
257
+ n1form = n1.form.name if n1 else None
258
+ n2form = n2.form.name if n2 else None
259
+ n2iden = n2.iden() if n2 else None
260
+ varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden}}}
261
+ with self._recursion_check():
262
+ cachekey = (n1form, verb, n2form)
263
+ cached = self.edgedelcache.get(cachekey)
264
+ if cached is None:
265
+ cached = []
266
+ for trig in self.edgedel.get((None, verb, None), ()):
267
+ cached.append(trig)
268
+
269
+ globs = self.edgedelglobs.get((None, None))
270
+ if globs:
271
+ for _, trig in globs.get(verb):
272
+ cached.append(trig)
273
+
274
+ if n1:
275
+ for trig in self.edgedel.get((n1form, verb, None), ()):
276
+ cached.append(trig)
277
+
278
+ globs = self.edgedelglobs.get((n1form, None))
279
+ if globs:
280
+ for _, trig in globs.get(verb):
281
+ cached.append(trig)
282
+
283
+ if n2:
284
+ for trig in self.edgedel.get((None, verb, n2form), ()):
285
+ cached.append(trig)
286
+
287
+ globs = self.edgedelglobs.get((None, n2form))
288
+ if globs:
289
+ for _, trig in globs.get(verb):
290
+ cached.append(trig)
291
+
292
+ if n1 and n2:
293
+ for trig in self.edgedel.get((n1form, verb, n2form), ()):
294
+ cached.append(trig)
295
+
296
+ globs = self.edgedelglobs.get((n1form, n2form))
297
+ if globs:
298
+ for _, trig in globs.get(verb):
299
+ cached.append(trig)
300
+
301
+ self.edgedelcache[cachekey] = cached
302
+
303
+ for trig in cached:
304
+ await trig.execute(n1, vars=varz, view=view)
305
+
185
306
  async def load(self, tdef):
186
307
 
187
308
  trig = Trigger(self.view, tdef)
@@ -194,6 +315,8 @@ class Triggers:
194
315
  tag = trig.tdef.get('tag')
195
316
  form = trig.tdef.get('form')
196
317
  prop = trig.tdef.get('prop')
318
+ verb = trig.tdef.get('verb')
319
+ n2form = trig.tdef.get('n2form')
197
320
 
198
321
  if cond not in Conditions:
199
322
  raise s_exc.NoSuchCond(name=cond)
@@ -206,6 +329,9 @@ class Triggers:
206
329
  if tag is None:
207
330
  raise s_exc.BadOptValu(mesg='missing tag')
208
331
  s_chop.validateTagMatch(tag)
332
+ if cond in ('edge:add', 'edge:del') and verb is None:
333
+ raise s_exc.BadOptValu(mesg='verb must be present for edge:add or edge:del')
334
+
209
335
  if prop is not None and cond != 'prop:set':
210
336
  raise s_exc.BadOptValu(mesg='prop parameter invalid')
211
337
 
@@ -237,6 +363,20 @@ class Triggers:
237
363
  else:
238
364
  self.tagdelglobs[form].add(tag, trig)
239
365
 
366
+ elif cond == 'edge:add':
367
+ self.edgeaddcache.clear()
368
+ if '*' not in verb:
369
+ self.edgeadd[(form, verb, n2form)].append(trig)
370
+ else:
371
+ self.edgeaddglobs[(form, n2form)].add(verb, trig)
372
+
373
+ elif cond == 'edge:del':
374
+ self.edgedelcache.clear()
375
+ if '*' not in verb:
376
+ self.edgedel[(form, verb, n2form)].append(trig)
377
+ else:
378
+ self.edgedelglobs[(form, n2form)].add(verb, trig)
379
+
240
380
  self.triggers[trig.iden] = trig
241
381
  return trig
242
382
 
@@ -292,6 +432,34 @@ class Triggers:
292
432
  globs.rem(tag, trig)
293
433
  return trig
294
434
 
435
+ if cond == 'edge:add':
436
+ verb = trig.tdef['verb']
437
+ form = trig.tdef.get('form')
438
+ n2form = trig.tdef.get('n2form')
439
+
440
+ self.edgeaddcache.clear()
441
+ if '*' not in verb:
442
+ self.edgeadd[(form, verb, n2form)].remove(trig)
443
+ return trig
444
+
445
+ globs = self.edgeaddglobs.get((form, n2form))
446
+ globs.rem(verb, trig)
447
+ return trig
448
+
449
+ if cond == 'edge:del':
450
+ verb = trig.tdef['verb']
451
+ form = trig.tdef.get('form')
452
+ n2form = trig.tdef.get('n2form')
453
+
454
+ self.edgedelcache.clear()
455
+ if '*' not in verb:
456
+ self.edgedel[(form, verb, n2form)].remove(trig)
457
+ return trig
458
+
459
+ globs = self.edgedelglobs.get((form, n2form))
460
+ globs.rem(verb, trig)
461
+ return trig
462
+
295
463
  raise AssertionError('trigger has invalid condition')
296
464
 
297
465
  def get(self, iden):
@@ -440,6 +608,14 @@ class Trigger:
440
608
  if prop is not None:
441
609
  props['prop'] = prop
442
610
 
611
+ verb = self.tdef.get('verb')
612
+ if verb is not None:
613
+ props['verb'] = verb
614
+
615
+ n2form = self.tdef.get('n2form')
616
+ if n2form is not None:
617
+ props['n2form'] = n2form
618
+
443
619
  pnorms = {}
444
620
  for prop, valu in props.items():
445
621
  formprop = form.props.get(prop)
synapse/lib/types.py CHANGED
@@ -239,7 +239,7 @@ class Type:
239
239
  return cmpr
240
240
 
241
241
  def _ctorCmprRe(self, text):
242
- regx = regex.compile(text)
242
+ regx = regex.compile(text, flags=regex.I)
243
243
 
244
244
  def cmpr(valu):
245
245
  vtxt = self.repr(valu)
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 152, 0)
226
+ version = (2, 154, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = 'f93d8dfc6fd1c4a1ed735ec746100100a9190e2e'
228
+ commit = '6c9600992a0ca2563239bc86b6631bd599707e3f'
synapse/lib/view.py CHANGED
@@ -746,6 +746,7 @@ class View(s_nexus.Pusher): # type: ignore
746
746
 
747
747
  await self.info.set('layers', [lyr.iden for lyr in self.layers])
748
748
  await self.core.feedBeholder('view:addlayer', {'iden': self.iden, 'layer': layriden, 'indx': indx}, gates=[self.iden, layriden])
749
+ self.core._calcViewsByLayer()
749
750
 
750
751
  @s_nexus.Pusher.onPushAuto('view:setlayers')
751
752
  async def setLayers(self, layers):
@@ -753,14 +754,11 @@ class View(s_nexus.Pusher): # type: ignore
753
754
  Set the view layers from a list of idens.
754
755
  NOTE: view layers are stored "top down" (the write layer is self.layers[0])
755
756
  '''
756
- for view in self.core.views.values():
757
- if view.parent is self:
758
- raise s_exc.ReadOnlyLayer(mesg='May not change layers that have been forked from')
757
+ layrs = []
759
758
 
760
759
  if self.parent is not None:
761
- raise s_exc.ReadOnlyLayer(mesg='May not change layers of forked view')
762
-
763
- layrs = []
760
+ mesg = 'You cannot set the layers of a forked view.'
761
+ raise s_exc.BadArg(mesg=mesg)
764
762
 
765
763
  for iden in layers:
766
764
  layr = self.core.layers.get(iden)
@@ -777,6 +775,39 @@ class View(s_nexus.Pusher): # type: ignore
777
775
  await self.info.set('layers', layers)
778
776
  await self.core.feedBeholder('view:setlayers', {'iden': self.iden, 'layers': layers}, gates=[self.iden, layers[0]])
779
777
 
778
+ await self._calcChildViews()
779
+ self.core._calcViewsByLayer()
780
+
781
+ async def _calcChildViews(self):
782
+
783
+ todo = collections.deque([self])
784
+
785
+ byparent = collections.defaultdict(list)
786
+ for view in self.core.views.values():
787
+ if view.parent is None:
788
+ continue
789
+
790
+ byparent[view.parent].append(view)
791
+
792
+ while todo:
793
+
794
+ view = todo.pop()
795
+
796
+ for child in byparent.get(view, ()):
797
+
798
+ layers = [child.layers[0]]
799
+ layers.extend(view.layers)
800
+
801
+ child.layers = layers
802
+
803
+ # convert layers to a list of idens...
804
+ lids = [layr.iden for layr in layers]
805
+ await child.info.set('layers', lids)
806
+
807
+ await self.core.feedBeholder('view:setlayers', {'iden': child.iden, 'layers': lids}, gates=[child.iden, lids[0]])
808
+
809
+ todo.append(child)
810
+
780
811
  async def fork(self, ldef=None, vdef=None):
781
812
  '''
782
813
  Make a new view inheriting from this view with the same layers and a new write layer on top
@@ -961,6 +992,24 @@ class View(s_nexus.Pusher): # type: ignore
961
992
 
962
993
  await self.triggers.runPropSet(node, prop, oldv, view=view)
963
994
 
995
+ async def runEdgeAdd(self, n1, edge, n2, view=None):
996
+ if not n1.snap.trigson:
997
+ return
998
+
999
+ if view is None:
1000
+ view = self.iden
1001
+
1002
+ await self.triggers.runEdgeAdd(n1, edge, n2, view=view)
1003
+
1004
+ async def runEdgeDel(self, n1, edge, n2, view=None):
1005
+ if not n1.snap.trigson:
1006
+ return
1007
+
1008
+ if view is None:
1009
+ view = self.iden
1010
+
1011
+ await self.triggers.runEdgeDel(n1, edge, n2, view=view)
1012
+
964
1013
  async def addTrigger(self, tdef):
965
1014
  '''
966
1015
  Adds a trigger to the view.
synapse/models/inet.py CHANGED
@@ -28,6 +28,8 @@ udots = regex.compile(r'[\u3002\uff0e\uff61]')
28
28
  cidrmasks = [((0xffffffff - (2 ** (32 - i) - 1)), (2 ** (32 - i))) for i in range(33)]
29
29
  ipv4max = 2 ** 32 - 1
30
30
 
31
+ rfc6598 = ipaddress.IPv4Network('100.64.0.0/10')
32
+
31
33
  def getAddrType(ip):
32
34
 
33
35
  if ip.is_multicast:
@@ -45,6 +47,9 @@ def getAddrType(ip):
45
47
  if ip.is_reserved:
46
48
  return 'reserved'
47
49
 
50
+ if ip in rfc6598:
51
+ return 'shared'
52
+
48
53
  return 'unicast'
49
54
 
50
55
  class Addr(s_types.Str):
@@ -924,6 +929,11 @@ class Url(s_types.Str):
924
929
  except Exception:
925
930
  pass
926
931
 
932
+ # allow MSFT specific wild card syntax
933
+ # https://learn.microsoft.com/en-us/windows/win32/http/urlprefix-strings
934
+ if host is None and part == '+':
935
+ host = '+'
936
+
927
937
  if host and local:
928
938
  raise s_exc.BadTypeValu(valu=orig, name=self.name,
929
939
  mesg='Host specified on local-only file URI') from None
@@ -950,10 +960,12 @@ class Url(s_types.Str):
950
960
  if port is not None:
951
961
  hostparts = f'{hostparts}:{port}'
952
962
 
953
- if proto != 'file':
954
- if (not subs.get('fqdn')) and (subs.get('ipv4') is None) and (subs.get('ipv6') is None):
955
- raise s_exc.BadTypeValu(valu=orig, name=self.name,
956
- mesg='Missing address/url') from None
963
+ if proto != 'file' and host is None:
964
+ raise s_exc.BadTypeValu(valu=orig, name=self.name, mesg='Missing address/url')
965
+
966
+ if not hostparts and not pathpart:
967
+ raise s_exc.BadTypeValu(valu=orig, name=self.name,
968
+ mesg='Missing address/url') from None
957
969
 
958
970
  base = f'{proto}://{hostparts}{pathpart}'
959
971
  subs['base'] = base
@@ -2305,6 +2317,7 @@ class InetModule(s_module.CoreModule):
2305
2317
  ('name:en', ('inet:user', {}), {
2306
2318
  'doc': 'The English version of the name associated with the (may be different from '
2307
2319
  'the account identifier, e.g., a display name).',
2320
+ 'deprecated': True,
2308
2321
  }),
2309
2322
  ('aliases', ('array', {'type': 'inet:user', 'uniq': True, 'sorted': True}), {
2310
2323
  'doc': 'An array of alternate names for the user.',
@@ -2322,7 +2335,8 @@ class InetModule(s_module.CoreModule):
2322
2335
  'doc': 'The localized version of the real name of the account owner / registrant.'
2323
2336
  }),
2324
2337
  ('realname:en', ('ps:name', {}), {
2325
- 'doc': 'The English version of the real name of the account owner / registrant.'
2338
+ 'doc': 'The English version of the real name of the account owner / registrant.',
2339
+ 'deprecated': True,
2326
2340
  }),
2327
2341
  ('signup', ('time', {}), {
2328
2342
  'doc': 'The date and time the account was registered.'
@@ -2540,7 +2554,8 @@ class InetModule(s_module.CoreModule):
2540
2554
  }),
2541
2555
  ('name:en', ('inet:group', {}), {
2542
2556
  'doc': 'The English version of the name associated with the group (may be different '
2543
- 'from the localized name).'
2557
+ 'from the localized name).',
2558
+ 'deprecated': True,
2544
2559
  }),
2545
2560
  ('url', ('inet:url', {}), {
2546
2561
  'doc': 'The service provider URL where the group is hosted.'
synapse/models/orgs.py CHANGED
@@ -202,6 +202,8 @@ class OuModule(s_module.CoreModule):
202
202
  ('ou:jobtitle', ('str', {'lower': True, 'onespace': True}), {
203
203
  'doc': 'A title for a position within an org.',
204
204
  }),
205
+ ('ou:requirement', ('guid', {}), {
206
+ 'doc': 'A specific requirement.'}),
205
207
  ),
206
208
  'edges': (
207
209
  (('ou:campaign', 'uses', 'ou:technique'), {
@@ -219,6 +221,8 @@ class OuModule(s_module.CoreModule):
219
221
  'doc': 'The campaign made use of the target node.'}),
220
222
  (('ou:contribution', 'includes', None), {
221
223
  'doc': 'The contribution includes the specific node.'}),
224
+ ((None, 'meets', 'ou:requirement'), {
225
+ 'doc': 'The requirement is met by the source node.'}),
222
226
  ),
223
227
  'forms': (
224
228
  ('ou:jobtype', {}, ()),
@@ -634,8 +638,10 @@ class OuModule(s_module.CoreModule):
634
638
  'doc': 'Deprecated for scalability. Please use -(uses)> ou:technique.',
635
639
  }),
636
640
  ('goals', ('array', {'type': 'ou:goal', 'sorted': True, 'uniq': True}), {
637
- 'doc': 'The assessed goals of the organization.'
638
- }),
641
+ 'doc': 'The assessed goals of the organization.'}),
642
+
643
+ ('tag', ('syn:tag', {}), {
644
+ 'doc': 'A base tag used to encode assessments made by the organization.'}),
639
645
  )),
640
646
  ('ou:team', {}, (
641
647
  ('org', ('ou:org', {}), {}),
@@ -708,6 +714,7 @@ class OuModule(s_module.CoreModule):
708
714
  'doc': 'An array of alternative names for the industry.'}),
709
715
 
710
716
  ('subs', ('array', {'type': 'ou:industry', 'split': ',', 'uniq': True, 'sorted': True}), {
717
+ 'deprecated': True,
711
718
  'doc': 'Deprecated. Please use ou:industry:type taxonomy.'}),
712
719
 
713
720
  ('sic', ('array', {'type': 'ou:sic', 'split': ',', 'uniq': True, 'sorted': True}), {
@@ -1122,6 +1129,45 @@ class OuModule(s_module.CoreModule):
1122
1129
  }),
1123
1130
  # TODO duration ('duration'
1124
1131
  )),
1132
+ ('ou:requirement', {}, (
1133
+
1134
+ ('name', ('str', {'lower': True, 'onespace': True}), {
1135
+ 'doc': 'A name for the requirement.'}),
1136
+
1137
+ ('text', ('str', {}), {
1138
+ 'disp': {'hint': 'text'},
1139
+ 'doc': 'The text of the stated requirement.'}),
1140
+
1141
+ ('optional', ('bool', {}), {
1142
+ 'doc': 'Set to true if the requirement is optional.'}),
1143
+
1144
+ ('priority', ('meta:priority', {}), {
1145
+ 'doc': 'The priority of the requirement.'}),
1146
+
1147
+ ('goal', ('ou:goal', {}), {
1148
+ 'doc': 'The goal that the requirement is designed to achieve.'}),
1149
+
1150
+ ('active', ('bool', {}), {
1151
+ 'doc': 'Set to true if the requirement is currently active.'}),
1152
+
1153
+ ('issued', ('time', {}), {
1154
+ 'doc': 'The time that the requirement was first issued.'}),
1155
+
1156
+ ('period', ('ival', {}), {
1157
+ 'doc': 'The time window where the goal must be met. Can be ongoing.'}),
1158
+
1159
+ ('issuer', ('ps:contact', {}), {
1160
+ 'doc': 'The contact information of the entity which issued the requirement.'}),
1161
+
1162
+ ('assignee', ('ps:contact', {}), {
1163
+ 'doc': 'The contact information of the entity which is assigned to meet the requirement.'}),
1164
+
1165
+ ('deps', ('array', {'type': 'ou:requirement', 'sorted': True, 'uniq': True}), {
1166
+ 'doc': 'A list of sub-requirements which must be met to complete the requirement.'}),
1167
+
1168
+ ('deps:min', ('int', {'min': 0}), {
1169
+ 'doc': 'The minimum number dependant requirements which must be met. If unset, assume all must be met.'}),
1170
+ )),
1125
1171
  )
1126
1172
  }
1127
1173