Skip to content

Commit

Permalink
feat(#188): allow negated constraints to override read_only
Browse files Browse the repository at this point in the history
  • Loading branch information
Jumpy-Squirrel committed Dec 29, 2023
1 parent 5261f0c commit 155f0a9
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 10 deletions.
12 changes: 8 additions & 4 deletions docs/config-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,23 @@ choices:
- regdesk
early:
description: 'Early Bird Discount'
price: -500
price: -1500
vat_percent: 19
visible_for:
- regdesk
default: true
read_only: true
door:
description: 'At The Door Fee'
price: 1000
constraint: '!day-wed,!day-thu,!day-fri,!day-sat'
constraint_msg: 'Early Bird Discount does not apply to Day Tickets'
late:
description: 'Late Fee'
price: 1500
vat_percent: 19
visible_for:
- regdesk
read_only: true
constraint: '!day-wed,!day-thu,!day-fri,!day-sat'
constraint_msg: 'Late Fee does not apply to Day Tickets'
stage:
description: 'Entrance Fee (Stage Ticket)'
price: 500
Expand Down
29 changes: 27 additions & 2 deletions internal/service/attendeesrv/attendeesrv.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,16 @@ func userAlreadyHasAnotherRegistration(ctx context.Context, identity string, exp
}

func checkNoForbiddenChanges(ctx context.Context, what string, key string, choiceConfig config.ChoiceConfig, originalChoices map[string]bool, newChoices map[string]bool) error {
if choiceConfig.AdminOnly || choiceConfig.ReadOnly {
if originalChoices[key] != newChoices[key] {
if originalChoices[key] != newChoices[key] {
// tolerate removing a read-only choice that has a constraint that forbids it anyway
if choiceConfig.ReadOnly {
if originalChoices[key] && !newChoices[key] {
if canAllowRemovalDueToConstraint(ctx, what, key, choiceConfig, originalChoices, newChoices) {
return nil
}
}
}
if choiceConfig.AdminOnly || choiceConfig.ReadOnly {
if !ctxvalues.HasApiToken(ctx) && !ctxvalues.IsAuthorizedAsGroup(ctx, config.OidcAdminGroup()) {
return fmt.Errorf("forbidden select or deselect of %s %s - only an admin can do that", what, key)
}
Expand All @@ -227,6 +235,23 @@ func checkNoForbiddenChanges(ctx context.Context, what string, key string, choic
return nil
}

func canAllowRemovalDueToConstraint(ctx context.Context, what string, key string, choiceConfig config.ChoiceConfig, originalChoices map[string]bool, newChoices map[string]bool) bool {
if choiceConfig.Constraint != "" {
constraints := strings.Split(choiceConfig.Constraint, ",")
for _, cn := range constraints {
constraintK := cn
if strings.HasPrefix(cn, "!") {
constraintK = strings.TrimPrefix(cn, "!")
if newChoices[constraintK] {
aulogging.Logger.Ctx(ctx).Info().Printf("can allow removal of read only %s %s - it would violate a constraint for %s anyway", what, key, constraintK)
return true
}
}
}
}
return false
}

func checkNoForbiddenChangesAfterPayment(ctx context.Context, what string, key string, choiceConfig config.ChoiceConfig, configuration map[string]config.ChoiceConfig, originalChoices map[string]bool, newChoices map[string]bool, currentStatus status.Status) error {
if ctxvalues.HasApiToken(ctx) || ctxvalues.IsAuthorizedAsGroup(ctx, config.OidcAdminGroup()) {
return nil
Expand Down
37 changes: 37 additions & 0 deletions test/acceptance/attendee_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,43 @@ func TestCreateNewAttendee_AutomaticGroupFlag_CannotSet(t *testing.T) {
})
}

func TestCreateNewAttendee_ReadonlyDefaultPackageWithConstraintRemovable(t *testing.T) {
docs.Given("given the configuration for login-only registration after normal reg is open")
tstSetup(true, false, true)
defer tstShutdown()

docs.Given("given a logged in user")
token := tstValidUserToken(t, 101)

docs.When("when they create a new attendee and remove a read-only default package with matching constraint (stage)")
attendeeSent := tstBuildValidAttendee("na63-")
attendeeSent.Packages = "room-none,day-sat,boat-trip"
response := tstPerformPost("/api/rest/v1/attendees", tstRenderJson(attendeeSent), token)

docs.Then("then the attendee is successfully created")
require.Equal(t, http.StatusCreated, response.status, "unexpected http response status")
require.Regexp(t, "^\\/api\\/rest\\/v1\\/attendees\\/[1-9][0-9]*$", response.location, "invalid location header in response")
}

func TestCreateNewAttendee_ReadonlyDefaultPackageNoConstraintNotRemovable(t *testing.T) {
docs.Given("given the configuration for login-only registration after normal reg is open")
tstSetup(true, false, true)
defer tstShutdown()

docs.Given("given a logged in user")
token := tstValidUserToken(t, 101)

docs.When("when they create a new attendee and try to remove a read-only default package with no matching constraint (room-none)")
attendeeSent := tstBuildValidAttendee("na65-")
attendeeSent.Packages = "day-sat"
response := tstPerformPost("/api/rest/v1/attendees", tstRenderJson(attendeeSent), token)

docs.Then("then the attempt is rejected as invalid (400) with an appropriate error response")
tstRequireErrorResponse(t, response, http.StatusBadRequest, "attendee.data.invalid", url.Values{
"packages": []string{"forbidden select or deselect of package room-none - only an admin can do that"},
})
}

// --- update attendee ---

func TestUpdateExistingAttendee_Self(t *testing.T) {
Expand Down
7 changes: 3 additions & 4 deletions test/testconfig-base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,12 @@ choices:
constraint: '!attendance,!stage'
constraint_msg: 'Must disable Convention Ticket and Stage Ticket for Day Guests.'
day-sat:
description: 'Day Guest (Saturday)'
description: 'Day Guest (Saturday) Self Booking Allowed due to constraints'
price: 6000
vat_percent: 19
read_only: true
at-least-one-mandatory: true
constraint: '!attendance,!stage'
constraint_msg: 'Must disable Convention Ticket and Stage Ticket for Day Guests.'
constraint: '!stage,!attendance'
constraint_msg: 'Must disable Stage Ticket for Saturday Day Guests.'
options:
art:
description: 'Artist'
Expand Down

0 comments on commit 155f0a9

Please sign in to comment.