Skip to content

Commit

Permalink
Merge pull request #224 from eurofurence/issue-222-find-api-expansion
Browse files Browse the repository at this point in the history
allow filtering fields for user permission finds
  • Loading branch information
Jumpy-Squirrel authored Aug 11, 2024
2 parents d32f0c9 + c99adda commit 376b9bc
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 8 deletions.
8 changes: 6 additions & 2 deletions api/openapi-spec/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2126,13 +2126,17 @@ components:
For a detailed description of the fields, please see the AttendeeSearchResult schema.
Keep in mind that your permissions may limit field visibility.
Keep in mind that your permissions may limit field visibility.
Non-Admins cannot use field sets, are limited to attending statuses, and are limited to these fields:
id, nickname, first_name, last_name, country, spoken_languages, registration_language, birthday, pronouns,
tshirt_size, flags, options, packages, status, total_dues, payment_balance, current_dues.
Also remember that even if you don't list the id field, you will still get it.
You will not get fields that you don't have permission to see even if you request them.
If you do not specify any fields, only the id will be returned.
If you do not specify any fields, a default set of fields will be returned that depends on your permissions.
items:
type: string
enum:
Expand Down
29 changes: 24 additions & 5 deletions internal/web/controller/adminctl/adminctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,7 @@ func findAttendeesHandler(w http.ResponseWriter, r *http.Request) {
}

if limitedAccess {
criteria.FillFields = []string{"id", "nickname", "first_name", "last_name", "country",
"spoken_languages", "registration_language", "birthday", "pronouns", "tshirt_size",
"flags", "options", "packages", "status",
"total_dues", "payment_balance", "current_dues",
}
criteria.FillFields = limitToAllowedFields(criteria.FillFields)
for i := range criteria.MatchAny {
criteria.MatchAny[i].Status = limitToAttendingStatus(criteria.MatchAny[i].Status)
}
Expand All @@ -134,6 +130,29 @@ func findAttendeesHandler(w http.ResponseWriter, r *http.Request) {
ctlutil.WriteJson(ctx, w, results)
}

func limitToAllowedFields(desired []string) []string {
allowed := []string{"id", "nickname", "first_name", "last_name", "country",
"spoken_languages", "registration_language", "birthday", "pronouns", "tshirt_size",
"flags", "options", "packages", "status",
"total_dues", "payment_balance", "current_dues",
}

result := make([]string, 0)
for _, d := range desired {
for _, a := range allowed {
if d == a {
result = append(result, d)
}
}
}

if len(result) == 0 {
return allowed
} else {
return result
}
}

func limitToAttendingStatus(desired []status.Status) []status.Status {
if len(desired) == 0 {
return []status.Status{status.Approved, status.PartiallyPaid, status.Paid, status.CheckedIn}
Expand Down
78 changes: 77 additions & 1 deletion test/acceptance/admin_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,82 @@ func TestSearch_OtherPermissionsDeny(t *testing.T) {
tstRequireErrorResponse(t, response, http.StatusForbidden, "auth.forbidden", "you are not authorized for this operation - the attempt has been logged")
}

func TestSearch_PermissionAllowedFields_Ok(t *testing.T) {
docs.Given("given the configuration for standard registration")
tstSetup(false, false, true)
defer tstShutdown()

docs.Given("given an existing attendee who has been given the sponsordesk permission")
loc, att := tstRegisterAttendeeAndTransitionToStatus(t, "search2d-", status.Paid)
permBody := admin.AdminInfoDto{
Permissions: "sponsordesk",
}
permissionResponse := tstPerformPut(loc+"/admin", tstRenderJson(permBody), tstValidAdminToken(t))
require.Equal(t, http.StatusNoContent, permissionResponse.status)

docs.When("when they search for attendees and limit the fields to allowed fields")
token := tstValidUserToken(t, att.Id)
searchAll := attendee.AttendeeSearchCriteria{
MatchAny: []attendee.AttendeeSearchSingleCriterion{
{},
},
FillFields: []string{"id", "nickname", "spoken_languages"},
}
response := tstPerformPost("/api/rest/v1/attendees/find", tstRenderJson(searchAll), token)

docs.Then("then the request is successful and the list of attendees is returned with only the desired fields")
require.Equal(t, http.StatusOK, response.status, "unexpected http response status")
expected := `{
"attendees": [
{
"id": 1,
"badge_id": "1C",
"nickname": "BlackCheetah",
"spoken_languages": "de,en",
"spoken_languages_list": ["de","en"]
}
]
}`
tstRequireSearchResultMatches(t, expected, response.body)
}

func TestSearch_PermissionAllowedFields_Filtered(t *testing.T) {
docs.Given("given the configuration for standard registration")
tstSetup(false, false, true)
defer tstShutdown()

docs.Given("given an existing attendee who has been given the sponsordesk permission")
loc, att := tstRegisterAttendeeAndTransitionToStatus(t, "search2d-", status.Paid)
permBody := admin.AdminInfoDto{
Permissions: "sponsordesk",
}
permissionResponse := tstPerformPut(loc+"/admin", tstRenderJson(permBody), tstValidAdminToken(t))
require.Equal(t, http.StatusNoContent, permissionResponse.status)

docs.When("when they search for attendees and ask for forbidden fields")
token := tstValidUserToken(t, att.Id)
searchAll := attendee.AttendeeSearchCriteria{
MatchAny: []attendee.AttendeeSearchSingleCriterion{
{},
},
FillFields: []string{"id", "nickname", "email", "phone"},
}
response := tstPerformPost("/api/rest/v1/attendees/find", tstRenderJson(searchAll), token)

docs.Then("then the request is successful and the list of attendees is returned, but with the field list filtered to allowed fields")
require.Equal(t, http.StatusOK, response.status, "unexpected http response status")
expected := `{
"attendees": [
{
"id": 1,
"badge_id": "1C",
"nickname": "BlackCheetah"
}
]
}`
tstRequireSearchResultMatches(t, expected, response.body)
}

func TestSearch_StaffDeny(t *testing.T) {
docs.Given("given the configuration for staff registration")
tstSetup(false, true, true)
Expand Down Expand Up @@ -1268,7 +1344,7 @@ func TestSearch_AdminOk(t *testing.T) {
}
response := tstPerformPost("/api/rest/v1/attendees/find", tstRenderJson(searchAll), token)

docs.Then("then the request is successful and the list of attendees is returned")
docs.Then("then the request is successful and the list of attendees is returned with all fields")
require.Equal(t, http.StatusOK, response.status, "unexpected http response status")
expected := `{
"attendees": [
Expand Down

0 comments on commit 376b9bc

Please sign in to comment.