Coverage for src/couchers/servicers/jail.py: 97%

70 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-03-24 14:08 +0000

1import logging 

2 

3import grpc 

4 

5from couchers import errors 

6from couchers.constants import GUIDELINES_VERSION, TOS_VERSION 

7from couchers.models import ActivenessProbe, ActivenessProbeStatus, HostingStatus, ModNote, User 

8from couchers.servicers.account import mod_note_to_pb 

9from couchers.sql import couchers_select as select 

10from couchers.utils import create_coordinate, now 

11from proto import jail_pb2, jail_pb2_grpc 

12 

13logger = logging.getLogger(__name__) 

14 

15 

16def _get_jail_info(session, user): 

17 res = jail_pb2.JailInfoRes( 

18 has_not_accepted_tos=user.jailed_missing_tos, 

19 has_not_added_location=user.is_missing_location, 

20 has_not_accepted_community_guidelines=user.jailed_missing_community_guidelines, 

21 has_pending_mod_notes=user.jailed_pending_mod_notes, 

22 pending_mod_notes=[mod_note_to_pb(note) for note in user.mod_notes.where(ModNote.is_pending)], 

23 has_pending_activeness_probe=user.jailed_pending_activeness_probe, 

24 ) 

25 

26 # if any of the bools in res are true, we're jailed 

27 jailed = False 

28 for field in res.DESCRIPTOR.fields: 

29 if getattr(res, field.name): 

30 jailed = True 

31 res.jailed = jailed 

32 

33 # double check 

34 assert user.is_jailed == jailed 

35 

36 return res 

37 

38 

39class Jail(jail_pb2_grpc.JailServicer): 

40 """ 

41 The Jail servicer. 

42 

43 API calls allowed for users who need to complete some tasks before being 

44 fully active 

45 """ 

46 

47 def JailInfo(self, request, context, session): 

48 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one() 

49 return _get_jail_info(session, user) 

50 

51 def AcceptTOS(self, request, context, session): 

52 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one() 

53 

54 if not request.accept: 

55 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.CANT_UNACCEPT_TOS) 

56 

57 user.accepted_tos = TOS_VERSION 

58 session.commit() 

59 

60 return _get_jail_info(session, user) 

61 

62 def SetLocation(self, request, context, session): 

63 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one() 

64 

65 if request.lat == 0 and request.lng == 0: 

66 context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_COORDINATE) 

67 

68 user.city = request.city 

69 user.geom = create_coordinate(request.lat, request.lng) 

70 user.geom_radius = request.radius 

71 

72 session.commit() 

73 

74 return _get_jail_info(session, user) 

75 

76 def AcceptCommunityGuidelines(self, request, context, session): 

77 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one() 

78 

79 if not request.accept: 

80 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.CANT_UNACCEPT_COMMUNITY_GUIDELINES) 

81 

82 user.accepted_community_guidelines = GUIDELINES_VERSION 

83 session.commit() 

84 

85 return _get_jail_info(session, user) 

86 

87 def AcknowledgePendingModNote(self, request, context, session): 

88 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one() 

89 

90 note = session.execute( 

91 select(ModNote) 

92 .where(ModNote.user_id == user.id) 

93 .where(ModNote.is_pending) 

94 .where(ModNote.id == request.note_id) 

95 ).scalar_one_or_none() 

96 

97 if not note: 

98 context.abort(grpc.StatusCode.NOT_FOUND, errors.MOD_NOTE_NOT_FOUND) 

99 

100 if not request.acknowledge: 

101 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.MOD_NOTE_NEED_TO_ACKNOWELDGE) 

102 

103 note.acknowledged = now() 

104 session.flush() 

105 

106 return _get_jail_info(session, user) 

107 

108 def RespondToActivenessProbe(self, request, context, session): 

109 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one() 

110 

111 probe = session.execute( 

112 select(ActivenessProbe).where(ActivenessProbe.user_id == user.id).where(ActivenessProbe.is_pending) 

113 ).scalar_one_or_none() 

114 

115 if not probe: 

116 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.PROBE_NOT_FOUND) 

117 

118 if request.response == jail_pb2.ACTIVENESS_PROBE_RESPONSE_STILL_ACTIVE: 

119 probe.response = ActivenessProbeStatus.still_active 

120 elif request.response == jail_pb2.ACTIVENESS_PROBE_RESPONSE_NO_LONGER_ACTIVE: 

121 probe.response = ActivenessProbeStatus.no_longer_active 

122 # disable hosting 

123 user.hosting_status = HostingStatus.cant_host 

124 else: 

125 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.PROBE_RESPONSE_INVALID) 

126 

127 probe.responded = now() 

128 session.flush() 

129 

130 return _get_jail_info(session, user)