@@ -147,7 +147,8 @@ def test_self_service_pending_approval(
147147 echo_server_port ,
148148 shared_wg : WarpgateProcess ,
149149 ):
150- """When auto-approve is off, request stays pending until admin approves."""
150+ """When auto-approve is off, request stays pending until admin approves.
151+ Admin approval does NOT create a ticket — the user must activate it."""
151152 url = f"https://localhost:{ shared_wg .http_port } "
152153 with admin_client (url ) as api :
153154 user , target , role = self ._setup_user_and_target (api , echo_server_port )
@@ -171,14 +172,23 @@ def test_self_service_pending_approval(
171172 assert data ["secret" ] is None
172173 request_id = data ["request" ]["id" ]
173174
174- # Admin approves via admin API
175+ # Admin approves via admin API — returns TicketRequest, no secret
175176 with admin_client (url ) as api :
176177 result = api .approve_ticket_request (request_id )
177- assert result .request .status == "Approved"
178- assert result .secret is not None
179- secret = result .secret
178+ assert result .status == "Approved"
179+ assert result .ticket_id is None # no ticket yet
180180
181- # The approved ticket should work
181+ # User activates the approved request via gateway API
182+ resp = session .post (
183+ f"{ url } /@warpgate/api/ticket-requests/{ request_id } /activate" ,
184+ )
185+ assert resp .status_code == 200
186+ activate_data = resp .json ()
187+ assert activate_data ["secret" ] is not None
188+ assert activate_data ["request" ]["ticket_id" ] is not None
189+ secret = activate_data ["secret" ]
190+
191+ # The activated ticket should work for HTTP access
182192 verify_session = requests .Session ()
183193 verify_session .verify = False
184194 resp = verify_session .get (
@@ -190,6 +200,122 @@ def test_self_service_pending_approval(
190200 finally :
191201 _disable_self_service (url )
192202
203+ def test_activate_double_rejected (
204+ self ,
205+ echo_server_port ,
206+ shared_wg : WarpgateProcess ,
207+ ):
208+ """Activating an already-activated request returns 409."""
209+ url = f"https://localhost:{ shared_wg .http_port } "
210+ with admin_client (url ) as api :
211+ user , target , role = self ._setup_user_and_target (api , echo_server_port )
212+ api .update_parameters (_default_params (
213+ ticket_self_service_enabled = True ,
214+ ticket_auto_approve_existing_access = False ,
215+ ))
216+
217+ try :
218+ session = self ._login (url , user .username )
219+ resp = session .post (
220+ f"{ url } /@warpgate/api/ticket-requests" ,
221+ json = {
222+ "target_name" : target .name ,
223+ "description" : "double activate test" ,
224+ },
225+ )
226+ assert resp .status_code == 201
227+ request_id = resp .json ()["request" ]["id" ]
228+
229+ with admin_client (url ) as api :
230+ api .approve_ticket_request (request_id )
231+
232+ # First activation succeeds
233+ resp = session .post (
234+ f"{ url } /@warpgate/api/ticket-requests/{ request_id } /activate" ,
235+ )
236+ assert resp .status_code == 200
237+
238+ # Second activation should fail with 409
239+ resp = session .post (
240+ f"{ url } /@warpgate/api/ticket-requests/{ request_id } /activate" ,
241+ )
242+ assert resp .status_code == 409
243+ finally :
244+ _disable_self_service (url )
245+
246+ def test_activate_pending_rejected (
247+ self ,
248+ echo_server_port ,
249+ shared_wg : WarpgateProcess ,
250+ ):
251+ """Activating a still-pending request returns 404 (not approved yet)."""
252+ url = f"https://localhost:{ shared_wg .http_port } "
253+ with admin_client (url ) as api :
254+ user , target , role = self ._setup_user_and_target (api , echo_server_port )
255+ api .update_parameters (_default_params (
256+ ticket_self_service_enabled = True ,
257+ ticket_auto_approve_existing_access = False ,
258+ ))
259+
260+ try :
261+ session = self ._login (url , user .username )
262+ resp = session .post (
263+ f"{ url } /@warpgate/api/ticket-requests" ,
264+ json = {
265+ "target_name" : target .name ,
266+ "description" : "not yet approved" ,
267+ },
268+ )
269+ assert resp .status_code == 201
270+ request_id = resp .json ()["request" ]["id" ]
271+
272+ # Try to activate without admin approval — should 404
273+ resp = session .post (
274+ f"{ url } /@warpgate/api/ticket-requests/{ request_id } /activate" ,
275+ )
276+ assert resp .status_code == 404
277+ finally :
278+ _disable_self_service (url )
279+
280+ def test_activate_target_gone (
281+ self ,
282+ echo_server_port ,
283+ shared_wg : WarpgateProcess ,
284+ ):
285+ """Activating when the target has been deleted returns 410."""
286+ url = f"https://localhost:{ shared_wg .http_port } "
287+ with admin_client (url ) as api :
288+ user , target , role = self ._setup_user_and_target (api , echo_server_port )
289+ api .update_parameters (_default_params (
290+ ticket_self_service_enabled = True ,
291+ ticket_auto_approve_existing_access = False ,
292+ ))
293+
294+ try :
295+ session = self ._login (url , user .username )
296+ resp = session .post (
297+ f"{ url } /@warpgate/api/ticket-requests" ,
298+ json = {
299+ "target_name" : target .name ,
300+ "description" : "target will be deleted" ,
301+ },
302+ )
303+ assert resp .status_code == 201
304+ request_id = resp .json ()["request" ]["id" ]
305+
306+ # Admin approves, then deletes the target
307+ with admin_client (url ) as api :
308+ api .approve_ticket_request (request_id )
309+ api .delete_target (target .id )
310+
311+ # User tries to activate — target is gone
312+ resp = session .post (
313+ f"{ url } /@warpgate/api/ticket-requests/{ request_id } /activate" ,
314+ )
315+ assert resp .status_code == 410
316+ finally :
317+ _disable_self_service (url )
318+
193319 def test_self_service_deny (
194320 self ,
195321 echo_server_port ,
0 commit comments