synapse 2.205.0__py311-none-any.whl → 2.206.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/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(204)
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 getJsonBody(self, validator=None):
119
- return self.loadJsonMesg(self.request.body, validator=validator)
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
- def sendRestErr(self, code, mesg):
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__, str(e))
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
- def sendRestRetn(self, valu):
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.set_status(401)
180
- self.sendRestErr('NotAuthenticated', 'The session is not logged in.')
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
- view = self.cell._viewFromOpts(opts)
583
+ flushed = False
584
+ try:
585
+ view = self.cell._viewFromOpts(opts)
511
586
 
512
- taskinfo = {'query': query, 'view': view.iden}
513
- await self.cell.boss.promote('storm', user=user, info=taskinfo)
587
+ taskinfo = {'query': query, 'view': view.iden}
588
+ await self.cell.boss.promote('storm', user=user, info=taskinfo)
514
589
 
515
- async for pode in view.iterStormPodes(query, opts=opts):
516
- self.write(s_json.dumps(pode, newline=jsonlines))
517
- await self.flush()
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
- async for mesg in self.getCore().storm(query, opts=opts):
546
- self.write(s_json.dumps(mesg, newline=jsonlines))
547
- await self.flush()
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
- mesg = str(e)
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
- return self.sendRestExc(e)
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
- valid = await self.cell.reqValidStorm(query, opts)
629
- except s_exc.SynErr as e:
630
- mesg = e.get('mesg', str(e))
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(valid)
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', 'The parameter "archived" must be 0 or 1 if specified.')
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.set_status(404)
1283
- self.sendRestErr('NoSuchPath', f'No Extended HTTP API endpoint matches {path}')
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.set_status(500)
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.set_status(500)
1432
- self.sendRestErr('StormRuntimeError', f'Extended HTTP API {iden} never set status code.')
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
@@ -1931,6 +1931,8 @@ class Scan:
1931
1931
  '''
1932
1932
  Returns if the cursor is at the value in atitem
1933
1933
  '''
1934
+ if not self.dupsort:
1935
+ return self.atitem[0] == self.curs.item()[0]
1934
1936
  return self.atitem == self.curs.item()
1935
1937
 
1936
1938
  class ScanKeys(Scan):