Creating Alice
In this lesson, we will create and switch to a user with more managed permissions, and create an address with this new user
Create Alice
Before the following, make sure Alice does not already exist in the system: Running the command user alice
should respond with Not Found: user: alice
. Otherwise, pick a different name for the following.
Run:
create human user alice
The outcome is that you (as a "root" user) created a new user Alice. This user cannot yet do anything, and cannot even sign requests to Treasury. Since it would be unsafe for you (as the administrator) to create a credential and then share it with Alice, Treasury has the concept of "invite codes". These are single-use credentials, intended for use only to register credentials, such that the administrator can pass the new user their invite code, the new user can privately create a keypair and register it as a credential, and then the invite code expires.
Set up Alice's invitation:
alice-invite = create invite client key alice-invite { code = hex("alice-invite")}
create invite credential for alice { public_key = $alice-invite.public_key }
You will see that Alice now has a single credential:
:: credentials for alice
invite credential 15 for alice { version = 1, public_key = "d216cac2a2b07184f50624a2b81aa7805a6966fb3196d73222955fbf8880ae56" }
Invite credentials currently do not have a binding to SSO. Anyone can use this invitation to enroll a credential to the user account.
Invites must be created with care, and in coordination with other approvers.
Setup Alice
Thinking as Alice, you can now run (in the same interpreter session):
alice-key = create k256 client key alice-key
alice-invite = client key alice-invite
set sign.with = alice-invite
create k256 credential for alice { public_key = $alice-key.public_key }
set sign.with = alice-key
You can verify that the client keyring contains a key for Alice
:: keyring
client-key alice-invite { algorithm = "ed25519", public_key = "d216cac2a2b07184f50624a2b81aa7805a6966fb3196d73222955fbf8880ae56" }
client-key alice-key { algorithm = "ecdsa-k256-sha256", public_key = "038aa9ec2cadd433920bc7504a4db8dcddf44e66948aa61b06300f50a5ca84d850" }
client-key connector-invite { algorithm = "ed25519", public_key = "f6b6bdec9cc00ef8e8cc324fa6f0dab2a093321330ebe1e13097c5a28cfce5f7" }
client-key oracle-invite { algorithm = "ed25519", public_key = "3aa616f98cf54a32cbd2636ddb8c4c005ce78ffbeea53ae15d724a469033e1b1" }
client-key root-key { algorithm = "ecdsa-k256-sha256", public_key = "03f0b46d4be0181cc89c0946154a5159c1b87f31ecaace194a44d52709a6f9f7ce" }
client-key signer-1-invite { algorithm = "ed25519", public_key = "597d11bdee0e494c468f3232893b647cbd12c825d729f424de8934d9476f344d" }
And you can verify that the Treasury contains a credential for Alice:
:: credentials for alice
cosmos credential 14 for alice { version = 1, public_key = "038aa9ec2cadd433920bc7504a4db8dcddf44e66948aa61b06300f50a5ca84d850" }
In particular, note that the public key of the "client-key alice-key" and the public key of the "credential for alice" coincide. The former (keyring or "client keys") are keypairs stored on your computer (including the secret signing keys). These are located in subfolders of the directory treasury data-directory
(on Linux, ~/.local/share/treasury
). The latter are public keys stored in the policy engine, i.e. in Treasury.
User Selection
In the following, if you run treasury script
, or explicitly run treasury script --sign-with root-key
, or run :: set sign.with = root-key
in the interpreter, the credential for your original user with extended permissions will be used.
Conversely, if you run treasury script --sign-with alice-key
, or run :: set sign.with = alice-key
in the interpreter, the credential for Alice will be used.
The way that signing works is that treasury
infers the active user by filtering credentials in the treasury (always tied to a user) by the selected client key's public key.
Conversely, Treasury verifies signed HTTP requests against the used credential to authenticate the user.
Access Policy: First Taste
As Alice (see previous section), you are currently not able to create addresses (this is again Treasury's default-deny policy):
:: create internal address for ETH
Permission Denied: 7: access policy did not pass
This is a new concept: For any resource in the Treasury, while reading is allowed, writing requires explicit permission -- another instance of default-deny. By resource, we mean any "thing" that exists in the Treasury, examples we have seen so far are users, addresses, transfers, and transfer rules.
We can grant a suitable permission:
set sign.with = root-key
create allow access rule { action = "create", resource = { type = "Address", variant = "internal" }, initiate = "users/alice" }
The naming "initiate" will become clear in the next lesson where we distinguish requests from operations. For our purposes you can read this rule as "User Alice is allowed to create (internal) addresses".
And indeed:
set sign.with = alice-key
create internal address for ETH { notes.description = "created by Alice" }
now works.
Access rules have flexible syntax and are rather expressive and powerful, and there are multiple other ways to allow Alice to create addresses. For instance, the following would have worked:
create allow access rule { action = "create", resource = "Address", initiate = "users/alice" }
This would more generally have allowed Alice to create any "variant" of address. There are two, "internal" (where Treasury has the key) and "external" (where Treasury does not have the key, these exist for the purpose of expressing policies to withdraw funds to external accounts). It is prudent to not allow anybody to create external addresses without approval (to which we get in the next section), as they can be mis-entered and lead to catastrophic loss of funds.
We could also have given Alice the broad permissions of your original user, with this rule:
create allow access rule { action = "any/action", resource = "any/resource", initiate = "users/alice" }
If you experiment with this, you can delete such access rules again with delete access rule <access-rule-id>
.