Yetto A portrait of Billy Marx Vanzetti, Yetto's nonbinary, anti-capitalist mascot

Assigning roles and tailoring permissions

Garen Torikian's Profile Picture

Garen Torikian

Co-Founder, CTO

Engineering

When developing an app, we naturally spend a lot of time thinking about, designing, and implementing the features we want users to see. But we don't spend enough time considering what someone shouldn't see. Often, these considerations are thrown together in a confusing matrix of checkboxes and radio buttons.

You've logged into a SaaS application before; you know what I'm talking about. Your admin has to give you permission to open a directory; or, you need to make sure everyone on your team has the right access level to comment on the document. Finding the right permissions, or keeping them up to date, is a challenge unto itself.

My background in API development (both the REST and GraphQL flavors) has given me a sort of warped view on how organization memberships and permissions should work. In an API, permissions are used to filter every action a user might take. No request is accepted and no response is given unless the client has been granted a specific set of permissions. That's because one bad database JOIN and someone's private records could be mistakenly included in a public feed; one missing permission check could lead to an unauthorized back door.

But when it comes to the UI, too often, the "solution" for these potential security lapses is one of two choices. Either the authorization system is designed to be an all-encompassing blob of Read/Write/Admin permissions, or it's diced up into hundreds of radio buttons granting varying levels of access to each and every part of an application. Both of these systems suck for users, who don't have the autonomy to understand why they can't perform an action, and for administrators, who must manage everyone's individual needs.

Beyond that, there's also the cognitive load of figuring out which permissions are correct to give out. If you just want someone to look at something (because you're coworkers, not CIA agents), what permission are they granted? What secondary and tertiary permissions does that role grant? How much damage is your coworker going to do once they're instead your inbox?

There has to be a better way!

And there is! At Yetto, we implement a policy-based access control system. The gist of how it works is like this:

  • There are various permission categories, with various access levels.
  • You create a role which can encompass a group of permissions.
  • You assign roles to users.
  • If the permissions need to be modified, you change them once (in the role) and they're applied everywhere.

In practice, that means that a role looks like this in the UI:

Editing an IC role's permission

And is stored like this in the database:

{
  "version": "2023-03-06",
  "inboxes": {
    "conversations": "write"
  },
  "organization": {
    "inboxes": "read",
    "billing": "none",
    "memberships": "read",
  },
  "conversations": {
    "public_messages": "admin",
    "internal_messages": "admin"
  }
}

We decided early on that our permission system needed to be simple enough to understand at a glance (hence, JSON), but not so complicated as to make it frustrating to decide which permissions should be applied. (That's kind of our thing: making the difficult, easy.)

When designing our permissions, we started from the first principles: who are these permissions even for? It's easy to assume that everyone who logs into Yetto would want to send messages, but that's not true, and its assumption is a huge pain point in other help desks. In some other systems, simply having an account automatically implies that you're the one who will use the tool (and relatedly, your business is charged a seat). But there are plenty of people who ought to have insight into a support tool, but not be the person actually answering tickets. That includes:

  • A manager, who just needs to manage labels
  • Engineers, who just needs to look at a customer's feedback
  • Accounting, who just needs to look at billing data

Our flexible JSON structure maps a person's ability to work within Yetto to an easily parsable list. You can create roles that slice and dice across any permission structure that fits your organization.

Roles as a unit of measure

Our policies are developed through Pundit, which lets us easily express access checks in a natural way. POP QUIZ: what do you think this block of Ruby code does?

current_user_can_write_outgoing_messages?

If you said something to the effect of, "It checks whether the current user can write an outgoing message"—you're correct! (If you came up with a different answer, please see me after class.)

The implementation of that method looks like this:

def current_user_can_write_outgoing_messages?
  current_user.role.permissions["conversations"]["public_messages"] == "write"
end

In this way, we can write legible method names that correlate to a role's JSON values:

def current_user_can_invite_others?
  current_user.role.permissions["organization"]["memberships"] == "admin"
end

def current_user_can_manage_labels?
  current_user.role.permissions["inboxes"]["labels"] == "write"
end

def current_user_can_write_messages?
  current_user.role.permissions["conversations"]["public_messages"] == "write" ||
    current_user.role.permissions["conversations"]["internal_messages"] == "write"
end

def current_user_can_read_public_messages?
  current_user.role.permissions["conversations"]["public_messages"] == "read"
end

Having everything spelled out like this makes it easy to know which parts of the application are visible to which permission set. But that's all just 🥱 backend code. Seeing is believing, so here's an example of how that last method is represented:

Yetto conversation pane showing that public messages are restricted

What's next?

We're designing a product that fits at the crossroads between your organization's internal and external communique, and it's important for us to acknowledge that not every Yetto user uses the product the same way. We want to support our support professionals by letting them use the tool to its maximum potential, while making sure that no one else at the company is going to mess with their system. To support that, we implement access checks all over the app, like the example above.

Everything is about balance: people in an organization need to be able to know what their customers are saying, but that doesn't mean that people who aren't experts should jump in the queue or people who want meta-information should be allowed to mess with labels. There is an operational cost to every user, to be sure, but they should be free to look around.

Want to see how it all comes together? Sign up today and try it with your team!