Write a User Permissions System in 5 lines of Prolog
Tags: prolog, permissions, authorization
Published: 2020-02-28 04:11:15

---
title: Write a User Permissions System in 5 lines of Prolog
published: true
description: Learn the basics on writing a permissions system using a logic programming language.
tags: prolog, permissions, authorization
---

Logic programming is something all developers should learn. It isn't actually that hard, and simply knowing **what's possible** will positively influence you in choosing better tools, for now and the future.

In this post I'm going to show you how easy it is to write a role-based permissions system in Prolog. In our first iteration, you'll only need **5 lines of logic**, and easily extendable to boot. This will give you a sample taste of the full power of Prolog.

**Note:** This is not a full introduction to Prolog. That said, you should still be able to follow along, because **logic programming is easy**. When you're ready to dive deeper, you can bookmark [this post](https://www.matchilling.com/introduction-to-logic-programming-with-prolog/) for later.

## The Scenario

Let's say we have users and clubs:

```pl
user(alice).
user(bob).
user(carly).
user(dan).
user(elli).

club(boxing).
club(chess).
```

with a [many-to-many relationship](https://en.wikipedia.org/wiki/Many-to-many_(data_model)) between the two:

```pl
member(alice, chess).
member(dan,   chess).
member(elli,  chess).

member(alice, boxing).
member(bob,   boxing).
member(carly, boxing).
```

And lastly, we want to specify which user is allowed to **moderate** which club:

```pl
moderator(dan,   chess).
moderator(alice, boxing).
moderator(bob,   boxing).
```

Note that we haven't yet written a single line of logic. All code so far has been **facts** – simple statements of truth. And yet, with only this, Prolog grants us great, expressive power! Allow me to demonstrate.

Here is how we ask Prolog for all the clubs `alice` is a member of:

```prolog
?- member(alice, C).
C = chess ;
C = boxing.
```

Note how Prolog gives us two solutions.

How about all clubs that `alice` is *also* a moderator of?

```prolog
?- member(alice, C), moderator(alice, C).
C = boxing.
```

Or how about all non-moderator members of all clubs?

```prolog
?- member(U, C), \+ moderator(U, C).
U = alice,
C = chess ;
U = elli,
C = chess ;
U = carly,
C = boxing.
```

Note how Prolog gives us **three** solutions when there are *six* memberships. Also note that `\+` is the operator for "not".

## The Logic

Given the incredible expressiveness Prolog grants us, it's very easy to begin adding logic for protected moderator actions.

Let's say we want a moderator to be able to **ban a club member**. A little harsh, I know, but it's a concept we all understand!

Here's how you can do it – in the advertised 5 lines – to be explained shortly afterwards:

```pl
can(User, Club, ban_user, Target) :-
  moderator(User, Club),
  dif(User, Target),
  member(Target, Club),
  \+ moderator(Target, Club).
```

and here's how you use it:

```prolog
% Alice can ban a member
?- can(alice, boxing, ban_user, carly).
true.

% But she cannot ban another moderator
?- can(alice, boxing, ban_user, bob).
false.

% Nor can she ban herself
?- can(alice, boxing, ban_user, alice).
false.

% And non-moderators cannot ban anyone
?- can(elli, chess, ban_user, alice).
false.
```

That's right, we now have fully working permission logic for banning a user. All moderators now have the ability to ban. To add another moderator, just add a new `moderator` clause.

But wait, there's more. We can still ask questions! Who can Dan ban?

```
?- can(dan, Club, ban_user, Person).
Club = chess,
Person = alice ;
Club = chess,
Person = elli.
```

Prolog gives us all the answers. Dan has the permission to ban Alice and Elli from the chess club. Wow!

## Code Explanation

Here's an explanation of the `can` rule:

- `can(User, Club, ban_user, Target)` - For a given user, club, and target, we are defining logic specifically for `ban_user`.
- `moderator(User, Club)` - The given user must be a moderator of the given club.
- `dif(User, Target)` - And lastly, the user performing the action cannot target themselves (`dif` is built into Prolog).
- `member(Target, Club)` - The given target user must be a member of *that same* club. You can't ban someone who's not even in the club!
- `\+ moderator(Target, Club)` - The target user must not be a moderator.

And that's all it takes. Gaze upon the beauty of Prolog.

You can see the full code [here](https://github.com/TheClause/learn-prolog/blob/master/articles/roles-permissions-1.md#full-code).

## Going Further

- The facts we wrote are hard-coded. In practice, these can be loaded from a database.
- The concept of roles and permissions can be decoupled and abstracted further, making our data even more serialization-friendly. This is covered in [Part 2](https://dev.to/theclause/write-a-role-permissions-system-in-14-lines-of-prolog-part-2-371n).
- [SWI Prolog](https://www.swi-prolog.org/pldoc/doc_for?object=manual) has HTTP and Docker support, so it's possible to host a "logic server" that answers questions for other web services you control.

Interested in learning more about Prolog? [Star our repo](https://github.com/TheClause/learn-prolog), join [our chatroom](https://gitter.im/TheClause/community), and [support our Patreon](https://www.patreon.com/TheClause)!

See Part 2 of this series here:

{% link https://dev.to/theclause/write-a-role-permissions-system-in-14-lines-of-prolog-part-2-371n %}