Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions src/cli/artemis.toit
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ class Artemis:
ensure-authenticated -> none:
connected-auth-provider_

create-device --device-id/Uuid? --organization-id/Uuid -> Device:
return connected-auth-provider_.create-device-in-organization
--device-id=device-id
--organization-id=organization-id

/**
Fetches the organizations with the given $id.

Expand Down
8 changes: 0 additions & 8 deletions src/cli/auth_providers/auth-provider.toit
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,6 @@ interface AuthProvider implements Authenticatable:
*/
logout

/**
Adds a new device to the organization with the given $organization-id.

Takes a $device-id, representing the user's chosen name for the device.
The $device-id may be null in which case the server creates an alias.
*/
create-device-in-organization --organization-id/Uuid --device-id/Uuid? -> Device

/** Returns the used-id of the authenticated user. */
get-current-user-id -> string

Expand Down
12 changes: 0 additions & 12 deletions src/cli/auth_providers/http/base.toit
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,6 @@ class AuthProviderHttpToit implements AuthProvider:
cli_.config.remove "$(CONFIG-SERVER-AUTHS-KEY).$(server-config_.name)"
cli_.config.write

create-device-in-organization --organization-id/Uuid --device-id/Uuid? -> Device:
map := {
"organization_id": "$organization-id",
}
if device-id: map["alias"] = "$device-id"

device-info := send-request_ COMMAND-CREATE-DEVICE-IN-ORGANIZATION_ map
return Device
--hardware-id=Uuid.parse device-info["id"]
--id=Uuid.parse device-info["alias"]
--organization-id=Uuid.parse device-info["organization_id"]

get-current-user-id -> Uuid:
return current-user-id_

Expand Down
13 changes: 0 additions & 13 deletions src/cli/auth_providers/supabase/supabase.toit
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,6 @@ class AuthProviderSupabase implements AuthProvider:
logout:
client_.auth.logout

create-device-in-organization --organization-id/Uuid --device-id/Uuid? -> Device:
payload := {
"organization_id": "$organization-id",
}

if device-id: payload["alias"] = "$device-id"

inserted := client_.rest.insert "devices" payload
return Device
--hardware-id=Uuid.parse inserted["id"]
--id=Uuid.parse inserted["alias"]
--organization-id=Uuid.parse inserted["organization_id"]

get-current-user-id -> Uuid:
return Uuid.parse client_.auth.get-current-user["id"]

Expand Down
5 changes: 4 additions & 1 deletion src/cli/broker.toit
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,10 @@ class Broker:
state := {
"identity": identity,
}
broker-connection_.notify-created --device-id=device.id --state=state
broker-connection_.notify-created
--hardware-id=device.hardware-id
--device-id=device.id
--state=state

device-for --id/Uuid -> DeviceDetailed:
devices := broker-connection_.get-devices --device-ids=[id]
Expand Down
12 changes: 8 additions & 4 deletions src/cli/brokers/broker.toit
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,15 @@ interface BrokerCli implements Authenticatable:
download-firmware --id/string -> ByteArray

/**
Informs the broker that a device with the given $device-id has been provisioned.
The $state map is the initial state of the device. Until it connects to the
broker there is (probably) only identity information in it.
Informs the broker that a new device has been provisioned.

The broker registers the device under the configured scope (from its
server-config). For a shared-tenancy broker, this also creates the
corresponding record on the auth side. The $state map is the initial
state of the device; until it connects to the broker there is
(probably) only identity information in it.
*/
notify-created --device-id/Uuid --state/Map -> none
notify-created --hardware-id/Uuid --device-id/Uuid --state/Map -> none

/**
Fetches all events of the given $types for all devices in the $device-ids list.
Expand Down
5 changes: 4 additions & 1 deletion src/cli/brokers/http/base.toit
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ class BrokerCliHttp implements BrokerCli:
"path": "/toit-artemis-assets/$scope/firmware/$id",
}

notify-created --device-id/Uuid --state/Map -> none:
notify-created --hardware-id/Uuid --device-id/Uuid --state/Map -> none:
// hardware-id is only used by overrides (e.g., the Supabase variant
// writing into the auth-side devices table). The on-the-wire
// notify-broker-created RPC takes just the device-id and state.
send-request_ COMMAND-NOTIFY-BROKER-CREATED_ {
"_device_id": "$device-id",
"_state": state,
Expand Down
19 changes: 19 additions & 0 deletions src/cli/brokers/supabase/supabase.toit
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cli show Cli
import http
import supabase
import certificate-roots
import uuid show Uuid

import ..http.base
import ...config
Expand Down Expand Up @@ -35,6 +36,7 @@ create-broker-cli-supabase-http server-config/ServerConfigSupabase --cli/Cli ->
--root-certificate-ders=server-config.root-certificate-der ? [server-config.root-certificate-der] : null
--poll-interval=server-config.poll-interval
--scope=server-config.scope
--tenancy=server-config.tenancy

return BrokerCliSupabase --id=id supabase-client http-config

Expand Down Expand Up @@ -69,6 +71,23 @@ class BrokerCliSupabase extends BrokerCliHttp:
logout:
supabase-client_.auth.logout

/**
Registers a newly provisioned device with the broker.

For a shared-tenancy deployment the broker and the auth provider live
in the same Supabase project; the broker is responsible for creating
the device row in the auth-side `devices` table as part of the
notify-created handshake.
*/
notify-created --hardware-id/Uuid --device-id/Uuid --state/Map -> none:
if server-config_.tenancy == TENANCY-SHARED:
supabase-client_.rest.insert "devices" --no-return-inserted {
"id": "$hardware-id",
"alias": "$device-id",
"organization_id": server-config_.scope.to-json,
}
super --hardware-id=hardware-id --device-id=device-id --state=state

extra-headers -> Map:
bearer/string := supabase-client_.session_
? supabase-client_.session_.access-token
Expand Down
19 changes: 10 additions & 9 deletions src/cli/fleet.toit
Original file line number Diff line number Diff line change
Expand Up @@ -1094,22 +1094,23 @@ class FleetWithDevices extends Fleet:
/**
Provisions a device.

Contacts the Artemis server and creates a new device entry with the
given $device-id (used as "alias" on the server side) in the
organization with the given $organization-id.
Mints a fresh hardware-id locally and registers the device with the
broker (which, for a shared-tenancy deployment, also creates the
corresponding row in the auth provider's device table).

Writes the identity file to $out-path.
*/
provision --device-id/Uuid? --out-path/string:
// Ensure that we are authenticated with both the Artemis server and the broker.
// We don't want to create a device on Artemis and then have an error with the broker.
provision --device-id/Uuid --out-path/string:
// Ensure that we are authenticated with both the auth provider and
// the broker before doing anything visible. The auth-provider check
// gates access; the broker is what actually receives the new device.
artemis.ensure-authenticated
broker.ensure-authenticated

device := artemis.create-device
--device-id=device-id
device := Device
--hardware-id=random-uuid
--id=device-id
--organization-id=organization-id
assert: device.id == device-id

broker.notify-created device

Expand Down
2 changes: 0 additions & 2 deletions src/shared/constants.toit
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

/* Artemis commands. */
COMMAND-CHECK-IN_ ::= 0
COMMAND-CREATE-DEVICE-IN-ORGANIZATION_ ::= 1
COMMAND-SIGN-UP_ ::= 2
COMMAND-SIGN-IN_ ::= 3
COMMAND-GET-ORGANIZATIONS_ ::= 4
Expand All @@ -19,7 +18,6 @@ COMMAND-UPDATE-PROFILE_ ::= 14

ARTEMIS-COMMAND-TO-STRING ::= {
COMMAND-CHECK-IN_: "check-in",
COMMAND-CREATE-DEVICE-IN-ORGANIZATION_: "create-device-in-organization",
COMMAND-SIGN-UP_: "sign-up",
COMMAND-SIGN-IN_: "sign-in",
COMMAND-GET-ORGANIZATIONS_: "get-organizations",
Expand Down
56 changes: 51 additions & 5 deletions src/shared/server-config.toit
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ import uuid show Uuid

import .scope show Scope

/**
A multi-tenant deployment: the broker shares its underlying storage with
an auth provider (so creating a device on the broker also has to land
a row in the auth provider's device table).
*/
TENANCY-SHARED ::= "shared"

/**
A single-tenant deployment: the broker is self-contained and does not
need to coordinate with an auth provider's storage.
*/
TENANCY-DEDICATED ::= "dedicated"

abstract class ServerConfig:
name/string

Expand All @@ -19,10 +32,18 @@ abstract class ServerConfig:
*/
scope/Scope?

/**
The deployment shape of this server.

Either $TENANCY-SHARED, $TENANCY-DEDICATED, or null (caller hasn't
specified; treated as dedicated by consumers).
*/
tenancy/string?

cache-key_/string? := null
ders-already-installed_/bool := false

constructor.from-sub_ .name --.scope/Scope?=null:
constructor.from-sub_ .name --.scope/Scope?=null --.tenancy/string?=null:

/**
Creates a new broker-config from a JSON map.
Expand Down Expand Up @@ -142,6 +163,7 @@ class ServerConfigSupabase extends ServerConfig implements supabase.ServerConfig
if use-tls == null: use-tls = json.contains "root_certificate_name"
scope-value := json.get "scope"
scope/Scope? := scope-value and (Scope scope-value)
tenancy/string? := json.get "tenancy"

return ServerConfigSupabase name
--host=json["host"]
Expand All @@ -150,15 +172,17 @@ class ServerConfigSupabase extends ServerConfig implements supabase.ServerConfig
--use-tls=use-tls
--root-certificate-der=root-der
--scope=scope
--tenancy=tenancy

constructor name/string
--.host
--.anon
--.use-tls=true
--.root-certificate-der=null
--.poll-interval=DEFAULT-POLL-INTERVAL
--scope/Scope?=null:
super.from-sub_ name --scope=scope
--scope/Scope?=null
--tenancy/string?=null:
super.from-sub_ name --scope=scope --tenancy=tenancy

operator== other:
if other is not ServerConfigSupabase: return false
Expand Down Expand Up @@ -189,6 +213,8 @@ class ServerConfigSupabase extends ServerConfig implements supabase.ServerConfig
result["root_certificate_der_id"] = serialized
if scope:
result["scope"] = scope.to-json
if tenancy:
result["tenancy"] = tenancy
return result

to-service-json [--der-serializer] --base64/bool=false -> Map:
Expand Down Expand Up @@ -228,6 +254,7 @@ class ServerConfigSupabase extends ServerConfig implements supabase.ServerConfig
--root-certificate-der=root-certificate-der
--poll-interval=poll-interval
--scope=scope
--tenancy=tenancy

with --scope/Scope -> ServerConfigSupabase:
return ServerConfigSupabase
Expand All @@ -238,6 +265,18 @@ class ServerConfigSupabase extends ServerConfig implements supabase.ServerConfig
--root-certificate-der=root-certificate-der
--poll-interval=poll-interval
--scope=scope
--tenancy=tenancy

with --tenancy/string -> ServerConfigSupabase:
return ServerConfigSupabase
name
--host=host
--anon=anon
--use-tls=use-tls
--root-certificate-der=root-certificate-der
--poll-interval=poll-interval
--scope=scope
--tenancy=tenancy

/**
A broker configuration for an HTTP-based broker.
Expand Down Expand Up @@ -266,6 +305,7 @@ class ServerConfigHttp extends ServerConfig:
if use-tls == null: use-tls = config.contains "root_certificate_names"
scope-value := config.get "scope"
scope/Scope? := scope-value and (Scope scope-value)
tenancy/string? := config.get "tenancy"
return ServerConfigHttp name
--host=config["host"]
--port=config.get "port"
Expand All @@ -276,6 +316,7 @@ class ServerConfigHttp extends ServerConfig:
--admin-headers=config.get "admin_headers"
--poll-interval=Duration --us=config["poll_interval"]
--scope=scope
--tenancy=tenancy

constructor name/string
--.host
Expand All @@ -286,9 +327,10 @@ class ServerConfigHttp extends ServerConfig:
--.device-headers
--.admin-headers
--.poll-interval=DEFAULT-POLL-INTERVAL
--scope/Scope?=null:
--scope/Scope?=null
--tenancy/string?=null:

super.from-sub_ name --scope=scope
super.from-sub_ name --scope=scope --tenancy=tenancy

operator== other:
if other is not ServerConfigHttp: return false
Expand Down Expand Up @@ -318,12 +360,15 @@ class ServerConfigHttp extends ServerConfig:
result["admin_headers"] = admin-headers
if scope:
result["scope"] = scope.to-json
if tenancy:
result["tenancy"] = tenancy
return result

to-service-json [--der-serializer] --base64/bool=false -> Map:
result := to-json --der-serializer=der-serializer --base64=base64
result.remove "admin_headers"
result.remove "scope"
result.remove "tenancy"
return result

compute-cache-key_ -> string:
Expand All @@ -341,3 +386,4 @@ class ServerConfigHttp extends ServerConfig:
--admin-headers=admin-headers
--poll-interval=poll-interval
--scope=scope
--tenancy=tenancy
Loading
Loading