synapse 2.184.0__py311-none-any.whl → 2.186.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 (49) hide show
  1. synapse/cortex.py +4 -3
  2. synapse/datamodel.py +41 -6
  3. synapse/exc.py +2 -0
  4. synapse/lib/ast.py +83 -22
  5. synapse/lib/auth.py +13 -0
  6. synapse/lib/cell.py +78 -2
  7. synapse/lib/drive.py +45 -10
  8. synapse/lib/modules.py +1 -0
  9. synapse/lib/parser.py +1 -0
  10. synapse/lib/snap.py +1 -6
  11. synapse/lib/storm.lark +12 -6
  12. synapse/lib/storm.py +45 -9
  13. synapse/lib/storm_format.py +1 -0
  14. synapse/lib/stormlib/stix.py +14 -5
  15. synapse/lib/stormtypes.py +64 -36
  16. synapse/lib/types.py +6 -0
  17. synapse/lib/version.py +2 -2
  18. synapse/models/doc.py +93 -0
  19. synapse/models/infotech.py +2 -1
  20. synapse/models/media.py +0 -1
  21. synapse/models/orgs.py +26 -3
  22. synapse/models/proj.py +56 -36
  23. synapse/models/risk.py +3 -0
  24. synapse/models/syn.py +64 -6
  25. synapse/tests/test_cortex.py +49 -6
  26. synapse/tests/test_lib_base.py +2 -2
  27. synapse/tests/test_lib_cell.py +59 -5
  28. synapse/tests/test_lib_grammar.py +2 -0
  29. synapse/tests/test_lib_storm.py +54 -1
  30. synapse/tests/test_lib_stormlib_stix.py +3 -2
  31. synapse/tests/test_model_doc.py +51 -0
  32. synapse/tests/test_model_orgs.py +41 -0
  33. synapse/tests/test_model_risk.py +2 -0
  34. synapse/tests/test_model_syn.py +43 -0
  35. synapse/tests/test_tools_promote.py +67 -0
  36. synapse/tests/test_tools_snapshot.py +47 -0
  37. synapse/tools/aha/clone.py +3 -1
  38. synapse/tools/aha/easycert.py +1 -1
  39. synapse/tools/aha/enroll.py +3 -1
  40. synapse/tools/aha/provision/service.py +3 -1
  41. synapse/tools/aha/provision/user.py +3 -1
  42. synapse/tools/livebackup.py +3 -1
  43. synapse/tools/promote.py +23 -4
  44. synapse/tools/snapshot.py +69 -0
  45. {synapse-2.184.0.dist-info → synapse-2.186.0.dist-info}/METADATA +5 -10
  46. {synapse-2.184.0.dist-info → synapse-2.186.0.dist-info}/RECORD +49 -44
  47. {synapse-2.184.0.dist-info → synapse-2.186.0.dist-info}/WHEEL +1 -1
  48. {synapse-2.184.0.dist-info → synapse-2.186.0.dist-info}/LICENSE +0 -0
  49. {synapse-2.184.0.dist-info → synapse-2.186.0.dist-info}/top_level.txt +0 -0
synapse/lib/storm.py CHANGED
@@ -1778,8 +1778,10 @@ class StormDmon(s_base.Base):
1778
1778
 
1779
1779
  text = self.ddef.get('storm')
1780
1780
  opts = self.ddef.get('stormopts', {})
1781
- vars = opts.setdefault('vars', {})
1781
+
1782
+ vars = await s_stormtypes.toprim(opts.get('vars', {}), use_list=True)
1782
1783
  vars.setdefault('auto', {'iden': self.iden, 'type': 'dmon'})
1784
+ opts['vars'] = vars
1783
1785
 
1784
1786
  viewiden = opts.get('view')
1785
1787
 
@@ -2732,15 +2734,28 @@ class Parser:
2732
2734
  self.exited = True
2733
2735
  return False
2734
2736
 
2737
+ def _wrap_text(self, text, width):
2738
+ lines, curline, curlen = [], [], 0
2739
+ for word in text.split():
2740
+ if curlen + len(word) + bool(curline) > width:
2741
+ lines.append(' '.join(curline))
2742
+ curline, curlen = [word], len(word)
2743
+ else:
2744
+ curline.append(word)
2745
+ curlen += len(word) + bool(curline)
2746
+ if curline:
2747
+ lines.append(' '.join(curline))
2748
+ return lines
2749
+
2735
2750
  def _print_optarg(self, names, argdef):
2736
2751
 
2737
2752
  dest = self._get_dest_str(argdef)
2738
2753
  oact = argdef.get('action', 'store')
2739
2754
 
2740
2755
  if oact in ('store_true', 'store_false'):
2741
- base = f' {names[0]}'.ljust(30)
2756
+ base = f' {names[0]}'
2742
2757
  else:
2743
- base = f' {names[0]} {dest}'.ljust(30)
2758
+ base = f' {names[0]} {dest}'
2744
2759
 
2745
2760
  defval = argdef.get('default', s_common.novalu)
2746
2761
  choices = argdef.get('choices')
@@ -2748,12 +2763,14 @@ class Parser:
2748
2763
 
2749
2764
  if defval is not s_common.novalu and oact not in ('store_true', 'store_false'):
2750
2765
  if isinstance(defval, (tuple, list, dict)):
2751
- defval = pprint.pformat(defval, indent=34, width=100)
2752
- if '\n' in defval:
2753
- defval = '\n' + defval
2766
+ defval_ls = pprint.pformat(defval, width=120).split('\n')
2767
+ defval = '\n'.join(ln.strip() for ln in defval_ls)
2754
2768
 
2755
2769
  if choices is None:
2756
- helpstr = f'{helpstr} (default: {defval})'
2770
+ if (lambda tst: '\n' in tst if isinstance(tst, str) else False)(defval):
2771
+ helpstr = f'{helpstr} (default: \n{defval})'
2772
+ else:
2773
+ helpstr = f'{helpstr} (default: {defval})'
2757
2774
  else:
2758
2775
  cstr = ', '.join(str(c) for c in choices)
2759
2776
  helpstr = f'{helpstr} (default: {defval}, choices: {cstr})'
@@ -2762,7 +2779,26 @@ class Parser:
2762
2779
  cstr = ', '.join(str(c) for c in choices)
2763
2780
  helpstr = f'{helpstr} (choices: {cstr})'
2764
2781
 
2765
- self._printf(f'{base}: {helpstr}')
2782
+ helplst = helpstr.split('\n')
2783
+ if helplst and not helplst[0].strip():
2784
+ helplst = helplst[1:]
2785
+ min_space = min((len(ln) - len(ln.lstrip()) for ln in helplst if ln.strip()), default=0)
2786
+
2787
+ base_w = 32
2788
+ wrap_w = 120 - base_w
2789
+
2790
+ first = helplst[0][min_space:]
2791
+ wrap_first = self._wrap_text(first, wrap_w)
2792
+ self._printf(f'{base:<{base_w-2}}: {wrap_first[0]}')
2793
+
2794
+ for ln in wrap_first[1:]: self._printf(f'{"":<{base_w}}{ln}')
2795
+ for ln in helplst[1:]:
2796
+ lead_s = len(ln) - len(ln.lstrip())
2797
+ rel_ind = lead_s - min_space
2798
+ ind = ' ' * (base_w + rel_ind)
2799
+ wrapped = self._wrap_text(ln.lstrip(), wrap_w - rel_ind)
2800
+ for wl in wrapped:
2801
+ self._printf(f'{ind}{wl}')
2766
2802
 
2767
2803
  def _print_posarg(self, name, argdef):
2768
2804
  dest = self._get_dest_str(argdef)
@@ -5232,7 +5268,7 @@ class BackgroundCmd(Cmd):
5232
5268
  async for item in genr:
5233
5269
  yield item
5234
5270
 
5235
- runtprims = await s_stormtypes.toprim(self.runt.getScopeVars())
5271
+ runtprims = await s_stormtypes.toprim(self.runt.getScopeVars(), use_list=True)
5236
5272
  runtvars = {k: v for (k, v) in runtprims.items() if s_msgpack.isok(v)}
5237
5273
 
5238
5274
  opts = {
@@ -29,6 +29,7 @@ TerminalPygMap = {
29
29
  'DOT': p_t.Punctuation,
30
30
  'DOUBLEQUOTEDSTRING': p_t.Literal.String,
31
31
  'ELIF': p_t.Keyword,
32
+ 'EMBEDPROPS': p_t.Name,
32
33
  'EQNOSPACE': p_t.Punctuation,
33
34
  'EQSPACE': p_t.Punctuation,
34
35
  'EQUAL': p_t.Punctuation,
@@ -486,7 +486,10 @@ _DefaultConfig = {
486
486
  },
487
487
  }
488
488
 
489
- def _validateConfig(core, config):
489
+ perm_maxsize = ('storm', 'lib', 'stix', 'export', 'maxsize')
490
+ def _validateConfig(runt, config):
491
+
492
+ core = runt.snap.core
490
493
 
491
494
  maxsize = config.get('maxsize', 10000)
492
495
 
@@ -506,9 +509,10 @@ def _validateConfig(core, config):
506
509
  mesg = f'STIX Bundle config maxsize option must be an integer.'
507
510
  raise s_exc.BadConfValu(mesg=mesg)
508
511
 
509
- if maxsize > 10000:
510
- mesg = f'STIX Bundle config maxsize option must be <= 10000.'
511
- raise s_exc.BadConfValu(mesg=mesg)
512
+ if maxsize > 10000 and not runt.allowed(perm_maxsize):
513
+ permstr = '.'.join(perm_maxsize)
514
+ mesg = f'Setting STIX export maxsize > 10,000 requires permission: {permstr}'
515
+ raise s_exc.AuthDeny(mesg=mesg, perm=permstr)
512
516
 
513
517
  formmaps = config.get('forms')
514
518
  if formmaps is None:
@@ -1040,6 +1044,11 @@ class LibStixExport(s_stormtypes.Lib):
1040
1044
  '''
1041
1045
  A Storm Library for exporting to STIX version 2.1 CS02.
1042
1046
  '''
1047
+ _storm_lib_perms = (
1048
+ {'perm': ('storm', 'lib', 'stix', 'export', 'maxsize'), 'gate': 'cortex',
1049
+ 'desc': 'Controls the ability to specify a STIX export bundle maxsize of greater than 10,000.'},
1050
+ )
1051
+
1043
1052
  _storm_locals = ( # type: ignore
1044
1053
  {
1045
1054
  'name': 'bundle',
@@ -1172,7 +1181,7 @@ class LibStixExport(s_stormtypes.Lib):
1172
1181
  config = _DefaultConfig
1173
1182
 
1174
1183
  config = await s_stormtypes.toprim(config)
1175
- _validateConfig(self.runt.snap.core, config)
1184
+ _validateConfig(self.runt, config)
1176
1185
 
1177
1186
  return StixBundle(self, self.runt, config)
1178
1187
 
synapse/lib/stormtypes.py CHANGED
@@ -1328,6 +1328,22 @@ class LibBase(Lib):
1328
1328
  ),
1329
1329
  'returns': {'type': 'list',
1330
1330
  'desc': 'A list of (<bool>, <prim>) for status and normalized value.', }}},
1331
+ {'name': 'repr', 'desc': '''
1332
+ Attempt to convert a system mode value to a display mode string.
1333
+
1334
+ Examples:
1335
+ Print the Synapse user name for an iden::
1336
+
1337
+ $lib.print($lib.repr(syn:user, $iden))
1338
+
1339
+ ''',
1340
+ 'type': {'type': 'function', '_funcname': '_repr',
1341
+ 'args': (
1342
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the model type.'},
1343
+ {'name': 'valu', 'type': 'any', 'desc': 'The value to convert.'},
1344
+ ),
1345
+ 'returns': {'type': 'str', 'desc': 'A display mode representation of the value.'}}},
1346
+
1331
1347
  {'name': 'debug', 'desc': '''
1332
1348
  True if the current runtime has debugging enabled.
1333
1349
 
@@ -1400,6 +1416,7 @@ class LibBase(Lib):
1400
1416
  'false': False,
1401
1417
  'text': self._text,
1402
1418
  'cast': self._cast,
1419
+ 'repr': self._repr,
1403
1420
  'warn': self._warn,
1404
1421
  'print': self._print,
1405
1422
  'raise': self._raise,
@@ -1489,22 +1506,26 @@ class LibBase(Lib):
1489
1506
  mesg = 'Nested type does not support being copied!'
1490
1507
  raise s_exc.BadArg(mesg=mesg) from None
1491
1508
 
1509
+ def _reqTypeByName(self, name):
1510
+ typeitem = self.runt.snap.core.model.type(name)
1511
+ if typeitem is not None:
1512
+ return typeitem
1513
+
1514
+ # If a type cannot be found for the form, see if name is a property
1515
+ # that has a type we can use
1516
+ propitem = self.runt.snap.core.model.prop(name)
1517
+ if propitem is not None:
1518
+ return propitem.type
1519
+
1520
+ mesg = f'No type or prop found for name {name}.'
1521
+ raise s_exc.NoSuchType(mesg=mesg)
1522
+
1492
1523
  @stormfunc(readonly=True)
1493
1524
  async def _cast(self, name, valu):
1494
1525
  name = await toprim(name)
1495
1526
  valu = await toprim(valu)
1496
1527
 
1497
- typeitem = self.runt.snap.core.model.type(name)
1498
- if typeitem is None:
1499
- # If a type cannot be found for the form, see if name is a property
1500
- # that has a type we can use
1501
- propitem = self.runt.snap.core.model.prop(name)
1502
- if propitem is None:
1503
- mesg = f'No type or prop found for name {name}.'
1504
- raise s_exc.NoSuchType(mesg=mesg)
1505
-
1506
- typeitem = propitem.type
1507
-
1528
+ typeitem = self._reqTypeByName(name)
1508
1529
  # TODO an eventual mapping between model types and storm prims
1509
1530
 
1510
1531
  norm, info = typeitem.norm(valu)
@@ -1515,16 +1536,7 @@ class LibBase(Lib):
1515
1536
  name = await toprim(name)
1516
1537
  valu = await toprim(valu)
1517
1538
 
1518
- typeitem = self.runt.snap.core.model.type(name)
1519
- if typeitem is None:
1520
- # If a type cannot be found for the form, see if name is a property
1521
- # that has a type we can use
1522
- propitem = self.runt.snap.core.model.prop(name)
1523
- if propitem is None:
1524
- mesg = f'No type or prop found for name {name}.'
1525
- raise s_exc.NoSuchType(mesg=mesg)
1526
-
1527
- typeitem = propitem.type
1539
+ typeitem = self._reqTypeByName(name)
1528
1540
 
1529
1541
  try:
1530
1542
  norm, info = typeitem.norm(valu)
@@ -1532,6 +1544,13 @@ class LibBase(Lib):
1532
1544
  except s_exc.BadTypeValu:
1533
1545
  return (False, None)
1534
1546
 
1547
+ @stormfunc(readonly=True)
1548
+ async def _repr(self, name, valu):
1549
+ name = await toprim(name)
1550
+ valu = await toprim(valu)
1551
+
1552
+ return self._reqTypeByName(name).repr(valu)
1553
+
1535
1554
  @stormfunc(readonly=True)
1536
1555
  async def _exit(self, mesg=None, **kwargs):
1537
1556
  if mesg:
@@ -1720,7 +1739,7 @@ class LibDict(Lib):
1720
1739
  'type': {'type': 'function', '_funcname': '_has',
1721
1740
  'args': (
1722
1741
  {'name': 'valu', 'type': 'dict', 'desc': 'The dictionary being checked.'},
1723
- {'name': 'name', 'type': 'str', 'desc': 'The key name to check.'},
1742
+ {'name': 'key', 'type': 'any', 'desc': 'The key to check.'},
1724
1743
  ),
1725
1744
  'returns': {'type': 'boolean', 'desc': 'True if the key is present, false if the key is not present.'}}},
1726
1745
  {'name': 'keys', 'desc': 'Retrieve a list of keys in the specified dictionary.',
@@ -1733,7 +1752,7 @@ class LibDict(Lib):
1733
1752
  'type': {'type': 'function', '_funcname': '_pop',
1734
1753
  'args': (
1735
1754
  {'name': 'valu', 'type': 'dict', 'desc': 'The dictionary to operate on.'},
1736
- {'name': 'key', 'type': 'str', 'desc': 'The key name of the value to pop.'},
1755
+ {'name': 'key', 'type': 'any', 'desc': 'The key to pop.'},
1737
1756
  {'name': 'default', 'type': 'any', 'default': '$lib.undef',
1738
1757
  'desc': 'Optional default value to return if the key does not exist in the dictionary.'},
1739
1758
  ),
@@ -1776,10 +1795,11 @@ class LibDict(Lib):
1776
1795
  raise s_exc.BadArg(mesg=mesg)
1777
1796
 
1778
1797
  @stormfunc(readonly=True)
1779
- async def _has(self, valu, name):
1798
+ async def _has(self, valu, key):
1780
1799
  await self._check_type(valu)
1800
+ key = await toprim(key)
1781
1801
  valu = await toprim(valu)
1782
- return name in valu
1802
+ return key in valu
1783
1803
 
1784
1804
  @stormfunc(readonly=True)
1785
1805
  async def _keys(self, valu):
@@ -1791,8 +1811,8 @@ class LibDict(Lib):
1791
1811
  async def _pop(self, valu, key, default=undef):
1792
1812
  await self._check_type(valu)
1793
1813
 
1814
+ key = await toprim(key)
1794
1815
  real = await toprim(valu)
1795
- key = await tostr(key)
1796
1816
 
1797
1817
  if key not in real:
1798
1818
  if default == undef:
@@ -4808,8 +4828,8 @@ class Dict(Prim):
4808
4828
  name = await toprim(name)
4809
4829
  return self.valu.get(name)
4810
4830
 
4811
- async def value(self):
4812
- return {await toprim(k): await toprim(v) for (k, v) in self.valu.items()}
4831
+ async def value(self, use_list=False):
4832
+ return {await toprim(k): await toprim(v, use_list=use_list) for (k, v) in self.valu.items()}
4813
4833
 
4814
4834
  async def stormrepr(self):
4815
4835
  reprs = ["{}: {}".format(await torepr(k), await torepr(v)) for (k, v) in list(self.valu.items())]
@@ -4845,9 +4865,9 @@ class CmdOpts(Dict):
4845
4865
  name = await tostr(name)
4846
4866
  return getattr(self.valu.opts, name, None)
4847
4867
 
4848
- async def value(self):
4868
+ async def value(self, use_list=False):
4849
4869
  valu = vars(self.valu.opts)
4850
- return {await toprim(k): await toprim(v) for (k, v) in valu.items()}
4870
+ return {await toprim(k): await toprim(v, use_list=use_list) for (k, v) in valu.items()}
4851
4871
 
4852
4872
  async def iter(self):
4853
4873
  valu = vars(self.valu.opts)
@@ -5193,8 +5213,10 @@ class List(Prim):
5193
5213
  async for item in toiter(valu):
5194
5214
  self.valu.append(item)
5195
5215
 
5196
- async def value(self):
5197
- return tuple([await toprim(v) for v in self.valu])
5216
+ async def value(self, use_list=False):
5217
+ if use_list:
5218
+ return [await toprim(v, use_list=use_list) for v in self.valu]
5219
+ return tuple([await toprim(v, use_list=use_list) for v in self.valu])
5198
5220
 
5199
5221
  async def iter(self):
5200
5222
  for item in self.valu:
@@ -9476,7 +9498,7 @@ class CronJob(Prim):
9476
9498
  return job
9477
9499
 
9478
9500
  # These will go away once we have value objects in storm runtime
9479
- async def toprim(valu, path=None):
9501
+ async def toprim(valu, path=None, use_list=False):
9480
9502
 
9481
9503
  if isinstance(valu, (str, int, bool, float, bytes, types.AsyncGeneratorType, types.GeneratorType)) or valu is None:
9482
9504
  return valu
@@ -9485,16 +9507,19 @@ async def toprim(valu, path=None):
9485
9507
  retn = []
9486
9508
  for v in valu:
9487
9509
  try:
9488
- retn.append(await toprim(v))
9510
+ retn.append(await toprim(v, use_list=use_list))
9489
9511
  except s_exc.NoSuchType:
9490
9512
  pass
9491
- return tuple(retn)
9513
+
9514
+ if not use_list:
9515
+ return tuple(retn)
9516
+ return retn
9492
9517
 
9493
9518
  if isinstance(valu, dict):
9494
9519
  retn = {}
9495
9520
  for k, v in valu.items():
9496
9521
  try:
9497
- retn[k] = await toprim(v)
9522
+ retn[k] = await toprim(v, use_list=use_list)
9498
9523
  except s_exc.NoSuchType:
9499
9524
  pass
9500
9525
  return retn
@@ -9502,6 +9527,9 @@ async def toprim(valu, path=None):
9502
9527
  if isinstance(valu, Number):
9503
9528
  return float(valu.value())
9504
9529
 
9530
+ if isinstance(valu, (Dict, List)):
9531
+ return await valu.value(use_list=use_list)
9532
+
9505
9533
  if isinstance(valu, Prim):
9506
9534
  return await s_coro.ornot(valu.value)
9507
9535
 
synapse/lib/types.py CHANGED
@@ -1903,6 +1903,12 @@ class Tag(Str):
1903
1903
  mesg = f'Tag does not match tagre: [{s_grammar.tagre.pattern}]'
1904
1904
  raise s_exc.BadTypeValu(valu=norm, name=self.name, mesg=mesg)
1905
1905
 
1906
+ core = self.modl.core
1907
+ if core is not None:
1908
+ (ok, mesg) = core.isTagValid(norm)
1909
+ if not ok:
1910
+ raise s_exc.BadTypeValu(valu=norm, name=self.name, mesg=mesg)
1911
+
1906
1912
  return norm, {'subs': subs, 'toks': toks}
1907
1913
 
1908
1914
  def _normPyStr(self, text):
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, 184, 0)
226
+ version = (2, 186, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '05940dce070d26f83bb7e4070f1d44d051804e3b'
228
+ commit = 'ffa4abc52513d4b8ac592496322227eed9e07dc5'
synapse/models/doc.py ADDED
@@ -0,0 +1,93 @@
1
+ import synapse.exc as s_exc
2
+ import synapse.lib.module as s_module
3
+
4
+ class DocModule(s_module.CoreModule):
5
+
6
+ def getModelDefs(self):
7
+ return (('doc', {
8
+ 'interfaces': (
9
+ ('doc:document', {
10
+
11
+ 'doc': 'A common interface for documents.',
12
+
13
+ 'template': {
14
+ 'type': 'NEWP',
15
+ 'document': 'document',
16
+ 'documents': 'documents'},
17
+
18
+ 'props': (
19
+
20
+ ('id', ('str', {'strip': True}), {
21
+ 'doc': 'The {document} ID.'}),
22
+
23
+ ('name', ('str', {'lower': True, 'onespace': True}), {
24
+ 'doc': 'The {document} name.'}),
25
+
26
+ ('type', ('{type}', {}), {
27
+ 'doc': 'The type of {document}.'}),
28
+
29
+ ('text', ('str', {}), {
30
+ 'doc': 'The text of the {document}.'}),
31
+
32
+ ('file', ('file:bytes', {}), {
33
+ 'doc': 'The file which contains the {document}.'}),
34
+
35
+ ('created', ('time', {}), {
36
+ 'doc': 'The time that the {document} was created.'}),
37
+
38
+ ('updated', ('time', {}), {
39
+ 'doc': 'The time that the {document} was last updated.'}),
40
+
41
+ ('author', ('ps:contact', {}), {
42
+ 'doc': 'The contact information of the primary author.'}),
43
+
44
+ ('contributors', ('array', {'type': 'ps:contact', 'sorted': True, 'uniq': True}), {
45
+ 'doc': 'An array of contacts which contributed to the {document}.'}),
46
+
47
+ ('version', ('it:semver', {}), {
48
+ 'doc': 'The version of the {document}.'}),
49
+
50
+ ('supersedes', ('array', {'type': '$self', 'sorted': True, 'uniq': True}), {
51
+ 'doc': 'An array of {documents} which are superseded by this {document}.'}),
52
+ ),
53
+ }),
54
+ ),
55
+ 'types': (
56
+
57
+ ('doc:policy:type:taxonomy', ('taxonomy', {}), {
58
+ 'interfaces': ('meta:taxonomy',),
59
+ 'doc': 'A taxonomy of policy types.'}),
60
+
61
+ ('doc:policy', ('guid', {}), {
62
+ 'interfaces': ('doc:document',),
63
+ 'template': {
64
+ 'document': 'policy',
65
+ 'documents': 'policies',
66
+ 'type': 'doc:policy:type:taxonomy'},
67
+ 'doc': 'Guiding principles used to reach a set of goals.'}),
68
+
69
+ ('doc:standard:type:taxonomy', ('taxonomy', {}), {
70
+ 'interfaces': ('meta:taxonomy',),
71
+ 'doc': 'A taxonomy of standard types.'}),
72
+
73
+ ('doc:standard', ('guid', {}), {
74
+ 'interfaces': ('doc:document',),
75
+ 'template': {
76
+ 'document': 'standard',
77
+ 'documents': 'standards',
78
+ 'type': 'doc:standard:type:taxonomy'},
79
+ 'doc': 'A group of requirements which define how to implement a policy or goal.'}),
80
+ ),
81
+ 'forms': (
82
+
83
+ ('doc:policy:type:taxonomy', {}, ()),
84
+ ('doc:policy', {}, ()),
85
+
86
+ ('doc:standard:type:taxonomy', {}, ()),
87
+ ('doc:standard', {}, (
88
+ ('policy', ('doc:policy', {}), {
89
+ 'doc': 'The policy which was used to derive the standard.'}),
90
+ )),
91
+ ),
92
+ 'edges': (),
93
+ }),)
@@ -1882,7 +1882,8 @@ class ItModule(s_module.CoreModule):
1882
1882
  'disp': {'hint': 'text'},
1883
1883
  'doc': 'The commit message describing the changes in the commit.'}),
1884
1884
 
1885
- ('id', ('str', {}), {
1885
+ # we mirror the interface type options...
1886
+ ('id', ('str', {'strip': True}), {
1886
1887
  'doc': 'The version control system specific commit identifier.'}),
1887
1888
 
1888
1889
  ('created', ('time', {}), {
synapse/models/media.py CHANGED
@@ -38,7 +38,6 @@ class MediaModule(s_module.CoreModule):
38
38
  ('publisher', ('ou:org', {}), {
39
39
  'doc': 'The organization which published the news.'}),
40
40
 
41
-
42
41
  ('publisher:name', ('ou:name', {}), {
43
42
  'doc': 'The name of the publishing org used to publish the news.'}),
44
43
 
synapse/models/orgs.py CHANGED
@@ -262,13 +262,25 @@ class OuModule(s_module.CoreModule):
262
262
  'interfaces': ('meta:taxonomy',),
263
263
  }),
264
264
  ('ou:jobtitle', ('str', {'lower': True, 'onespace': True}), {
265
- 'doc': 'A title for a position within an org.',
266
- }),
265
+ 'doc': 'A title for a position within an org.'}),
266
+
267
+ ('ou:enacted:status:taxonomy', ('taxonomy', {}), {
268
+ 'interfaces': ('meta:taxonomy',),
269
+ 'doc': 'A taxonomy of enacted statuses.'}),
270
+
271
+ ('ou:enacted', ('guid', {}), {
272
+ 'interfaces': ('proj:task',),
273
+ 'template': {
274
+ 'task': 'adoption task'},
275
+ 'doc': 'An organization enacting a document.'}),
276
+
267
277
  ('ou:requirement:type:taxonomy', ('taxonomy', {}), {
268
278
  'interfaces': ('meta:taxonomy',),
269
279
  'doc': 'A taxonomy of requirement types.'}),
280
+
270
281
  ('ou:requirement', ('guid', {}), {
271
282
  'doc': 'A specific requirement.'}),
283
+
272
284
  ),
273
285
  'edges': (
274
286
  (('ou:campaign', 'uses', 'ou:technique'), {
@@ -1281,8 +1293,19 @@ class OuModule(s_module.CoreModule):
1281
1293
  ('url', ('inet:url', {}), {
1282
1294
  'doc': 'The contest result website URL.',
1283
1295
  }),
1284
- # TODO duration ('duration'
1285
1296
  )),
1297
+ ('ou:enacted:status:taxonomy', {}, ()),
1298
+ ('ou:enacted', {}, (
1299
+ ('org', ('ou:org', {}), {
1300
+ 'doc': 'The organization which is enacting the document.'}),
1301
+
1302
+ ('doc', ('ndef', {'forms': ('doc:policy', 'doc:standard')}), {
1303
+ 'doc': 'The document enacted by the organization.'}),
1304
+
1305
+ ('scope', ('ndef', {}), {
1306
+ 'doc': 'The scope of responsbility for the assignee to enact the document.'}),
1307
+ )),
1308
+
1286
1309
  ('ou:requirement:type:taxonomy', {}, ()),
1287
1310
  ('ou:requirement', {}, (
1288
1311