Skip to content
Open
13 changes: 5 additions & 8 deletions reboot/demos/fig/backend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Application, ServicerFactory } from "@reboot-dev/reboot";
import presence from "@reboot-dev/reboot-std/presence/v1";
import { Application, allow } from "@reboot-dev/reboot";
import { presenceLibrary } from "@reboot-dev/reboot-std/presence/v1";
import { FigBoard } from "../../api/fig/v1/fig_rbt.js";
import { Users } from "../../api/user/v1/user_rbt.js";
import { FIG_BOARD_ID, USERS_ID } from "../../constants.js";
Expand All @@ -13,13 +13,10 @@ const initialize = async (context) => {
};

new Application({
servicers: [
FigServicer,
FigBoardServicer,
UsersServicer,
UserServicer,
servicers: [FigServicer, FigBoardServicer, UsersServicer, UserServicer],
libraries: [
// We use `Presence` from the std library.
...presence.servicers(),
presenceLibrary({ authorizer: allow() }),
],
initialize,
}).run();
6 changes: 4 additions & 2 deletions reboot/examples/kcdc-2025/backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
from chat.v1.channel_rbt import Channel
from chatbot.v1.chatbot_rbt import Chatbot
from reboot.aio.applications import Application
from reboot.aio.auth.authorizers import allow
from reboot.std.collections.queue.v1.queue import QueueServicer
from reboot.std.index.v1 import index
from reboot.std.presence.v1 import presence
from reboot.std.presence.v1.presence import presence_library
from reboot.std.pubsub.v1.pubsub import PubSubServicer
from servicers.channel import ChannelServicer
from servicers.chatbot import ChatbotServicer
Expand Down Expand Up @@ -42,7 +43,8 @@ async def main():
ChatbotServicer,
QueueServicer,
PubSubServicer,
] + index.servicers() + presence.servicers(),
] + index.servicers(),
libraries=[presence_library(authorizer=allow())],
initialize=initialize,
).run()

Expand Down
2 changes: 1 addition & 1 deletion reboot/nodejs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ export function isAppInternal({
export type NativeLibrary = {
nativeLibraryModule: string;
nativeLibraryFunction: string;
authorizer?: Authorizer<unknown, unknown>;
authorizers?: Record<string, Authorizer<unknown, unknown>>;
};

export type TypeScriptLibrary = {
Expand Down
46 changes: 26 additions & 20 deletions reboot/nodejs/reboot_native.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,7 @@ struct TypeScriptLibraryDetails {
struct PythonNativeLibraryDetails {
std::string py_library_module;
std::string py_library_function;
std::optional<NapiSafeObjectReference> js_authorizer;
std::map<std::string, NapiSafeObjectReference> js_authorizers;
};

using LibraryDetails =
Expand All @@ -1566,16 +1566,26 @@ std::vector<std::shared_ptr<LibraryDetails>> make_library_details(
.As<Napi::String>()
.Utf8Value();

// Get authorizer.
std::optional<NapiSafeObjectReference> js_authorizer;
if (!js_library.Get("authorizer").IsUndefined()) {
js_authorizer = NapiSafeObjectReference(
js_library.Get("authorizer").As<Napi::Object>());
// Get authorizers.
std::map<std::string, NapiSafeObjectReference> js_authorizers;
if (!js_library.Get("authorizers").IsUndefined()) {
Napi::Object js_authorizers_object =
js_library.Get("authorizers").As<Napi::Object>();
Napi::Array keys = js_authorizers_object.GetPropertyNames();
for (size_t i = 0; i < keys.Length(); ++i) {
std::string key = keys.Get(i).As<Napi::String>().Utf8Value();
js_authorizers.emplace(
key,
NapiSafeObjectReference(
js_authorizers_object.Get(key).As<Napi::Object>()));
}
}

library_details.push_back(
std::make_shared<LibraryDetails>(
PythonNativeLibraryDetails{module, function, js_authorizer}));
std::make_shared<LibraryDetails>(PythonNativeLibraryDetails{
module,
function,
std::move(js_authorizers)}));
} else if (
!js_library.Get("name").IsUndefined()
&& !js_library.Get("servicers").IsUndefined()) {
Expand Down Expand Up @@ -1706,19 +1716,15 @@ py::list make_py_libraries(
} else if (
auto python_details =
std::get_if<PythonNativeLibraryDetails>(details.get())) {
// Call the library() function with `authorizer` if one is provided.
py::object py_library;
if (python_details->js_authorizer.has_value()) {
py_library =
py::module::import(python_details->py_library_module.c_str())
.attr(python_details->py_library_function.c_str())(
"authorizer"_a =
make_py_authorizer(*python_details->js_authorizer));
} else {
py_library =
py::module::import(python_details->py_library_module.c_str())
.attr(python_details->py_library_function.c_str())();
// Call the library() function, passing any configured authorizers
// as keyword arguments.
py::dict kwargs;
for (const auto& [key, js_authorizer] : python_details->js_authorizers) {
kwargs[py::str(key)] = make_py_authorizer(js_authorizer);
}
py::object py_library =
py::module::import(python_details->py_library_module.c_str())
.attr(python_details->py_library_function.c_str())(**kwargs);
py_libraries.append(py_library);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Presence Library: Change in API, Change in default authorization

This is the one migration note for changes in the presence library.

### `presence.servicers()` deprecated in favor of `presence_library()`

Instead of adding `presence.servicers()` in the list of `servicers`
passed to `Application`, either include `presence_library()` (for Python) or
`presenceLibrary()` (for Typescript) in the list of `libraries`
passed to `Application`.

In Python, `presence_library()` is imported from `reboot.std.presence.v1.presence`.

In Typescript, `presenceLibrary()` is imported from `"@reboot-dev/reboot-std/presence/v1"`.

### Change in default authorization.

Previously, the default authorization for servicers in the presence library was
to allow all calls. The default has now changed to allow calls that are internal
(`is_app_internal()`) or has a token verified by a `TokenVerifier`
()`has_verified_token()`).

The best way to address this change is to introduce a `TokenVerifier` when
starting up your Reboot `Application`.

To allow for anonymous presence, the recommendation is to have the
`TokenVerifier` present an anonymous `Auth()` object. Alternatively, the
`authorizer`s for the presence library by passing instantiating in Python with
`presence_library(authorizer=allow())` or in Typescript with
`presenceLibrary({ authorizer: allow() })`.

Presence servicers can be individually configured by passing
`presence_authorizer`, `subscriber_authorizer`, and `mouse_position_authorizer`
in Python, or `presenceAuthorizer`, `subscriberAuthorizer`, and
`mousePositionAuthorizer` in Typescript.
24 changes: 20 additions & 4 deletions reboot/std/collections/ordered_map/v1/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { NativeLibrary, NativeServicer } from "@reboot-dev/reboot";
import { OrderedMap } from "@reboot-dev/reboot-std-api/collections/ordered_map/v1/ordered_map_rbt.js";
import {
AuthorizerRule,
NativeLibrary,
NativeServicer,
} from "@reboot-dev/reboot";
import {
OrderedMap,
OrderedMapRequestTypes,
} from "@reboot-dev/reboot-std-api/collections/ordered_map/v1/ordered_map_rbt.js";
export * from "@reboot-dev/reboot-std-api/collections/ordered_map/v1/ordered_map_rbt.js";

export default {
Expand All @@ -22,11 +29,20 @@ export function orderedMapLibrary({
// Just using `OrderedMap.Authorizer` results in ts(2749), "refers to a value,
// but is being used as a type." `InstanceType<typeof ...>` allows us to
// refer to the type.
authorizer?: InstanceType<typeof OrderedMap.Authorizer>;
authorizer?:
| InstanceType<typeof OrderedMap.Authorizer>
| AuthorizerRule<OrderedMap.State, OrderedMapRequestTypes>;
} = {}): NativeLibrary {
const authorizers: NativeLibrary["authorizers"] = {};
if (authorizer !== undefined) {
authorizers["authorizer"] =
authorizer instanceof AuthorizerRule
? new OrderedMap.Authorizer({ _default: authorizer })
: authorizer;
}
return {
nativeLibraryModule: "reboot.std.collections.ordered_map.v1.ordered_map",
nativeLibraryFunction: "ordered_map_library",
authorizer,
authorizers,
};
}
14 changes: 10 additions & 4 deletions reboot/std/collections/ordered_map/v1/ordered_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@
from rbt.std.item.v1.item_pb2 import Item
from rbt.v1alpha1.errors_pb2 import InvalidArgument, StateAlreadyConstructed
from reboot.aio.applications import Library
from reboot.aio.auth.authorizers import allow_if, is_app_internal
from reboot.aio.auth.authorizers import (
AuthorizerRule,
allow_if,
is_app_internal,
)
from reboot.aio.concurrently import concurrently
from reboot.aio.contexts import (
ReaderContext,
Expand Down Expand Up @@ -693,7 +697,7 @@ class OrderedMapServicer(OrderedMap.singleton.Servicer):
# Singleton authorizer as class variable.
# Discussion here for singleton authorizer vs subclassing the servicer:
# https://github.com/reboot-dev/mono/pull/5140#issuecomment-3667592432
_authorizer: Optional[OrderedMap.Authorizer] = None
_authorizer: Optional[OrderedMap.Authorizer | AuthorizerRule] = None

def authorizer(self):
if self._authorizer:
Expand Down Expand Up @@ -1148,7 +1152,7 @@ class OrderedMapLibrary(Library):

def __init__(
self,
authorizer: Optional[OrderedMap.Authorizer] = None,
authorizer: Optional[OrderedMap.Authorizer | AuthorizerRule] = None,
):
OrderedMapServicer._authorizer = authorizer

Expand All @@ -1163,5 +1167,7 @@ def servicers():
]


def ordered_map_library(authorizer: Optional[OrderedMap.Authorizer] = None):
def ordered_map_library(
authorizer: Optional[OrderedMap.Authorizer | AuthorizerRule] = None
):
return OrderedMapLibrary(authorizer)
24 changes: 20 additions & 4 deletions reboot/std/collections/queue/v1/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { NativeLibrary, NativeServicer } from "@reboot-dev/reboot";
import { Queue } from "@reboot-dev/reboot-std-api/collections/queue/v1/queue_rbt.js";
import {
AuthorizerRule,
NativeLibrary,
NativeServicer,
} from "@reboot-dev/reboot";
import {
Queue,
QueueRequestTypes,
} from "@reboot-dev/reboot-std-api/collections/queue/v1/queue_rbt.js";
export * from "@reboot-dev/reboot-std-api/collections/queue/v1/queue_rbt.js";

export default {
Expand All @@ -20,11 +27,20 @@ export function queueLibrary({
// Just using `Queue.Authorizer` results in ts(2749), "refers to a value,
// but is being used as a type." `InstanceType<typeof ...>` allows us to
// refer to the type.
authorizer?: InstanceType<typeof Queue.Authorizer>;
authorizer?:
| InstanceType<typeof Queue.Authorizer>
| AuthorizerRule<Queue.State, QueueRequestTypes>;
} = {}): NativeLibrary {
const authorizers: NativeLibrary["authorizers"] = {};
if (authorizer !== undefined) {
authorizers["authorizer"] =
authorizer instanceof AuthorizerRule
? new Queue.Authorizer({ _default: authorizer })
: authorizer;
}
return {
nativeLibraryModule: "reboot.std.collections.queue.v1.queue",
nativeLibraryFunction: "queue_library",
authorizer,
authorizers,
};
}
14 changes: 10 additions & 4 deletions reboot/std/collections/queue/v1/queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
Queue,
)
from reboot.aio.applications import Library
from reboot.aio.auth.authorizers import allow_if, is_app_internal
from reboot.aio.auth.authorizers import (
AuthorizerRule,
allow_if,
is_app_internal,
)
from reboot.aio.contexts import (
ReaderContext,
TransactionContext,
Expand All @@ -35,7 +39,7 @@ class QueueServicer(Queue.Servicer):
# Singleton authorizer as class variable.
# Discussion here for singleton authorizer vs subclassing the servicer:
# https://github.com/reboot-dev/mono/pull/5140#issuecomment-3667592432
_authorizer: Optional[Queue.Authorizer] = None
_authorizer: Optional[Queue.Authorizer | AuthorizerRule] = None

def authorizer(self):
if self._authorizer:
Expand Down Expand Up @@ -213,7 +217,7 @@ class QueueLibrary(Library):

def __init__(
self,
authorizer: Optional[Queue.Authorizer] = None,
authorizer: Optional[Queue.Authorizer | AuthorizerRule] = None,
):
QueueServicer._authorizer = authorizer

Expand All @@ -228,5 +232,7 @@ def servicers():
return [QueueServicer] + sorted_map.servicers()


def queue_library(authorizer: Optional[Queue.Authorizer] = None):
def queue_library(
authorizer: Optional[Queue.Authorizer | AuthorizerRule] = None
):
return QueueLibrary(authorizer)
14 changes: 10 additions & 4 deletions reboot/std/collections/v1/sorted_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
SortedMap,
)
from reboot.aio.applications import Library
from reboot.aio.auth.authorizers import allow_if, is_app_internal
from reboot.aio.auth.authorizers import (
AuthorizerRule,
allow_if,
is_app_internal,
)
from reboot.aio.contexts import ReaderContext, WriterContext
from typing import Optional

Expand All @@ -30,7 +34,7 @@ class SortedMapServicer(SortedMap.singleton.Servicer):
# Singleton authorizer as class variable.
# Discussion here for singleton authorizer vs subclassing the servicer:
# https://github.com/reboot-dev/mono/pull/5140#issuecomment-3667592432
_authorizer: Optional[SortedMap.Authorizer] = None
_authorizer: Optional[SortedMap.Authorizer | AuthorizerRule] = None

def authorizer(self):
if self._authorizer:
Expand Down Expand Up @@ -176,7 +180,7 @@ class SortedMapLibrary(Library):

def __init__(
self,
authorizer: Optional[SortedMap.Authorizer] = None,
authorizer: Optional[SortedMap.Authorizer | AuthorizerRule] = None,
):
SortedMapServicer._authorizer = authorizer

Expand All @@ -188,5 +192,7 @@ def servicers():
return [SortedMapServicer]


def sorted_map_library(authorizer: Optional[SortedMap.Authorizer] = None):
def sorted_map_library(
authorizer: Optional[SortedMap.Authorizer | AuthorizerRule] = None
):
return SortedMapLibrary(authorizer)
24 changes: 20 additions & 4 deletions reboot/std/collections/v1/sorted_map.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { NativeLibrary, NativeServicer } from "@reboot-dev/reboot";
import { SortedMap } from "@reboot-dev/reboot-std-api/collections/v1/sorted_map_rbt.js";
import {
AuthorizerRule,
NativeLibrary,
NativeServicer,
} from "@reboot-dev/reboot";
import {
SortedMap,
SortedMapRequestTypes,
} from "@reboot-dev/reboot-std-api/collections/v1/sorted_map_rbt.js";
export * from "@reboot-dev/reboot-std-api/collections/v1/sorted_map_rbt.js";

export default {
Expand All @@ -20,11 +27,20 @@ export function sortedMapLibrary({
// Just using `SortedMap.Authorizer` results in ts(2749), "refers to a value,
// but is being used as a type." `InstanceType<typeof ...>` allows us to refer
// to the type.
authorizer?: InstanceType<typeof SortedMap.Authorizer>;
authorizer?:
| InstanceType<typeof SortedMap.Authorizer>
| AuthorizerRule<SortedMap.State, SortedMapRequestTypes>;
} = {}): NativeLibrary {
const authorizers: NativeLibrary["authorizers"] = {};
if (authorizer !== undefined) {
authorizers["authorizer"] =
authorizer instanceof AuthorizerRule
? new SortedMap.Authorizer({ _default: authorizer })
: authorizer;
}
return {
nativeLibraryModule: "reboot.std.collections.v1.sorted_map",
nativeLibraryFunction: "sorted_map_library",
authorizer,
authorizers,
};
}
Loading
Loading