mirror of
https://gitlab.com/fabinfra/fabaccess/fabaccess-api.git
synced 2025-03-12 23:01:47 +01:00
Update claims from our discussion two days ago
This commit is contained in:
parent
c10dc43f77
commit
a13478a3f0
196
resource.capnp
196
resource.capnp
@ -46,72 +46,87 @@ struct Resource {
|
|||||||
# in a free-form format.
|
# in a free-form format.
|
||||||
# Similar to the human-meaningful name this description can be translated.
|
# Similar to the human-meaningful name this description can be translated.
|
||||||
|
|
||||||
notifiable @4 :Notifiable;
|
grants @4 :ResourceCaps;
|
||||||
|
|
||||||
|
grant :union {
|
||||||
|
# If the current session has already been given a grant this field will contain a reference
|
||||||
|
# to it. Since stronger grants extend weaker grants only one of these needs to be set at any
|
||||||
|
# given point.
|
||||||
|
# This is mostly useful for session resumption.
|
||||||
|
|
||||||
|
none @5 :Void;
|
||||||
|
# No previous grant for this resource exists for the current user
|
||||||
|
|
||||||
|
notify @6 :Notify;
|
||||||
|
interest @7 :Interest;
|
||||||
|
claim @8 :Claim;
|
||||||
|
# The user has a respective grant on the resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResourceCaps {
|
||||||
|
# Capabilities transfered for a resource. Users will have some or all of these set to non-null
|
||||||
|
# depending on their permission level.
|
||||||
|
|
||||||
|
getState @0 () -> State;
|
||||||
# Readonly access to the state of a resource.
|
# Readonly access to the state of a resource.
|
||||||
# A resource can have "state". State are values attached to a resource that describe a specific
|
# A resource can have "state". State are values attached to a resource that describe a specific
|
||||||
# state that users or administrators want this resource to be in. Usually this state consists of
|
# state that users or administrators want this resource to be in. Usually this state consists of
|
||||||
# a number of primitive values encoding for example "turned on" or "turned off".
|
# a number of primitive values encoding for example "turned on" or "turned off".
|
||||||
# Users with the required permissions may change this state.
|
|
||||||
|
|
||||||
claimable @5 :Claimable;
|
setNotify @1 ( callback :Callback ) -> ( notify :Notify );
|
||||||
# Writeable access to the state of a resource.
|
# Notify allows clients to be informed about state changes asyncronously. A client can register
|
||||||
|
# a callback that is called every time state changes happen to the resource in question.
|
||||||
|
# Notify callbacks are ephermal. If the connection to the server is lost any callbacks from that
|
||||||
|
# client for any resource are unregistered.
|
||||||
|
|
||||||
|
claim @2 () -> ClaimResponse;
|
||||||
|
# Request writeable access to the state of a resource.
|
||||||
# Resources are semaphores. They allow writeable access for n ∈ ℕ\{0} clients, depending on the
|
# Resources are semaphores. They allow writeable access for n ∈ ℕ\{0} clients, depending on the
|
||||||
# exact resource in question. In some cases n ≔ 1, and the only write access is exclusive.
|
# exact resource in question. In some cases n ≔ 1, and the only write access is exclusive.
|
||||||
# "Claims" model this by requiring a client to first assert a claim, thus reserving a semaphore
|
# "Claims" model this by requiring a client to first assert a claim, thus reserving a semaphore
|
||||||
# slot or failing if no more are available, and then using this claim to write to a resources
|
# slot or failing if no more are available, and then using this claim to write to a resources
|
||||||
# state.
|
# state.
|
||||||
|
|
||||||
interestable @6 :Interestable;
|
interest @3 () -> ( interest :Interest );
|
||||||
|
# Register an "Interest" on this resource.
|
||||||
# Sometimes clients are not just interested in the state of a resource but rather want a
|
# Sometimes clients are not just interested in the state of a resource but rather want a
|
||||||
# resource to stay in a specific state. e.g. somebody working in a makerspace wants the space to
|
# resource to stay in a specific state. e.g. somebody working in a makerspace wants the space to
|
||||||
# stay open, even though they themselves may not have permission to keep the makerspace open.
|
# stay open, even though they themselves may not have permission to keep the makerspace open.
|
||||||
# "Interest" represents this. Specifically right now it tells BFFH that the client wants at
|
# "Interest" represents this. Specifically right now it tells BFFH that the client wants at
|
||||||
# least one `Claim` to remain.
|
# least one `Claim` to remain.
|
||||||
|
|
||||||
|
override @4 () -> ( claim :Claim );
|
||||||
|
# Override forces a claim to a resource, even if it is already exhausted. This is
|
||||||
|
# primarely useful for administrative overrides.
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Notifiable {
|
struct ClaimResponse {
|
||||||
|
enum Error {
|
||||||
|
# Error describing why a claim failed.
|
||||||
|
|
||||||
}
|
exhausted @0;
|
||||||
|
# There are no more free Claim slots
|
||||||
|
|
||||||
interface Interestable {
|
locked @1;
|
||||||
|
# The resource was locked
|
||||||
|
|
||||||
}
|
precondition @2;
|
||||||
|
# Some precondition was not met
|
||||||
|
|
||||||
interface Claimable {
|
dependencies @3;
|
||||||
# Having this capability set (i.e. not be a `nullptr`) means the user has at least writeable
|
# Resource failed to secure dependencies
|
||||||
# access to a resource
|
}
|
||||||
|
|
||||||
claim @0 () -> ClaimResponse;
|
union {
|
||||||
# Assert a claim on a resource.
|
failed :group {
|
||||||
|
error @0 :Error;
|
||||||
struct ClaimResponse {
|
reason @1 :L10NString;
|
||||||
enum Error {
|
|
||||||
# Error describing why a claim failed.
|
|
||||||
|
|
||||||
exhausted @0;
|
|
||||||
# There are no more free Claim slots
|
|
||||||
|
|
||||||
locked @1;
|
|
||||||
# The resource was locked
|
|
||||||
|
|
||||||
precondition @2;
|
|
||||||
# Some precondition was not met
|
|
||||||
|
|
||||||
dependencies @3;
|
|
||||||
# Resource failed to secure dependencies
|
|
||||||
}
|
|
||||||
|
|
||||||
union {
|
|
||||||
failed :group {
|
|
||||||
error @0 :Error;
|
|
||||||
reason @1 :L10NString;
|
|
||||||
}
|
|
||||||
success @2 :Claim;
|
|
||||||
}
|
}
|
||||||
|
success @2 :Claim;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Map(Key, Value) {
|
struct Map(Key, Value) {
|
||||||
# A serialized key-value map represented as a list of (k,v) tuples.
|
# A serialized key-value map represented as a list of (k,v) tuples.
|
||||||
|
|
||||||
@ -123,7 +138,7 @@ struct Map(Key, Value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using State = Map(Oid, Value);
|
using State = Map(OID, Value);
|
||||||
# Update state provided to a resource via a claim is represented as a Map of human-readable
|
# Update state provided to a resource via a claim is represented as a Map of human-readable
|
||||||
# identifiers to Cap'n Proto Values. These Values can be either primitive types such as Uint8,
|
# identifiers to Cap'n Proto Values. These Values can be either primitive types such as Uint8,
|
||||||
# Float64 or more complex types such as structs, lists, or enums.
|
# Float64 or more complex types such as structs, lists, or enums.
|
||||||
@ -149,76 +164,54 @@ using State = Map(Oid, Value);
|
|||||||
|
|
||||||
|
|
||||||
interface Notify {
|
interface Notify {
|
||||||
# The Notify interface allows clients to be informed about state changes asyncronously. It is
|
# If an user has a notify callback registered it can use this capability to remove it again
|
||||||
# mainly designed around the `register` function which allows a client to register a callback
|
|
||||||
# that is called every time state changes happen to the resource in question. It also allows
|
|
||||||
# syncronous read access to a resource's output state.
|
|
||||||
# Notify are ephermal. If the connection to the server is lost all `Notify` from that client are
|
|
||||||
# unregistered.
|
|
||||||
|
|
||||||
readOutput @0 State;
|
remove @0 ();
|
||||||
# TODO: There should probably be a more efficient approach for reading state than "read *all*
|
# Remove any notify callbacks from this user for this resource.
|
||||||
# state".
|
|
||||||
|
|
||||||
setNotify @1 ( cb :Callback );
|
install @1 ( callback :Callback );
|
||||||
# Register a given callback to be called on every state update. If this client already has a
|
# Install a notify callback, replacing any existing one. This method is useful when getting this
|
||||||
# callback registered for this resource the old callback is replaced.
|
# interface implicitly via Interest or Claim.
|
||||||
|
}
|
||||||
|
|
||||||
delNotify @2 () -> :Bool;
|
interface Callback {
|
||||||
# Unregister a registered callback
|
# This callback interface needs to be implemented on the client
|
||||||
|
|
||||||
interface Callback {
|
newState @0 State;
|
||||||
# This callback interface needs to be implemented on the client
|
# A server will call newState() with the updated output state. However a server will only
|
||||||
|
# allow one in-flight call, so as long as the previous call to newState() hasn't completed
|
||||||
|
# the server will drop intermediary updates as to not overload a client.
|
||||||
|
# Specifically, example timeline:
|
||||||
|
# 1. Update A
|
||||||
|
# 2. Server calls newState(A)
|
||||||
|
# 3. Update B
|
||||||
|
# 4. Update C
|
||||||
|
# 5. Call to newState(A) completes
|
||||||
|
# 6. Server calls newState(C)
|
||||||
|
# So Update B was never sent to the client but the client will eventually always end up with
|
||||||
|
# the latest state.
|
||||||
|
|
||||||
newState @0 State;
|
# TODO: There should probably be a more efficient approach here too, something along the
|
||||||
# A server will call newState() with the updated output state. However a server will only
|
# lines of server-side filtering.
|
||||||
# allow one in-flight call, so as long as the previous call to newState() hasn't completed
|
|
||||||
# the server will drop intermediary updates as to not overload a client.
|
|
||||||
# Specifically, example timeline:
|
|
||||||
# 1. Update A
|
|
||||||
# 2. Server calls newState(A)
|
|
||||||
# 3. Update B
|
|
||||||
# 4. Update C
|
|
||||||
# 5. Call to newState(A) completes
|
|
||||||
# 6. Server calls newState(C)
|
|
||||||
# So Update B was never sent to the client but the client will eventually always end up with
|
|
||||||
# the latest state.
|
|
||||||
|
|
||||||
# TODO: There should probably be a more efficient approach here too, something along the
|
|
||||||
# lines of server-side filtering.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Interest extends(Notify) {
|
interface Interest extends(Notify) {
|
||||||
# "Interest" right now it tells BFFH that the client wants at least one `Claim` to remain.
|
# "Interest" right now tells BFFH that the client wants at least one `Claim` to remain.
|
||||||
|
# However, more generally an Interest allows hooking into state changes and block or modify
|
||||||
|
# them.
|
||||||
|
|
||||||
register @0 ( cb :Callback ) -> ( handle :Handle );
|
dropInterest @0 ();
|
||||||
# Register a callback that BFFH will use to send notifications back to the client asyncronously.
|
# Remove this interest from a resource.
|
||||||
# This creates an "Interest" on this resource. Setting the callback to a `nullptr` will still
|
|
||||||
# register an interest but the server will not be able to inform a client about an impeding
|
|
||||||
# claim drop.
|
|
||||||
|
|
||||||
blocking @1 ();
|
lock @1 ();
|
||||||
# As an alternative to the `register`/`Callback` system you can also call `blocking` which will
|
# Lock a resource, making all future state changes from any user but the current one fail until
|
||||||
# — as the name suggests — block until the last claim is being dropped. This will register an
|
# the lock is released.
|
||||||
# ephermal Interest that can not survive a disconnect.
|
|
||||||
|
|
||||||
interface Callback {
|
unlock @2 ();
|
||||||
drop @0 ();
|
# Unlock the resource again, allowing other users to change state again.
|
||||||
# The last claim on the resource this Interest is registered is being dropped, invalidating
|
|
||||||
# the Interest.
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Handle {
|
|
||||||
# A Handle back to the server side Interest registered. Destroying this capability will also
|
|
||||||
# inform the server and remove the Interest again.
|
|
||||||
# TODO: `extends (Persistance)` so that clients can `save` this capability and thus make the
|
|
||||||
# Interest survive disconnects.
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Claim extends(Access) {
|
interface Claim extends(Interest) {
|
||||||
# TODO: extend Persistance. Claims and Interests need to be able to survive a connection loss,
|
# TODO: extend Persistance. Claims and Interests need to be able to survive a connection loss,
|
||||||
# which is exactly what `SturdyRef`/Persistance are designed to provide. The Persistance
|
# which is exactly what `SturdyRef`/Persistance are designed to provide. The Persistance
|
||||||
# interface only provides one method, `save`, returning a `SturdyRef`. A SturdyRef is a generic
|
# interface only provides one method, `save`, returning a `SturdyRef`. A SturdyRef is a generic
|
||||||
@ -234,6 +227,9 @@ interface Claim extends(Access) {
|
|||||||
update @1 State -> UpdateResult;
|
update @1 State -> UpdateResult;
|
||||||
# Update the State of the claimed resource with the given one
|
# Update the State of the claimed resource with the given one
|
||||||
|
|
||||||
|
dropClaim @2 ();
|
||||||
|
# Drop this claim
|
||||||
|
|
||||||
struct UpdateResult {
|
struct UpdateResult {
|
||||||
enum Error {
|
enum Error {
|
||||||
# Reason why the update failed
|
# Reason why the update failed
|
||||||
@ -249,12 +245,16 @@ interface Claim extends(Access) {
|
|||||||
|
|
||||||
typeError @3;
|
typeError @3;
|
||||||
# A field in the update has a known identifier but a bad type for that identifier
|
# A field in the update has a known identifier but a bad type for that identifier
|
||||||
|
|
||||||
|
locked @4;
|
||||||
|
# The state is currently locked and can not be modified by anybody but the user that
|
||||||
|
# issued the lock.
|
||||||
}
|
}
|
||||||
|
|
||||||
union {
|
union {
|
||||||
failed :group {
|
failed :group {
|
||||||
error @0 :Error;
|
error @0 :Error;
|
||||||
field @1 :Text;
|
field @1 :OID;
|
||||||
reason @2 :L10NString;
|
reason @2 :L10NString;
|
||||||
}
|
}
|
||||||
success @3 :Void;
|
success @3 :Void;
|
||||||
|
@ -53,7 +53,7 @@ struct UUID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct OID {
|
struct OID {
|
||||||
bytes @0 :Data
|
bytes @0 :Data;
|
||||||
# The OID, encoded as a sequence of varints. In this encoding the lower 7 bits of each octet
|
# The OID, encoded as a sequence of varints. In this encoding the lower 7 bits of each octet
|
||||||
# contain data bits while the MSB indicates if the *following* octet is still part of this edge.
|
# contain data bits while the MSB indicates if the *following* octet is still part of this edge.
|
||||||
# It is the same encoding UTF-8 uses. To decode you simply collect octets until you find an
|
# It is the same encoding UTF-8 uses. To decode you simply collect octets until you find an
|
||||||
|
Loading…
x
Reference in New Issue
Block a user