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
« prev ^ index » next coverage.py v7.6.10, created at 2025-03-24 14:08 +0000
1import logging
3import grpc
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
13logger = logging.getLogger(__name__)
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 )
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
33 # double check
34 assert user.is_jailed == jailed
36 return res
39class Jail(jail_pb2_grpc.JailServicer):
40 """
41 The Jail servicer.
43 API calls allowed for users who need to complete some tasks before being
44 fully active
45 """
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)
51 def AcceptTOS(self, request, context, session):
52 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
54 if not request.accept:
55 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.CANT_UNACCEPT_TOS)
57 user.accepted_tos = TOS_VERSION
58 session.commit()
60 return _get_jail_info(session, user)
62 def SetLocation(self, request, context, session):
63 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
65 if request.lat == 0 and request.lng == 0:
66 context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_COORDINATE)
68 user.city = request.city
69 user.geom = create_coordinate(request.lat, request.lng)
70 user.geom_radius = request.radius
72 session.commit()
74 return _get_jail_info(session, user)
76 def AcceptCommunityGuidelines(self, request, context, session):
77 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
79 if not request.accept:
80 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.CANT_UNACCEPT_COMMUNITY_GUIDELINES)
82 user.accepted_community_guidelines = GUIDELINES_VERSION
83 session.commit()
85 return _get_jail_info(session, user)
87 def AcknowledgePendingModNote(self, request, context, session):
88 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
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()
97 if not note:
98 context.abort(grpc.StatusCode.NOT_FOUND, errors.MOD_NOTE_NOT_FOUND)
100 if not request.acknowledge:
101 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.MOD_NOTE_NEED_TO_ACKNOWELDGE)
103 note.acknowledged = now()
104 session.flush()
106 return _get_jail_info(session, user)
108 def RespondToActivenessProbe(self, request, context, session):
109 user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
111 probe = session.execute(
112 select(ActivenessProbe).where(ActivenessProbe.user_id == user.id).where(ActivenessProbe.is_pending)
113 ).scalar_one_or_none()
115 if not probe:
116 context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.PROBE_NOT_FOUND)
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)
127 probe.responded = now()
128 session.flush()
130 return _get_jail_info(session, user)