synapse 2.205.0__py311-none-any.whl → 2.207.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 +8 -8
- synapse/cortex.py +14 -1
- synapse/lib/aha.py +13 -8
- synapse/lib/httpapi.py +196 -97
- synapse/lib/lmdbslab.py +2 -0
- synapse/lib/modelrev.py +3 -3
- synapse/lib/schemas.py +11 -0
- synapse/lib/spooled.py +2 -1
- synapse/lib/stormhttp.py +6 -6
- synapse/lib/stormlib/aha.py +5 -1
- synapse/lib/stormlib/model.py +1 -1
- synapse/lib/stormtypes.py +53 -17
- synapse/lib/version.py +2 -2
- synapse/models/base.py +9 -0
- synapse/models/inet.py +9 -1
- synapse/models/infotech.py +5 -0
- synapse/models/telco.py +10 -0
- synapse/tests/test_axon.py +52 -41
- synapse/tests/test_cortex.py +18 -6
- synapse/tests/test_lib_aha.py +17 -0
- synapse/tests/test_lib_cell.py +5 -3
- synapse/tests/test_lib_httpapi.py +225 -33
- synapse/tests/test_lib_lmdbslab.py +30 -0
- synapse/tests/test_lib_modelrev.py +7 -7
- synapse/tests/test_lib_spooled.py +2 -0
- synapse/tests/test_lib_stormhttp.py +13 -0
- synapse/tests/test_lib_stormlib_aha.py +7 -7
- synapse/tests/test_lib_stormlib_cortex.py +61 -60
- synapse/tests/test_lib_stormtypes.py +8 -0
- synapse/tests/test_model_infotech.py +2 -0
- synapse/tests/test_model_telco.py +4 -1
- synapse/tools/aha/easycert.py +4 -0
- synapse/tools/aha/mirror.py +6 -4
- {synapse-2.205.0.dist-info → synapse-2.207.0.dist-info}/METADATA +1 -1
- {synapse-2.205.0.dist-info → synapse-2.207.0.dist-info}/RECORD +38 -38
- {synapse-2.205.0.dist-info → synapse-2.207.0.dist-info}/WHEEL +0 -0
- {synapse-2.205.0.dist-info → synapse-2.207.0.dist-info}/licenses/LICENSE +0 -0
- {synapse-2.205.0.dist-info → synapse-2.207.0.dist-info}/top_level.txt +0 -0
synapse/lib/httpapi.py
CHANGED
|
@@ -2,6 +2,7 @@ import base64
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
+
from http import HTTPStatus
|
|
5
6
|
from urllib.parse import urlparse
|
|
6
7
|
|
|
7
8
|
import tornado.web as t_web
|
|
@@ -13,6 +14,7 @@ import synapse.common as s_common
|
|
|
13
14
|
import synapse.lib.base as s_base
|
|
14
15
|
import synapse.lib.json as s_json
|
|
15
16
|
import synapse.lib.msgpack as s_msgpack
|
|
17
|
+
import synapse.lib.schemas as s_schemas
|
|
16
18
|
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
18
20
|
|
|
@@ -99,7 +101,7 @@ class HandlerBase:
|
|
|
99
101
|
return self.cell
|
|
100
102
|
|
|
101
103
|
def options(self):
|
|
102
|
-
self.set_status(
|
|
104
|
+
self.set_status(HTTPStatus.NO_CONTENT)
|
|
103
105
|
self.finish()
|
|
104
106
|
|
|
105
107
|
def isOrigHost(self, origin):
|
|
@@ -115,21 +117,71 @@ class HandlerBase:
|
|
|
115
117
|
def check_origin(self, origin):
|
|
116
118
|
return self.isOrigHost(origin)
|
|
117
119
|
|
|
118
|
-
def
|
|
119
|
-
|
|
120
|
+
def sendRestErr(self, code: str, mesg: str, *, status_code: int | HTTPStatus | None =None) -> None:
|
|
121
|
+
'''
|
|
122
|
+
Send a JSON REST error message with a code and message.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
code: The error code.
|
|
126
|
+
mesg: The error message.
|
|
127
|
+
status_code: The HTTP status code. This is optional.
|
|
120
128
|
|
|
121
|
-
|
|
129
|
+
Notes:
|
|
130
|
+
If the status code is not provided or set prior to calling this API, the response
|
|
131
|
+
will have an HTTP status code of 200 (OK).
|
|
132
|
+
|
|
133
|
+
This does write the response stream. No further content should be written
|
|
134
|
+
in the response after calling this.
|
|
135
|
+
'''
|
|
136
|
+
if status_code is not None:
|
|
137
|
+
self.set_status(status_code)
|
|
122
138
|
self.set_header('Content-Type', 'application/json')
|
|
123
139
|
return self.write({'status': 'err', 'code': code, 'mesg': mesg})
|
|
124
140
|
|
|
125
|
-
def sendRestExc(self, e):
|
|
141
|
+
def sendRestExc(self, e: Exception, *, status_code: int | HTTPStatus | None = None) -> None:
|
|
142
|
+
'''
|
|
143
|
+
Send a JSON REST error message based on the exception.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
e: The exception to send. The exception class name will be used as the error code.
|
|
147
|
+
status_code: The HTTP status code. This is optional.
|
|
148
|
+
|
|
149
|
+
Notes:
|
|
150
|
+
If the status code is not provided or set prior to calling this API, the response
|
|
151
|
+
will have an HTTP status code of 200.
|
|
152
|
+
|
|
153
|
+
This does write the response stream. No further content should be written
|
|
154
|
+
in the response after calling this.
|
|
155
|
+
'''
|
|
156
|
+
mesg = str(e)
|
|
157
|
+
if isinstance(e, s_exc.SynErr):
|
|
158
|
+
mesg = e.get('mesg', mesg)
|
|
126
159
|
self.set_header('Content-Type', 'application/json')
|
|
127
|
-
return self.sendRestErr(e.__class__.__name__,
|
|
160
|
+
return self.sendRestErr(e.__class__.__name__, mesg, status_code=status_code)
|
|
161
|
+
|
|
162
|
+
def sendRestRetn(self, valu, *, status_code: int | HTTPStatus | None = None) -> None:
|
|
163
|
+
'''
|
|
164
|
+
Send a successful JSON REST response.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
valu: The JSON compatible value to send.
|
|
168
|
+
status_code: The HTTP status code. This is optional.
|
|
128
169
|
|
|
129
|
-
|
|
170
|
+
Notes:
|
|
171
|
+
If the status code is not provided or set prior to calling this API, the response
|
|
172
|
+
will have an HTTP status code of 200.
|
|
173
|
+
|
|
174
|
+
This does write the response stream. No further content should be written
|
|
175
|
+
in the response after calling this.
|
|
176
|
+
'''
|
|
177
|
+
if status_code is not None:
|
|
178
|
+
self.set_status(status_code)
|
|
130
179
|
self.set_header('Content-Type', 'application/json')
|
|
131
180
|
return self.write({'status': 'ok', 'result': valu})
|
|
132
181
|
|
|
182
|
+
def getJsonBody(self, validator=None):
|
|
183
|
+
return self.loadJsonMesg(self.request.body, validator=validator)
|
|
184
|
+
|
|
133
185
|
def loadJsonMesg(self, byts, validator=None):
|
|
134
186
|
try:
|
|
135
187
|
item = s_json.loads(byts)
|
|
@@ -138,11 +190,12 @@ class HandlerBase:
|
|
|
138
190
|
return item
|
|
139
191
|
|
|
140
192
|
except s_exc.SchemaViolation as e:
|
|
141
|
-
self.sendRestErr('SchemaViolation', str(e))
|
|
193
|
+
self.sendRestErr('SchemaViolation', str(e), status_code=HTTPStatus.BAD_REQUEST)
|
|
142
194
|
return None
|
|
143
195
|
|
|
144
196
|
except Exception:
|
|
145
|
-
self.sendRestErr('SchemaViolation', 'Invalid JSON content.'
|
|
197
|
+
self.sendRestErr('SchemaViolation', 'Invalid JSON content.',
|
|
198
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
146
199
|
return None
|
|
147
200
|
|
|
148
201
|
def logAuthIssue(self, mesg=None, user=None, username=None, level=logging.WARNING):
|
|
@@ -176,8 +229,8 @@ class HandlerBase:
|
|
|
176
229
|
|
|
177
230
|
def sendAuthRequired(self):
|
|
178
231
|
self.set_header('WWW-Authenticate', 'Basic realm=synapse')
|
|
179
|
-
self.
|
|
180
|
-
|
|
232
|
+
self.sendRestErr('NotAuthenticated', 'The session is not logged in.',
|
|
233
|
+
status_code=HTTPStatus.UNAUTHORIZED)
|
|
181
234
|
|
|
182
235
|
async def reqAuthUser(self):
|
|
183
236
|
if await self.authenticated():
|
|
@@ -222,7 +275,8 @@ class HandlerBase:
|
|
|
222
275
|
authcell = self.getAuthCell()
|
|
223
276
|
udef = await authcell.getUserDef(iden, packroles=False)
|
|
224
277
|
if not udef.get('admin'):
|
|
225
|
-
self.sendRestErr('AuthDeny', f'User {self.web_useriden} ({self.web_username}) is not an admin.'
|
|
278
|
+
self.sendRestErr('AuthDeny', f'User {self.web_useriden} ({self.web_username}) is not an admin.',
|
|
279
|
+
status_code=HTTPStatus.FORBIDDEN)
|
|
226
280
|
return False
|
|
227
281
|
|
|
228
282
|
return True
|
|
@@ -375,14 +429,12 @@ class HandlerBase:
|
|
|
375
429
|
if await authcell.isUserAllowed(useriden, perm, gateiden=gateiden, default=default):
|
|
376
430
|
return True
|
|
377
431
|
|
|
378
|
-
self.set_status(403)
|
|
379
|
-
|
|
380
432
|
mesg = f'User ({self.web_username}) must have permission {".".join(perm)}'
|
|
381
433
|
if default:
|
|
382
434
|
mesg = f'User ({self.web_username}) is denied the permission {".".join(perm)}'
|
|
383
435
|
if gateiden:
|
|
384
436
|
mesg = f'{mesg} on object {gateiden}'
|
|
385
|
-
self.sendRestErr('AuthDeny', mesg)
|
|
437
|
+
self.sendRestErr('AuthDeny', mesg, status_code=HTTPStatus.FORBIDDEN)
|
|
386
438
|
return False
|
|
387
439
|
|
|
388
440
|
async def authenticated(self):
|
|
@@ -442,20 +494,6 @@ class Handler(HandlerBase, t_web.RequestHandler):
|
|
|
442
494
|
if hasattr(self, 'task'):
|
|
443
495
|
self.task.cancel()
|
|
444
496
|
|
|
445
|
-
async def _reqValidOpts(self, opts):
|
|
446
|
-
|
|
447
|
-
if opts is None:
|
|
448
|
-
opts = {}
|
|
449
|
-
|
|
450
|
-
useriden = await self.useriden()
|
|
451
|
-
|
|
452
|
-
opts.setdefault('user', useriden)
|
|
453
|
-
if opts.get('user') != useriden:
|
|
454
|
-
if not await self.allowed(('impersonate',)):
|
|
455
|
-
return None
|
|
456
|
-
|
|
457
|
-
return opts
|
|
458
|
-
|
|
459
497
|
class RobotHandler(HandlerBase, t_web.RequestHandler):
|
|
460
498
|
async def get(self):
|
|
461
499
|
self.write('User-agent: *\n')
|
|
@@ -484,6 +522,42 @@ class StormHandler(Handler):
|
|
|
484
522
|
# a reference to the cortex is returned from the handler.
|
|
485
523
|
return self.cell
|
|
486
524
|
|
|
525
|
+
async def _reqValidOpts(self, opts: dict | None) -> dict | None:
|
|
526
|
+
'''
|
|
527
|
+
Creates or validates an opts dict with the current session useriden.
|
|
528
|
+
|
|
529
|
+
If the session useriden differs from the user key, validate the user
|
|
530
|
+
has the impersonate permission ( that may require a round trip to authcell ).
|
|
531
|
+
|
|
532
|
+
Notes:
|
|
533
|
+
This API sets up HTTP response values if it returns None.
|
|
534
|
+
|
|
535
|
+
Args:
|
|
536
|
+
opts: The opts dictionary to validate.
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
Opts dict if allowed; None if not allowed.
|
|
540
|
+
'''
|
|
541
|
+
|
|
542
|
+
if opts is None:
|
|
543
|
+
opts = {}
|
|
544
|
+
|
|
545
|
+
useriden = await self.useriden()
|
|
546
|
+
|
|
547
|
+
opts.setdefault('user', useriden)
|
|
548
|
+
if opts.get('user') != useriden:
|
|
549
|
+
if not await self.allowed(('impersonate',)):
|
|
550
|
+
return None
|
|
551
|
+
|
|
552
|
+
return opts
|
|
553
|
+
|
|
554
|
+
def _handleStormErr(self, err: Exception):
|
|
555
|
+
if isinstance(err, s_exc.AuthDeny):
|
|
556
|
+
return self.sendRestExc(err, status_code=HTTPStatus.FORBIDDEN)
|
|
557
|
+
if isinstance(err, s_exc.NoSuchView):
|
|
558
|
+
return self.sendRestExc(err, status_code=HTTPStatus.NOT_FOUND)
|
|
559
|
+
return self.sendRestExc(err, status_code=HTTPStatus.BAD_REQUEST)
|
|
560
|
+
|
|
487
561
|
class StormNodesV1(StormHandler):
|
|
488
562
|
|
|
489
563
|
async def post(self):
|
|
@@ -497,7 +571,6 @@ class StormNodesV1(StormHandler):
|
|
|
497
571
|
|
|
498
572
|
s_common.deprecated('HTTP API /api/v1/storm/nodes', curv='2.110.0')
|
|
499
573
|
|
|
500
|
-
# dont allow a user to be specified
|
|
501
574
|
opts = body.get('opts')
|
|
502
575
|
query = body.get('query')
|
|
503
576
|
stream = body.get('stream')
|
|
@@ -507,14 +580,20 @@ class StormNodesV1(StormHandler):
|
|
|
507
580
|
if opts is None:
|
|
508
581
|
return
|
|
509
582
|
|
|
510
|
-
|
|
583
|
+
flushed = False
|
|
584
|
+
try:
|
|
585
|
+
view = self.cell._viewFromOpts(opts)
|
|
511
586
|
|
|
512
|
-
|
|
513
|
-
|
|
587
|
+
taskinfo = {'query': query, 'view': view.iden}
|
|
588
|
+
await self.cell.boss.promote('storm', user=user, info=taskinfo)
|
|
514
589
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
590
|
+
async for pode in view.iterStormPodes(query, opts=opts):
|
|
591
|
+
self.write(s_json.dumps(pode, newline=jsonlines))
|
|
592
|
+
await self.flush()
|
|
593
|
+
flushed = True
|
|
594
|
+
except Exception as e:
|
|
595
|
+
if not flushed:
|
|
596
|
+
return self._handleStormErr(e)
|
|
518
597
|
|
|
519
598
|
class StormV1(StormHandler):
|
|
520
599
|
|
|
@@ -541,10 +620,15 @@ class StormV1(StormHandler):
|
|
|
541
620
|
return
|
|
542
621
|
|
|
543
622
|
opts.setdefault('editformat', 'nodeedits')
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
self.
|
|
547
|
-
|
|
623
|
+
flushed = None
|
|
624
|
+
try:
|
|
625
|
+
async for mesg in self.getCore().storm(query, opts=opts):
|
|
626
|
+
self.write(s_json.dumps(mesg, newline=jsonlines))
|
|
627
|
+
await self.flush()
|
|
628
|
+
flushed = True
|
|
629
|
+
except Exception as e:
|
|
630
|
+
if not flushed:
|
|
631
|
+
return self._handleStormErr(e)
|
|
548
632
|
|
|
549
633
|
class StormCallV1(StormHandler):
|
|
550
634
|
|
|
@@ -569,14 +653,8 @@ class StormCallV1(StormHandler):
|
|
|
569
653
|
|
|
570
654
|
try:
|
|
571
655
|
ret = await self.getCore().callStorm(query, opts=opts)
|
|
572
|
-
except s_exc.SynErr as e:
|
|
573
|
-
mesg = e.get('mesg', str(e))
|
|
574
|
-
return self.sendRestErr(e.__class__.__name__, mesg)
|
|
575
|
-
except asyncio.CancelledError: # pragma: no cover
|
|
576
|
-
raise
|
|
577
656
|
except Exception as e:
|
|
578
|
-
|
|
579
|
-
return self.sendRestErr(e.__class__.__name__, mesg)
|
|
657
|
+
return self._handleStormErr(e)
|
|
580
658
|
else:
|
|
581
659
|
return self.sendRestRetn(ret)
|
|
582
660
|
|
|
@@ -601,14 +679,16 @@ class StormExportV1(StormHandler):
|
|
|
601
679
|
if opts is None:
|
|
602
680
|
return
|
|
603
681
|
|
|
682
|
+
flushed = False
|
|
604
683
|
try:
|
|
605
684
|
self.set_header('Content-Type', 'application/x-synapse-nodes')
|
|
606
685
|
async for pode in self.getCore().exportStorm(query, opts=opts):
|
|
607
686
|
self.write(s_msgpack.en(pode))
|
|
608
687
|
await self.flush()
|
|
609
|
-
|
|
688
|
+
flushed = True
|
|
610
689
|
except Exception as e:
|
|
611
|
-
|
|
690
|
+
if not flushed:
|
|
691
|
+
return self._handleStormErr(e)
|
|
612
692
|
|
|
613
693
|
class ReqValidStormV1(StormHandler):
|
|
614
694
|
|
|
@@ -625,12 +705,11 @@ class ReqValidStormV1(StormHandler):
|
|
|
625
705
|
query = body.get('query')
|
|
626
706
|
|
|
627
707
|
try:
|
|
628
|
-
|
|
629
|
-
except
|
|
630
|
-
|
|
631
|
-
return self.sendRestErr(e.__class__.__name__, mesg)
|
|
708
|
+
ret = await self.cell.reqValidStorm(query, opts)
|
|
709
|
+
except Exception as e:
|
|
710
|
+
return self._handleStormErr(e)
|
|
632
711
|
else:
|
|
633
|
-
return self.sendRestRetn(
|
|
712
|
+
return self.sendRestRetn(ret)
|
|
634
713
|
|
|
635
714
|
class BeholdSockV1(WebSocket):
|
|
636
715
|
|
|
@@ -671,7 +750,7 @@ class LoginV1(Handler):
|
|
|
671
750
|
|
|
672
751
|
async def post(self):
|
|
673
752
|
|
|
674
|
-
body = self.getJsonBody()
|
|
753
|
+
body = self.getJsonBody(validator=s_schemas.reqValidHttpLoginV1)
|
|
675
754
|
if body is None:
|
|
676
755
|
return
|
|
677
756
|
|
|
@@ -682,15 +761,15 @@ class LoginV1(Handler):
|
|
|
682
761
|
udef = await authcell.getUserDefByName(name)
|
|
683
762
|
if udef is None:
|
|
684
763
|
self.logAuthIssue(mesg='No such user.', username=name)
|
|
685
|
-
return self.sendRestErr('AuthDeny', 'No such user.')
|
|
764
|
+
return self.sendRestErr('AuthDeny', 'No such user.', status_code=HTTPStatus.NOT_FOUND)
|
|
686
765
|
|
|
687
766
|
if udef.get('locked'):
|
|
688
767
|
self.logAuthIssue(mesg='User is locked.', user=udef.get('iden'), username=name)
|
|
689
|
-
return self.sendRestErr('AuthDeny', 'User is locked.')
|
|
768
|
+
return self.sendRestErr('AuthDeny', 'User is locked.', status_code=HTTPStatus.FORBIDDEN)
|
|
690
769
|
|
|
691
770
|
if not await authcell.tryUserPasswd(name, passwd):
|
|
692
771
|
self.logAuthIssue(mesg='Incorrect password.', user=udef.get('iden'), username=name)
|
|
693
|
-
return self.sendRestErr('AuthDeny', 'Incorrect password.')
|
|
772
|
+
return self.sendRestErr('AuthDeny', 'Incorrect password.', status_code=HTTPStatus.FORBIDDEN)
|
|
694
773
|
|
|
695
774
|
iden = udef.get('iden')
|
|
696
775
|
sess = await self.sess()
|
|
@@ -726,10 +805,13 @@ class AuthUsersV1(Handler):
|
|
|
726
805
|
|
|
727
806
|
archived = int(self.get_argument('archived', default='0'))
|
|
728
807
|
if archived not in (0, 1):
|
|
729
|
-
return self.sendRestErr('BadHttpParam',
|
|
808
|
+
return self.sendRestErr('BadHttpParam',
|
|
809
|
+
'The parameter "archived" must be 0 or 1 if specified.',
|
|
810
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
730
811
|
|
|
731
812
|
except Exception:
|
|
732
|
-
return self.sendRestErr('BadHttpParam', 'The parameter "archived" must be 0 or 1 if specified.'
|
|
813
|
+
return self.sendRestErr('BadHttpParam', 'The parameter "archived" must be 0 or 1 if specified.',
|
|
814
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
733
815
|
|
|
734
816
|
users = await self.getAuthCell().getUserDefs()
|
|
735
817
|
|
|
@@ -758,7 +840,8 @@ class AuthUserV1(Handler):
|
|
|
758
840
|
|
|
759
841
|
udef = await self.getAuthCell().getUserDef(iden, packroles=False)
|
|
760
842
|
if udef is None:
|
|
761
|
-
self.sendRestErr('NoSuchUser', f'User {iden} does not exist.'
|
|
843
|
+
self.sendRestErr('NoSuchUser', f'User {iden} does not exist.',
|
|
844
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
762
845
|
return
|
|
763
846
|
|
|
764
847
|
self.sendRestRetn(udef)
|
|
@@ -773,7 +856,8 @@ class AuthUserV1(Handler):
|
|
|
773
856
|
|
|
774
857
|
udef = await authcell.getUserDef(iden)
|
|
775
858
|
if udef is None:
|
|
776
|
-
self.sendRestErr('NoSuchUser', f'User {iden} does not exist.'
|
|
859
|
+
self.sendRestErr('NoSuchUser', f'User {iden} does not exist.',
|
|
860
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
777
861
|
return
|
|
778
862
|
|
|
779
863
|
body = self.getJsonBody()
|
|
@@ -817,7 +901,8 @@ class AuthUserPasswdV1(Handler):
|
|
|
817
901
|
authcell = self.getAuthCell()
|
|
818
902
|
udef = await authcell.getUserDef(iden)
|
|
819
903
|
if udef is None:
|
|
820
|
-
self.sendRestErr('NoSuchUser', f'User does not exist: {iden}'
|
|
904
|
+
self.sendRestErr('NoSuchUser', f'User does not exist: {iden}',
|
|
905
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
821
906
|
return
|
|
822
907
|
|
|
823
908
|
password = body.get('passwd')
|
|
@@ -827,7 +912,7 @@ class AuthUserPasswdV1(Handler):
|
|
|
827
912
|
try:
|
|
828
913
|
await authcell.setUserPasswd(iden, password)
|
|
829
914
|
except s_exc.BadArg as e:
|
|
830
|
-
self.sendRestErr('BadArg', e.get('mesg'))
|
|
915
|
+
self.sendRestErr('BadArg', e.get('mesg'), status_code=HTTPStatus.BAD_REQUEST)
|
|
831
916
|
return
|
|
832
917
|
self.sendRestRetn(await authcell.getUserDef(iden, packroles=False))
|
|
833
918
|
|
|
@@ -840,7 +925,7 @@ class AuthRoleV1(Handler):
|
|
|
840
925
|
|
|
841
926
|
rdef = await self.getAuthCell().getRoleDef(iden)
|
|
842
927
|
if rdef is None:
|
|
843
|
-
self.sendRestErr('NoSuchRole', f'Role {iden} does not exist.')
|
|
928
|
+
self.sendRestErr('NoSuchRole', f'Role {iden} does not exist.', status_code=HTTPStatus.NOT_FOUND)
|
|
844
929
|
return
|
|
845
930
|
|
|
846
931
|
self.sendRestRetn(rdef)
|
|
@@ -853,7 +938,7 @@ class AuthRoleV1(Handler):
|
|
|
853
938
|
authcell = self.getAuthCell()
|
|
854
939
|
rdef = await authcell.getRoleDef(iden)
|
|
855
940
|
if rdef is None:
|
|
856
|
-
self.sendRestErr('NoSuchRole', f'Role {iden} does not exist.')
|
|
941
|
+
self.sendRestErr('NoSuchRole', f'Role {iden} does not exist.', status_code=HTTPStatus.NOT_FOUND)
|
|
857
942
|
return
|
|
858
943
|
|
|
859
944
|
body = self.getJsonBody()
|
|
@@ -886,13 +971,15 @@ class AuthGrantV1(Handler):
|
|
|
886
971
|
authcell = self.getAuthCell()
|
|
887
972
|
udef = await authcell.getUserDef(useriden)
|
|
888
973
|
if udef is None:
|
|
889
|
-
self.sendRestErr('NoSuchUser', f'User iden {useriden} not found.'
|
|
974
|
+
self.sendRestErr('NoSuchUser', f'User iden {useriden} not found.',
|
|
975
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
890
976
|
return
|
|
891
977
|
|
|
892
978
|
roleiden = body.get('role')
|
|
893
979
|
rdef = await authcell.getRoleDef(roleiden)
|
|
894
980
|
if rdef is None:
|
|
895
|
-
self.sendRestErr('NoSuchRole', f'Role iden {roleiden} not found.'
|
|
981
|
+
self.sendRestErr('NoSuchRole', f'Role iden {roleiden} not found.',
|
|
982
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
896
983
|
return
|
|
897
984
|
|
|
898
985
|
await authcell.addUserRole(useriden, roleiden)
|
|
@@ -920,13 +1007,15 @@ class AuthRevokeV1(Handler):
|
|
|
920
1007
|
authcell = self.getAuthCell()
|
|
921
1008
|
udef = await authcell.getUserDef(useriden)
|
|
922
1009
|
if udef is None:
|
|
923
|
-
self.sendRestErr('NoSuchUser', f'User iden {useriden} not found.'
|
|
1010
|
+
self.sendRestErr('NoSuchUser', f'User iden {useriden} not found.',
|
|
1011
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
924
1012
|
return
|
|
925
1013
|
|
|
926
1014
|
roleiden = body.get('role')
|
|
927
1015
|
rdef = await authcell.getRoleDef(roleiden)
|
|
928
1016
|
if rdef is None:
|
|
929
|
-
self.sendRestErr('NoSuchRole', f'Role iden {roleiden} not found.'
|
|
1017
|
+
self.sendRestErr('NoSuchRole', f'Role iden {roleiden} not found.',
|
|
1018
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
930
1019
|
return
|
|
931
1020
|
|
|
932
1021
|
await authcell.delUserRole(useriden, roleiden)
|
|
@@ -947,12 +1036,14 @@ class AuthAddUserV1(Handler):
|
|
|
947
1036
|
|
|
948
1037
|
name = body.get('name')
|
|
949
1038
|
if name is None:
|
|
950
|
-
self.sendRestErr('MissingField', 'The adduser API requires a "name" argument.'
|
|
1039
|
+
self.sendRestErr('MissingField', 'The adduser API requires a "name" argument.',
|
|
1040
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
951
1041
|
return
|
|
952
1042
|
|
|
953
1043
|
authcell = self.getAuthCell()
|
|
954
1044
|
if await authcell.getUserDefByName(name) is not None:
|
|
955
|
-
self.sendRestErr('DupUser', f'A user named {name} already exists.'
|
|
1045
|
+
self.sendRestErr('DupUser', f'A user named {name} already exists.',
|
|
1046
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
956
1047
|
return
|
|
957
1048
|
|
|
958
1049
|
udef = await authcell.addUser(name=name)
|
|
@@ -992,12 +1083,14 @@ class AuthAddRoleV1(Handler):
|
|
|
992
1083
|
|
|
993
1084
|
name = body.get('name')
|
|
994
1085
|
if name is None:
|
|
995
|
-
self.sendRestErr('MissingField', 'The addrole API requires a "name" argument.'
|
|
1086
|
+
self.sendRestErr('MissingField', 'The addrole API requires a "name" argument.',
|
|
1087
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
996
1088
|
return
|
|
997
1089
|
|
|
998
1090
|
authcell = self.getAuthCell()
|
|
999
1091
|
if await authcell.getRoleDefByName(name) is not None:
|
|
1000
|
-
self.sendRestErr('DupRole', f'A role named {name} already exists.'
|
|
1092
|
+
self.sendRestErr('DupRole', f'A role named {name} already exists.',
|
|
1093
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
1001
1094
|
return
|
|
1002
1095
|
|
|
1003
1096
|
rdef = await authcell.addRole(name)
|
|
@@ -1023,13 +1116,15 @@ class AuthDelRoleV1(Handler):
|
|
|
1023
1116
|
|
|
1024
1117
|
name = body.get('name')
|
|
1025
1118
|
if name is None:
|
|
1026
|
-
self.sendRestErr('MissingField', 'The delrole API requires a "name" argument.'
|
|
1119
|
+
self.sendRestErr('MissingField', 'The delrole API requires a "name" argument.',
|
|
1120
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
1027
1121
|
return
|
|
1028
1122
|
|
|
1029
1123
|
authcell = self.getAuthCell()
|
|
1030
1124
|
rdef = await authcell.getRoleDefByName(name)
|
|
1031
1125
|
if rdef is None:
|
|
1032
|
-
return self.sendRestErr('NoSuchRole', f'The role {name} does not exist!'
|
|
1126
|
+
return self.sendRestErr('NoSuchRole', f'The role {name} does not exist!',
|
|
1127
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
1033
1128
|
|
|
1034
1129
|
await authcell.delRole(rdef.get('iden'))
|
|
1035
1130
|
|
|
@@ -1055,15 +1150,17 @@ class ModelNormV1(Handler):
|
|
|
1055
1150
|
typeopts = body.get('typeopts')
|
|
1056
1151
|
|
|
1057
1152
|
if propname is None:
|
|
1058
|
-
self.sendRestErr('MissingField', 'The property normalization API requires a prop name.'
|
|
1153
|
+
self.sendRestErr('MissingField', 'The property normalization API requires a prop name.',
|
|
1154
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
1059
1155
|
return
|
|
1060
1156
|
|
|
1061
1157
|
try:
|
|
1062
1158
|
valu, info = await self.cell.getPropNorm(propname, propvalu, typeopts=typeopts)
|
|
1063
1159
|
except s_exc.NoSuchProp:
|
|
1064
|
-
return self.sendRestErr('NoSuchProp', 'The property {propname} does not exist.'
|
|
1160
|
+
return self.sendRestErr('NoSuchProp', f'The property {propname} does not exist.',
|
|
1161
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
1065
1162
|
except Exception as e:
|
|
1066
|
-
return self.sendRestExc(e)
|
|
1163
|
+
return self.sendRestExc(e, status_code=HTTPStatus.BAD_REQUEST)
|
|
1067
1164
|
else:
|
|
1068
1165
|
self.sendRestRetn({'norm': valu, 'info': info})
|
|
1069
1166
|
|
|
@@ -1136,7 +1233,8 @@ class StormVarsSetV1(Handler):
|
|
|
1136
1233
|
varname = str(body.get('name'))
|
|
1137
1234
|
varvalu = body.get('value', s_common.novalu)
|
|
1138
1235
|
if varvalu is s_common.novalu:
|
|
1139
|
-
return self.sendRestErr('BadArg', 'The "value" field is required.'
|
|
1236
|
+
return self.sendRestErr('BadArg', 'The "value" field is required.',
|
|
1237
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
1140
1238
|
|
|
1141
1239
|
if not await self.allowed(('globals', 'set', varname)):
|
|
1142
1240
|
return
|
|
@@ -1163,7 +1261,8 @@ class OnePassIssueV1(Handler):
|
|
|
1163
1261
|
try:
|
|
1164
1262
|
passwd = await authcell.genUserOnepass(iden, duration)
|
|
1165
1263
|
except s_exc.NoSuchUser:
|
|
1166
|
-
return self.sendRestErr('NoSuchUser', 'The user iden does not exist.'
|
|
1264
|
+
return self.sendRestErr('NoSuchUser', 'The user iden does not exist.',
|
|
1265
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
1167
1266
|
|
|
1168
1267
|
return self.sendRestRetn(passwd)
|
|
1169
1268
|
|
|
@@ -1196,13 +1295,15 @@ class FeedV1(Handler):
|
|
|
1196
1295
|
|
|
1197
1296
|
func = self.cell.getFeedFunc(name)
|
|
1198
1297
|
if func is None:
|
|
1199
|
-
return self.sendRestErr('NoSuchFunc', f'The feed type {name} does not exist.'
|
|
1298
|
+
return self.sendRestErr('NoSuchFunc', f'The feed type {name} does not exist.',
|
|
1299
|
+
status_code=HTTPStatus.BAD_REQUEST)
|
|
1200
1300
|
|
|
1201
1301
|
user = self.cell.auth.user(self.web_useriden)
|
|
1202
1302
|
|
|
1203
1303
|
view = self.cell.getView(body.get('view'), user)
|
|
1204
1304
|
if view is None:
|
|
1205
|
-
return self.sendRestErr('NoSuchView', 'The specified view does not exist.'
|
|
1305
|
+
return self.sendRestErr('NoSuchView', 'The specified view does not exist.',
|
|
1306
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
1206
1307
|
|
|
1207
1308
|
wlyr = view.layers[0]
|
|
1208
1309
|
perm = ('feed:data', *name.split('.'))
|
|
@@ -1210,7 +1311,7 @@ class FeedV1(Handler):
|
|
|
1210
1311
|
if not user.allowed(perm, gateiden=wlyr.iden):
|
|
1211
1312
|
permtext = '.'.join(perm)
|
|
1212
1313
|
mesg = f'User does not have {permtext} permission on gate: {wlyr.iden}.'
|
|
1213
|
-
return self.sendRestErr('AuthDeny', mesg)
|
|
1314
|
+
return self.sendRestErr('AuthDeny', mesg, status_code=HTTPStatus.FORBIDDEN)
|
|
1214
1315
|
|
|
1215
1316
|
try:
|
|
1216
1317
|
|
|
@@ -1224,7 +1325,7 @@ class FeedV1(Handler):
|
|
|
1224
1325
|
return self.sendRestRetn(None)
|
|
1225
1326
|
|
|
1226
1327
|
except Exception as e: # pragma: no cover
|
|
1227
|
-
return self.sendRestExc(e)
|
|
1328
|
+
return self.sendRestExc(e, status_code=HTTPStatus.BAD_REQUEST)
|
|
1228
1329
|
|
|
1229
1330
|
class CoreInfoV1(Handler):
|
|
1230
1331
|
'''
|
|
@@ -1279,8 +1380,8 @@ class ExtApiHandler(StormHandler):
|
|
|
1279
1380
|
core = self.getCore()
|
|
1280
1381
|
adef, args = await core.getHttpExtApiByPath(path)
|
|
1281
1382
|
if adef is None:
|
|
1282
|
-
self.
|
|
1283
|
-
|
|
1383
|
+
self.sendRestErr('NoSuchPath', f'No Extended HTTP API endpoint matches {path}',
|
|
1384
|
+
status_code=HTTPStatus.NOT_FOUND)
|
|
1284
1385
|
return await self.finish()
|
|
1285
1386
|
|
|
1286
1387
|
requester = ''
|
|
@@ -1304,13 +1405,12 @@ class ExtApiHandler(StormHandler):
|
|
|
1304
1405
|
|
|
1305
1406
|
storm = adef['methods'].get(meth)
|
|
1306
1407
|
if storm is None:
|
|
1307
|
-
self.set_status(405)
|
|
1308
1408
|
meths = [meth.upper() for meth in adef.get('methods')]
|
|
1309
1409
|
self.set_header('Allowed', ', '.join(meths))
|
|
1310
1410
|
mesg = f'Extended HTTP API {iden} has no method for {meth.upper()}.'
|
|
1311
1411
|
if meths:
|
|
1312
1412
|
mesg = f'{mesg} Supports {", ".join(meths)}.'
|
|
1313
|
-
self.sendRestErr('NeedConfValu', mesg)
|
|
1413
|
+
self.sendRestErr('NeedConfValu', mesg, status_code=HTTPStatus.METHOD_NOT_ALLOWED)
|
|
1314
1414
|
return await self.finish()
|
|
1315
1415
|
|
|
1316
1416
|
# We flatten the request headers and parameters into a flat key/valu map.
|
|
@@ -1389,9 +1489,9 @@ class ExtApiHandler(StormHandler):
|
|
|
1389
1489
|
elif mtyp == 'http:resp:body':
|
|
1390
1490
|
if not rcode:
|
|
1391
1491
|
self.clear()
|
|
1392
|
-
self.set_status(500)
|
|
1393
1492
|
self.sendRestErr('StormRuntimeError',
|
|
1394
|
-
f'Extended HTTP API {iden} must set status code before sending body.'
|
|
1493
|
+
f'Extended HTTP API {iden} must set status code before sending body.',
|
|
1494
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
1395
1495
|
return await self.finish()
|
|
1396
1496
|
rbody = True
|
|
1397
1497
|
body = info['body']
|
|
@@ -1411,8 +1511,7 @@ class ExtApiHandler(StormHandler):
|
|
|
1411
1511
|
# Since we haven't flushed the body yet, we can clear the handler
|
|
1412
1512
|
# and send the error the user.
|
|
1413
1513
|
self.clear()
|
|
1414
|
-
self.
|
|
1415
|
-
self.sendRestErr(errname, erfo.get('mesg'))
|
|
1514
|
+
self.sendRestErr(errname, erfo.get('mesg'), status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
1416
1515
|
rcode = True
|
|
1417
1516
|
rbody = True
|
|
1418
1517
|
|
|
@@ -1422,13 +1521,13 @@ class ExtApiHandler(StormHandler):
|
|
|
1422
1521
|
logger.exception(f'Extended HTTP API {iden} encountered fatal error: {enfo[1].get("mesg")}')
|
|
1423
1522
|
if rbody is False:
|
|
1424
1523
|
self.clear()
|
|
1425
|
-
self.set_status(500)
|
|
1426
1524
|
self.sendRestErr(enfo[0],
|
|
1427
|
-
f'Extended HTTP API {iden} encountered fatal error: {enfo[1].get("mesg")}'
|
|
1525
|
+
f'Extended HTTP API {iden} encountered fatal error: {enfo[1].get("mesg")}',
|
|
1526
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
1428
1527
|
|
|
1429
1528
|
if rcode is False:
|
|
1430
1529
|
self.clear()
|
|
1431
|
-
self.
|
|
1432
|
-
|
|
1530
|
+
self.sendRestErr('StormRuntimeError', f'Extended HTTP API {iden} never set status code.',
|
|
1531
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
1433
1532
|
|
|
1434
1533
|
await self.finish()
|
synapse/lib/lmdbslab.py
CHANGED