Skip to content

Commit 6daf330

Browse files
authored
chore: allow organization name or uuid for audit log searching (#13721)
* chore: allow organization name or uuid for audit log searching
1 parent 3cc86cf commit 6daf330

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

coderd/audit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) {
4545
}
4646

4747
queryStr := r.URL.Query().Get("q")
48-
filter, errs := searchquery.AuditLogs(queryStr)
48+
filter, errs := searchquery.AuditLogs(ctx, api.Database, queryStr)
4949
if len(errs) > 0 {
5050
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
5151
Message: "Invalid audit search query.",

coderd/audit_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,48 @@ func TestAuditLogs(t *testing.T) {
177177

178178
// Using the organization selector allows the org admin to fetch audit logs
179179
alogs, err := orgAdmin.AuditLogs(ctx, codersdk.AuditLogsRequest{
180-
SearchQuery: fmt.Sprintf("organization_id:%s", owner.OrganizationID.String()),
180+
SearchQuery: fmt.Sprintf("organization:%s", owner.OrganizationID.String()),
181181
Pagination: codersdk.Pagination{
182182
Limit: 5,
183183
},
184184
})
185185
require.NoError(t, err)
186186
require.Len(t, alogs.AuditLogs, 1)
187+
188+
// Also try fetching by organization name
189+
organization, err := orgAdmin.Organization(ctx, owner.OrganizationID)
190+
require.NoError(t, err)
191+
192+
alogs, err = orgAdmin.AuditLogs(ctx, codersdk.AuditLogsRequest{
193+
SearchQuery: fmt.Sprintf("organization:%s", organization.Name),
194+
Pagination: codersdk.Pagination{
195+
Limit: 5,
196+
},
197+
})
198+
require.NoError(t, err)
199+
require.Len(t, alogs.AuditLogs, 1)
200+
})
201+
202+
t.Run("Organization404", func(t *testing.T) {
203+
t.Parallel()
204+
205+
logger := slogtest.Make(t, &slogtest.Options{
206+
IgnoreErrors: true,
207+
})
208+
ctx := context.Background()
209+
client := coderdtest.New(t, &coderdtest.Options{
210+
Logger: &logger,
211+
})
212+
owner := coderdtest.CreateFirstUser(t, client)
213+
orgAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgAdmin(owner.OrganizationID))
214+
215+
_, err := orgAdmin.AuditLogs(ctx, codersdk.AuditLogsRequest{
216+
SearchQuery: fmt.Sprintf("organization:%s", "random-name"),
217+
Pagination: codersdk.Pagination{
218+
Limit: 5,
219+
},
220+
})
221+
require.Error(t, err)
187222
})
188223
}
189224

coderd/searchquery/search.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package searchquery
22

33
import (
4+
"context"
45
"database/sql"
56
"fmt"
67
"net/url"
@@ -16,7 +17,9 @@ import (
1617
"github.com/coder/coder/v2/codersdk"
1718
)
1819

19-
func AuditLogs(query string) (database.GetAuditLogsOffsetParams, []codersdk.ValidationError) {
20+
// AuditLogs requires the database to fetch an organization by name
21+
// to convert to organization uuid.
22+
func AuditLogs(ctx context.Context, db database.Store, query string) (database.GetAuditLogsOffsetParams, []codersdk.ValidationError) {
2023
// Always lowercase for all searches.
2124
query = strings.ToLower(query)
2225
values, errors := searchTerms(query, func(term string, values url.Values) error {
@@ -30,7 +33,6 @@ func AuditLogs(query string) (database.GetAuditLogsOffsetParams, []codersdk.Vali
3033
const dateLayout = "2006-01-02"
3134
parser := httpapi.NewQueryParamParser()
3235
filter := database.GetAuditLogsOffsetParams{
33-
OrganizationID: parser.UUID(values, uuid.Nil, "organization_id"),
3436
ResourceID: parser.UUID(values, uuid.Nil, "resource_id"),
3537
ResourceTarget: parser.String(values, "", "resource_target"),
3638
Username: parser.String(values, "", "username"),
@@ -44,6 +46,28 @@ func AuditLogs(query string) (database.GetAuditLogsOffsetParams, []codersdk.Vali
4446
if !filter.DateTo.IsZero() {
4547
filter.DateTo = filter.DateTo.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
4648
}
49+
50+
// Convert the "organization" parameter to an organization uuid. This can require
51+
// a database lookup.
52+
organizationArg := parser.String(values, "", "organization")
53+
if organizationArg != "" {
54+
organizationID, err := uuid.Parse(organizationArg)
55+
if err == nil {
56+
filter.OrganizationID = organizationID
57+
} else {
58+
// Organization could be a name
59+
organization, err := db.GetOrganizationByName(ctx, organizationArg)
60+
if err != nil {
61+
parser.Errors = append(parser.Errors, codersdk.ValidationError{
62+
Field: "organization",
63+
Detail: fmt.Sprintf("Organization %q either does not exist, or you are unauthorized to view it", organizationArg),
64+
})
65+
} else {
66+
filter.OrganizationID = organization.ID
67+
}
68+
}
69+
}
70+
4771
parser.ErrorExcessParams(values)
4872
return filter, parser.Errors
4973
}

coderd/searchquery/search_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package searchquery_test
22

33
import (
4+
"context"
45
"database/sql"
56
"fmt"
67
"strings"
@@ -11,6 +12,7 @@ import (
1112
"github.com/stretchr/testify/require"
1213

1314
"github.com/coder/coder/v2/coderd/database"
15+
"github.com/coder/coder/v2/coderd/database/dbmem"
1416
"github.com/coder/coder/v2/coderd/searchquery"
1517
"github.com/coder/coder/v2/codersdk"
1618
)
@@ -315,7 +317,10 @@ func TestSearchAudit(t *testing.T) {
315317
c := c
316318
t.Run(c.Name, func(t *testing.T) {
317319
t.Parallel()
318-
values, errs := searchquery.AuditLogs(c.Query)
320+
// Do not use a real database, this is only used for an
321+
// organization lookup.
322+
db := dbmem.New()
323+
values, errs := searchquery.AuditLogs(context.Background(), db, c.Query)
319324
if c.ExpectedErrorContains != "" {
320325
require.True(t, len(errs) > 0, "expect some errors")
321326
var s strings.Builder

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy