diff --git a/internal/home/clientshttp.go b/internal/home/clientshttp.go index e53cf1a8..0e6be245 100644 --- a/internal/home/clientshttp.go +++ b/internal/home/clientshttp.go @@ -435,25 +435,30 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http break } - ip, _ := netip.ParseAddr(idStr) - c, ok := clients.storage.Find(idStr) - var cj *clientJSON - if !ok { - cj = clients.findRuntime(ip, idStr) - } else { - cj = clientToJSON(c) - disallowed, rule := clients.clientChecker.IsBlockedClient(ip, idStr) - cj.Disallowed, cj.DisallowedRule = &disallowed, &rule - } - data = append(data, map[string]*clientJSON{ - idStr: cj, + idStr: clients.findClient(idStr), }) } aghhttp.WriteJSONResponseOK(w, r, data) } +// findClient returns available information about a client by idStr from the +// client's storage or access settings. cj is guaranteed to be non-nil. +func (clients *clientsContainer) findClient(idStr string) (cj *clientJSON) { + ip, _ := netip.ParseAddr(idStr) + c, ok := clients.storage.Find(idStr) + if !ok { + return clients.findRuntime(ip, idStr) + } + + cj = clientToJSON(c) + disallowed, rule := clients.clientChecker.IsBlockedClient(ip, idStr) + cj.Disallowed, cj.DisallowedRule = &disallowed, &rule + + return cj +} + // searchQueryJSON is a request to the POST /control/clients/search HTTP API. // // TODO(s.chzhen): Add UIDs. @@ -480,19 +485,12 @@ func (clients *clientsContainer) handleSearchClient(w http.ResponseWriter, r *ht data := []map[string]*clientJSON{} for _, c := range q.Clients { idStr := c.ID - ip, _ := netip.ParseAddr(idStr) - c, ok := clients.storage.Find(idStr) - var cj *clientJSON - if !ok { - cj = clients.findRuntime(ip, idStr) - } else { - cj = clientToJSON(c) - disallowed, rule := clients.clientChecker.IsBlockedClient(ip, idStr) - cj.Disallowed, cj.DisallowedRule = &disallowed, &rule + if idStr == "" { + break } data = append(data, map[string]*clientJSON{ - idStr: cj, + idStr: clients.findClient(idStr), }) } diff --git a/internal/home/clientshttp_internal_test.go b/internal/home/clientshttp_internal_test.go index 95e5950d..c1c495f2 100644 --- a/internal/home/clientshttp_internal_test.go +++ b/internal/home/clientshttp_internal_test.go @@ -16,6 +16,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/internal/client" "github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/schedule" + "github.com/AdguardTeam/AdGuardHome/internal/whois" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -410,10 +411,28 @@ func TestClientsContainer_HandleFindClient(t *testing.T) { } func TestClientsContainer_HandleSearchClient(t *testing.T) { + var ( + runtimeCli = "runtime_client1" + + runtimeCliIP = "3.3.3.3" + blockedCliIP = "4.4.4.4" + nonExistentCliIP = "5.5.5.5" + + allowed = false + dissallowed = true + + emptyRule = "" + disallowedRule = "disallowed_rule" + ) + clients := newClientsContainer(t) clients.clientChecker = &testBlockedClientChecker{ - onIsBlockedClient: func(ip netip.Addr, clientID string) (ok bool, rule string) { - return false, "" + onIsBlockedClient: func(ip netip.Addr, _ string) (ok bool, rule string) { + if ip == netip.MustParseAddr(blockedCliIP) { + return true, disallowedRule + } + + return false, emptyRule }, } @@ -429,11 +448,13 @@ func TestClientsContainer_HandleSearchClient(t *testing.T) { assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo}) + clients.UpdateAddress(ctx, netip.MustParseAddr(runtimeCliIP), runtimeCli, nil) + testCases := []struct { - name string - query *searchQueryJSON - wantCode int - wantClient []*client.Persistent + name string + query *searchQueryJSON + wantPersistent []*client.Persistent + wantRuntime *clientJSON }{{ name: "single", query: &searchQueryJSON{ @@ -441,8 +462,7 @@ func TestClientsContainer_HandleSearchClient(t *testing.T) { ID: testClientIP1, }}, }, - wantCode: http.StatusOK, - wantClient: []*client.Persistent{clientOne}, + wantPersistent: []*client.Persistent{clientOne}, }, { name: "multiple", query: &searchQueryJSON{ @@ -452,8 +472,47 @@ func TestClientsContainer_HandleSearchClient(t *testing.T) { ID: testClientIP2, }}, }, - wantCode: http.StatusOK, - wantClient: []*client.Persistent{clientOne, clientTwo}, + wantPersistent: []*client.Persistent{clientOne, clientTwo}, + }, { + name: "runtime", + query: &searchQueryJSON{ + Clients: []searchClientJSON{{ + ID: runtimeCliIP, + }}, + }, + wantRuntime: &clientJSON{ + Name: runtimeCli, + IDs: []string{runtimeCliIP}, + Disallowed: &allowed, + DisallowedRule: &emptyRule, + WHOIS: &whois.Info{}, + }, + }, { + name: "blocked_access", + query: &searchQueryJSON{ + Clients: []searchClientJSON{{ + ID: blockedCliIP, + }}, + }, + wantRuntime: &clientJSON{ + IDs: []string{blockedCliIP}, + Disallowed: &dissallowed, + DisallowedRule: &disallowedRule, + WHOIS: &whois.Info{}, + }, + }, { + name: "non_existing_client", + query: &searchQueryJSON{ + Clients: []searchClientJSON{{ + ID: nonExistentCliIP, + }}, + }, + wantRuntime: &clientJSON{ + IDs: []string{nonExistentCliIP}, + Disallowed: &allowed, + DisallowedRule: &emptyRule, + WHOIS: &whois.Info{}, + }, }} for _, tc := range testCases { @@ -469,7 +528,7 @@ func TestClientsContainer_HandleSearchClient(t *testing.T) { rw := httptest.NewRecorder() clients.handleSearchClient(rw, r) require.NoError(t, err) - require.Equal(t, tc.wantCode, rw.Code) + require.Equal(t, http.StatusOK, rw.Code) body, err = io.ReadAll(rw.Body) require.NoError(t, err) @@ -478,7 +537,17 @@ func TestClientsContainer_HandleSearchClient(t *testing.T) { err = json.Unmarshal(body, &clientData) require.NoError(t, err) - assertPersistentClientsData(t, clients, clientData, tc.wantClient) + if tc.wantPersistent != nil { + assertPersistentClientsData(t, clients, clientData, tc.wantPersistent) + + return + } + + require.Len(t, clientData, 1) + require.Len(t, clientData[0], 1) + + rc := clientData[0][tc.wantRuntime.IDs[0]] + assert.Equal(t, tc.wantRuntime, rc) }) } } diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md index 8911b3a2..c7aab4f6 100644 --- a/openapi/CHANGELOG.md +++ b/openapi/CHANGELOG.md @@ -13,9 +13,8 @@ ### New client APIs -* The new `POST /control/clients/search` HTTP API allows config updates. - -These APIs accept and return a JSON object with the following format: +* The new `POST /control/clients/search` HTTP API allows config updates. It + accepts a JSON object with the following format: ```json { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 06173426..f0a8ea6c 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -2779,8 +2779,8 @@ 'clients': 'type': 'array' 'items': - '$ref': '#/components/schemas/ClientsIDEntry' - 'ClientsIDEntry': + '$ref': '#/components/schemas/ClientsSearchRequestItem' + 'ClientsSearchRequestItem': 'type': 'object' 'properties': 'id':