Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is a placeholder PR to propose one approach to meeting the authentication & authorization requirements specified in #37.
Overview
Authentication is required by the signal server, and provided by a lightweight authentication server, which in turn relies on external OAuth providers.
Authorization information about roles, permissions, and authorized users is encoded in a special JSON document that is included in the repo under a reserved key. This document is included with the repository, digitally signed, and replicated in plaintext to all actors.
Permissions can be set both at the document level (e.g. some actors can't see agent records) and at the field level (e.g. some actors can't read salary data).
Sensitive documents are simply never shared with unauthorized actors.
Sensitive fields are encrypted using a key that is only available to authorized actors. The encrypted data is otherwise treated the same as any other data, and replicated to all actors.
All actors' changes are digitally signed to prove they come from that actor. Unauthorized changes by actors with read-only access to certain documents or fields are simply ignored by other actors.
Authentication
cevitxe-signal-server
will now require an identity verified by a new authentication microservice,cevitxe-auth-server
. This is a very minimal service that does just two things:Step by step:
raw-fish
, and that she is willing to connect with anyone who can prove they are Bob.raw-fish
.Authorization
Encryption keys
This system relies on three types of keypairs:
These keys are generated and stored by the auth server, and provided to actors when they successfully authenticate.
Encrypting for readers with different keys
The encrypted salary data needs to be independently decrypted by users with different roles. To accomplish this, we:
K
that we'll use for the salary field;K
;Now to decrypt salary data, an actor first finds and decrypts the key corresponding to their role; then uses that key to decrypt the data itself.
Enforcing
read
permissionsIn our example scenario, Dan isn't allowed to see agents (document-level permissions) or salaries (field-level permissions).
When Alice synchronizes with Dan, she has two ways to hide information from him:
Omission: Alice can choose to share some documents but not others. This is how we enforce
document-level permissions: Alice can share nothing but civilian records with Dan, and
that's all he'll ever see.
Encryption: Alice can encrypt the contents of individual fields. This is how we enforce
field-level permissions: Alice can encrypt the contents of the salary field using a key that
Dan doesn't have, but that authorized readers (Carol, Frank, and Gloria) do.
In a client/server setup, read permissions are typically enforced by omission; so that's the model we're familiar with.
In our distributed network, this works for document-level permissions. But Automerge does its magic by keeping the history of an entire document in sync. So any sensitive information within a document needs to be passed around in encrypted form, such that everyone can see it's there, resolve conflicting versions, and so on; but only authorized readers can decrypt it.
Enforcing
write
permissionsIn a client/server model, enforcing write permissions is straightforward, since a central server can simply reject changes coming from unauthorized clients.
In this distributed model, changes might be passed from one actor to another several times before we receive them. Suppose Alice receives Bob's changes via Carol. She needs to be confident that Carol hasn't modified those changes before passing them on. In fact, she needs to be confident that Carol isn't making her own changes and claiming they're Bob's!
To properly enforce
write
permissions, each actor needs to digitally sign their own changes. Step by step:change; it checks out.
salary field; he does.
record.
permission to make the change. She updates her own records as well.
If at any point one of these checks fails - say Carol was trying to pass off her changes as Bob's, or Bob didn't have the right permissions - the receiving actor can simply ignore the change.
Enforcing admin permissions
Only admins can edit the authorization rules for a repository. This is enforced by treating the access control document as read-only for anyone not in an
admin
role: Changes by unauthorized actors will be rejected by other actors.Revoking or downgrading permissions
Revoking write permissions at any level, and read permissions at the document level, is a matter of applying the new rules going forward (and in the case of read permissions, removing the newly disallowed documents are removed from the storage we control on the actor's device).
The tricky part is revoking read permissions at the field level, because of the way we encrypt for readers with different keys.
As an example, let's suppose Gloria is demoted from from
civilian-manager
tocivilian
, and as a result she should no longer see salary information.The first thing we do is to regenerate encryption keys for the
civilian-manager
role, and we re-encrypt the salary key forcivilian-manager
. This way Gloria can no longer access the symmetric key used to encrypt salary data.For many applications, this is be sufficient. If we're really worried, though, we need regenerate that symmetric key and re-encrypt all the salary data. (Technically Gloria has 'seen' the symmetric key, since she's decrypted it in the past any time she's needed to see salary data.)
Access control document
We encode the information about roles and permissions and the list of authorized actors and their roles in a special JSON document that is included in the repo under a reserved key.
This document is included with the repository, digitally signed with the root key, and replicated in plaintext to all actors.
For example:
Schema
An access control document has the following root-level elements:
roles
Dictionary mapping unique IDs to role definitions. A role definition can have the following elements:
isAdmin
(boolean; defaults tofalse
) indicates a role with no read or write restrictions, and with the ability to modify the access control document.Normally this
documentExclusions
andfieldExclusions
(object; defaults to{}
) hasread
andwrite
properties, each of which is set to an array of keys from the root-leveldocumentExclusions
andfieldExclusions
, respectively.actors
Dictionary mapping unique actor IDs to actor definitions. An actor definition can have the following elements:
role
is a reference to a role ID from theroles
element.publicKey
is the shared half of a keypair selected by the actor.documentExclusions
Dictionary mapping exclusion IDs to JSONPath filter expressions.
fieldExclusions
Dictionary mapping exclusion IDs to field exclusion definitions. A field exclusion definition has two elements:
path
is a JSONPath expression identifying the sensitive property. For properties at the root of the document, this will just be the name of the property.keys
is a dictionary mapping TODOsignature
The entire access control document is signed with the repository's root private key, and can be verified against the repository's root public key (provided by the signal server when connecting peers).