Users and Environments
Howto create new users? Howto modify them?
▽Dec. 22, 2020|Bernhard Kauer
developerdraft
1. Environments
The users are the top-level objects of the Puzzle API. They are the containers for all currency accounts, which itself keep the transactions and their attachments.
Each user object resides inside an environment. Currently two of them
are defined. First the real environment where normal money
can be transfered between Puzzle accounts and outside Puzzle through the
SEPA system. Secondly, the sandbox environment. It can be
use for playing with the API and implementing use-cases where no real
money is required.
The assignment of a user to an environment is done at creation time and cannot be changed afterwards.
1.1 The differences between the environments
Placing a user inside the sandbox has the following
advantages compared to the real environment:
- no identification is required - one can start immediately
- a credit line is possible
- more currencies are enabled
- new features will be made available in the
sandboxfirst
However, a few operations are not possible inside a
sandbox:
- sending money to external banks
- testing the identification methods
2. Creating a new User
Every other API call needs an existing user object. Creating one is therefore often the first task one has to perform. In this section we therefore show this process step-by-step to ease the implementation of this basic functionality.
To create a new user, send a POST request to the
/pzl endpoint.
$ curl -s -X POST https://$HOST/pzl
{"reason": "need JSON body"}
The content-type must be application/json.
$ curl -s -X POST -H "Content-type: application/json" https://$HOST/pzl
{"reason": "invalid JSON"}
And the body has to be valid JSON.
$ echo -n '{}' | curl -s -T - -X POST -H "Content-type: application/json" https://$HOST/pzl
{"reason": "invalid environment"}
An environment must be given where the user should reside.
$ echo -n '{"env": "sandbox"}' | curl -T - -X POST -s -H "Content-type: application/json" https://$HOST/pzl
{"reason": "invalid keytype"}
The keytype must be ed25519. Other keytypes are not
supported yet.
$ echo -n '{"env": "sandbox", "keytype": "ed25519"}' | curl -T - -X POST -s -H "Content-type: application/json" https://$HOST/pzl
{"reason": "invalid pubkey"}
The public key for ed25519 is 32-bytes long and is transfered
base64-encoded inside the JSON. The URL-safe variant of the
encoding is prefered.
$ echo -n '{"env": "sandbox", "keytype":"ed25519", "pubkey":"TtMvY7818O7vyyXyii4fvchzrig1ZxsMlGD18S5FVqg="}' | curl -T - -X POST -s -H "Content-type: application/json" https://$HOST/pzl
{"reason": "authorization missing"}
An authorization header for the puzzle authentication scheme must be provided. The signature is validated against the public key that is send in the body. A full trace therefore looks like:
$ puzzle-client -d -d user 2>&1
< POST /pzl HTTP/2
< authorization: pzl time=1608641178+60,sig=TRplU9GOA2yHTtQkSy-cG1w5RTs7u9X_EwUcBBUz1EF-CK9DvhqICTF_s7WThgdc6Actf7AfxP-SNQ4JvxtxDA
< content-type: application/json
<
< {"env":"sandbox","keytype":"ed25519","pubkey":"lml7WeRunbqRp0VTeAc68YLnz6KKVHb_WoYP1GJGBwM="}
> HTTP/2 200
> etag: g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ
> content-type: application/json
> cache-control: no-cache
> location: /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN
> date: Tue, 22 Dec 2020 12:46:18 GMT
> content-length: 783
>
>
> [{"type": "user",
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN",
> "canonical": "/pzl/s3e8.g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ",
> "event": {"date": 1608641178.51564,
> "author": "puzzle-service api <318637@n18-1>",
> "category": "user",
> "name": "s3e8",
> "operation": "init"},
> "children": {"accounts": "g4Krr2gT1hqmVBirI5tCGY-9f3yULCRpIrJhd54Pga8G",
> "auths": "g2AwJgwSUC0pikuVXAv70pct7sWdVFP-cdRz6WBKh8HP",
> "info": "gzKZK5RbBgOaiYlaWdq395VMFFBa6p6h9IL5QBt6O6Yq"}},
> {"type": "user-info",
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/info",
> "canonical": "/pzl/s3e8/info.gzKZK5RbBgOaiYlaWdq395VMFFBa6p6h9IL5QBt6O6Yq",
> "properties": {"api": "1 20200304",
> "current": "AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN",
> "env": "sandbox",
> "id": "s3e8",
> "name": "Spoon Cloud sandbox"}}]
>
/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN
The location header in the reply is the current path of the newly
created user object. The object itself as well as the
user-info is returned to get instant feedback before a
potential asynchronous synchronization operation succeeds.
2.1 Duplicate Keys
Since the server cannot verify the actual strength of a private key, it is up to the client to submit only secure public keys. Deriving the private key from a user-provided password is not sufficient. The key must be generated from a cryptographically secure random number generator.
However, the server will detect duplicate and blacklisted keys. A 400 error is returned in this case. The corresponding reply body will be:
{"reason": "duplicate key"}
This error may also occur if an interrupted operation is retried, as the key might already be recorded by the backend even if the client has not received a successfull reply.
2.2 Enumerating all Users
Existing users cannot be enumerated through the API for security
reasons. The client must store the location and the private keys
securely. Recovery through the API is not possible if this information
is lost. It is therefore advisable to use a secondary auth
for backup purposes.
In the future there will be alternative recovery options for
real accounts involving a potentially expensive
re-identification of the user.
2.3 Deleting a User
There is no way to delete a user within the API. Owners and transactions might have to be kept for several years for legal reasons.
However, inactive users will be garbage collected and archieved after
some time. To speedup this process, delete all auth objects
so that external access is not possible anymore.
3. User Object
3.1 Retrieving the current version
The current version can be retrieved via a GET request
to the current URL of the user object. An authorization header for the
puzzle authentication scheme
must be provided.
The signature in the following example is validated against the default public key that was given to the server during user creation. The return body is formated in JSON.
$ puzzle-client -d get /pzl/s3e8 2>&1
< GET /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN HTTP/2
< authorization: pzl time=1608641178+60,sig=fQ2dWD_xll50RdceKba83PKR59clcTUpbx8YmSniVUxCd9sNeDPt2ZPE62iZX9Fa65wnDRG1nQWecW8u8JabBA
<
> HTTP/2 200
> cache-control: no-cache
> content-length: 473
> content-type: application/json
> date: Tue, 22 Dec 2020 12:46:18 GMT
> etag: g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ
>
> {
> "canonical": "/pzl/s3e8.g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ",
> "children": {
> "accounts": "g4Krr2gT1hqmVBirI5tCGY-9f3yULCRpIrJhd54Pga8G",
> "auths": "g2AwJgwSUC0pikuVXAv70pct7sWdVFP-cdRz6WBKh8HP",
> "info": "gzKZK5RbBgOaiYlaWdq395VMFFBa6p6h9IL5QBt6O6Yq"
> },
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN",
> "event": {
> "author": "puzzle-service api <318637@n18-1>",
> "category": "user",
> "date": 1608641178.51564,
> "name": "s3e8",
> "operation": "init"
> },
> "type": "user"
> }
The canonical attribute defines a path to this version
that never changes. It can be seen as the version-number of the object.
Requesting the canonical path returns exactly the very same object in
the future. This allows one to refetch objects that where dropped at the
client.
The prev attribute links to the previous version of this
object. This enables backward iteration through the different versions.
The prev field is not present on the very first
version.
3.2 Conditional Requests
The GET and HEAD request return the
etag of the object. This is a hash over the object or
sub-object and guaranteed to be unique over time.
$ puzzle-client head /pzl/s3e8 | grep etag | cut -c 3-
etag: g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ
This can be used with a if-none-match header to detect
whether the current state of an object was modifed or not. The server
returns a 304 error if the etag is still
current.
$ puzzle-client -d head -H "if-none-match: $(puzzle-client head /pzl/s3e8 | grep etag | cut -c 9-)" /pzl/s3e8 2>&1
< HEAD /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN HTTP/2
< authorization: pzl time=1608641179+60,sig=PgEx7SVxbI1K1RZ7XRlftub6SgrneU1La51BfWjy1heTTAm920DypUjtAkBnRKaYqhwwTZlEJEpdMBky3VA4DQ
< if-none-match: g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ
<
> HTTP/2 304
> content-length: 0
> date: Tue, 22 Dec 2020 12:46:19 GMT
>
It may also be used together with a prefer header to
implement long-polling.
3.3 Events
The event object inside the user looks like:
{
"author": "puzzle-service api <267976@n18-1>",
"category": "user",
"date": 1608582480.928915,
"name": "s:3e8",
"operation": "merge",
"subcategory": "account",
"versions": "0-0"
}
The example shows a merge of certain
versions from the account s:3e8 into this user
object. The author field reveals the backend service that
created this version. This is usefull for debugging purposes.
The date of this version is given as Unix time (UTC
seconds since 1970) in micro-second resolution. The accuracy of this
timestamp might be a lot lower due to clock-drifts and process
interruptions on the server.
The category and subcategory specify where
this event comes from.
Examples for operation done through the API are:
init- for initializing the objectcreate- for creating new child objectsupdate- for modifying a childdelete- for deleting a child
The name gives a hint where this operation
happenend.
4. User Information Object
The user-info object details the information the backend
has about a certain user. To retrieve it, request the info
child from the user.
$ puzzle-client -q get /pzl/s3e8/info
{
"canonical":"/pzl/s3e8/info.gzKZK5RbBgOaiYlaWdq395VMFFBa6p6h9IL5QBt6O6Yq",
"current":"/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/info",
"properties":{
"api":"1 20200304",
"current":"AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN",
"env":"sandbox",
"id":"s3e8",
"name":"Spoon Cloud sandbox"
},
"type":"user-info"
}
These are the properties for the user-info object:
- api - version number for the API
- current - the TAG to be used in all current links
- env - the environment the user is in
- id - the id of the user
- name - a name for the user
- description - a human-readable description
- icon - the name of an icon to be shown in the App
4.1 Modifications
Certain properties of the user-info object are writable.
An OPTIONS call reveals that currently description,
icon and name fall in this class. This list
will be extended in the future.
$ puzzle-client -q options /pzl/s3e8/info
{
"optional":[
"description",
"icon",
"name"
]
}
Modifications are performed with a POST request:
echo "info description=$(date +%s)" | puzzle-client -d update /pzl/s3e8 2>&1
< POST /pzl/s3e8.AJjrTPfvyraFORT1SPnPOOJygikA9Qa0/info HTTP/2
< authorization: pzl time=1583943032+60,sig=MYLpiaV-s4gaFUWiuM186aFnSxq_ZRbaIQm7gwS6TYbeO46c576YZEYYN8WDak-pzwo_HktYd0vgDyRYa34FAQ
< content-type: application/json
<
< {"description":"1583943032"}
> HTTP/2 200
> content-length: 413
> content-type: application/json
> date: Wed, 11 Mar 2020 16:10:32 GMT
> etag: g3AOF-hhII3GQBbm2_QS9KmpliNnEboM_Vicr9jUtPKy
>
> {
> "canonical": "/pzl/s3e8/info.g3AOF-hhII3GQBbm2_QS9KmpliNnEboM_Vicr9jUtPKy",
> "current": "/pzl/s3e8.AJjrTPfvyraFORT1SPnPOOJygikA9Qa0/info",
> "prev": "/pzl/s3e8.g23_JGQ6GIeh8u5D4AxLRYiGAg8X0fD7tgFF2jnM3A_-/info",
> "properties": {
> "api": "1 20200304",
> "current": "AJjrTPfvyraFORT1SPnPOOJygikA9Qa0",
> "description": "1583943032",
> "env": "sandbox",
> "id": "s3e8",
> "name": "Sandbox 3e8"
> },
> "type": "user-info",
> "version": 7
> }
This either returns a 200 and the new object or a 204 if no modification was performed.
$ echo "info foo=bar" | puzzle-client -q update /pzl/s3e8
204 /pzl/s3e8/info
5. Authorization Objects
The authorization objects contain all data needed to authorize
requests. Most important are the public key for the puzzle authentication scheme.
Multiple auth objects can be created per user to enable
multi-device access and for backup purposes.
We have implement a fine-grained access-model, that supports read-only and time-limited access.
5.1 Listing all Auths
All authorization objects of a user can be listed with a
GET request.
$ puzzle-client -q get /pzl/s3e8/auths
{
"canonical":"/pzl/s3e8/auths.g2AwJgwSUC0pikuVXAv70pct7sWdVFP-cdRz6WBKh8HP",
"children":{
"x1":"g2mMkX3ksrvl-MO3efFNxtvSrkYCmpLDZyXrjIIAhOir"
},
"current":"/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths",
"type":"auth-list"
}
The children lists the available authorization objects
with their etag.
5.2 Retrival
A authorization object can be retrieved by combining its name with
the cannonical or current path of the
auth-list.
$ puzzle-client -q get /pzl/s3e8/auths/x1
{
"canonical":"/pzl/s3e8/auths/x1.g2mMkX3ksrvl-MO3efFNxtvSrkYCmpLDZyXrjIIAhOir",
"current":"/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x1",
"properties":{
"description":"default",
"keytype":"ed25519",
"name":"x1",
"policies":[
{
"until":1671540378
}
],
"pubkey":"lml7WeRunbqRp0VTeAc68YLnz6KKVHb_WoYP1GJGBwM="
},
"type":"auth"
}
The object keeps the pubkey and type as
well as a description to let the user distinguish different
authorizations. The default policies grants full access
until the object is valid.
5.3 Creation
To create a new auth object, one sends a new public key
to the auth-list. An OPTIONS call to the URL reveals the
supported parameters.
$ puzzle-client -q options /pzl/s3e8/auths
{
"choice":{
"keytype":[
"ed25519"
]
},
"mandatory":[
"keytype",
"pubkey"
],
"optional":[
"description",
"policies"
]
}
Both keytype and pubkey must be given. A
human-readable description is optional.
The creation is done throught the POST method.
$ puzzle-client -d -d auth /pzl/s3e8 2>&1
< POST /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths HTTP/2
< authorization: pzl time=1608641180+60,sig=E95mGNgvt0ghnTKYpb0dmA_Uk3IHAO2NpCuMHCGCqfC7safVc_khQvhltbZ9Ohfk5dqIBNCvC5JxmljniGJ5CQ
< content-type: application/json
<
< {"description":"default","keytype":"ed25519","pubkey":"Kvmfyj78jlsQp08GJ3NrzXdBybOuz0osMlWctwymQms="}
> HTTP/2 200
> content-type: application/json
> cache-control: no-cache
> location: /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x2
> date: Tue, 22 Dec 2020 12:46:20 GMT
> content-length: 709
>
>
> [{"type": "auth-list",
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths",
> "canonical": "/pzl/s3e8/auths.gwPhGzUedeMhKjp0rsuxm8ikqcHBwDLrCtsb83dZzGGa",
> "prev": "/pzl/s3e8.g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ/auths",
> "version": 1,
> "children": {"x1": "g2mMkX3ksrvl-MO3efFNxtvSrkYCmpLDZyXrjIIAhOir",
> "x2": "gwfMOq4MZP-PsUKzpVzaah8Nyd6ItxVsAciAszu7Elg7"}},
> {"type": "auth",
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x2",
> "canonical": "/pzl/s3e8/auths/x2.gwfMOq4MZP-PsUKzpVzaah8Nyd6ItxVsAciAszu7Elg7",
> "properties": {"description": "default",
> "keytype": "ed25519",
> "name": "x2",
> "policies": [{"until": 1671540380}],
> "pubkey": "Kvmfyj78jlsQp08GJ3NrzXdBybOuz0osMlWctwymQms="}}]
>
/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x2
The location header in the reply is the current path of the newly created authorization object.
5.4 Modification
An existing auth object can be modified. An OPTIONS call
to the URL reveals the supported parameters.
$ puzzle-client -q options /pzl/s3e8/auths/x1
{
"choice":{
"keytype":[
"ed25519"
]
},
"optional":[
"description",
[
"keytype",
"pubkey"
],
"policies"
]
}
This means description and the public key are both
optional. If the later is changed both keytype and
pubkey must be provided.
Unknown fields and non-modifying updates are ignored.
$ echo 'auths/x1 description=default foo=bar' | puzzle-client -q update /pzl/s3e8
204 /pzl/s3e8/auths/x1
Updates are better performed on the canonical URL, as this allows to detect race conditions. Any succesfull modification returns the new object version.
$ echo "$(puzzle-client -q get /pzl/s3e8/auths/x1 | grep canonical | cut -d'"' -f 4) description=first" | puzzle-client -d update / 2>&1
< POST /pzl/s3e8/auths/x1.g2mMkX3ksrvl-MO3efFNxtvSrkYCmpLDZyXrjIIAhOir HTTP/2
< authorization: pzl time=1608641180+60,key=x2,sig=6U-gc1_l7-sy7R-STeGAo0v8q2BV0Jh5LuEdOHZrOuntSQePfx3gjliPare3IXqMOrNAyeM3BxQWGDVEOOLCAg
< content-type: application/json
<
< {"description":"first"}
> HTTP/2 200
> content-length: 501
> content-type: application/json
> date: Tue, 22 Dec 2020 12:46:20 GMT
> etag: g_L3O1knFl2u9xMQVY54DfojgRQZusPMbAx97kiUouKq
>
> {
> "ancient": "/pzl/s3e8.g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ/auths/x1",
> "canonical": "/pzl/s3e8/auths/x1.g_L3O1knFl2u9xMQVY54DfojgRQZusPMbAx97kiUouKq",
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x1",
> "prev": "/pzl/s3e8.g-Rf0fttdUnl88btdmxnBo0aSiFneLsRoJ-1y71XNHBI/auths/x1",
> "properties": {
> "description": "first",
> "keytype": "ed25519",
> "name": "x1",
> "policies": [
> {
> "until": 1671540378
> }
> ],
> "pubkey": "lml7WeRunbqRp0VTeAc68YLnz6KKVHb_WoYP1GJGBwM="
> },
> "type": "auth",
> "version": 2
> }
Performing the very same operation on an older version leads to a
412 precondition failed as the given URL is not current
anymore.
$ echo "$(puzzle-client -q get /pzl/s3e8/auths/x1 | grep prev | cut -d'"' -f 4) description=second" | puzzle-client update /
412 /pzl/s3e8.g-Rf0fttdUnl88btdmxnBo0aSiFneLsRoJ-1y71XNHBI/auths/x1
In this case one has to refetch the current state of the
auth object before performing the update again.
5.5 Rolling Key Updates
Modifying a key directly is tricky to implement at the client, as the reply might not reach the client due to limited internet availability. If this happens the client may only guess which key the server will accept in the future. We therefore support a rolling update scheme where the old key is retained until the new key is confirmed.
The first step in the process is to register a new key:
$ puzzle-client -d auth --postfix=/auths/x1 /pzl/s3e8 2>&1
< POST /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x1 HTTP/2
< authorization: pzl time=1608641181+60,key=x2,sig=OuwAryoQozSXUdMGdhyYXWk-AOUnNKDvhUMGafqcOkO2EnDMpHIc_KPo9uTnuCILk6hvkGsMu1EJ6bm8J9dUBA
< content-type: application/json
<
< {"description":"default","keytype":"ed25519","pubkey":"I30_pyo0YBFqAr-NAEI97ihxcQeb3DSbDFUEsWANDK0="}
{"type": "auth",
"current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x1",
"canonical": "/pzl/s3e8/auths/x1.gxIYL3PVOB3MZoqCazjKuWM_FB7cLvtpMl9gF4pNNFp_",
"properties": {"description": "default",
"keytype": "ed25519",
"keytype2": "ed25519",
"name": "x1",
"policies": [{"until": 1671540378}],
"pubkey": "lml7WeRunbqRp0VTeAc68YLnz6KKVHb_WoYP1GJGBwM=",
"pubkey2": "I30_pyo0YBFqAr-NAEI97ihxcQeb3DSbDFUEsWANDK0="},
"prev": "/pzl/s3e8.g6Wgeojhq9jl9WL_9IGhSC0hlHr-F6bVmT3lMIkmg5UL/auths/x1",
"version": 3,
"ancient": "/pzl/s3e8.g8scu7crfIr2mCU8dL41XC0EiCMkJ9fE27py_DfjSzrQ/auths/x1"}
This installs a pubkey2 of keytype2 that
can be used in parallel to the previous key. To confirm the new key,
issue the operation with the very same public key again.
$ echo -n x1 keytype=ed25519 pubkey=$(puzzle-client -q get /pzl/s3e8/auths/x1 | grep pubkey2 | cut -d'"' -f4) | puzzle-client -d update /pzl/s3e8/auths 2>&1
< POST /pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x1 HTTP/2
< authorization: pzl time=1608641181+60,key=x2,sig=rNrjiHlBx2sDjpakyLlMqLUEpHlobSjwxbDJDNhsY0zZdYjOfvpNlgDIFycbFnOQjlfBdXTz2rFKgrJvu42PCA
< content-type: application/json
<
< {"keytype":"ed25519","pubkey":"I30_pyo0YBFqAr-NAEI97ihxcQeb3DSbDFUEsWANDK0="}
> HTTP/2 200
> content-length: 503
> content-type: application/json
> date: Tue, 22 Dec 2020 12:46:21 GMT
> etag: g6UI_ELkHDLxCEM84k-w_v2TIltwIKfY0yV1rp_NTa1A
>
> {
> "ancient": "/pzl/s3e8.g6Wgeojhq9jl9WL_9IGhSC0hlHr-F6bVmT3lMIkmg5UL/auths/x1",
> "canonical": "/pzl/s3e8/auths/x1.g6UI_ELkHDLxCEM84k-w_v2TIltwIKfY0yV1rp_NTa1A",
> "current": "/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x1",
> "prev": "/pzl/s3e8.g6tClDljNyf4UthkNwXRiNo0UTQHYI3ceid9gyeXIRQV/auths/x1",
> "properties": {
> "description": "default",
> "keytype": "ed25519",
> "name": "x1",
> "policies": [
> {
> "until": 1671540378
> }
> ],
> "pubkey": "I30_pyo0YBFqAr-NAEI97ihxcQeb3DSbDFUEsWANDK0="
> },
> "type": "auth",
> "version": 4
> }
One may also drop the new key by sending the older one instead. To
avoid race conditions, one should use the canonical path
for both operations.
5.6 Access Control Policies
Fine-grained access control is possible by defining a policy that
decides which operations are allowed for a given auth
object. This policy is checked only for requests with valid signatures
to avoid information leaks. The policy itself is a list of entries that
can be specified when creating or modifying an auth object.
At least one of the entries must match for any request.
The fields of the entries are as follows:
- until - a UTC timestamp until the entry is valid
- method - the HTTP Methods allowed
- prefix - the prefixes of the allowed requests
The until field is always set. This enables timely replacement of unecessary rights and deprecated cryptograpic methods. If no expiration date is given, the maximum duration (currently two years) is choosen.
One can set a read-only policy by allowing only the GET method:
$ echo -n 'x2 policies=[{"method":"GET"}]' | puzzle-client update /pzl/s3e8/auths
{
"canonical":"/pzl/s3e8/auths/x2.g2vqNv01IHzpQZMGitFNXaMw0MhmT93lvi0RgcCEwzm5",
"current":"/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths/x2",
"prev":"/pzl/s3e8.g0w5tKJ80gb-Uv3XxXhyv4gGtEsCbYoYuJOYN0LzhPQr/auths/x2",
"properties":{
"description":"default",
"keytype":"ed25519",
"name":"x2",
"policies":[
{
"method":"GET",
"until":1671540381
}
],
"pubkey":"Kvmfyj78jlsQp08GJ3NrzXdBybOuz0osMlWctwymQms="
},
"type":"auth",
"version":5
}
If the policy check fails on any method, a 401 error is returned.
$ echo -n 'x2 policies=[{"method":"GET"}]' | puzzle-client --keyname=x2 update /pzl/s3e8/auths
401 /pzl/s3e8/auths/x2
5.7 Deletion
An existing auth object can be deleted.
$ puzzle-client --keyname=x1 -q delete /pzl/s3e8/auths/x2
{
"ancient":"/pzl/s3e8.g0w5tKJ80gb-Uv3XxXhyv4gGtEsCbYoYuJOYN0LzhPQr/auths",
"canonical":"/pzl/s3e8/auths.g3-GqC0y53_LQ76Yut8dOBvrZADUvSlZZy2fxjO8Urhm",
"children":{
"x1":"g6UI_ELkHDLxCEM84k-w_v2TIltwIKfY0yV1rp_NTa1A"
},
"current":"/pzl/s3e8.AGPyrPuKeB_kFgCB2b-uL35EqLKrwZyN/auths",
"prev":"/pzl/s3e8.g5i9_Yu0nELFyKaw0exCFIKU9FJ2Q1ybVfeH8ihwhnxa/auths",
"type":"auth-list",
"version":6
}
Further access with this key is prohibited.
$ puzzle-client -q --keyname=x2 get /pzl/s3e8 2>&1
{
"reason":"key not found"
}
6. Further Questions
6.1 Where can I try this API? Is there a demo instance of Puzzle?
There is no special demo instance of Puzzle. For testing purposes
create a user inside the sandbox environment.
6.2 How do I know in which environment a user resides?
The user and account names start with a different prefix. A user
named s3f5 would be in the sandbox.