Skip to content

Commit

Permalink
feat(#105): allow users to update payments tentative to pending
Browse files Browse the repository at this point in the history
  • Loading branch information
Jumpy-Squirrel committed Dec 19, 2023
1 parent d4eb9c3 commit fdafbda
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 3 deletions.
25 changes: 24 additions & 1 deletion internal/interaction/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,20 @@ func (s *serviceInteractor) UpdateTransaction(ctx context.Context, tran *entitie

logger := logging.LoggerFromContext(ctx)

if !mgr.IsAdmin() && !mgr.IsAPITokenCall() {
if mgr.IsAdmin() || mgr.IsAPITokenCall() {
// ok
} else if mgr.IsRegisteredUser() {
// registered users may update some of their own transactions, but only from tentative to pending
regIDs, err := s.attendeeClient.ListMyRegistrationIds(ctx)
if err != nil {
logger.Error("could not call the attendee service. [error]: %v", err)
return apierrors.NewInternalServerError("attendee service error - see log for details")
}

if !containsDebitor(regIDs, tran.DebitorID) {
return apierrors.NewForbidden(fmt.Sprintf("subject %s may not access transactions for debitor %d", mgr.Subject(), tran.DebitorID))
}
} else {
return apierrors.NewForbidden("no permission to update transaction")
}

Expand All @@ -228,6 +241,16 @@ func (s *serviceInteractor) UpdateTransaction(ctx context.Context, tran *entitie
return apierrors.NewForbidden("cannot change transactions of type due")
}

if !mgr.IsAdmin() && !mgr.IsAPITokenCall() {
// non-admin users may only change transactions in status Tentative to Pending
if curTran.TransactionStatus != entities.TransactionStatusTentative ||
tran.TransactionStatus != entities.TransactionStatusPending ||
tran.TransactionType != entities.TransactionTypePayment {
logger.Warn("forbidden attempt to change transaction %s to target status %s by subject %s", tran.TransactionID, tran.TransactionStatus, mgr.Subject())
return apierrors.NewForbidden(fmt.Sprintf("subject %s may not make this transaction change - the attempt has been logged", mgr.Subject()))
}
}

// check if a valid payment should be deleted or not by an admin
if tran.TransactionStatus == entities.TransactionStatusDeleted &&
curTran.TransactionStatus == entities.TransactionStatusValid &&
Expand Down
111 changes: 109 additions & 2 deletions internal/interaction/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1077,20 +1077,127 @@ func TestUpdateTransaction(t *testing.T) {
status entities.TransactionStatus
}

attendeeRegistrationsFunc := func(ctx context.Context) ([]int64, error) {
return []int64{1, 2, 3}, nil
}

tests := []struct {
name string
args args
expected expected
}{
{
name: "should fail if not admin or service token call",
name: "should fail if not logged in",
args: args{
ctx: attendeeCtx(),
ctx: context.TODO(),
},
expected: expected{
err: apierrors.NewForbidden("no permission to update transaction"),
},
},
// --- user test cases ---
{
name: "user access should fail if attendee service unreachable",
args: args{
ctx: attendeeCtx(),
listRegistrationsFunc: func(ctx context.Context) ([]int64, error) { return nil, errors.New("connection refused") },
},
expected: expected{
err: apierrors.NewInternalServerError("attendee service error - see log for details"),
},
},
{
name: "user access should fail if not their registration",
args: args{
transaction: &entities.Transaction{
DebitorID: 17,
},
ctx: attendeeCtx(),
listRegistrationsFunc: attendeeRegistrationsFunc,
},
expected: expected{
err: apierrors.NewForbidden("subject 1234567890 may not access transactions for debitor 17"),
},
},
{
name: "user access should deny when trying to update non-tentative",
args: args{
transaction: &entities.Transaction{
DebitorID: 1,
TransactionID: "12345",
TransactionStatus: entities.TransactionStatusPending,
TransactionType: entities.TransactionTypePayment,
},
seed: []entities.Transaction{
{
Model: gorm.Model{
CreatedAt: time.Now().AddDate(0, 0, -3).Add(-time.Second * 10),
},
DebitorID: 1,
TransactionID: "12345",
TransactionStatus: entities.TransactionStatusValid,
TransactionType: entities.TransactionTypePayment,
},
},
ctx: attendeeCtx(),
listRegistrationsFunc: attendeeRegistrationsFunc,
},
expected: expected{
err: apierrors.NewForbidden("subject 1234567890 may not make this transaction change - the attempt has been logged"),
},
},
{
name: "user access should deny when trying to update to non-pending",
args: args{
transaction: &entities.Transaction{
DebitorID: 1,
TransactionID: "12345",
TransactionStatus: entities.TransactionStatusValid,
TransactionType: entities.TransactionTypePayment,
},
seed: []entities.Transaction{
{
Model: gorm.Model{
CreatedAt: time.Now().AddDate(0, 0, -3).Add(-time.Second * 10),
},
DebitorID: 1,
TransactionID: "12345",
TransactionStatus: entities.TransactionStatusTentative,
TransactionType: entities.TransactionTypePayment,
},
},
ctx: attendeeCtx(),
listRegistrationsFunc: attendeeRegistrationsFunc,
},
expected: expected{
err: apierrors.NewForbidden("subject 1234567890 may not make this transaction change - the attempt has been logged"),
},
},
{
name: "user should successfully update a transaction",
args: args{
transaction: &entities.Transaction{
DebitorID: 1,
TransactionID: "12345",
TransactionType: entities.TransactionTypePayment,
TransactionStatus: entities.TransactionStatusPending,
},
seed: []entities.Transaction{
{
DebitorID: 1,
TransactionID: "12345",
TransactionType: entities.TransactionTypePayment,
TransactionStatus: entities.TransactionStatusTentative,
},
},
ctx: apiKeyCtx(),
},
expected: expected{
err: nil,
status: entities.TransactionStatusPending,
},
},
// --- admin test cases ---
{
name: "should return not found if original transaction could not be found in the database",
args: args{
Expand Down

0 comments on commit fdafbda

Please sign in to comment.