fix: implement ban lease

Hee Sung Son committed Nov 27, 2025 at 21:10 UTC 9f98d163616ad25ce5f88f706f1c6613e1ff7175
4 files changed +37 -27
cmd/relay-server/frontend/src/hooks/useAdmin.ts
+15 -10
@@ -39,10 +39,18 @@ export function useAdmin() {
39 }
40
41 const leasesData = await leasesRes.json();
42 - const bannedData = await bannedRes.json();
42 + const bannedData: string[] = await bannedRes.json();
43
44 setLeases(leasesData || []);
45 - setBannedLeases(bannedData || []);
45 + // bannedData is base64 encoded byte arrays, decode them
46 + const decodedBanned = (bannedData || []).map((b64: string) => {
47 + try {
48 + return atob(b64);
49 + } catch {
50 + return b64;
51 + }
52 + });
53 + setBannedLeases(decodedBanned);
54 } catch (err: any) {
55 setError(err.message);
56 } finally {
@@ -54,15 +62,12 @@ export function useAdmin() {
62 fetchData();
63 }, [fetchData]);
64
57 - const toUrlSafe = (base64: string) => {
58 - return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
59 - };
60 -
61 - const handleBanStatus = async (base64Id: string, isBan: boolean) => {
65 + const handleBanStatus = async (leaseId: string, isBan: boolean) => {
66 try {
63 - const safeId = toUrlSafe(base64Id);
64 - await fetch(`/admin/leases/${safeId}/ban`, {
65 - method: isBan ? "POST" : "DELETE"
67 + // URL-safe base64 encode the lease ID
68 + const safeId = btoa(leaseId).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
69 + await fetch(`/admin/leases/${safeId}/ban`, {
70 + method: isBan ? "POST" : "DELETE"
71 });
72 fetchData();
73 } catch (err) {
cmd/relay-server/view.go
+22
@@ -277,6 +277,11 @@ func convertLeaseEntriesToRows(serv *portal.RelayServer) []leaseRow {
277 lease := leaseEntry.Lease
278 identityID := string(lease.Identity.Id)
279
280 + // Skip banned leases for user-facing list
281 + if isLeaseBanned(serv, identityID) {
282 + continue
283 + }
284 +
285 // Metadata parsing
286 var meta sdk.Metadata
287 _ = json.Unmarshal([]byte(lease.Metadata), &meta)
@@ -388,3 +393,20 @@ func isLocalhost(r *http.Request) bool {
393 return host == "127.0.0.1" || host == "::1"
394 }
395
396 +// isLeaseBanned checks if a lease ID is in the banned list
397 +func isLeaseBanned(serv *portal.RelayServer, leaseID string) bool {
398 + bannedList := serv.GetLeaseManager().GetBannedLeases()
399 + for _, banned := range bannedList {
400 + bannedStr := string(banned)
401 + log.Debug().
402 + Str("checking_lease", leaseID).
403 + Str("banned_entry", bannedStr).
404 + Bool("match", bannedStr == leaseID).
405 + Msg("[BanCheck] Comparing lease IDs")
406 + if bannedStr == leaseID {
407 + return true
408 + }
409 + }
410 + return false
411 +}
412 +
portal/lease.go
-5
@@ -158,11 +158,6 @@ func (lm *LeaseManager) GetLease(identity *rdsec.Identity) (*LeaseEntry, bool) {
158 defer lm.leasesLock.RUnlock()
159
160 identityID := string(identity.Id)
161 -
162 - // Check if banned
163 - if _, banned := lm.bannedLeases[identityID]; banned {
164 - return nil, false
165 - }
161
162 lease, exists := lm.leases[identityID]
163 if !exists {
portal/relay.go
-12
@@ -301,18 +301,6 @@ func (g *RelayServer) IsConnectionActive(connectionID int64) bool {
301 return exists
302 }
303
304 -// CloseConnection closes the connection with the given ID
305 -func (g *RelayServer) CloseConnection(connectionID int64) {
306 - g.connectionsLock.Lock()
307 - connection, exists := g.connections[connectionID]
308 - g.connectionsLock.Unlock()
309 -
310 - if exists {
311 - log.Info().Int64("conn_id", connectionID).Msg("[RelayServer] Force closing connection")
312 - connection.conn.Close()
313 - }
314 -}
315 -
304 // GetAllLeaseEntries returns all lease entries from the lease manager
305 func (g *RelayServer) GetAllLeaseEntries() []*LeaseEntry {
306 g.leaseManager.leasesLock.RLock()