Tutorials
Using Attributes to Control Permissions
Modeling Attribute Patterns

Modeling Attribute Patterns

In this section you will:

  • Model the authorization for an example credit card app
  • Use attributes to control permissions on resources within the app

Application Details

In this tutorial you'll model a credit card app with a rewards program. The application has the following resources described in detail below.

  • Card Account: The card account is the top level resource in this example. A card account can have one or more owners. Only owners have card account permissions. Card accounts also have members. Card account members are users associated with the card account. In the implementation, you'll use this role as another way to establish a user's relationships to a particular card account. However, card account members do not have any permissions at this level.

    RolePermissions
    OwnerAdd new credit cards
    View card accounts
    Modify card account options and settings
    MemberNone
    RelationshipResource
    Parent ofCredit Cards
    AttributeDescription
    Is ActiveFeatures and permissions of card accounts are accessible if and only if the account is active.
    Rewards StatusFor accounts enrolled in the rewards program, the status reveals what tier the account member has achieved.
  • Credit Card: The credit card is second level resource. A credit card belongs to a single card account. Each credit has one card holder. Card holders can perform all actions associated with their credit card except modifying the credit card limits. Card account owners can modify credit card limits and have all card holder permissions for the credit cards under their accounts.

    RolePermissions
    Card HolderView the details of the credit card account
    Make credit card transactions
    Dispute any credit card transactions
    Account OwnerView the details of the credit card account
    Make credit card transactions
    Dispute any credit card transactions
    Modify the spending limits of the credit card
    RelationshipResource
    Child ofCard Accounts
    AttributeDescription
    Is ActiveCertain features and permissions of credit cards are accessible if and only if the account is active.
  • Rewards Program: The rewards program is available to members of card accounts. Once card account member becomes a member oef the rewards program they automatically given access to the rewards tier. Subsequent reward tiers are achieved based on the reward program rules and conditions.

    RolePermissions
    MemberView the rewards program
    Rewards
    Bronze (if Bronze status)
    Silver (if Silver status)
    Gold (if Gold status)
    Platinum (if Platinum status)
    RelationshipResource
    None
    AttributeDescription
    None

Creating the Policy

Create the scaffolding for the authorization model in Polar. You can start by defining an actor User and the three resources detailed above: CardAccount, CreditCard, and RewardsProgram. For now, only worry about defining roles, permissions, and relationships — don't add any rules just yet!

actor User {}
 
resource CardAccount {
    roles = [
        "owner",
        "member"
    ];
    permissions = [
        "add_card",
        "view",
        "modify_accounts"
    ];
}
 
resource CreditCard {
    roles = [
        "card_holder"
    ];
 
    permissions = [
        "view",
        "make_transactions",
        "dispute_transactions",
        "modify_limits"
    ];
 
    relations = {parent_account: CardAccount};
}
 
resource RewardsProgram {
    roles = [
        "member"
    ];
 
    permissions = [
        "rewards",
        "view",
        "bronze_rewards",
        "silver_rewards",
        "gold_rewards",
        "platinum_rewards"
    ];
}

When writing policies in Polar, only simple rules can be defined within resource blocks. Since the goal is to add attributes to enhance the authorization logic, you will need to write the policy rules using the longhand rule syntax. In each case you'll start with the basic roles-based access control pattern and extend it using attribute conditions.

has_permission(user: User, <PERMISSION>, resource: <SOME_RESOURCE>) if
    has_role(user, <SOME_ROLE>, resource) and
    ...;

*NOTE: Refer to the attribute description section for each resource to understand how they should be applied in rule definitions.

Writing CardAccount Resource Rules

Create has_permission rules for each of the three permissions listed for the CardAccount resource. Next, use the toggle attribute pattern to define is_active which takes a CardAccount and Boolean as arguments. This will ensure that account permissions are only granted to active accounts.

###############################################################################
# CardAccount Rules
###############################################################################
# Each permission is only granted if the user has the appropriate role, and the
# account is active.
has_permission(user: User, "add_card", account: CardAccount) if
    has_role(user, "owner", account) and
    is_active(account, true);
 
has_permission(user: User, "view", account: CardAccount) if
    has_role(user, "owner", account) and
    is_active(account, true);
 
has_permission(user: User, "modify_accounts", account: CardAccount) if
    has_role(user, "owner", account) and
    is_active(account, true);

Writing CreditCard Resource Rules

Use the same toggle pattern and apply it to the rules for CreditCard resources. This resource has a bit more complex set of rules. You will be adding up to two toggle patterns:

  • is_active(CardAccount, Boolean)
  • is_active(CreditCard, Boolean)

Remember to also add the has_relations rule which models a credit card's relation to a particular account. A couple examples are given below.

###############################################################################
# CreditCard Rules
###############################################################################
# Only add the attribute condition is_active(account, true). Users should still
# be able to view their account if their card is inactive.
has_permission(user: User, "view", card: CreditCard) if
    (has_role(user, "card_holder", card) or has_role(user, "owner", account)) and
    account matches CardAccount and
    has_relation(card, "parent_account", account) and
    is_active(account, true);
 
...
 
# Add both attribute conditions: is_active(account, true) and
# is_active(card, true). User's should not be able to modify credit card
# limits if their account OR their card is inactive.
has_permission(user: User, "modify_limits", card: CreditCard) if
    has_role(user, "owner", account) and
    account matches CardAccount and
    has_relation(card, "parent_account", account) and
    is_active(account, true) and
    is_active(card, true);

Action Items

Writing RewardProgram Resource Rules

The last resource to model is the RewardsProgram. It uses the existing ruleis_active(CardAccount, Boolean) along with a multiple attribute state pattern to model a member's reward status:

rewards_status(CardAccount, String);

NOTE: Use the string argument to make the attribute rule unique to the permission that it accompanies.

This section of the policy shows permissions for the basic rewards tier (available to all rewards members) and the platinum tier.

###############################################################################
# RewardsProgram Rules
###############################################################################
# The rewards tier is the default tier in the rewards program. It only requires
# a user to be a member of an active CardAccount and enrolled as a member of
# the RewardsProgram. Therefore, no additional rewards_status rule is needed.
has_permission(user: User, "rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true);
 
 ...
 
has_permission(user: User, "platinum_rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true) and
    rewards_status(user, account, "platinum");

Action Items

Complete Policy Example

actor User {}
 
resource CardAccount {
    roles = [
        "owner",
        "member"
    ];
    permissions = [
        "add_card",
        "view",
        "modify_accounts"
    ];
}
 
resource CreditCard {
    roles = [
        "card_holder"
    ];
 
    permissions = [
        "view",
        "make_transactions",
        "dispute_transactions",
        "modify_limits"
    ];
 
    relations = {parent_account: CardAccount};
}
 
resource RewardsProgram {
    roles = [
        "member"
    ];
 
    permissions = [
        "view",
        "rewards",
        "bronze_rewards",
        "silver_rewards",
        "gold_rewards",
        "platinum_rewards"
    ];
}
 
###############################################################################
# CardAccount Rules
###############################################################################
# Each permission is only granted if the user has the appropriate role, and the
# account is active.
has_permission(user: User, "add_card", account: CardAccount) if
    has_role(user, "owner", account) and
    is_active(account, true);
 
has_permission(user: User, "view", account: CardAccount) if
    has_role(user, "owner", account) and
    is_active(account, true);
 
has_permission(user: User, "modify_accounts", account: CardAccount) if
    has_role(user, "owner", account) and
    is_active(account, true);
 
###############################################################################
# CreditCard Rules
###############################################################################
# Only add the attribute condition is_active(account, true). Users should still
# be able to view their account if their card is inactive.
has_permission(user: User, "view", card: CreditCard) if
    (has_role(user, "card_holder", card) or has_role(user, "owner", account)) and
    account matches CardAccount and
    has_relation(card, "parent_account", account) and
    is_active(account, true);
 
# Only add the attribute condition is_active(account, true). Users should still
# be able to dispute transactions on their account if their card is inactive.
has_permission(user: User, "dispute_transactions", card: CreditCard) if
    (has_role(user, "card_holder", card) or has_role(user, "owner", account)) and
    account matches CardAccount and
    has_relation(card, "parent_account", account) and
    is_active(account, true);
 
# Add both attribute conditions: is_active(account, true) and
# is_active(card, true). User's should not be able to make transactions if
# their account OR their card is inactive.
has_permission(user: User, "make_transactions", card: CreditCard) if
    (has_role(user, "card_holder", card) or has_role(user, "owner", account)) and
    account matches CardAccount and
    has_relation(card, "parent_account", account) and
    is_active(account, true) and
    is_active(card, true);
 
# Add both attribute conditions: is_active(account, true) and
# is_active(card, true). User's should not be able to modify credit card
# limits if their account OR their card is inactive.
has_permission(user: User, "modify_limits", card: CreditCard) if
    has_role(user, "owner", account) and
    account matches CardAccount and
    has_relation(card, "parent_account", account) and
    is_active(account, true) and
    is_active(card, true);
 
###############################################################################
# RewardsProgram Rules
###############################################################################
# The rewards tier is the default tier in the rewards program. It only requires
# a user to be a member of an active CardAccount and enrolled as a member of
# the RewardsProgram. Therefore, no additional rewards_status rule is needed.
has_permission(user: User, "rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true);
 
# Users can only view rewards if they meet the same criteria above for rewards.
has_permission(user: User, "view", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true);
 
has_permission(user: User, "bronze_rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true) and
    rewards_status(user, account, "bronze");
 
has_permission(user: User, "silver_rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true) and
    rewards_status(user, account, "silver");
 
has_permission(user: User, "gold_rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true) and
    rewards_status(user, account, "gold");
 
has_permission(user: User, "platinum_rewards", rewards_program: RewardsProgram) if
    has_role(user, "member", rewards_program) and
    account matches CardAccount and
    has_role(user, "member", account) and
    is_active(account, true) and
    rewards_status(user, account, "platinum");

Action Items

Additional Resources

Talk to an Oso Engineer

If you'd like to learn more about using Oso Cloud in your app or have any questions about this guide, connect with us on Slack. We're happy to help.

Get started with Oso Cloud →