diff --git a/stores/stripe/accounts.fga b/stores/stripe/accounts.fga new file mode 100644 index 0000000..71ee8ae --- /dev/null +++ b/stores/stripe/accounts.fga @@ -0,0 +1,28 @@ + +module account + +type user +type account + relations + define owner: [user] + define admin: [user] or owner + define iam_admin: [user] or admin + define developer: [user] or admin + define transfer_analyst: [user] or admin + define view_only: [user] or admin + define transfer_analyst_in_test_mode: transfer_analyst but not transfer_analyst from test_mode # intermediate relations, just used for evaluating other relations + + define test_mode: [account] # self-defining relationship, used to set a type into a true/false state + + define can_close_account: owner + define can_change_owner: owner + define can_invite: iam_admin + define can_add_bank_account: admin but not admin from test_mode # exclusion operator + define can_delete_default_bank_account: owner + +type bank_account + relations + define account: [account] + define can_delete_bank_account: admin from account + define can_view: admin from account + define can_transfer_balance: transfer_analyst from account \ No newline at end of file diff --git a/stores/stripe/fga.mod b/stores/stripe/fga.mod new file mode 100644 index 0000000..49ad3d4 --- /dev/null +++ b/stores/stripe/fga.mod @@ -0,0 +1,5 @@ +schema: '1.2' +contents: + - accounts.fga + - payments.fga + - issuing.fga \ No newline at end of file diff --git a/stores/stripe/issuing.fga b/stores/stripe/issuing.fga new file mode 100644 index 0000000..c6ba8d7 --- /dev/null +++ b/stores/stripe/issuing.fga @@ -0,0 +1,34 @@ +module issuing + +extend type account + relations + define card: [card] # dual-writes relationships between card & account, when you need to traverse relations on two types checking both ways (starting at card then going up or account then going down) + define cardholder: cardholder from card + define cardholder_in_test_mode: cardholder from test_mode # exclusion operator + +type card + relations + define account: [account] + define cardholder: [user] + define digital_wallet: [digital_wallet] + define active: [card] # self-defining relationship + define can_purchase: cardholder from active but not cardholder_in_test_mode from account + define can_view_transactions: cardholder or admin from account + define spending_limit_policy: [card#cardholder with spending_limit] + +type transaction + relations + define card: [card] + define can_view: can_view_transactions from card + +type digital_wallet + relations + define owner: [user] + +# spending control condition +## default spending limit is 500 USD a day +## could be 100 USD per transaction +## could be 3000 USD per month +condition spending_limit(transaction_amount: double, transaction_limit: double, daily_amount: double, daily_limit: double, monthly_amount: double, monthly_limit: double) { +transaction_amount + daily_amount <= daily_limit || transaction_amount <= transaction_limit || transaction_amount + monthly_amount <= monthly_limit +} \ No newline at end of file diff --git a/stores/stripe/payments.fga b/stores/stripe/payments.fga new file mode 100644 index 0000000..ee3bfa0 --- /dev/null +++ b/stores/stripe/payments.fga @@ -0,0 +1,5 @@ +module payments + +type payment +type subscription +type invoice \ No newline at end of file diff --git a/stores/stripe/stripe.fga.yaml b/stores/stripe/stripe.fga.yaml new file mode 100644 index 0000000..45d3757 --- /dev/null +++ b/stores/stripe/stripe.fga.yaml @@ -0,0 +1,179 @@ +name: Stripe +model_file: ./fga.mod +tuples: + ### Stripe Core Tuples ### + # Tyler is the owner of account:1 + - user: user:tyler + relation: owner + object: account:1 + # Andres is an admin of account:1 + - user: user:andres + relation: admin + object: account:1 + # Dongni can only view account:1 + - user: user:dongni + relation: view_only + object: account:1 + # The bank_account:1 belongs to account:1 + - user: account:1 + relation: account + object: bank_account:1 + + ### Stripe Issuing Tuples ### + # SCENARIO 1: An account manages 2 cards, assinged to users Raghd and Jon. Raghd's card is active and makes two purcahses. Jon's card is inactive and makes one purchase + # OBJECTS: account:1, user:raghd, user:jon, card:1, card:2, transaction:1, transaction:2, transaction:3 + - user: user:raghd # Raghd is the cardholder of card:1 + relation: cardholder + object: card:1 + - user: account:1 # The card:1 belongs to account:1 + relation: account + object: card:1 + - user: card:1 # The account:1 manages card:1 + relation: card + object: account:1 + - user: card:1 # The card:1 is activated + relation: active + object: card:1 + - user: card:1 # The card:1 was used for transaction:1 + relation: card + object: transaction:1 + - user: card:1 # The card:1 was used for transaction:2 + relation: card + object: transaction:2 + - user: user:jon # Jon is the cardholder of card:2 + relation: cardholder + object: card:2 + - user: account:1 # The card:2 belongs to account:1 + relation: account + object: card:2 + - user: card:2 # The account:1 manages card:2 + relation: card + object: account:1 + - user: card:2 # The card:2 was used for transaction:3 + relation: card + object: transaction:3 + # but no card:2 "active" relationship tuple + + # SCENARIO 2: A Stripe account (in test mode) manges one active card that tries to make a purchase. + # OBJECTS: account:2, card:3, transaction:4 + - user: user:tyler # Tyler is the owner of account:2 + relation: owner + object: account:2 + - user: account:2 # The account:2 is in test mode + relation: test_mode + object: account:2 + - user: card:3 # The account:2 manages card:3 + relation: card + object: account:2 + - user: account:2 # The card:3 belongs to account:2 + relation: account + object: card:3 + - user: user:maria # Maria is the cardholder of card:3 + relation: cardholder + object: card:3 + - user: card:3 # The card:3 is activated + relation: active + object: card:3 + +tests: + - name: Stripe Core Tests + check: + - user: user:tyler + object: account:1 + assertions: + owner: true # "Is Tyler an owner of account:1?" | check direct relationship with account owner + iam_admin: true # "Is Tyler an iam_admin of account:1?" | check indirect 'concentric' relationship inherited from admin and owner + - user: user:dongni + object: account:1 + assertions: + admin: false # "Is Dongni an admin of account:1?" + - user: user:tyler + object: bank_account:1 + assertions: + can_delete_bank_account: true # "Can Tyler delete bank_account:1 from account:1?" | check parent-child inheritance + can_view: true # "Can Tyler view bank_account:1?" + can_transfer_balance: true # "Can Tyler transfer the balance in bank_account:1?" + - user: user:dongni + object: bank_account:1 + assertions: + can_view: false # "Can Dongni view bank_account:1?" + - user: user:tyler + object: account:2 + assertions: + can_add_bank_account: false # "Can Tyler add a bank_account if account:2 is in test_mode?" | check exclusion operator with self-defining attribute + + - name: Stripe Issuing Tests + check: + - user: user:raghd + object: card:1 + assertions: + cardholder: true # "Is Raghd the cardholder for card:1?" + - user: user:raghd + object: card:2 + assertions: + cardholder: false # "Is Raghd the cardholder for card:2?" + - user: user:raghd + object: transaction:1 + assertions: + can_view: true # "Can Raghd view transaction:1?" + - user: user:tyler + object: transaction:1 + assertions: + can_view: true # "Can Tyler (an account owner that manages the card) view transaction:1?" + + - user: card:1 + object: card:1 + assertions: + active: true # "Is card:1 active?" | check if card:1=active self-defining attribute tuple is set + - user: user:raghd + object: card:1 + assertions: + can_purchase: true # "Can Raghd make a purchase with card:1?" if card:1 is active + + - user: card:2 + object: card:2 + assertions: + active: false # "Is card:2 active?" | check if the active self-defining relationship tuple is set on card:2 + - user: user:jon + object: card:2 + assertions: + can_purchase: false # "Can Jon make a purchase with card:2?" if card:2 is inactive + + - user: card:3 + object: card:3 + assertions: + active: true # "Is card:3 active?" | check if the active self-defining relationship tuple is set on card:3 + - user: account:2 + object: account:2 + assertions: + test_mode: true # "Is account:2 in test_mode?" | check if the test_mode self-defining relationship tuple is set on account:2 + - user: user:maria + object: card:3 + assertions: + can_purchase: false # "Can Maria make a purchase with card:3 but the account is in test_mode)?" | check dual-write relationship inheritance + + # todo: can someone make a purchase above a contextual spending limit? + # todo: can someone add a card to a wallet? + + list_objects: + - user: user:raghd + type: card + assertions: + cardholder: + - card:1 + - user: user:raghd + type: transaction + assertions: + can_view: + - transaction:1 + - transaction:2 + - user: user:jon + type: card + assertions: + cardholder: + - card:2 + - user: user:jon + type: transaction + assertions: + can_view: + - transaction:3