synapse 2.171.0__py311-none-any.whl → 2.173.1__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.
- synapse/common.py +20 -0
- synapse/cortex.py +86 -4
- synapse/lib/agenda.py +13 -7
- synapse/lib/ast.py +9 -8
- synapse/lib/cache.py +2 -2
- synapse/lib/cell.py +5 -0
- synapse/lib/coro.py +12 -0
- synapse/lib/hiveauth.py +81 -4
- synapse/lib/layer.py +124 -84
- synapse/lib/lmdbslab.py +17 -10
- synapse/lib/node.py +1 -1
- synapse/lib/slabseqn.py +11 -5
- synapse/lib/storm.py +7 -71
- synapse/lib/stormhttp.py +1 -1
- synapse/lib/stormlib/auth.py +19 -0
- synapse/lib/stormlib/cell.py +42 -4
- synapse/lib/stormlib/compression.py +6 -6
- synapse/lib/stormlib/env.py +50 -0
- synapse/lib/stormlib/gen.py +1 -1
- synapse/lib/stormlib/model.py +1 -1
- synapse/lib/stormtypes.py +35 -11
- synapse/lib/types.py +6 -6
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +6 -12
- synapse/models/base.py +13 -0
- synapse/models/biz.py +14 -0
- synapse/models/economic.py +3 -0
- synapse/models/inet.py +474 -4
- synapse/models/infotech.py +163 -22
- synapse/models/orgs.py +22 -2
- synapse/models/person.py +5 -2
- synapse/models/planning.py +5 -0
- synapse/models/risk.py +15 -1
- synapse/models/transport.py +1 -1
- synapse/tests/test_common.py +15 -0
- synapse/tests/test_lib_ast.py +2 -1
- synapse/tests/test_lib_hiveauth.py +139 -1
- synapse/tests/test_lib_layer.py +207 -44
- synapse/tests/test_lib_lmdbslab.py +13 -0
- synapse/tests/test_lib_stormlib_auth.py +22 -0
- synapse/tests/test_lib_stormlib_cell.py +47 -0
- synapse/tests/test_lib_stormlib_env.py +25 -0
- synapse/tests/test_lib_view.py +9 -9
- synapse/tests/test_model_base.py +5 -3
- synapse/tests/test_model_economic.py +4 -0
- synapse/tests/test_model_inet.py +405 -1
- synapse/tests/test_model_infotech.py +135 -3
- synapse/tests/test_model_orgs.py +14 -2
- synapse/tests/test_model_person.py +2 -0
- synapse/tests/test_model_risk.py +8 -0
- synapse/tests/test_tools_storm.py +46 -8
- synapse/tools/storm.py +14 -6
- {synapse-2.171.0.dist-info → synapse-2.173.1.dist-info}/METADATA +1 -1
- {synapse-2.171.0.dist-info → synapse-2.173.1.dist-info}/RECORD +57 -55
- {synapse-2.171.0.dist-info → synapse-2.173.1.dist-info}/WHEEL +1 -1
- {synapse-2.171.0.dist-info → synapse-2.173.1.dist-info}/LICENSE +0 -0
- {synapse-2.171.0.dist-info → synapse-2.173.1.dist-info}/top_level.txt +0 -0
synapse/common.py
CHANGED
|
@@ -1183,6 +1183,26 @@ def httpcodereason(code):
|
|
|
1183
1183
|
except ValueError:
|
|
1184
1184
|
return f'Unknown HTTP status code {code}'
|
|
1185
1185
|
|
|
1186
|
+
def trimText(text: str, n: int = 256, placeholder: str = '...') -> str:
|
|
1187
|
+
'''
|
|
1188
|
+
Trim a text string larger than n characters and add a placeholder at the end.
|
|
1189
|
+
|
|
1190
|
+
Args:
|
|
1191
|
+
text: String to trim.
|
|
1192
|
+
n: Number of characters to allow.
|
|
1193
|
+
placeholder: Placeholder text.
|
|
1194
|
+
|
|
1195
|
+
Returns:
|
|
1196
|
+
The original string or the trimmed string.
|
|
1197
|
+
'''
|
|
1198
|
+
if len(text) <= n:
|
|
1199
|
+
return text
|
|
1200
|
+
plen = len(placeholder)
|
|
1201
|
+
mlen = n - plen
|
|
1202
|
+
assert plen > 0
|
|
1203
|
+
assert n > plen
|
|
1204
|
+
return f'{text[:mlen]}{placeholder}'
|
|
1205
|
+
|
|
1186
1206
|
# TODO: Switch back to using asyncio.wait_for when we are using py 3.12+
|
|
1187
1207
|
# This is a workaround for a race where asyncio.wait_for can end up
|
|
1188
1208
|
# ignoring cancellation https://github.com/python/cpython/issues/86296
|
synapse/cortex.py
CHANGED
|
@@ -56,6 +56,7 @@ import synapse.lib.stormwhois as s_stormwhois # NOQA
|
|
|
56
56
|
import synapse.lib.stormtypes as s_stormtypes
|
|
57
57
|
|
|
58
58
|
import synapse.lib.stormlib.aha as s_stormlib_aha # NOQA
|
|
59
|
+
import synapse.lib.stormlib.env as s_stormlib_env # NOQA
|
|
59
60
|
import synapse.lib.stormlib.gen as s_stormlib_gen # NOQA
|
|
60
61
|
import synapse.lib.stormlib.gis as s_stormlib_gis # NOQA
|
|
61
62
|
import synapse.lib.stormlib.hex as s_stormlib_hex # NOQA
|
|
@@ -4689,6 +4690,13 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
4689
4690
|
|
|
4690
4691
|
return self.layers.get(iden)
|
|
4691
4692
|
|
|
4693
|
+
def reqLayer(self, iden=None):
|
|
4694
|
+
layr = self.getLayer(iden=iden)
|
|
4695
|
+
if layr is None:
|
|
4696
|
+
mesg = f'No layer found with iden: {iden}'
|
|
4697
|
+
raise s_exc.NoSuchLayer(mesg=mesg, iden=iden)
|
|
4698
|
+
return layr
|
|
4699
|
+
|
|
4692
4700
|
def listLayers(self):
|
|
4693
4701
|
return self.layers.values()
|
|
4694
4702
|
|
|
@@ -5045,10 +5053,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
5045
5053
|
await self.setNexsIndx(maxindx)
|
|
5046
5054
|
|
|
5047
5055
|
async def saveLayerNodeEdits(self, layriden, edits, meta):
|
|
5048
|
-
layr = self.
|
|
5049
|
-
if layr is None:
|
|
5050
|
-
mesg = f'No layer found with iden: {layriden}'
|
|
5051
|
-
raise s_exc.NoSuchLayer(mesg=mesg, iden=layriden)
|
|
5056
|
+
layr = self.reqLayer(layriden)
|
|
5052
5057
|
return await layr.saveNodeEdits(edits, meta)
|
|
5053
5058
|
|
|
5054
5059
|
async def cloneLayer(self, iden, ldef=None):
|
|
@@ -6912,6 +6917,83 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
6912
6917
|
self.slab.delete(name.encode(), db=self.vaultsbynamedb)
|
|
6913
6918
|
self.slab.delete(bidn, db=self.vaultsdb)
|
|
6914
6919
|
|
|
6920
|
+
def _propAllowedReason(self, user, perms, gateiden=None, default=None):
|
|
6921
|
+
'''
|
|
6922
|
+
Similar to allowed, but always prefer the default value specified by the caller.
|
|
6923
|
+
Default values are still pulled from permdefs if there is a match there; but still prefer caller default.
|
|
6924
|
+
This results in a ternary response that can be used to know if a rule had a positive/negative or no match.
|
|
6925
|
+
The matching reason metadata is also returned.
|
|
6926
|
+
'''
|
|
6927
|
+
if default is None:
|
|
6928
|
+
permdef = self.getPermDef(perms)
|
|
6929
|
+
if permdef:
|
|
6930
|
+
default = permdef.get('default', default)
|
|
6931
|
+
|
|
6932
|
+
return user.getAllowedReason(perms, gateiden=gateiden, default=default)
|
|
6933
|
+
|
|
6934
|
+
def confirmPropSet(self, user, prop, layriden):
|
|
6935
|
+
meta0 = self._propAllowedReason(user, prop.setperms[0], gateiden=layriden)
|
|
6936
|
+
|
|
6937
|
+
if meta0.isadmin:
|
|
6938
|
+
return
|
|
6939
|
+
|
|
6940
|
+
allowed0 = meta0.value
|
|
6941
|
+
|
|
6942
|
+
meta1 = self._propAllowedReason(user, prop.setperms[1], gateiden=layriden)
|
|
6943
|
+
allowed1 = meta1.value
|
|
6944
|
+
|
|
6945
|
+
if allowed0:
|
|
6946
|
+
if allowed1:
|
|
6947
|
+
return
|
|
6948
|
+
elif allowed1 is False:
|
|
6949
|
+
# This is a allow-with-precedence case.
|
|
6950
|
+
# Inspect meta to determine if the rule a0 is more specific than rule a1
|
|
6951
|
+
if len(meta0.rule) >= len(meta1.rule):
|
|
6952
|
+
return
|
|
6953
|
+
user.raisePermDeny(prop.setperms[0], gateiden=layriden)
|
|
6954
|
+
return
|
|
6955
|
+
|
|
6956
|
+
if allowed1:
|
|
6957
|
+
if allowed0 is None:
|
|
6958
|
+
return
|
|
6959
|
+
# allowed0 here is False. This is a deny-with-precedence case.
|
|
6960
|
+
# Inspect meta to determine if the rule a1 is more specific than rule a0
|
|
6961
|
+
if len(meta1.rule) > len(meta0.rule):
|
|
6962
|
+
return
|
|
6963
|
+
|
|
6964
|
+
user.raisePermDeny(prop.setperms[0], gateiden=layriden)
|
|
6965
|
+
|
|
6966
|
+
def confirmPropDel(self, user, prop, layriden):
|
|
6967
|
+
meta0 = self._propAllowedReason(user, prop.delperms[0], gateiden=layriden)
|
|
6968
|
+
|
|
6969
|
+
if meta0.isadmin:
|
|
6970
|
+
return
|
|
6971
|
+
|
|
6972
|
+
allowed0 = meta0.value
|
|
6973
|
+
meta1 = self._propAllowedReason(user, prop.delperms[1], gateiden=layriden)
|
|
6974
|
+
allowed1 = meta1.value
|
|
6975
|
+
|
|
6976
|
+
if allowed0:
|
|
6977
|
+
if allowed1:
|
|
6978
|
+
return
|
|
6979
|
+
elif allowed1 is False:
|
|
6980
|
+
# This is a allow-with-precedence case.
|
|
6981
|
+
# Inspect meta to determine if the rule a0 is more specific than rule a1
|
|
6982
|
+
if len(meta0.rule) >= len(meta1.rule):
|
|
6983
|
+
return
|
|
6984
|
+
user.raisePermDeny(prop.delperms[0], gateiden=layriden)
|
|
6985
|
+
return
|
|
6986
|
+
|
|
6987
|
+
if allowed1:
|
|
6988
|
+
if allowed0 is None:
|
|
6989
|
+
return
|
|
6990
|
+
# allowed0 here is False. This is a deny-with-precedence case.
|
|
6991
|
+
# Inspect meta to determine if the rule a1 is more specific than rule a0
|
|
6992
|
+
if len(meta1.rule) > len(meta0.rule):
|
|
6993
|
+
return
|
|
6994
|
+
|
|
6995
|
+
user.raisePermDeny(prop.delperms[0], gateiden=layriden)
|
|
6996
|
+
|
|
6915
6997
|
@contextlib.asynccontextmanager
|
|
6916
6998
|
async def getTempCortex(mods=None):
|
|
6917
6999
|
'''
|
synapse/lib/agenda.py
CHANGED
|
@@ -572,7 +572,8 @@ class Agenda(s_base.Base):
|
|
|
572
572
|
self._next_indx += 1
|
|
573
573
|
|
|
574
574
|
if iden in self.appts:
|
|
575
|
-
|
|
575
|
+
mesg = f'Cron job already exists with iden: {iden}'
|
|
576
|
+
raise s_exc.DupIden(iden=iden, mesg=mesg)
|
|
576
577
|
|
|
577
578
|
if not query:
|
|
578
579
|
raise ValueError('"query" key of cdef parameter is not present or empty')
|
|
@@ -622,20 +623,22 @@ class Agenda(s_base.Base):
|
|
|
622
623
|
if appt is not None:
|
|
623
624
|
return appt
|
|
624
625
|
|
|
625
|
-
mesg = f'No cron job with
|
|
626
|
+
mesg = f'No cron job with iden {iden}'
|
|
626
627
|
raise s_exc.NoSuchIden(iden=iden, mesg=mesg)
|
|
627
628
|
|
|
628
629
|
async def enable(self, iden):
|
|
629
630
|
appt = self.appts.get(iden)
|
|
630
631
|
if appt is None:
|
|
631
|
-
|
|
632
|
+
mesg = f'No cron job with iden: {iden}'
|
|
633
|
+
raise s_exc.NoSuchIden(iden=iden, mesg=mesg)
|
|
632
634
|
|
|
633
635
|
await self.mod(iden, appt.query)
|
|
634
636
|
|
|
635
637
|
async def disable(self, iden):
|
|
636
638
|
appt = self.appts.get(iden)
|
|
637
639
|
if appt is None:
|
|
638
|
-
|
|
640
|
+
mesg = f'No cron job with iden: {iden}'
|
|
641
|
+
raise s_exc.NoSuchIden(iden=iden, mesg=mesg)
|
|
639
642
|
|
|
640
643
|
appt.enabled = False
|
|
641
644
|
await appt.save()
|
|
@@ -646,7 +649,8 @@ class Agenda(s_base.Base):
|
|
|
646
649
|
'''
|
|
647
650
|
appt = self.appts.get(iden)
|
|
648
651
|
if appt is None:
|
|
649
|
-
|
|
652
|
+
mesg = f'No cron job with iden: {iden}'
|
|
653
|
+
raise s_exc.NoSuchIden(iden=iden, mesg=mesg)
|
|
650
654
|
|
|
651
655
|
if not query:
|
|
652
656
|
raise ValueError('empty query')
|
|
@@ -664,7 +668,8 @@ class Agenda(s_base.Base):
|
|
|
664
668
|
'''
|
|
665
669
|
appt = self.appts.get(croniden)
|
|
666
670
|
if appt is None:
|
|
667
|
-
|
|
671
|
+
mesg = f'No cron job with iden: {croniden}'
|
|
672
|
+
raise s_exc.NoSuchIden(iden=croniden, mesg=mesg)
|
|
668
673
|
|
|
669
674
|
appt.view = viewiden
|
|
670
675
|
|
|
@@ -676,7 +681,8 @@ class Agenda(s_base.Base):
|
|
|
676
681
|
'''
|
|
677
682
|
appt = self.appts.get(iden)
|
|
678
683
|
if appt is None:
|
|
679
|
-
|
|
684
|
+
mesg = f'No cron job with iden: {iden}'
|
|
685
|
+
raise s_exc.NoSuchIden(iden=iden, mesg=mesg)
|
|
680
686
|
|
|
681
687
|
try:
|
|
682
688
|
heappos = self.apptheap.index(appt)
|
synapse/lib/ast.py
CHANGED
|
@@ -733,8 +733,9 @@ class SubQuery(Oper):
|
|
|
733
733
|
retn.append(valunode.ndef[1])
|
|
734
734
|
|
|
735
735
|
if len(retn) > limit:
|
|
736
|
-
|
|
737
|
-
|
|
736
|
+
query = self.kids[0].text
|
|
737
|
+
mesg = f'Subquery used as a value yielded too many (>{limit}) nodes. {s_common.trimText(query)}'
|
|
738
|
+
raise self.addExcInfo(s_exc.BadTypeValu(mesg=mesg, text=query))
|
|
738
739
|
|
|
739
740
|
return retn
|
|
740
741
|
|
|
@@ -987,12 +988,12 @@ class ForLoop(Oper):
|
|
|
987
988
|
try:
|
|
988
989
|
numitems = len(item)
|
|
989
990
|
except TypeError:
|
|
990
|
-
mesg = f'Number of items to unpack does not match the number of variables: {repr(item)
|
|
991
|
+
mesg = f'Number of items to unpack does not match the number of variables: {s_common.trimText(repr(item))}'
|
|
991
992
|
exc = s_exc.StormVarListError(mesg=mesg, names=name)
|
|
992
993
|
raise self.kids[1].addExcInfo(exc)
|
|
993
994
|
|
|
994
995
|
if len(name) != numitems:
|
|
995
|
-
mesg = f'Number of items to unpack does not match the number of variables: {repr(item)
|
|
996
|
+
mesg = f'Number of items to unpack does not match the number of variables: {s_common.trimText(repr(item))}'
|
|
996
997
|
exc = s_exc.StormVarListError(mesg=mesg, names=name, numitems=numitems)
|
|
997
998
|
raise self.kids[1].addExcInfo(exc)
|
|
998
999
|
|
|
@@ -1053,12 +1054,12 @@ class ForLoop(Oper):
|
|
|
1053
1054
|
try:
|
|
1054
1055
|
numitems = len(item)
|
|
1055
1056
|
except TypeError:
|
|
1056
|
-
mesg = f'Number of items to unpack does not match the number of variables: {repr(item)
|
|
1057
|
+
mesg = f'Number of items to unpack does not match the number of variables: {s_common.trimText(repr(item))}'
|
|
1057
1058
|
exc = s_exc.StormVarListError(mesg=mesg, names=name)
|
|
1058
1059
|
raise self.kids[1].addExcInfo(exc)
|
|
1059
1060
|
|
|
1060
1061
|
if len(name) != numitems:
|
|
1061
|
-
mesg = f'Number of items to unpack does not match the number of variables: {repr(item)
|
|
1062
|
+
mesg = f'Number of items to unpack does not match the number of variables: {s_common.trimText(repr(item))}'
|
|
1062
1063
|
exc = s_exc.StormVarListError(mesg=mesg, names=name, numitems=numitems)
|
|
1063
1064
|
raise self.kids[1].addExcInfo(exc)
|
|
1064
1065
|
|
|
@@ -1306,7 +1307,7 @@ class VarListSetOper(Oper):
|
|
|
1306
1307
|
item = [i async for i in s_stormtypes.toiter(item)]
|
|
1307
1308
|
|
|
1308
1309
|
if len(item) < len(names):
|
|
1309
|
-
mesg = f'Attempting to assign more items than we have variables to assign to: {repr(item)
|
|
1310
|
+
mesg = f'Attempting to assign more items than we have variables to assign to: {s_common.trimText(repr(item))}'
|
|
1310
1311
|
exc = s_exc.StormVarListError(mesg=mesg, names=names, numitems=len(item))
|
|
1311
1312
|
raise self.kids[0].addExcInfo(exc)
|
|
1312
1313
|
|
|
@@ -1322,7 +1323,7 @@ class VarListSetOper(Oper):
|
|
|
1322
1323
|
item = [i async for i in s_stormtypes.toiter(item)]
|
|
1323
1324
|
|
|
1324
1325
|
if len(item) < len(names):
|
|
1325
|
-
mesg = f'Attempting to assign more items than we have variables to assign to: {repr(item)
|
|
1326
|
+
mesg = f'Attempting to assign more items than we have variables to assign to: {s_common.trimText(repr(item))}'
|
|
1326
1327
|
exc = s_exc.StormVarListError(mesg=mesg, names=names, numitems=len(item))
|
|
1327
1328
|
raise self.kids[0].addExcInfo(exc)
|
|
1328
1329
|
|
synapse/lib/cache.py
CHANGED
|
@@ -66,7 +66,7 @@ class FixedCache:
|
|
|
66
66
|
|
|
67
67
|
def get(self, key):
|
|
68
68
|
if self.iscorocall:
|
|
69
|
-
raise s_exc.BadArg('cache was initialized with coroutine. Must use aget')
|
|
69
|
+
raise s_exc.BadArg(mesg='cache was initialized with coroutine. Must use aget')
|
|
70
70
|
|
|
71
71
|
valu = self.cache.get(key, s_common.novalu)
|
|
72
72
|
if valu is not s_common.novalu:
|
|
@@ -81,7 +81,7 @@ class FixedCache:
|
|
|
81
81
|
|
|
82
82
|
async def aget(self, key):
|
|
83
83
|
if not self.iscorocall:
|
|
84
|
-
raise s_exc.BadOperArg('cache was initialized with non coroutine. Must use get')
|
|
84
|
+
raise s_exc.BadOperArg(mesg='cache was initialized with non coroutine. Must use get')
|
|
85
85
|
|
|
86
86
|
valu = self.cache.get(key, s_common.novalu)
|
|
87
87
|
if valu is not s_common.novalu:
|
synapse/lib/cell.py
CHANGED
|
@@ -4142,6 +4142,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
4142
4142
|
async with await s_spooled.Set.anit(dirn=self.dirn, cell=self) as sset:
|
|
4143
4143
|
yield sset
|
|
4144
4144
|
|
|
4145
|
+
@contextlib.asynccontextmanager
|
|
4146
|
+
async def getSpooledDict(self):
|
|
4147
|
+
async with await s_spooled.Dict.anit(dirn=self.dirn, cell=self) as sdict:
|
|
4148
|
+
yield sdict
|
|
4149
|
+
|
|
4145
4150
|
async def addSignalHandlers(self):
|
|
4146
4151
|
await s_base.Base.addSignalHandlers(self)
|
|
4147
4152
|
|
synapse/lib/coro.py
CHANGED
|
@@ -39,6 +39,18 @@ async def agen(item):
|
|
|
39
39
|
for x in item:
|
|
40
40
|
yield x
|
|
41
41
|
|
|
42
|
+
async def pause(genr, iterations=10):
|
|
43
|
+
idx = 0
|
|
44
|
+
|
|
45
|
+
async for out in agen(genr):
|
|
46
|
+
yield out
|
|
47
|
+
idx += 1
|
|
48
|
+
|
|
49
|
+
if idx % iterations == 0:
|
|
50
|
+
await asyncio.sleep(0)
|
|
51
|
+
|
|
52
|
+
return
|
|
53
|
+
|
|
42
54
|
def executor(func, *args, **kwargs):
|
|
43
55
|
'''
|
|
44
56
|
Execute a non-coroutine function in the ioloop executor pool.
|
synapse/lib/hiveauth.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import dataclasses
|
|
3
3
|
|
|
4
|
-
from typing import Union
|
|
4
|
+
from typing import Optional, Union
|
|
5
5
|
|
|
6
6
|
import synapse.exc as s_exc
|
|
7
7
|
import synapse.common as s_common
|
|
@@ -941,16 +941,38 @@ class HiveUser(HiveRuler):
|
|
|
941
941
|
if not self.allowed(perm):
|
|
942
942
|
await self.addRule((True, perm), indx=0)
|
|
943
943
|
|
|
944
|
-
def allowed(self,
|
|
944
|
+
def allowed(self,
|
|
945
|
+
perm: tuple[str, ...],
|
|
946
|
+
default: Optional[str] = None,
|
|
947
|
+
gateiden: Optional[str] = None,
|
|
948
|
+
deepdeny: bool = False) -> Union[bool, None]:
|
|
949
|
+
'''
|
|
950
|
+
Check if a user is allowed a given permission.
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
perm: The permission tuple to check.
|
|
954
|
+
default: The default rule value if there is no match.
|
|
955
|
+
gateiden: The gate iden to check against.
|
|
956
|
+
deepdeny: If True, give precedence for checking deny rules which are more specific than the requested
|
|
957
|
+
permission.
|
|
958
|
+
|
|
959
|
+
Notes:
|
|
960
|
+
The use of the deepdeny argument is intended for checking a less-specific part of a permissions tree, in
|
|
961
|
+
order to know about possible short circuit options. Using it to check a more specific part may have
|
|
962
|
+
unintended results.
|
|
963
|
+
|
|
964
|
+
Returns:
|
|
965
|
+
The allowed value of the permission.
|
|
966
|
+
'''
|
|
945
967
|
perm = tuple(perm)
|
|
946
|
-
return self.permcache.get((perm, default, gateiden))
|
|
968
|
+
return self.permcache.get((perm, default, gateiden, deepdeny))
|
|
947
969
|
|
|
948
970
|
def _allowed(self, pkey):
|
|
949
971
|
'''
|
|
950
972
|
NOTE: This must remain in sync with any changes to _getAllowedReason()!
|
|
951
973
|
'''
|
|
952
974
|
|
|
953
|
-
perm, default, gateiden = pkey
|
|
975
|
+
perm, default, gateiden, deepdeny = pkey
|
|
954
976
|
|
|
955
977
|
if self.info.get('locked'):
|
|
956
978
|
return False
|
|
@@ -958,6 +980,9 @@ class HiveUser(HiveRuler):
|
|
|
958
980
|
if self.info.get('admin'):
|
|
959
981
|
return True
|
|
960
982
|
|
|
983
|
+
if deepdeny and self._hasDeepDeny(perm, gateiden):
|
|
984
|
+
return False
|
|
985
|
+
|
|
961
986
|
# 1. check authgate user rules
|
|
962
987
|
if gateiden is not None:
|
|
963
988
|
|
|
@@ -1055,6 +1080,58 @@ class HiveUser(HiveRuler):
|
|
|
1055
1080
|
|
|
1056
1081
|
return _allowedReason(default, default=True)
|
|
1057
1082
|
|
|
1083
|
+
def _hasDeepDeny(self, perm, gateiden):
|
|
1084
|
+
|
|
1085
|
+
permlen = len(perm)
|
|
1086
|
+
|
|
1087
|
+
# 1. check authgate user rules
|
|
1088
|
+
if gateiden is not None:
|
|
1089
|
+
|
|
1090
|
+
info = self.authgates.get(gateiden)
|
|
1091
|
+
if info is not None:
|
|
1092
|
+
|
|
1093
|
+
if info.get('admin'):
|
|
1094
|
+
return False
|
|
1095
|
+
|
|
1096
|
+
for allow, path in info.get('rules', ()):
|
|
1097
|
+
if allow:
|
|
1098
|
+
continue
|
|
1099
|
+
if path[:permlen] == perm and len(path) > permlen:
|
|
1100
|
+
return True
|
|
1101
|
+
|
|
1102
|
+
# 2. check user rules
|
|
1103
|
+
for allow, path in self.info.get('rules', ()):
|
|
1104
|
+
if allow:
|
|
1105
|
+
continue
|
|
1106
|
+
|
|
1107
|
+
if path[:permlen] == perm and len(path) > permlen:
|
|
1108
|
+
return True
|
|
1109
|
+
|
|
1110
|
+
# 3. check authgate role rules
|
|
1111
|
+
if gateiden is not None:
|
|
1112
|
+
|
|
1113
|
+
for role in self.getRoles():
|
|
1114
|
+
|
|
1115
|
+
info = role.authgates.get(gateiden)
|
|
1116
|
+
if info is None:
|
|
1117
|
+
continue
|
|
1118
|
+
|
|
1119
|
+
for allow, path in info.get('rules', ()):
|
|
1120
|
+
if allow:
|
|
1121
|
+
continue
|
|
1122
|
+
if path[:permlen] == perm and len(path) > permlen:
|
|
1123
|
+
return True
|
|
1124
|
+
|
|
1125
|
+
# 4. check role rules
|
|
1126
|
+
for role in self.getRoles():
|
|
1127
|
+
for allow, path in role.info.get('rules', ()):
|
|
1128
|
+
if allow:
|
|
1129
|
+
continue
|
|
1130
|
+
if path[:permlen] == perm and len(path) > permlen:
|
|
1131
|
+
return True
|
|
1132
|
+
|
|
1133
|
+
return False
|
|
1134
|
+
|
|
1058
1135
|
def clearAuthCache(self):
|
|
1059
1136
|
self.permcache.clear()
|
|
1060
1137
|
self.allowedcache.clear()
|