satellite/payments: prevent removing other users' cards
This change patches a loophole that allowed accounts to remove cards that belong to other users. Closes #storj/storj-private#326 Change-Id: I33e9efe5c9cdb03aa48ad4c6b1d3283c396a7890
This commit is contained in:
parent
31bb6d54c7
commit
7cc873a62a
@ -13,6 +13,13 @@ import (
|
||||
"storj.io/storj/satellite/payments"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCardNotFound is returned when card is not found for a user.
|
||||
ErrCardNotFound = errs.Class("card not found")
|
||||
// ErrDefaultCard is returned when a user tries to delete their default card.
|
||||
ErrDefaultCard = errs.Class("default card")
|
||||
)
|
||||
|
||||
// creditCards is an implementation of payments.CreditCards.
|
||||
//
|
||||
// architecture: Service
|
||||
@ -156,7 +163,29 @@ func (creditCards *creditCards) Remove(ctx context.Context, userID uuid.UUID, ca
|
||||
if customer.InvoiceSettings != nil &&
|
||||
customer.InvoiceSettings.DefaultPaymentMethod != nil &&
|
||||
customer.InvoiceSettings.DefaultPaymentMethod.ID == cardID {
|
||||
return Error.Wrap(errs.New("can not detach default payment method."))
|
||||
return ErrDefaultCard.New("can not detach default payment method.")
|
||||
}
|
||||
|
||||
cardIter := creditCards.service.stripeClient.PaymentMethods().List(&stripe.PaymentMethodListParams{
|
||||
ListParams: stripe.ListParams{Context: ctx},
|
||||
Customer: &customerID,
|
||||
Type: stripe.String(string(stripe.PaymentMethodTypeCard)),
|
||||
})
|
||||
|
||||
isUserCard := false
|
||||
for cardIter.Next() {
|
||||
if cardIter.PaymentMethod().ID == cardID {
|
||||
isUserCard = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err = cardIter.Err(); err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
if !isUserCard {
|
||||
return ErrCardNotFound.New("this card is not attached to this account.")
|
||||
}
|
||||
|
||||
cardParams := &stripe.PaymentMethodDetachParams{Params: stripe.Params{Context: ctx}}
|
||||
|
@ -87,31 +87,42 @@ func TestCreditCards_Add(t *testing.T) {
|
||||
|
||||
func TestCreditCards_Remove(t *testing.T) {
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
||||
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 2,
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
satellite := planet.Satellites[0]
|
||||
userID := planet.Uplinks[0].Projects[0].Owner.ID
|
||||
user2ID := planet.Uplinks[1].Projects[0].Owner.ID
|
||||
|
||||
_, err := satellite.API.Payments.Accounts.CreditCards().Add(ctx, userID, "test")
|
||||
card1, err := satellite.API.Payments.Accounts.CreditCards().Add(ctx, userID, "test")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = satellite.API.Payments.Accounts.CreditCards().Add(ctx, userID, "test2")
|
||||
// card2 becomes the default card
|
||||
card2, err := satellite.API.Payments.Accounts.CreditCards().Add(ctx, userID, "test2")
|
||||
require.NoError(t, err)
|
||||
|
||||
cards, err := satellite.API.Payments.Accounts.CreditCards().List(ctx, userID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cards, 2)
|
||||
|
||||
// Save card that should remain after deletion.
|
||||
savedCard := cards[0]
|
||||
// user2ID should not be able to delete userID's cards
|
||||
for _, card := range cards {
|
||||
err = satellite.API.Payments.Accounts.CreditCards().Remove(ctx, user2ID, card.ID)
|
||||
require.Error(t, err)
|
||||
require.True(t, stripe.ErrCardNotFound.Has(err))
|
||||
}
|
||||
|
||||
err = satellite.API.Payments.Accounts.CreditCards().Remove(ctx, userID, cards[1].ID)
|
||||
// Can not remove default card
|
||||
err = satellite.API.Payments.Accounts.CreditCards().Remove(ctx, userID, card2.ID)
|
||||
require.Error(t, err)
|
||||
require.True(t, stripe.ErrDefaultCard.Has(err))
|
||||
|
||||
err = satellite.API.Payments.Accounts.CreditCards().Remove(ctx, userID, card1.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
cards, err = satellite.API.Payments.Accounts.CreditCards().List(ctx, userID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cards, 1)
|
||||
require.Equal(t, savedCard, cards[0])
|
||||
require.Equal(t, card2, cards[0])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -356,6 +356,15 @@ func (m *mockCustomers) Update(id string, params *stripe.CustomerParams) (*strip
|
||||
if params.Balance != nil {
|
||||
customer.Balance = *params.Balance
|
||||
}
|
||||
if params.InvoiceSettings != nil {
|
||||
if params.InvoiceSettings.DefaultPaymentMethod != nil {
|
||||
customer.InvoiceSettings = &stripe.CustomerInvoiceSettings{
|
||||
DefaultPaymentMethod: &stripe.PaymentMethod{
|
||||
ID: *params.InvoiceSettings.DefaultPaymentMethod,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO update customer with more params as necessary
|
||||
|
||||
return customer, nil
|
||||
|
Loading…
Reference in New Issue
Block a user