diff --git a/authenticationsystem.capnp b/authenticationsystem.capnp index 5aa111a..bd05dac 100644 --- a/authenticationsystem.capnp +++ b/authenticationsystem.capnp @@ -1,135 +1,76 @@ @0xb9cffd29ac983e9f; + using Rust = import "programming_language/rust.capnp"; $Rust.parentModule("schema"); using CSharp = import "programming_language/csharp.capnp"; $CSharp.namespace("FabAccessAPI.Schema"); -using User = import "user.capnp".User; - -interface AuthenticationSystem { - mechanisms @0 () -> ( mechs :List(Text) ); - # Get the list of mechanisms supported by the server - - start @1 ( request :Request ) -> ( response :Response ); - # Initiate an authentication exchange - # NOTE: Calling start() after an authentication exchange has already - # finished is undefined behaviour. If you want to double-authenticate call - # auth() from connection.capnp again to get a fresh capability you can use. - # This may however return NULL if you are not allowed to authenticate twice. - - step @2 ( response :Data ) -> ( response :Response ); - # Respond to a challenge with more data - # NOTE: As with start() calling this after having received an outcome is - # undefined behaviour. - - abort @3 () -> (); - # Abort the current exchange. This may be sent by both client and server - # at any point during the exchange. It MUST not be sent by a server - # after sending an outcome or by a client after receiving an outcome. - # A server receiving an abort after sending an outcome but before - # receiving any other message MUST respect the abort. - - whoami @4 () -> ( you :User, dummy :UInt8 = 0 ); - # Returns NULL if not authenticated and an User object if authenticated. -} - -struct Request { - mechanism @0 :Text; # The SASL mechanism name. - - initialResponse :union { - # A client may send some intial data when requesting an auth exchange. - # According to RFC4422 section 4.3a an implementation MUST be able to - # distinguish between an empty initial reponse and no initial response. - - none @1 :Void; - # No initial reponse is being sent. - # The lowest numbered field in an union is the default value in Cap'n - # Proto. - - initial @2 :Data; - # A response may be any sequence of octets, including zero-length - # sequences and zero-valued octets. - } -} +using Session = import "connection.capnp".Session; struct Response { - enum Result { - # Result code of the outcome - successful @0; + enum Error { + aborted @0; + # This authentication exchange was aborted by either side. badMechanism @1; # The server does not support this mechanism in this context. - unwilling @2; - # Generic "I'm sorry dave, I can't do that" response. MAY be set for any - # reason, all reasons or no reason at all. - # A server SHOULD set the `action` and `helpText` fields as appropiate. - # This code SHOULD only be sent if no other value is more fitting. + invalidCredentials @2; + # The exchange was valid, but the provided credentials are invalid. This may mean that the + # authcid is not known to the server or that the password/certificate/key/ticket/etc is not + # correct. - invalidCredentials @3; - # The exchange was valid, but the provided credentials are invalid. This - # may mean that the authcid is not known to the server or that the - # password/certificate/key/etc. is not correct. - - unauthorized @4; - # The given authcid is not authorized to act as the requested authzid. - # This MAY also be returned for the cases `authzid == NULL` or - # `authzid == authcid`, for example because login is disabled for that - # authcid. - - malformedAuthZid @5; - # The provided authzid is malformed in some way. - - failed @6; - # A generic failed result. A server sending this result MUST set the - # `action` field to indicate whether this is a temporary or permanent - # failure and SHOULD set `helpText` to a human-readable error message. + failed @3; + # A generic failed result. A server sending this result MUST set the `action` field to + # indicate whether this is a temporary or permanent failure and SHOULD set `description` to + # a human-readable error message. } - enum Action { - # In case of a unsuccessful outcome, how should a client proceed? - - unset @0; - # Not a unsuccessful outcome, or the server doesn't know either. - # Also, the default value - - retry @1; - # A client should just try again, potentially after prompting the user - # to enter their credentials again. - - wait @2; - # The client should wait and try again later. This MAY mean that - # whatever failure happened was temporary or the server is applying - # rate-limiting to the connection. - - permanent @3; - # The issue appears to the server as being permanent. Another try is - # very likely to return the exact same result. In most cases the user - # should notify the responsible system administrator. - } - union { - challence @0 :Data; - outcome :group { - result @1 :Result; # Result code + error :group { + code @0 :Error; + # Error code indicating the cause of the error - action @2 :Action; # Hints for the client how to proceed in case of an error + additionalData @1 :Data; + # Some mechanisms will send additional data after an error + } + # Some kind of error happened. This in the first entry in the union because it is the + # default set value meaning if a server fails to set any of the values, indicating some + # pretty severe server bugs, it is parsed as an "aborted" error. + + challenge @2 :Data; + # The data provided so far is not enough to authenticate the user. The data MAY be a + # NULL-ptr or a non-NULL list ptr of zero bytes which clients MUST pass to their SASL + # implementation as "no data" and "some data of zero length" respectively. - helpText @3 :Text; # Human-readable further information in case of an error + successful :group { + # The exchange was successful and a new session has been created for the authzid that + # was established by the SASL exchange. - additionalData :union { - # Additional data that may be sent by the server to the client after a - # successful authentication exchange. + session @3 :Session; + # The session that was created. It grants access to all capabilities the connecting + # party has permissions for. - none @4 :Void; - # No additional data is being sent. This MUST be set on unsuccessful - # outcomes. - - additional @5 :Data; - # Additional data may be any sequence of octets, including zero-length - # sequences and zero-value octets. - } + additionalData @4 :Data; + # SASL may send additional data with the successful result. This MAY be a NULL-ptr or a + # non-NULL list ptr of zero bytes which clients MUST pass to their SASL implementation + # as "no additional data" and "some additional data of zero length" respectively. } } } + +interface Authentication { + step @0 ( data: Data ) -> ( response: Response ); + # Respond to a challenge with more data. A client MUST NOT call this after having received an + # "successful" response. + + abort @1 () -> (); + # Abort the current exchange. This will invalidate the Authentication making all further calls + # to `step` return an error response. A client MUST NOT call this function after + # having received an "successful" response. + # A server will indicate that they have aborted an authentication exchange by replying with an + # "aborted" Error to the next `step` call. A server SHOULD directly terminate the underlying stream + # after sending this response. The server MAY after a short grace period terminate the stream + # without sending a response if no call to `step` was received by the client. +} diff --git a/connection.capnp b/connection.capnp index 95349f2..a21bf5a 100644 --- a/connection.capnp +++ b/connection.capnp @@ -6,7 +6,7 @@ $Rust.parentModule("schema"); using CSharp = import "programming_language/csharp.capnp"; $CSharp.namespace("FabAccessAPI.Schema"); -using AuthenticationSystem = import "authenticationsystem.capnp".AuthenticationSystem; +using Authentication = import "authenticationsystem.capnp".Authentication; using MachineSystem = import "machinesystem.capnp".MachineSystem; using UserSystem = import "usersystem.capnp".UserSystem; using PermissionSystem = import "permissionsystem.capnp".PermissionSystem; @@ -31,11 +31,18 @@ interface Bootstrap # Designed only for human-facing debugging output so should be informative over machine-readable # Example: ( name = "bffhd", release = "0.3.1-f397e1e [rustc 1.57.0 (f1edd0429 2021-11-29)]") - authenticationSystem @2 () -> ( authenticationSystem : AuthenticationSystem ); + mechanisms @2 () -> ( mechs: List(Text) ); + # Get a list of Mechanisms this server allows in this context. - machineSystem @3 () -> ( machineSystem : MachineSystem ); - - userSystem @4 () -> ( userSystem : UserSystem ); - - permissionSystem @5 () -> ( permissionSystem : PermissionSystem ); + createSession @3 ( mechanism :Text, initialData :Data ) -> ( authentication :Authentication); + # Create a new session with the server that you wish to authenticate using `mechanism`. + # If the mechanism is a client-first mechanism you MAY set `initialData` to contain the data you + # want to send. If the mechanism is server-first or you do not wish to send initial data, make + # initialData a NULL-pointer. +} + +struct Session { + machineSystem @0 : MachineSystem; + userSystem @1 : UserSystem; + permissionSystem @2 : PermissionSystem; } diff --git a/machine.capnp b/machine.capnp index 38254fe..08989d3 100644 --- a/machine.capnp +++ b/machine.capnp @@ -18,10 +18,12 @@ struct Machine { blocked @3; disabled @4; reserved @5; + totakeover @6; } struct MachineInfoExtended { currentUser @0 :User; - transferUser @1 :User; + lastUser @1 :User; + instructorUser @2 :User; } struct Reservation { @@ -42,11 +44,9 @@ struct Machine { info @6 :Info; interface Info $CSharp.name("InfoInterface") { - getMachineInfoExtended @0 () -> ( machineInfoExtended :MachineInfoExtended, dummy :UInt8 = 0); + getPropertyList @0 () -> ( propertyList :List(General.KeyValuePair) ); - getPropertyList @1 () -> ( propertyList :List(General.KeyValuePair) ); - - getReservationList @2 () -> ( reservationList :List(Reservation) ); + getReservationList @1 () -> ( reservationList :List(Reservation) ); } use @7 :Use; @@ -61,10 +61,11 @@ struct Machine { interface InUse $CSharp.name("InUseInterface") { giveBack @0 (); sendRawData @1 (data :Data); + releasefortakeover @2 (); } - transfer @9 :Transfer; - interface Transfer $CSharp.name("TransferInterface") { + takeover @9 :Takeover; + interface Takeover $CSharp.name("TakeoverInterface") { accept @0 (); reject @1 (); } @@ -77,16 +78,18 @@ struct Machine { manage @11 :Manage; interface Manage $CSharp.name("ManageInterface") { - setProperty @0 (property :General.KeyValuePair); - removeProperty @1 (property :General.KeyValuePair); + getMachineInfoExtended @0 () -> ( machineInfoExtended :MachineInfoExtended, dummy :UInt8 = 0); + + setProperty @1 (property :General.KeyValuePair); + removeProperty @2 (property :General.KeyValuePair); - forceUse @2 (); - forceFree @3 (); + forceUse @3 (); + forceFree @4 (); - forceTransfer @4 (user :User); + forceTransfer @5 (user :User); - block @5 (); - disabled @6 (); + block @6 (); + disabled @7 (); } admin @12 :Admin; diff --git a/user.capnp b/user.capnp index 180efee..9ab4733 100644 --- a/user.capnp +++ b/user.capnp @@ -24,8 +24,7 @@ struct User info @3 :Info; interface Info $CSharp.name("InfoInterface") { - getUserInfoExtended @0 () -> ( userInfoExtended :UserInfoExtended ); - listRoles @1 () -> ( roles :List(Role) ); + listRoles @0 () -> ( roles :List(Role) ); } manage @4 :Manage; @@ -35,10 +34,12 @@ struct User admin @5 :Admin; interface Admin $CSharp.name("AdminInterface") { - addRole @0 ( role :Role ) -> (); - removeRole @1 ( role :Role ) -> (); + getUserInfoExtended @0 () -> ( userInfoExtended :UserInfoExtended ); + + addRole @1 ( role :Role ) -> (); + removeRole @2 ( role :Role ) -> (); - pwd @2 ( new_pwd :Text ) -> (); + pwd @3 ( new_pwd :Text ) -> (); } cardDESFireEV2 @6 :CardDESFireEV2;