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.
- synapse/axon.py +19 -16
- synapse/cortex.py +203 -15
- synapse/exc.py +0 -2
- synapse/lib/ast.py +42 -23
- synapse/lib/autodoc.py +2 -2
- synapse/lib/cache.py +16 -1
- synapse/lib/cell.py +5 -5
- synapse/lib/httpapi.py +198 -2
- synapse/lib/layer.py +5 -2
- synapse/lib/modelrev.py +36 -3
- synapse/lib/node.py +2 -5
- synapse/lib/parser.py +1 -1
- synapse/lib/schemas.py +51 -0
- synapse/lib/snap.py +10 -0
- synapse/lib/storm.lark +24 -4
- synapse/lib/storm.py +98 -19
- synapse/lib/storm_format.py +1 -1
- synapse/lib/stormhttp.py +11 -4
- synapse/lib/stormlib/auth.py +16 -2
- synapse/lib/stormlib/backup.py +1 -0
- synapse/lib/stormlib/basex.py +2 -0
- synapse/lib/stormlib/cell.py +7 -0
- synapse/lib/stormlib/compression.py +3 -0
- synapse/lib/stormlib/cortex.py +1168 -0
- synapse/lib/stormlib/ethereum.py +1 -0
- synapse/lib/stormlib/graph.py +2 -0
- synapse/lib/stormlib/hashes.py +5 -0
- synapse/lib/stormlib/hex.py +6 -0
- synapse/lib/stormlib/infosec.py +6 -1
- synapse/lib/stormlib/ipv6.py +1 -0
- synapse/lib/stormlib/iters.py +58 -1
- synapse/lib/stormlib/json.py +5 -0
- synapse/lib/stormlib/mime.py +1 -0
- synapse/lib/stormlib/model.py +19 -3
- synapse/lib/stormlib/modelext.py +1 -0
- synapse/lib/stormlib/notifications.py +2 -0
- synapse/lib/stormlib/pack.py +2 -0
- synapse/lib/stormlib/random.py +1 -0
- synapse/lib/stormlib/smtp.py +0 -7
- synapse/lib/stormlib/stats.py +223 -0
- synapse/lib/stormlib/stix.py +8 -0
- synapse/lib/stormlib/storm.py +1 -0
- synapse/lib/stormlib/version.py +3 -0
- synapse/lib/stormlib/xml.py +3 -0
- synapse/lib/stormlib/yaml.py +2 -0
- synapse/lib/stormtypes.py +250 -170
- synapse/lib/trigger.py +180 -4
- synapse/lib/types.py +1 -1
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +55 -6
- synapse/models/inet.py +21 -6
- synapse/models/orgs.py +48 -2
- synapse/models/risk.py +126 -2
- synapse/models/syn.py +6 -0
- synapse/tests/files/stormpkg/badapidef.yaml +13 -0
- synapse/tests/files/stormpkg/storm/modules/apimod +10 -0
- synapse/tests/files/stormpkg/testpkg.yaml +23 -0
- synapse/tests/test_axon.py +7 -2
- synapse/tests/test_cortex.py +231 -35
- synapse/tests/test_lib_ast.py +138 -43
- synapse/tests/test_lib_autodoc.py +1 -1
- synapse/tests/test_lib_modelrev.py +9 -0
- synapse/tests/test_lib_node.py +55 -0
- synapse/tests/test_lib_storm.py +14 -1
- synapse/tests/test_lib_stormhttp.py +65 -6
- synapse/tests/test_lib_stormlib_auth.py +12 -3
- synapse/tests/test_lib_stormlib_cortex.py +1327 -0
- synapse/tests/test_lib_stormlib_iters.py +116 -0
- synapse/tests/test_lib_stormlib_stats.py +187 -0
- synapse/tests/test_lib_stormlib_storm.py +8 -0
- synapse/tests/test_lib_stormsvc.py +24 -1
- synapse/tests/test_lib_stormtypes.py +124 -69
- synapse/tests/test_lib_trigger.py +315 -0
- synapse/tests/test_lib_view.py +1 -2
- synapse/tests/test_model_base.py +26 -0
- synapse/tests/test_model_inet.py +22 -0
- synapse/tests/test_model_orgs.py +28 -0
- synapse/tests/test_model_risk.py +73 -0
- synapse/tests/test_tools_autodoc.py +25 -0
- synapse/tests/test_tools_genpkg.py +9 -3
- synapse/tests/utils.py +39 -0
- synapse/tools/autodoc.py +42 -2
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/METADATA +2 -2
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/RECORD +87 -79
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/WHEEL +1 -1
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/LICENSE +0 -0
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1168 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import synapse.exc as s_exc
|
|
6
|
+
|
|
7
|
+
import synapse.lib.stormtypes as s_stormtypes
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
stormcmds = [
|
|
13
|
+
{
|
|
14
|
+
'name': 'cortex.httpapi.list',
|
|
15
|
+
'descr': 'List Extended HTTP API endpoints',
|
|
16
|
+
'cmdargs': (),
|
|
17
|
+
'storm': '''
|
|
18
|
+
$apis = $lib.cortex.httpapi.list()
|
|
19
|
+
if $apis {
|
|
20
|
+
$header = 'order iden owner auth runas path'
|
|
21
|
+
$lib.print($header)
|
|
22
|
+
for ($n, $api) in $lib.iters.enum($apis) {
|
|
23
|
+
try {
|
|
24
|
+
$user = $api.owner.name
|
|
25
|
+
} catch NoSuchUser as err {
|
|
26
|
+
$user = `No user found ({$err.info.user})`
|
|
27
|
+
}
|
|
28
|
+
$auth = `{$api.authenticated}`
|
|
29
|
+
$order = `{$n}`
|
|
30
|
+
$mesg=`{$order.ljust(5)} {$api.iden} {$user.ljust(20)} {$auth.ljust(5)} {$api.runas.ljust(6)} {$api.path}`
|
|
31
|
+
$lib.print($mesg)
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
$lib.print('No Extended HTTP API endpoints are registered.')
|
|
35
|
+
}
|
|
36
|
+
'''
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
'name': 'cortex.httpapi.stat',
|
|
40
|
+
'descr': 'Get details for an Extended HTTP API endpoint.',
|
|
41
|
+
'cmdargs': (
|
|
42
|
+
('iden', {'help': 'The iden of the endpoint to inspect. This will also match iden prefixes or name prefixes.',
|
|
43
|
+
'type': 'str'}),
|
|
44
|
+
),
|
|
45
|
+
'storm': '''
|
|
46
|
+
$iden = $lib.null
|
|
47
|
+
for $api in $lib.cortex.httpapi.list() {
|
|
48
|
+
if $api.iden.startswith($cmdopts.iden) {
|
|
49
|
+
if $iden {
|
|
50
|
+
$lib.raise(StormRuntimeError, 'Already matched one Extended HTTP API!')
|
|
51
|
+
}
|
|
52
|
+
$iden = $api.iden
|
|
53
|
+
}
|
|
54
|
+
if $api.name.startswith($cmdopts.iden) {
|
|
55
|
+
if $iden {
|
|
56
|
+
$lib.raise(StormRuntimeError, 'Already matched one Extended HTTP API!')
|
|
57
|
+
}
|
|
58
|
+
$iden = $api.iden
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (not $iden) {
|
|
62
|
+
$lib.raise(StormRuntimeError, 'Failed to match Extended HTTP API by iden or name!')
|
|
63
|
+
}
|
|
64
|
+
$time = $lib.model.type(time)
|
|
65
|
+
$api = $lib.cortex.httpapi.get($iden)
|
|
66
|
+
$lib.print(`Iden: {$api.iden}`)
|
|
67
|
+
try {
|
|
68
|
+
$lib.print(`Creator: {$api.creator.name} ({$api.creator.iden})`)
|
|
69
|
+
} catch NoSuchUser as err {
|
|
70
|
+
$lib.print(`!Creator: No user found ({$err.info.user})`)
|
|
71
|
+
}
|
|
72
|
+
$lib.print(`Created: {$time.repr($api.created)}`)
|
|
73
|
+
$lib.print(`Updated: {$time.repr($api.updated)}`)
|
|
74
|
+
$lib.print(`Path: {$api.path}`)
|
|
75
|
+
try {
|
|
76
|
+
$lib.print(`Owner: {$api.owner.name} ({$api.owner.iden})`)
|
|
77
|
+
} catch NoSuchUser as err {
|
|
78
|
+
$lib.print(`!Owner: No user found ({$err.info.user})`)
|
|
79
|
+
}
|
|
80
|
+
$lib.print(`Runas: {$api.runas}`)
|
|
81
|
+
try {
|
|
82
|
+
$lib.print(`View: {$api.view.get(name)} ({$api.view.iden})`)
|
|
83
|
+
} catch NoSuchView as err {
|
|
84
|
+
$lib.print(`!View: No view found ({$err.info.iden})`)
|
|
85
|
+
}
|
|
86
|
+
$lib.print(`Readonly: {$api.readonly}`)
|
|
87
|
+
$lib.print(`Authenticated: {$api.authenticated}`)
|
|
88
|
+
$lib.print(`Name: {$api.name}`)
|
|
89
|
+
$lib.print(`Description: {$api.desc}`)
|
|
90
|
+
$lib.print('')
|
|
91
|
+
$perms = $api.perms
|
|
92
|
+
if $perms {
|
|
93
|
+
$lib.print('The following user permissions are required to run this HTTP API endpoint:')
|
|
94
|
+
for $pdef in $perms {
|
|
95
|
+
$perm = $lib.str.join(".", $pdef.perm)
|
|
96
|
+
$lib.print($perm)
|
|
97
|
+
$lib.print(` default: {$pdef.default}`)
|
|
98
|
+
}
|
|
99
|
+
$lib.print('')
|
|
100
|
+
} else {
|
|
101
|
+
$lib.print('No user permissions are required to run this HTTP API endpoint.')
|
|
102
|
+
}
|
|
103
|
+
$methods = $api.methods
|
|
104
|
+
if $methods {
|
|
105
|
+
$lib.print('The handler defines the following HTTP methods:')
|
|
106
|
+
for ($meth, $storm) in $methods {
|
|
107
|
+
$lib.print(`Method: {$meth.upper()}`)
|
|
108
|
+
$lib.print($storm)
|
|
109
|
+
$lib.print('')
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
$lib.print('No HTTP Methods are set for the handler.')
|
|
113
|
+
}
|
|
114
|
+
$vars = $api.vars
|
|
115
|
+
if $vars {
|
|
116
|
+
$lib.print('The handler has the following runtime variables set:')
|
|
117
|
+
for ($key, $valu) in $vars {
|
|
118
|
+
$lib.print(`{$key.ljust(16)} => {$valu}`)
|
|
119
|
+
}
|
|
120
|
+
$lib.print('')
|
|
121
|
+
} else {
|
|
122
|
+
$lib.print('No vars are set for the handler.')
|
|
123
|
+
}
|
|
124
|
+
'''
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
'name': 'cortex.httpapi.index',
|
|
128
|
+
'descr': '''Set the index of an Extended HTTP API endpoint.
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
|
|
132
|
+
// Move an endpoint to the first index.
|
|
133
|
+
cortex.httpapi.index 60e5ba38e90958fd8e2ddd9e4730f16b 0
|
|
134
|
+
|
|
135
|
+
// Move an endpoint to the third index.
|
|
136
|
+
cortex.httpapi.index dd9e4730f16b60e5ba58fd8e2d38e909 2
|
|
137
|
+
''',
|
|
138
|
+
'cmdargs': (
|
|
139
|
+
('iden', {'help': 'The iden of the endpoint to move. This will also match iden prefixes or name prefixes.',
|
|
140
|
+
'type': 'str'}),
|
|
141
|
+
('index', {'help': 'Specify the endpoint location as a 0 based index.', 'type': 'int'}),
|
|
142
|
+
),
|
|
143
|
+
'storm': '''
|
|
144
|
+
$iden = $lib.null
|
|
145
|
+
for $api in $lib.cortex.httpapi.list() {
|
|
146
|
+
if $api.iden.startswith($cmdopts.iden) {
|
|
147
|
+
if $iden {
|
|
148
|
+
$lib.raise(StormRuntimeError, 'Already matched one Extended HTTP API!')
|
|
149
|
+
}
|
|
150
|
+
$iden = $api.iden
|
|
151
|
+
}
|
|
152
|
+
if $api.name.startswith($cmdopts.iden) {
|
|
153
|
+
if $iden {
|
|
154
|
+
$lib.raise(StormRuntimeError, 'Already matched one Extended HTTP API!')
|
|
155
|
+
}
|
|
156
|
+
$iden = $api.iden
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (not $iden) {
|
|
160
|
+
$lib.raise(StormRuntimeError, 'Failed to match Extended HTTP API by iden or name!')
|
|
161
|
+
}
|
|
162
|
+
$api = $lib.cortex.httpapi.get($iden)
|
|
163
|
+
$index = $lib.cortex.httpapi.index($api.iden, $cmdopts.index)
|
|
164
|
+
$lib.print(`Set HTTP API {$api.iden} to index {$index}`)
|
|
165
|
+
'''
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
def _normPermString(perm):
|
|
170
|
+
if perm.startswith('!'):
|
|
171
|
+
raise s_exc.BadArg(mesg=f'Permission assignment must not start with a !, got {perm}')
|
|
172
|
+
parts = perm.split('.')
|
|
173
|
+
pdef = {'perm': parts, 'default': False}
|
|
174
|
+
return pdef
|
|
175
|
+
|
|
176
|
+
@s_stormtypes.registry.registerType
|
|
177
|
+
class HttpApi(s_stormtypes.StormType):
|
|
178
|
+
'''
|
|
179
|
+
Extended HTTP API object.
|
|
180
|
+
|
|
181
|
+
This object represents an extended HTTP API that has been configured on the Cortex.
|
|
182
|
+
'''
|
|
183
|
+
_storm_typename = 'http:api'
|
|
184
|
+
_storm_locals = (
|
|
185
|
+
{'name': 'iden', 'desc': 'The iden of the Extended HTTP API.', 'type': 'str'},
|
|
186
|
+
{'name': 'created', 'desc': 'The time the Extended HTTP API was created.', 'type': 'int'},
|
|
187
|
+
{'name': 'updated', 'desc': 'The time the Extended HTTP API was last modified.',
|
|
188
|
+
'type': {'type': 'gtor', '_gtorfunc': '_gtorUpdated', 'returns': {'type': 'int'}}},
|
|
189
|
+
{'name': 'creator', 'desc': 'The user that created the Extended HTTP API.',
|
|
190
|
+
'type': {'type': 'gtor', '_gtorfunc': '_gtorCreator', 'returns': {'type': 'auth:user'}}},
|
|
191
|
+
{'name': 'owner', 'desc': 'The user that runs the endpoint query logic when runas="owner".',
|
|
192
|
+
'type': {'type': ['gtor', 'stor'], '_gtorfunc': '_gtorOwner', '_storfunc': '_storOwner',
|
|
193
|
+
'returns': {'type': 'auth:user'}}},
|
|
194
|
+
{'name': 'pack', 'desc': 'Get a packed copy of the HTTP API object.',
|
|
195
|
+
'type': {'type': 'function', '_funcname': '_methPack', 'args': (),
|
|
196
|
+
'returns': {'type': 'dict'}}},
|
|
197
|
+
{'name': 'name', 'desc': 'The name of the API instance.',
|
|
198
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storName', '_gtorfunc': '_gtorName',
|
|
199
|
+
'returns': {'type': 'str'}}},
|
|
200
|
+
{'name': 'desc', 'desc': 'The description of the API instance.',
|
|
201
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storDesc', '_gtorfunc': '_gtorDesc',
|
|
202
|
+
'returns': {'type': 'str'}}},
|
|
203
|
+
{'name': 'path', 'desc': '''
|
|
204
|
+
The path of the API instance.
|
|
205
|
+
|
|
206
|
+
This path may contain regular expression capture groups, which are used to populate
|
|
207
|
+
request arguments.
|
|
208
|
+
|
|
209
|
+
Note:
|
|
210
|
+
The Cortex does not inspect paths in order to identify duplicates or overlapping paths.
|
|
211
|
+
It is the responsibility of the Cortex administrator to configure their Extended HTTP API
|
|
212
|
+
paths so that they are correct for their use cases.
|
|
213
|
+
|
|
214
|
+
Example:
|
|
215
|
+
Update an API path to contain a single wildcard argument::
|
|
216
|
+
|
|
217
|
+
$api.path = 'foo/bar/(.*)/baz'
|
|
218
|
+
|
|
219
|
+
Update an API path to contain a two wildcard arguments with restricted character sets::
|
|
220
|
+
|
|
221
|
+
$api.path = 'hehe/([a-z]*)/([0-9]{1-4})'
|
|
222
|
+
''',
|
|
223
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storPath', '_gtorfunc': '_gtorPath',
|
|
224
|
+
'returns': {'type': 'str'}}},
|
|
225
|
+
{'name': 'vars', 'desc': 'The Storm runtime variables specific for the API instance.',
|
|
226
|
+
'type': {'type': ['stor', 'ctor'], '_storfunc': '_storVars', '_ctorfunc': '_ctorVars',
|
|
227
|
+
'returns': {'type': 'http:api:vars'}}},
|
|
228
|
+
{'name': 'view', 'desc': 'The View of the API instance. This is the view that Storm methods are executed in.',
|
|
229
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storView', '_gtorfunc': '_gtorView',
|
|
230
|
+
'returns': {'type': 'view'}}},
|
|
231
|
+
{'name': 'runas', 'desc': 'String indicating whether the requests run as the owner or the authenticated user.',
|
|
232
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storRunas', '_gtorfunc': '_gtorRunas',
|
|
233
|
+
'returns': {'type': 'str'}}},
|
|
234
|
+
{'name': 'perms', 'desc': 'The permissions an authenticated user must have in order to access the HTTP API.',
|
|
235
|
+
'type': {'type': ['stor', 'ctor'], '_storfunc': '_storPerms', '_ctorfunc': '_ctorPerms',
|
|
236
|
+
'returns': {'type': 'http:api:perms'}}},
|
|
237
|
+
{'name': 'readonly', 'desc': 'Boolean value indicating if the Storm methods are executed in a readonly Storm runtime.',
|
|
238
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storReadonly', '_gtorfunc': '_gtorReadonly',
|
|
239
|
+
'returns': {'type': 'boolean'}}},
|
|
240
|
+
{'name': 'authenticated', 'desc': 'Boolean value indicating if the Extended HTTP API requires an authenticated user or session.',
|
|
241
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storAuthenticated', '_gtorfunc': '_gtorAuthenticated',
|
|
242
|
+
'returns': {'type': 'boolean'}}},
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def __init__(self, runt, info):
|
|
246
|
+
s_stormtypes.StormType.__init__(self)
|
|
247
|
+
self.runt = runt
|
|
248
|
+
self.info = info
|
|
249
|
+
self.iden = self.info.get('iden')
|
|
250
|
+
# Perms comes in as a tuple - convert it to a list to we can have a mutable object
|
|
251
|
+
self.info['perms'] = list(self.info.get('perms'))
|
|
252
|
+
|
|
253
|
+
self.stors.update({
|
|
254
|
+
# General helpers
|
|
255
|
+
'name': self._storName,
|
|
256
|
+
'desc': self._storDesc,
|
|
257
|
+
'path': self._storPath,
|
|
258
|
+
'vars': self._storVars,
|
|
259
|
+
'view': self._storView,
|
|
260
|
+
'runas': self._storRunas,
|
|
261
|
+
'owner': self._storOwner,
|
|
262
|
+
'perms': self._storPerms,
|
|
263
|
+
'readonly': self._storReadonly,
|
|
264
|
+
'authenticated': self._storAuthenticated,
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
self.gtors.update({
|
|
268
|
+
'name': self._gtorName,
|
|
269
|
+
'desc': self._gtorDesc,
|
|
270
|
+
'path': self._gtorPath,
|
|
271
|
+
'view': self._gtorView,
|
|
272
|
+
'runas': self._gtorRunas,
|
|
273
|
+
'owner': self._gtorOwner,
|
|
274
|
+
'creator': self._gtorCreator,
|
|
275
|
+
'updated': self._gtorUpdated,
|
|
276
|
+
'readonly': self._gtorReadonly,
|
|
277
|
+
'authenticated': self._gtorAuthenticated,
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
self.ctors.update({
|
|
281
|
+
'vars': self._ctorVars,
|
|
282
|
+
'perms': self._ctorPerms,
|
|
283
|
+
'methods': self._ctorMethods
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
# constants
|
|
287
|
+
self.locls.update(self.getObjLocals())
|
|
288
|
+
self.locls.update({
|
|
289
|
+
'iden': self.iden,
|
|
290
|
+
'created': self.info.get('created'),
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
def getObjLocals(self):
|
|
294
|
+
return {
|
|
295
|
+
'pack': self._methPack,
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
299
|
+
async def _methPack(self):
|
|
300
|
+
return copy.deepcopy(self.info)
|
|
301
|
+
|
|
302
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
303
|
+
def _ctorMethods(self, path=None):
|
|
304
|
+
return HttpApiMethods(self)
|
|
305
|
+
|
|
306
|
+
async def _storPath(self, path):
|
|
307
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
308
|
+
path = await s_stormtypes.tostr(path)
|
|
309
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'path', path)
|
|
310
|
+
self.info['path'] = path
|
|
311
|
+
self.info['updated'] = adef.get('updated')
|
|
312
|
+
|
|
313
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
314
|
+
async def _gtorPath(self):
|
|
315
|
+
return self.info.get('path')
|
|
316
|
+
|
|
317
|
+
async def _storName(self, name):
|
|
318
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
319
|
+
name = await s_stormtypes.tostr(name)
|
|
320
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'name', name)
|
|
321
|
+
self.info['name'] = name
|
|
322
|
+
self.info['updated'] = adef.get('updated')
|
|
323
|
+
|
|
324
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
325
|
+
async def _gtorView(self):
|
|
326
|
+
iden = self.info.get('view')
|
|
327
|
+
vdef = await self.runt.snap.core.getViewDef(iden)
|
|
328
|
+
if vdef is None:
|
|
329
|
+
raise s_exc.NoSuchView(mesg=f'No view with {iden=}', iden=iden)
|
|
330
|
+
return s_stormtypes.View(self.runt, vdef, path=self.path)
|
|
331
|
+
|
|
332
|
+
async def _storVars(self, varz):
|
|
333
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
334
|
+
varz = await s_stormtypes.toprim(varz)
|
|
335
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'vars', varz)
|
|
336
|
+
_varz = self.info.get('vars')
|
|
337
|
+
_varz.clear()
|
|
338
|
+
_varz.update(**adef.get('vars'))
|
|
339
|
+
self.info['updated'] = adef.get('updated')
|
|
340
|
+
|
|
341
|
+
async def _storView(self, iden):
|
|
342
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
343
|
+
if isinstance(iden, s_stormtypes.View):
|
|
344
|
+
view = iden.value().get('iden')
|
|
345
|
+
else:
|
|
346
|
+
view = await s_stormtypes.tostr(iden)
|
|
347
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'view', view)
|
|
348
|
+
self.info['view'] = view
|
|
349
|
+
self.info['updated'] = adef.get('updated')
|
|
350
|
+
|
|
351
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
352
|
+
async def _gtorName(self):
|
|
353
|
+
return self.info.get('name')
|
|
354
|
+
|
|
355
|
+
async def _storDesc(self, desc):
|
|
356
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
357
|
+
desc = await s_stormtypes.tostr(desc)
|
|
358
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'desc', desc)
|
|
359
|
+
self.info['desc'] = desc
|
|
360
|
+
self.info['updated'] = adef.get('updated')
|
|
361
|
+
|
|
362
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
363
|
+
async def _gtorDesc(self):
|
|
364
|
+
return self.info.get('desc')
|
|
365
|
+
|
|
366
|
+
async def _storRunas(self, runas):
|
|
367
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
368
|
+
runas = await s_stormtypes.tostr(runas)
|
|
369
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'runas', runas)
|
|
370
|
+
self.info['runas'] = runas
|
|
371
|
+
self.info['updated'] = adef.get('updated')
|
|
372
|
+
|
|
373
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
374
|
+
async def _gtorRunas(self):
|
|
375
|
+
return self.info.get('runas')
|
|
376
|
+
|
|
377
|
+
async def _storReadonly(self, readonly):
|
|
378
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
379
|
+
readonly = await s_stormtypes.tobool(readonly)
|
|
380
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'readonly', readonly)
|
|
381
|
+
self.info['readonly'] = readonly
|
|
382
|
+
self.info['updated'] = adef.get('updated')
|
|
383
|
+
|
|
384
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
385
|
+
def _ctorVars(self, path=None):
|
|
386
|
+
return HttpApiVars(self, path=path)
|
|
387
|
+
|
|
388
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
389
|
+
async def _gtorReadonly(self):
|
|
390
|
+
return self.info.get('readonly')
|
|
391
|
+
|
|
392
|
+
async def _storOwner(self, owner):
|
|
393
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
394
|
+
if isinstance(owner, s_stormtypes.User):
|
|
395
|
+
info = await owner.value()
|
|
396
|
+
owner = info.get('iden')
|
|
397
|
+
else:
|
|
398
|
+
owner = await s_stormtypes.tostr(owner)
|
|
399
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'owner', owner)
|
|
400
|
+
self.info['owner'] = owner
|
|
401
|
+
self.info['updated'] = adef.get('updated')
|
|
402
|
+
|
|
403
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
404
|
+
async def _gtorOwner(self):
|
|
405
|
+
iden = self.info.get('owner')
|
|
406
|
+
udef = await self.runt.snap.core.getUserDef(iden)
|
|
407
|
+
if udef is None:
|
|
408
|
+
raise s_exc.NoSuchUser(mesg=f'HTTP API owner does not exist {iden}', user=iden)
|
|
409
|
+
return s_stormtypes.User(self.runt, udef['iden'])
|
|
410
|
+
|
|
411
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
412
|
+
async def _gtorCreator(self):
|
|
413
|
+
iden = self.info.get('creator')
|
|
414
|
+
udef = await self.runt.snap.core.getUserDef(iden)
|
|
415
|
+
if udef is None:
|
|
416
|
+
raise s_exc.NoSuchUser(mesg=f'HTTP API creator does not exist {iden}', user=iden)
|
|
417
|
+
return s_stormtypes.User(self.runt, udef['iden'])
|
|
418
|
+
|
|
419
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
420
|
+
async def _gtorUpdated(self):
|
|
421
|
+
return self.info.get('updated')
|
|
422
|
+
|
|
423
|
+
async def _storPerms(self, perms):
|
|
424
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
425
|
+
perms = await s_stormtypes.toprim(perms)
|
|
426
|
+
pdefs = []
|
|
427
|
+
for pdef in perms:
|
|
428
|
+
if isinstance(pdef, str):
|
|
429
|
+
pdef = _normPermString(pdef)
|
|
430
|
+
pdefs.append(pdef)
|
|
431
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'perms', pdefs)
|
|
432
|
+
self.info['perms'].clear()
|
|
433
|
+
self.info['perms'].extend(pdefs)
|
|
434
|
+
self.info['updated'] = adef.get('updated')
|
|
435
|
+
|
|
436
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
437
|
+
def _ctorPerms(self, path):
|
|
438
|
+
return HttpPermsList(self, path)
|
|
439
|
+
|
|
440
|
+
async def _storAuthenticated(self, authenticated):
|
|
441
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
442
|
+
authenticated = await s_stormtypes.tobool(authenticated)
|
|
443
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'authenticated', authenticated)
|
|
444
|
+
self.info['authenticated'] = authenticated
|
|
445
|
+
self.info['updated'] = adef.get('updated')
|
|
446
|
+
|
|
447
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
448
|
+
async def _gtorAuthenticated(self):
|
|
449
|
+
return self.info.get('authenticated')
|
|
450
|
+
|
|
451
|
+
@s_stormtypes.registry.registerType
|
|
452
|
+
class HttpApiMethods(s_stormtypes.Prim):
|
|
453
|
+
'''
|
|
454
|
+
Accessor dictionary for getting and setting Extened HTTP API methods.
|
|
455
|
+
|
|
456
|
+
Notes:
|
|
457
|
+
The Storm code used to run these methods will have a $request object
|
|
458
|
+
injected into them. This allows the method to send data back to the
|
|
459
|
+
caller when it is run.
|
|
460
|
+
|
|
461
|
+
Examples:
|
|
462
|
+
Setting a simple GET method::
|
|
463
|
+
|
|
464
|
+
$api.methods.get = ${
|
|
465
|
+
$data = ({"someKey": "someValue})
|
|
466
|
+
$headers = ({"someHeader": "someOtherValue"})
|
|
467
|
+
$request.reply(200, headers=$headers, body=$data)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
Removing a PUT method::
|
|
471
|
+
|
|
472
|
+
$api.methods.put = $lib.undef
|
|
473
|
+
|
|
474
|
+
Crafting a custom text response::
|
|
475
|
+
|
|
476
|
+
$api.methods.get = ${
|
|
477
|
+
// Create the body
|
|
478
|
+
$data = 'some value'
|
|
479
|
+
// Encode the response as bytes
|
|
480
|
+
$data = $data.encode()
|
|
481
|
+
// Set the headers
|
|
482
|
+
$headers = ({"Content-Type": "text/plain", "Content-Length": $lib.len($data})
|
|
483
|
+
$request.reply(200, headers=$headers, body=$data)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
Streaming multiple chunks of data as JSON lines. This sends the code, headers and body separately::
|
|
487
|
+
|
|
488
|
+
$api.methods.get = ${
|
|
489
|
+
$request.sendcode(200)
|
|
490
|
+
$request.sendheaders(({"Content-Type": "text/plain; charset=utf8"}))
|
|
491
|
+
$values = ((1), (2), (3))
|
|
492
|
+
for $i in $values {
|
|
493
|
+
$body=`{$lib.json.save(({"value": $i}))}\n`
|
|
494
|
+
$request.sendbody($body.encode())
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
'''
|
|
499
|
+
_storm_typename = 'http:api:methods'
|
|
500
|
+
_storm_locals = (
|
|
501
|
+
{'name': 'get', 'desc': 'The GET request Storm code.',
|
|
502
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethGet', '_gtorfunc': '_gtorMethGet',
|
|
503
|
+
'returns': {'type': ['str', 'null']}}},
|
|
504
|
+
{'name': 'put', 'desc': 'The PUT request Storm code.',
|
|
505
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethPut', '_gtorfunc': '_gtorMethPut',
|
|
506
|
+
'returns': {'type': ['str', 'null']}}},
|
|
507
|
+
{'name': 'head', 'desc': 'The HEAD request Storm code',
|
|
508
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethHead', '_gtorfunc': '_gtorMethHead',
|
|
509
|
+
'returns': {'type': ['str', 'null']}}},
|
|
510
|
+
{'name': 'post', 'desc': 'The POST request Storm code.',
|
|
511
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethPost', '_gtorfunc': '_gtorMethPost',
|
|
512
|
+
'returns': {'type': ['str', 'null']}}},
|
|
513
|
+
{'name': 'delete', 'desc': 'The DELETE request Storm code.',
|
|
514
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethDelete', '_gtorfunc': '_gtorMethDelete',
|
|
515
|
+
'returns': {'type': ['str', 'null']}}},
|
|
516
|
+
{'name': 'patch', 'desc': 'The PATCH request Storm code.',
|
|
517
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethPatch', '_gtorfunc': '_gtorMethPatch',
|
|
518
|
+
'returns': {'type': ['str', 'null']}}},
|
|
519
|
+
{'name': 'options', 'desc': 'The OPTIONS request Storm code.',
|
|
520
|
+
'type': {'type': ['gtor', 'stor'], '_storfunc': '_storMethOptions', '_gtorfunc': '_gtorMethOptions',
|
|
521
|
+
'returns': {'type': ['str', 'null']}}},
|
|
522
|
+
)
|
|
523
|
+
_ismutable = True
|
|
524
|
+
|
|
525
|
+
def __init__(self, httpapi: HttpApi):
|
|
526
|
+
s_stormtypes.Prim.__init__(self, httpapi.info.get('methods'))
|
|
527
|
+
self.httpapi = httpapi
|
|
528
|
+
|
|
529
|
+
self.gtors.update({
|
|
530
|
+
'get': self._gtorMethGet,
|
|
531
|
+
'head': self._gtorMethHead,
|
|
532
|
+
'post': self._gtorMethPost,
|
|
533
|
+
'put': self._gtorMethPut,
|
|
534
|
+
'delete': self._gtorMethDelete,
|
|
535
|
+
'patch': self._gtorMethPatch,
|
|
536
|
+
'options': self._gtorMethOptions,
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
self.stors.update({
|
|
540
|
+
'get': self._storMethGet,
|
|
541
|
+
'head': self._storMethHead,
|
|
542
|
+
'post': self._storMethPost,
|
|
543
|
+
'put': self._storMethPut,
|
|
544
|
+
'delete': self._storMethDelete,
|
|
545
|
+
'patch': self._storMethPatch,
|
|
546
|
+
'options': self._storMethOptions,
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
async def iter(self):
|
|
550
|
+
for k, v in list(self.valu.items()):
|
|
551
|
+
yield (k, v)
|
|
552
|
+
|
|
553
|
+
async def _storMethFunc(self, meth, query):
|
|
554
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
555
|
+
meth = await s_stormtypes.tostr(meth)
|
|
556
|
+
methods = self.valu.copy()
|
|
557
|
+
|
|
558
|
+
if query is s_stormtypes.undef:
|
|
559
|
+
methods.pop(meth, None)
|
|
560
|
+
adef = await self.httpapi.runt.snap.core.modHttpExtApi(self.httpapi.iden, 'methods', methods)
|
|
561
|
+
self.valu.pop(meth, None)
|
|
562
|
+
self.httpapi.info['updated'] = adef.get('updated')
|
|
563
|
+
else:
|
|
564
|
+
query = await s_stormtypes.tostr(query)
|
|
565
|
+
query = query.strip()
|
|
566
|
+
|
|
567
|
+
# Ensure our query can be parsed.
|
|
568
|
+
await self.httpapi.runt.snap.core.getStormQuery(query)
|
|
569
|
+
|
|
570
|
+
methods[meth] = query
|
|
571
|
+
adef = await self.httpapi.runt.snap.core.modHttpExtApi(self.httpapi.iden, 'methods', methods)
|
|
572
|
+
self.valu[meth] = query
|
|
573
|
+
self.httpapi.info['updated'] = adef.get('updated')
|
|
574
|
+
|
|
575
|
+
async def _storMethGet(self, query):
|
|
576
|
+
return await self._storMethFunc('get', query)
|
|
577
|
+
|
|
578
|
+
async def _storMethHead(self, query):
|
|
579
|
+
return await self._storMethFunc('head', query)
|
|
580
|
+
|
|
581
|
+
async def _storMethPost(self, query):
|
|
582
|
+
return await self._storMethFunc('post', query)
|
|
583
|
+
|
|
584
|
+
async def _storMethPut(self, query):
|
|
585
|
+
return await self._storMethFunc('put', query)
|
|
586
|
+
|
|
587
|
+
async def _storMethPatch(self, query):
|
|
588
|
+
return await self._storMethFunc('patch', query)
|
|
589
|
+
|
|
590
|
+
async def _storMethOptions(self, query):
|
|
591
|
+
return await self._storMethFunc('options', query)
|
|
592
|
+
|
|
593
|
+
async def _storMethDelete(self, query):
|
|
594
|
+
return await self._storMethFunc('delete', query)
|
|
595
|
+
|
|
596
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
597
|
+
async def _gtorMethGet(self):
|
|
598
|
+
return self.valu.get('get')
|
|
599
|
+
|
|
600
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
601
|
+
async def _gtorMethHead(self):
|
|
602
|
+
return self.valu.get('head')
|
|
603
|
+
|
|
604
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
605
|
+
async def _gtorMethPost(self):
|
|
606
|
+
return self.valu.get('post')
|
|
607
|
+
|
|
608
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
609
|
+
async def _gtorMethPut(self):
|
|
610
|
+
return self.valu.get('put')
|
|
611
|
+
|
|
612
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
613
|
+
async def _gtorMethDelete(self):
|
|
614
|
+
return self.valu.get('delete')
|
|
615
|
+
|
|
616
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
617
|
+
async def _gtorMethPatch(self):
|
|
618
|
+
return self.valu.get('patch')
|
|
619
|
+
|
|
620
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
621
|
+
async def _gtorMethOptions(self):
|
|
622
|
+
return self.valu.get('options')
|
|
623
|
+
|
|
624
|
+
@s_stormtypes.registry.registerType
|
|
625
|
+
class HttpHeaderDict(s_stormtypes.Dict):
|
|
626
|
+
'''
|
|
627
|
+
Immutable lowercase key access dictionary for HTTP request headers.
|
|
628
|
+
|
|
629
|
+
Example:
|
|
630
|
+
Request headers can be accessed in a case insensitive manner::
|
|
631
|
+
|
|
632
|
+
$valu = $request.headers.Cookie
|
|
633
|
+
// or the lower case value
|
|
634
|
+
$valu = $request.headers.cookie
|
|
635
|
+
'''
|
|
636
|
+
_storm_typename = 'http:api:request:headers'
|
|
637
|
+
_storm_locals = ()
|
|
638
|
+
_ismutable = True
|
|
639
|
+
|
|
640
|
+
async def setitem(self, name, valu):
|
|
641
|
+
mesg = f'{self._storm_typename} may not be modified by the runtime.'
|
|
642
|
+
raise s_exc.StormRuntimeError(mesg=mesg, name=name)
|
|
643
|
+
|
|
644
|
+
async def deref(self, name):
|
|
645
|
+
name = await s_stormtypes.tostr(name)
|
|
646
|
+
name = name.lower()
|
|
647
|
+
return self.valu.get(name)
|
|
648
|
+
|
|
649
|
+
@s_stormtypes.registry.registerType
|
|
650
|
+
class HttpPermsList(s_stormtypes.List):
|
|
651
|
+
'''
|
|
652
|
+
Accessor list for getting and setting http:api permissions.
|
|
653
|
+
'''
|
|
654
|
+
_storm_typename = 'http:api:perms'
|
|
655
|
+
_storm_locals = (
|
|
656
|
+
{'name': 'has', 'desc': 'Check it a permission is in the list.',
|
|
657
|
+
'type': {'type': 'function', '_funcname': '_methListHas',
|
|
658
|
+
'args': (
|
|
659
|
+
{'name': 'valu', 'type': 'any', 'desc': 'The permission to check.', },
|
|
660
|
+
),
|
|
661
|
+
'returns': {'type': 'boolean', 'desc': 'True if the permission is in the list, false otherwise.', }}},
|
|
662
|
+
{'name': 'pop', 'desc': 'Pop and return the last permission in the list.',
|
|
663
|
+
'type': {'type': 'function', '_funcname': '_methListPop',
|
|
664
|
+
'returns': {'type': 'any', 'desc': 'The last permission from the list.', }}},
|
|
665
|
+
{'name': 'size', 'desc': 'Return the length of the list.',
|
|
666
|
+
'type': {'type': 'function', '_funcname': '_methListSize',
|
|
667
|
+
'returns': {'type': 'int', 'desc': 'The size of the list.', }}},
|
|
668
|
+
{'name': 'index', 'desc': 'Return a single permission from the list by index.',
|
|
669
|
+
'type': {'type': 'function', '_funcname': '_methListIndex',
|
|
670
|
+
'args': (
|
|
671
|
+
{'name': 'valu', 'type': 'int', 'desc': 'The list index value.', },
|
|
672
|
+
),
|
|
673
|
+
'returns': {'type': 'any', 'desc': 'The permission present in the list at the index position.', }}},
|
|
674
|
+
{'name': 'length', 'desc': 'Get the length of the list. This is deprecated; please use ``.size()`` instead.',
|
|
675
|
+
'type': {'type': 'function', '_funcname': '_methListLength',
|
|
676
|
+
'returns': {'type': 'int', 'desc': 'The size of the list.', }}},
|
|
677
|
+
{'name': 'append', 'desc': 'Append a permission to the list.',
|
|
678
|
+
'type': {'type': 'function', '_funcname': '_methListAppend',
|
|
679
|
+
'args': (
|
|
680
|
+
{'name': 'valu', 'type': 'any', 'desc': 'The permission to append to the list.', },
|
|
681
|
+
),
|
|
682
|
+
'returns': {'type': 'null', }}},
|
|
683
|
+
{'name': 'reverse', 'desc': 'Reverse the order of the list in place',
|
|
684
|
+
'type': {'type': 'function', '_funcname': '_methListReverse',
|
|
685
|
+
'returns': {'type': 'null', }}},
|
|
686
|
+
{'name': 'slice', 'desc': 'Get a slice of the list.',
|
|
687
|
+
'type': {'type': 'function', '_funcname': '_methListSlice',
|
|
688
|
+
'args': (
|
|
689
|
+
{'name': 'start', 'type': 'int', 'desc': 'The starting index.'},
|
|
690
|
+
{'name': 'end', 'type': 'int', 'default': None,
|
|
691
|
+
'desc': 'The ending index. If not specified, slice to the end of the list.'},
|
|
692
|
+
),
|
|
693
|
+
'returns': {'type': 'list', 'desc': 'The slice of the list.'}}},
|
|
694
|
+
|
|
695
|
+
{'name': 'extend', 'desc': 'Extend a list using another iterable.',
|
|
696
|
+
'type': {'type': 'function', '_funcname': '_methListExtend',
|
|
697
|
+
'args': (
|
|
698
|
+
{'name': 'valu', 'type': 'list', 'desc': 'A list or other iterable.'},
|
|
699
|
+
),
|
|
700
|
+
'returns': {'type': 'null'}}},
|
|
701
|
+
)
|
|
702
|
+
_ismutable = True
|
|
703
|
+
|
|
704
|
+
def __init__(self, httpapi, path=None):
|
|
705
|
+
s_stormtypes.Prim.__init__(self, httpapi.info.get('perms'))
|
|
706
|
+
self.httpapi = httpapi
|
|
707
|
+
self.locls.update(self.getObjLocals())
|
|
708
|
+
|
|
709
|
+
async def setitem(self, name, valu):
|
|
710
|
+
indx = await s_stormtypes.toint(name)
|
|
711
|
+
pdefs = self.valu.copy()
|
|
712
|
+
if valu is s_stormtypes.undef:
|
|
713
|
+
try:
|
|
714
|
+
pdefs.pop(indx)
|
|
715
|
+
except IndexError:
|
|
716
|
+
pass
|
|
717
|
+
else:
|
|
718
|
+
await self.httpapi._storPerms(pdefs)
|
|
719
|
+
else:
|
|
720
|
+
pdef = await s_stormtypes.toprim(valu)
|
|
721
|
+
if isinstance(pdef, str):
|
|
722
|
+
pdef = _normPermString(pdef)
|
|
723
|
+
pdefs[indx] = pdef
|
|
724
|
+
await self.httpapi._storPerms(pdefs)
|
|
725
|
+
|
|
726
|
+
async def _methListAppend(self, valu):
|
|
727
|
+
pdef = await s_stormtypes.toprim(valu)
|
|
728
|
+
if isinstance(pdef, str):
|
|
729
|
+
pdef = _normPermString(pdef)
|
|
730
|
+
pdefs = self.valu.copy()
|
|
731
|
+
pdefs.append(pdef)
|
|
732
|
+
await self.httpapi._storPerms(pdefs)
|
|
733
|
+
|
|
734
|
+
async def _methListHas(self, valu):
|
|
735
|
+
pdef = await s_stormtypes.toprim(valu)
|
|
736
|
+
if isinstance(pdef, str):
|
|
737
|
+
pdef = _normPermString(pdef)
|
|
738
|
+
return await s_stormtypes.List._methListHas(self, pdef)
|
|
739
|
+
|
|
740
|
+
async def _methListReverse(self):
|
|
741
|
+
pdefs = self.valu.copy()
|
|
742
|
+
pdefs.reverse()
|
|
743
|
+
await self.httpapi._storPerms(pdefs)
|
|
744
|
+
|
|
745
|
+
async def _methListPop(self):
|
|
746
|
+
pdefs = self.valu.copy()
|
|
747
|
+
try:
|
|
748
|
+
valu = pdefs.pop()
|
|
749
|
+
except IndexError:
|
|
750
|
+
mesg = 'The permissions list is empty. Nothing to pop.'
|
|
751
|
+
raise s_exc.StormRuntimeError(mesg=mesg)
|
|
752
|
+
else:
|
|
753
|
+
await self.httpapi._storPerms(pdefs)
|
|
754
|
+
return valu
|
|
755
|
+
|
|
756
|
+
async def _methListSort(self, reverse=False):
|
|
757
|
+
# This is not a documented method. Attempting to sort a list of permissions has no meaning.
|
|
758
|
+
raise s_exc.StormRuntimeError(mesg=f'{self._storm_typename} does not support sorting.')
|
|
759
|
+
|
|
760
|
+
async def _methListExtend(self, valu):
|
|
761
|
+
pdefs = self.valu.copy()
|
|
762
|
+
async for pdef in s_stormtypes.toiter(valu):
|
|
763
|
+
pdef = await s_stormtypes.toprim(pdef)
|
|
764
|
+
if isinstance(pdef, str):
|
|
765
|
+
pdef = _normPermString(pdef)
|
|
766
|
+
pdefs.append(pdef)
|
|
767
|
+
await self.httpapi._storPerms(pdefs)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
@s_stormtypes.registry.registerType
|
|
771
|
+
class HttpApiVars(s_stormtypes.Dict):
|
|
772
|
+
'''
|
|
773
|
+
Accessor dictionary for getting and setting Extended HTTP API variables.
|
|
774
|
+
|
|
775
|
+
This can be used to set, unset or iterate over the runtime variables that are
|
|
776
|
+
set for an Extended HTTP API endpoint. These variables are set in the Storm
|
|
777
|
+
runtime for all of the HTTP methods configured to be executed by the endpoint.
|
|
778
|
+
|
|
779
|
+
Example:
|
|
780
|
+
Set a few variables on a given API::
|
|
781
|
+
|
|
782
|
+
$api.vars.foo = 'the foo string'
|
|
783
|
+
$api.vars.bar = (1234)
|
|
784
|
+
|
|
785
|
+
Remove a variable::
|
|
786
|
+
|
|
787
|
+
$api.vars.foo = $lib.undef
|
|
788
|
+
|
|
789
|
+
Iterate over the variables set for the endpoint::
|
|
790
|
+
|
|
791
|
+
for ($key, $valu) in $api.vars {
|
|
792
|
+
$lib.print(`{$key) -> {$valu}`)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
Overwrite all of the variables for a given API with a new dictionary::
|
|
796
|
+
|
|
797
|
+
$api.vars = ({"foo": "a new string", "bar": (137)})
|
|
798
|
+
'''
|
|
799
|
+
_storm_typename = 'http:api:vars'
|
|
800
|
+
_storm_locals = ()
|
|
801
|
+
_ismutable = True
|
|
802
|
+
|
|
803
|
+
def __init__(self, httpapi, path=None):
|
|
804
|
+
s_stormtypes.Dict.__init__(self, httpapi.info.get('vars'), path=path)
|
|
805
|
+
self.httpapi = httpapi
|
|
806
|
+
|
|
807
|
+
async def setitem(self, name, valu):
|
|
808
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
809
|
+
name = await s_stormtypes.tostr(name)
|
|
810
|
+
|
|
811
|
+
varz = self.valu.copy()
|
|
812
|
+
if valu is s_stormtypes.undef:
|
|
813
|
+
varz.pop(name, None)
|
|
814
|
+
adef = await self.httpapi.runt.snap.core.modHttpExtApi(self.httpapi.iden, 'vars', varz)
|
|
815
|
+
self.valu.pop(name, None)
|
|
816
|
+
self.httpapi.info['updated'] = adef.get('updated')
|
|
817
|
+
else:
|
|
818
|
+
valu = await s_stormtypes.toprim(valu)
|
|
819
|
+
varz[name] = valu
|
|
820
|
+
adef = await self.httpapi.runt.snap.core.modHttpExtApi(self.httpapi.iden, 'vars', varz)
|
|
821
|
+
self.valu[name] = valu
|
|
822
|
+
self.httpapi.info['updated'] = adef.get('updated')
|
|
823
|
+
|
|
824
|
+
@s_stormtypes.registry.registerType
|
|
825
|
+
class HttpReq(s_stormtypes.StormType):
|
|
826
|
+
'''
|
|
827
|
+
Extended HTTP API Request object.
|
|
828
|
+
'''
|
|
829
|
+
_storm_typename = 'http:api:request'
|
|
830
|
+
_storm_locals = (
|
|
831
|
+
{'name': 'args', 'desc': '''
|
|
832
|
+
A list of path arguments made as part of the HTTP API request.
|
|
833
|
+
These are the results of any capture groups defined in the Extended HTTP API path regular expression.''',
|
|
834
|
+
'type': 'list'},
|
|
835
|
+
{'name': 'body', 'desc': 'The raw request body.', 'type': 'bytes'},
|
|
836
|
+
{'name': 'method', 'desc': 'The request method', 'type': 'str'},
|
|
837
|
+
{'name': 'params', 'desc': 'Request parameters.', 'type': 'dict'},
|
|
838
|
+
{'name': 'client', 'desc': 'The remote IP of the requester.', 'type': 'str'},
|
|
839
|
+
{'name': 'uri', 'desc': 'The full request URI.', 'type': 'str'},
|
|
840
|
+
{'name': 'path', 'desc': 'The path which was matched against the Extended HTTPAPI endpoint.', 'type': 'str'},
|
|
841
|
+
{'name': 'user',
|
|
842
|
+
'desc': 'The user iden who made the HTTP API request.', 'type': 'str'},
|
|
843
|
+
{'name': 'api', 'desc': 'The http:api object for the request.',
|
|
844
|
+
'type': {'type': 'gtor', '_gtorfunc': '_gtorApi',
|
|
845
|
+
'returns': {'type': 'http:api'}}},
|
|
846
|
+
{'name': 'headers', 'desc': 'The request headers.',
|
|
847
|
+
'type': {'type': 'ctor', '_ctorfunc': '_ctorJson',
|
|
848
|
+
'returns': {'type': 'http:api:request:headers'}}},
|
|
849
|
+
{'name': 'json', 'desc': 'The request body as json.',
|
|
850
|
+
'type': {'type': 'ctor', '_ctorfunc': '_ctorJson',
|
|
851
|
+
'returns': {'type': 'dict'}}},
|
|
852
|
+
{'name': 'sendcode', 'desc': 'Send the HTTP response code.',
|
|
853
|
+
'type': {'type': 'function', '_funcname': '_methSendCode',
|
|
854
|
+
'args': (
|
|
855
|
+
{'name': 'code', 'type': 'int',
|
|
856
|
+
'desc': 'The response code.'},
|
|
857
|
+
),
|
|
858
|
+
'returns': {'type': 'null'}}},
|
|
859
|
+
{'name': 'sendheaders', 'desc': 'Send the HTTP response headers.',
|
|
860
|
+
'type': {'type': 'function', '_funcname': '_methSendHeaders',
|
|
861
|
+
'args': (
|
|
862
|
+
{'name': 'headers', 'type': 'dict',
|
|
863
|
+
'desc': 'The response headers.'},
|
|
864
|
+
),
|
|
865
|
+
'returns': {'type': 'null'}}},
|
|
866
|
+
{'name': 'sendbody', 'desc': 'Send the HTTP response body.',
|
|
867
|
+
'type': {'type': 'function', '_funcname': '_methSendBody',
|
|
868
|
+
'args': (
|
|
869
|
+
{'name': 'body', 'type': 'bytes',
|
|
870
|
+
'desc': 'The response body.'},
|
|
871
|
+
),
|
|
872
|
+
'returns': {'type': 'null'}}},
|
|
873
|
+
{'name': 'reply', 'desc': '''
|
|
874
|
+
Convenience method to send the response code, headers and body together.
|
|
875
|
+
|
|
876
|
+
Notes:
|
|
877
|
+
This can only be called once.
|
|
878
|
+
|
|
879
|
+
If the response body is not bytes, this method will serialize the body as JSON
|
|
880
|
+
and set the ``Content-Type`` and ``Content-Length`` response headers.
|
|
881
|
+
''',
|
|
882
|
+
'type': {'type': 'function', '_funcname': '_methReply',
|
|
883
|
+
'args': (
|
|
884
|
+
{'name': 'code', 'type': 'int',
|
|
885
|
+
'desc': 'The response code.'},
|
|
886
|
+
{'name': 'headers', 'type': 'dict',
|
|
887
|
+
'desc': 'The response headers.', 'default': None},
|
|
888
|
+
{'name': 'body', 'type': 'any',
|
|
889
|
+
'desc': 'The response body.', 'default': '$lib.undef'},
|
|
890
|
+
),
|
|
891
|
+
'returns': {'type': 'null'}}},
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
def __init__(self, runt, rnfo):
|
|
895
|
+
s_stormtypes.StormType.__init__(self)
|
|
896
|
+
|
|
897
|
+
self.replied = False
|
|
898
|
+
|
|
899
|
+
self.runt = runt
|
|
900
|
+
self.rnfo = rnfo
|
|
901
|
+
self.rcode = None
|
|
902
|
+
self.rbody = None
|
|
903
|
+
self.rheaders = None
|
|
904
|
+
self.locls.update(self.getObjLocals())
|
|
905
|
+
|
|
906
|
+
# Constants for a given instance
|
|
907
|
+
self.locls.update({
|
|
908
|
+
'args': self.rnfo.get('args'),
|
|
909
|
+
'body': self.rnfo.get('body'),
|
|
910
|
+
'method': self.rnfo.get('method'),
|
|
911
|
+
'params': self.rnfo.get('params'),
|
|
912
|
+
'client': self.rnfo.get('client'),
|
|
913
|
+
'uri': self.rnfo.get('uri'),
|
|
914
|
+
'path': self.rnfo.get('path'),
|
|
915
|
+
'user': self.rnfo.get('user'),
|
|
916
|
+
})
|
|
917
|
+
|
|
918
|
+
self.gtors.update({
|
|
919
|
+
'api': self._gtorApi, # Not a ctor since the adef retrieval is an async process
|
|
920
|
+
})
|
|
921
|
+
|
|
922
|
+
self.ctors.update({
|
|
923
|
+
'json': self._ctorJson,
|
|
924
|
+
'headers': self._ctorHeaders,
|
|
925
|
+
})
|
|
926
|
+
|
|
927
|
+
def getObjLocals(self):
|
|
928
|
+
return {
|
|
929
|
+
'sendcode': self._methSendCode,
|
|
930
|
+
'sendheaders': self._methSendHeaders,
|
|
931
|
+
'sendbody': self._methSendBody,
|
|
932
|
+
'reply': self._methReply,
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
936
|
+
def _ctorHeaders(self, path=None):
|
|
937
|
+
headers = self.rnfo.get('headers')
|
|
938
|
+
return HttpHeaderDict(valu=headers, path=path)
|
|
939
|
+
|
|
940
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
941
|
+
async def _gtorApi(self):
|
|
942
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'get'))
|
|
943
|
+
adef = await self.runt.snap.core.getHttpExtApi(self.rnfo.get('iden'))
|
|
944
|
+
return HttpApi(self.runt, adef)
|
|
945
|
+
|
|
946
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
947
|
+
def _ctorJson(self, path=None):
|
|
948
|
+
try:
|
|
949
|
+
return json.loads(self.rnfo.get('body'))
|
|
950
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as e:
|
|
951
|
+
raise s_exc.StormRuntimeError(mesg='Failed to decode request body as JSON: {e}') from None
|
|
952
|
+
|
|
953
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
954
|
+
async def _methSendCode(self, code):
|
|
955
|
+
code = await s_stormtypes.toint(code)
|
|
956
|
+
await self.runt.snap.fire('http:resp:code', code=code)
|
|
957
|
+
|
|
958
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
959
|
+
async def _methSendHeaders(self, headers):
|
|
960
|
+
headers = await s_stormtypes.toprim(headers)
|
|
961
|
+
if not isinstance(headers, dict):
|
|
962
|
+
typ = await s_stormtypes.totype(headers)
|
|
963
|
+
raise s_exc.BadArg(mesg=f'HTTP Response headers must be a dictionary, got {typ}.')
|
|
964
|
+
await self.runt.snap.fire('http:resp:headers', headers=headers)
|
|
965
|
+
|
|
966
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
967
|
+
async def _methSendBody(self, body):
|
|
968
|
+
body = await s_stormtypes.toprim(body)
|
|
969
|
+
if not isinstance(body, bytes):
|
|
970
|
+
typ = await s_stormtypes.totype(body)
|
|
971
|
+
raise s_exc.BadArg(mesg=f'HTTP Response body must be bytes, got {typ}.')
|
|
972
|
+
await self.runt.snap.fire('http:resp:body', body=body)
|
|
973
|
+
|
|
974
|
+
# Convenience method
|
|
975
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
976
|
+
async def _methReply(self, code, headers=None, body=s_stormtypes.undef):
|
|
977
|
+
if self.replied:
|
|
978
|
+
raise s_exc.BadArg(mesg='Response.reply() has already been called.')
|
|
979
|
+
|
|
980
|
+
headers = await s_stormtypes.toprim(headers)
|
|
981
|
+
if headers is None:
|
|
982
|
+
headers = {}
|
|
983
|
+
|
|
984
|
+
if body is not s_stormtypes.undef:
|
|
985
|
+
if not isinstance(body, bytes):
|
|
986
|
+
body = await s_stormtypes.toprim(body)
|
|
987
|
+
body = json.dumps(body).encode('utf-8', 'surrogatepass')
|
|
988
|
+
headers['Content-Type'] = 'application/json; charset=utf8"'
|
|
989
|
+
headers['Content-Length'] = len(body)
|
|
990
|
+
|
|
991
|
+
await self._methSendCode(code)
|
|
992
|
+
|
|
993
|
+
if headers:
|
|
994
|
+
await self._methSendHeaders(headers)
|
|
995
|
+
|
|
996
|
+
if body is not s_stormtypes.undef:
|
|
997
|
+
await self._methSendBody(body)
|
|
998
|
+
|
|
999
|
+
self.replied = True
|
|
1000
|
+
|
|
1001
|
+
@s_stormtypes.registry.registerLib
|
|
1002
|
+
class CortexHttpApi(s_stormtypes.Lib):
|
|
1003
|
+
'''
|
|
1004
|
+
Library for interacting with the Extended HTTP API.
|
|
1005
|
+
'''
|
|
1006
|
+
_storm_locals = (
|
|
1007
|
+
{'name': 'add', 'desc': '''
|
|
1008
|
+
Add an Extended HTTP API endpoint to the Cortex.
|
|
1009
|
+
|
|
1010
|
+
This can be used to add an API endpoint which will be resolved under
|
|
1011
|
+
the API path "/api/ext/". New API endpoint objects are appended to
|
|
1012
|
+
a list of APIs to resolve in order.
|
|
1013
|
+
|
|
1014
|
+
Notes:
|
|
1015
|
+
The Cortex does not make any attempt to do any inspection of path values which may conflict between one
|
|
1016
|
+
another. This is because the paths for a given endpoint may be changed, they can contain regular
|
|
1017
|
+
expressions, and they may have their resolution order changed. Cortex administrators are responsible for
|
|
1018
|
+
configuring their Extended HTTP API endpoints with correct paths and order to meet their use cases.
|
|
1019
|
+
|
|
1020
|
+
Example:
|
|
1021
|
+
Add a simple API handler::
|
|
1022
|
+
|
|
1023
|
+
// Create an endpoint for /api/ext/foo/bar
|
|
1024
|
+
$api = $lib.cortex.httpapi.add('foo/bar')
|
|
1025
|
+
|
|
1026
|
+
// Define a GET response handler via storm that makes a simple reply.
|
|
1027
|
+
$api.methods.get = ${ $request.reply(200, body=({"some": "data}) }
|
|
1028
|
+
|
|
1029
|
+
Add a wildcard handler::
|
|
1030
|
+
|
|
1031
|
+
// Create a wildcard endpoint for /api/ext/some/thing([a-zA-Z0-9]*)/([a-zA-Z0-9]*)
|
|
1032
|
+
$api = $lib.cortex.httpapi.add('some/thing([a-zA-Z0-9]*)/([a-zA-Z0-9]*)')
|
|
1033
|
+
|
|
1034
|
+
// The capture groups are exposed as request arguments.
|
|
1035
|
+
// Echo them back to the caller.
|
|
1036
|
+
$api.methods.get = ${
|
|
1037
|
+
$request.reply(200, body=({"args": $request.args})
|
|
1038
|
+
}
|
|
1039
|
+
''',
|
|
1040
|
+
'type': {'type': 'function', '_funcname': 'addHttpApi',
|
|
1041
|
+
'args': (
|
|
1042
|
+
{'name': 'path', 'type': 'string',
|
|
1043
|
+
'desc': 'The extended HTTP API path.'},
|
|
1044
|
+
{'name': 'name', 'type': 'string',
|
|
1045
|
+
'desc': 'Friendly name for the Extended HTTP API', 'default': ''},
|
|
1046
|
+
{'name': 'desc', 'type': 'string',
|
|
1047
|
+
'desc': 'Description for the Extended HTTP API.', 'default': ''},
|
|
1048
|
+
{'name': 'runas', 'type': 'string',
|
|
1049
|
+
'desc': 'Run the storm query as the API "owner" or as the authenticated "user".',
|
|
1050
|
+
'default': 'owner'},
|
|
1051
|
+
{'name': 'authenticated', 'type': 'boolean',
|
|
1052
|
+
'desc': 'Require the API endpoint to be authenticated.', 'default': True},
|
|
1053
|
+
{'name': 'readonly', 'type': 'boolean',
|
|
1054
|
+
'desc': 'Run the Extended HTTP Storm methods in readonly mode.', 'default': False},
|
|
1055
|
+
),
|
|
1056
|
+
'returns': {'type': 'http:api', 'desc': 'A new http:api object.'}}},
|
|
1057
|
+
{'name': 'del', 'desc': 'Delete an Extended HTTP API endpoint.',
|
|
1058
|
+
'type': {'type': 'function', '_funcname': 'delHttpApi',
|
|
1059
|
+
'args': (
|
|
1060
|
+
{'name': 'iden', 'type': 'string',
|
|
1061
|
+
'desc': 'The iden of the API to delete.'},
|
|
1062
|
+
),
|
|
1063
|
+
'returns': {'type': 'null'}}},
|
|
1064
|
+
{'name': 'get', 'desc': 'Get an Extended HTTP API object.',
|
|
1065
|
+
'type': {'type': 'function', '_funcname': 'getHttpApi',
|
|
1066
|
+
'args': (
|
|
1067
|
+
{'name': 'iden', 'type': 'string',
|
|
1068
|
+
'desc': 'The iden of the API to retreive.'},
|
|
1069
|
+
),
|
|
1070
|
+
'returns': {'type': 'http:api', 'desc': 'The http:api object.'}}},
|
|
1071
|
+
{'name': 'list', 'desc': 'Get all the Extneded HTTP APIs on the Cortex',
|
|
1072
|
+
'type': {'type': 'function', '_funcname': 'listHttpApis', 'args': (),
|
|
1073
|
+
'returns': {'type': 'list', 'desc': 'A list of http:api objects'}}},
|
|
1074
|
+
{'name': 'index', 'desc': 'Set the index for a given Extended HTTP API.',
|
|
1075
|
+
'type': {'type': 'function', '_funcname': 'setHttpApiIndx',
|
|
1076
|
+
'args': (
|
|
1077
|
+
{'name': 'iden', 'type': 'string',
|
|
1078
|
+
'desc': 'The iden of the API to modify.'},
|
|
1079
|
+
{'name': 'index', 'type': 'int', 'default': 0,
|
|
1080
|
+
'desc': 'The new index of the API. Uses zero based indexing.'},
|
|
1081
|
+
),
|
|
1082
|
+
'returns': {'type': 'int', 'desc': 'The new index location of the API.'}}},
|
|
1083
|
+
{'name': 'response', 'desc': 'Make a response object. Used by API handlers automatically.',
|
|
1084
|
+
'type': {'type': 'function', '_funcname': 'makeHttpResponse',
|
|
1085
|
+
'args': (
|
|
1086
|
+
{'name': 'requestinfo', 'type': 'dict',
|
|
1087
|
+
'desc': 'Request info dictionary. This is an opaque data structure which may change.'},
|
|
1088
|
+
),
|
|
1089
|
+
'returns': {'type': 'http:api:request'}}},
|
|
1090
|
+
)
|
|
1091
|
+
_storm_lib_path = ('cortex', 'httpapi')
|
|
1092
|
+
|
|
1093
|
+
_storm_lib_perms = (
|
|
1094
|
+
{'perm': ('storm', 'lib', 'cortex', 'httpapi', 'add'), 'gate': 'cortex',
|
|
1095
|
+
'desc': 'Controls the ability to add a new Extended HTTP API on the Cortex.'},
|
|
1096
|
+
{'perm': ('storm', 'lib', 'cortex', 'httpapi', 'get'), 'gate': 'cortex',
|
|
1097
|
+
'desc': 'Controls the ability to get or list Extended HTTP APIs on the Cortex.'},
|
|
1098
|
+
{'perm': ('storm', 'lib', 'cortex', 'httpapi', 'del'), 'gate': 'cortex',
|
|
1099
|
+
'desc': 'Controls the ability to delete an Extended HTTP API on the Cortex.'},
|
|
1100
|
+
{'perm': ('storm', 'lib', 'cortex', 'httpapi', 'set'), 'gate': 'cortex',
|
|
1101
|
+
'desc': 'Controls the ability to modify an Extended HTTP API on the Cortex.'},
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
def getObjLocals(self):
|
|
1105
|
+
return {
|
|
1106
|
+
'add': self.addHttpApi,
|
|
1107
|
+
'del': self.delHttpApi,
|
|
1108
|
+
'get': self.getHttpApi,
|
|
1109
|
+
'list': self.listHttpApis,
|
|
1110
|
+
'index': self.setHttpApiIndx,
|
|
1111
|
+
'response': self.makeHttpResponse,
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
1115
|
+
async def makeHttpResponse(self, requestinfo):
|
|
1116
|
+
requestinfo = await s_stormtypes.toprim(requestinfo)
|
|
1117
|
+
return HttpReq(self.runt, requestinfo)
|
|
1118
|
+
|
|
1119
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
1120
|
+
async def getHttpApi(self, iden):
|
|
1121
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'get'))
|
|
1122
|
+
iden = await s_stormtypes.tostr(iden)
|
|
1123
|
+
adef = await self.runt.snap.core.getHttpExtApi(iden)
|
|
1124
|
+
return HttpApi(self.runt, adef)
|
|
1125
|
+
|
|
1126
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
1127
|
+
async def listHttpApis(self):
|
|
1128
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'get'))
|
|
1129
|
+
adefs = await self.runt.snap.core.getHttpExtApis()
|
|
1130
|
+
apis = [HttpApi(self.runt, adef) for adef in adefs]
|
|
1131
|
+
return apis
|
|
1132
|
+
|
|
1133
|
+
async def addHttpApi(self, path, name='', desc='', runas='owner', authenticated=True, readonly=False):
|
|
1134
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'add'))
|
|
1135
|
+
|
|
1136
|
+
path = await s_stormtypes.tostr(path)
|
|
1137
|
+
name = await s_stormtypes.tostr(name)
|
|
1138
|
+
desc = await s_stormtypes.tostr(desc)
|
|
1139
|
+
runas = await s_stormtypes.tostr(runas)
|
|
1140
|
+
readonly = await s_stormtypes.tobool(readonly)
|
|
1141
|
+
authenticated = await s_stormtypes.tobool(authenticated)
|
|
1142
|
+
|
|
1143
|
+
adef = {
|
|
1144
|
+
'path': path,
|
|
1145
|
+
'view': self.runt.snap.view.iden,
|
|
1146
|
+
'runas': runas,
|
|
1147
|
+
'creator': self.runt.user.iden,
|
|
1148
|
+
'owner': self.runt.user.iden,
|
|
1149
|
+
'methods': {},
|
|
1150
|
+
'authenticated': authenticated,
|
|
1151
|
+
'name': name,
|
|
1152
|
+
'desc': desc,
|
|
1153
|
+
'readonly': readonly,
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
adef = await self.runt.snap.core.addHttpExtApi(adef)
|
|
1157
|
+
return HttpApi(self.runt, adef)
|
|
1158
|
+
|
|
1159
|
+
async def delHttpApi(self, iden):
|
|
1160
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'del'))
|
|
1161
|
+
iden = await s_stormtypes.tostr(iden)
|
|
1162
|
+
return await self.runt.snap.view.core.delHttpExtApi(iden)
|
|
1163
|
+
|
|
1164
|
+
async def setHttpApiIndx(self, iden, index=0):
|
|
1165
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
1166
|
+
iden = await s_stormtypes.tostr(iden)
|
|
1167
|
+
index = await s_stormtypes.toint(index)
|
|
1168
|
+
return await self.runt.snap.view.core.setHttpApiIndx(iden, index)
|