Skip to content
Merged
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
15 changes: 15 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ Version 2.0.2

To be released.

### @fedify/fedify

- Removed the deprecated third and fourth parameters (`signedKey` and
`signedKeyOwner`) from `AuthorizePredicate` and
`ObjectAuthorizePredicate`. These parameters were deprecated since
Fedify 1.5.0 in favor of `RequestContext.getSignedKey()` and
`RequestContext.getSignedKeyOwner()` methods, but were mistakenly
left in the Fedify 2.0.0 release. The internal handler code that
eagerly called `getSignedKey()` and `getSignedKeyOwner()` before
invoking the predicate has also been removed; predicates should now
call those methods themselves when needed. [[#473], [#590]]

[#473]: https://github.com/fedify-dev/fedify/issues/473
[#590]: https://github.com/fedify-dev/fedify/pull/590


Version 2.0.1
-------------
Expand Down
3 changes: 2 additions & 1 deletion docs/manual/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -1690,8 +1690,9 @@ federation
}
)
.setFirstCursor(async (ctx, values) => "")
.authorize(async (ctx, values, signedKey, signedKeyOwner) => {
.authorize(async (ctx, values) => {
// Only allow access if the viewer is the owner of the bookmarks
const signedKeyOwner = await ctx.getSignedKeyOwner();
if (signedKeyOwner == null) return false;

const viewerId = await getActorIdentifier(signedKeyOwner.id);
Expand Down
29 changes: 3 additions & 26 deletions packages/fedify/src/federation/callback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Activity, Actor, CryptographicKey, Object } from "@fedify/vocab";
import type { Activity, Actor, Object } from "@fedify/vocab";
import type { Link } from "@fedify/webfinger";
import type { NodeInfo } from "../nodeinfo/types.ts";
import type { PageItems } from "./collection.ts";
Expand Down Expand Up @@ -273,25 +273,14 @@ export type OutboxPermanentFailureHandler<TContextData> = (
*
* @template TContextData The context data to pass to the {@link Context}.
* @param context The request context.
* @param identifier The internal identifier of the actor that is being requested.
* @param signedKey *Deprecated in Fedify 1.5.0 in favor of
* {@link RequestContext.getSignedKey} method.*
* The key that was used to sign the request, or `null` if
* the request was not signed or the signature was invalid.
* @param signedKeyOwner *Deprecated in Fedify 1.5.0 in favor of
* {@link RequestContext.getSignedKeyOwner} method.*
* The actor that owns the key that was used to sign the
* request, or `null` if the request was not signed or the
* signature was invalid, or if the key is not associated
* with an actor.
* @param identifier The internal identifier of the actor that is being
* requested.
* @returns `true` if the request is authorized, `false` otherwise.
* @since 0.7.0
*/
export type AuthorizePredicate<TContextData> = (
context: RequestContext<TContextData>,
identifier: string,
signedKey: CryptographicKey | null,
signedKeyOwner: Actor | null,
) => boolean | Promise<boolean>;

/**
Expand All @@ -301,24 +290,12 @@ export type AuthorizePredicate<TContextData> = (
* @template TParam The parameter names of the requested URL.
* @param context The request context.
* @param values The parameters of the requested URL.
* @param signedKey *Deprecated in Fedify 1.5.0 in favor of
* {@link RequestContext.getSignedKey} method.*
* The key that was used to sign the request, or `null` if
* the request was not signed or the signature was invalid.
* @param signedKeyOwner *Deprecated in Fedify 1.5.0 in favor of
* {@link RequestContext.getSignedKeyOwner} method.*
* The actor that owns the key that was used to sign the
* request, or `null` if the request was not signed or the
* signature was invalid, or if the key is not associated
* with an actor.
* @returns `true` if the request is authorized, `false` otherwise.
* @since 0.7.0
*/
export type ObjectAuthorizePredicate<TContextData, TParam extends string> = (
context: RequestContext<TContextData>,
values: Record<TParam, string>,
signedKey: CryptographicKey | null,
signedKeyOwner: Actor | null,
) => boolean | Promise<boolean>;

/**
Expand Down
40 changes: 24 additions & 16 deletions packages/fedify/src/federation/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,9 @@ test("handleActor()", async () => {
context,
identifier: "someone",
actorDispatcher,
authorizePredicate: (_ctx, _handle, signedKey, signedKeyOwner) =>
signedKey != null && signedKeyOwner != null,
authorizePredicate: async (ctx, _handle) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
onNotFound,
onUnauthorized,
},
Expand All @@ -215,8 +216,9 @@ test("handleActor()", async () => {
context,
identifier: "someone",
actorDispatcher,
authorizePredicate: (_ctx, _handle, signedKey, signedKeyOwner) =>
signedKey != null && signedKeyOwner != null,
authorizePredicate: async (ctx, _handle) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
onNotFound,
onUnauthorized,
},
Expand Down Expand Up @@ -452,8 +454,9 @@ test("handleObject()", async () => {
context,
values: { identifier: "someone", id: "123" },
objectDispatcher,
authorizePredicate: (_ctx, _values, signedKey, signedKeyOwner) =>
signedKey != null && signedKeyOwner != null,
authorizePredicate: async (ctx, _values) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
onNotFound,
onUnauthorized,
},
Expand All @@ -474,8 +477,9 @@ test("handleObject()", async () => {
context,
values: { identifier: "someone", id: "123" },
objectDispatcher,
authorizePredicate: (_ctx, _values, signedKey, signedKeyOwner) =>
signedKey != null && signedKeyOwner != null,
authorizePredicate: async (ctx, _values) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
onNotFound,
onUnauthorized,
},
Expand Down Expand Up @@ -751,8 +755,9 @@ test("handleCollection()", async () => {
},
collectionCallbacks: {
dispatcher,
authorizePredicate: (_ctx, _handle, key, keyOwner) =>
key != null && keyOwner != null,
authorizePredicate: async (ctx, _handle) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
},
onNotFound,
onUnauthorized,
Expand All @@ -779,8 +784,9 @@ test("handleCollection()", async () => {
},
collectionCallbacks: {
dispatcher,
authorizePredicate: (_ctx, _handle, key, keyOwner) =>
key != null && keyOwner != null,
authorizePredicate: async (ctx, _handle) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
},
onNotFound,
onUnauthorized,
Expand Down Expand Up @@ -1634,8 +1640,9 @@ test("handleCustomCollection()", async () => {
values: { identifier: "someone" },
collectionCallbacks: {
dispatcher,
authorizePredicate: (_ctx, _values, key, keyOwner) =>
key != null && keyOwner != null,
authorizePredicate: async (ctx, _values) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
},
...errorHandlers,
},
Expand All @@ -1659,8 +1666,9 @@ test("handleCustomCollection()", async () => {
values: { identifier: "someone" },
collectionCallbacks: {
dispatcher,
authorizePredicate: (_ctx, _values, key, keyOwner) =>
key != null && keyOwner != null,
authorizePredicate: async (ctx, _values) =>
await ctx.getSignedKey() != null &&
await ctx.getSignedKeyOwner() != null,
},
...errorHandlers,
},
Expand Down
101 changes: 4 additions & 97 deletions packages/fedify/src/federation/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,27 +92,7 @@ export async function handleActor<TContextData>(
return await onNotFound(request);
}
if (authorizePredicate != null) {
let key = await context.getSignedKey();
key = key?.clone({}, {
// @ts-expect-error: $warning is not part of the type definition
$warning: {
category: ["fedify", "federation", "actor"],
message: "The third parameter of AuthorizePredicate is deprecated " +
"in favor of RequestContext.getSignedKey() method. The third " +
"parameter will be removed in a future release.",
},
}) ?? null;
let keyOwner = await context.getSignedKeyOwner();
keyOwner = keyOwner?.clone({}, {
// @ts-expect-error: $warning is not part of the type definition
$warning: {
category: ["fedify", "federation", "actor"],
message: "The fourth parameter of AuthorizePredicate is deprecated " +
"in favor of RequestContext.getSignedKeyOwner() method. The " +
"fourth parameter will be removed in a future release.",
},
}) ?? null;
if (!await authorizePredicate(context, identifier, key, keyOwner)) {
if (!await authorizePredicate(context, identifier)) {
return await onUnauthorized(request);
}
}
Expand Down Expand Up @@ -160,27 +140,7 @@ export async function handleObject<TContextData>(
const object = await objectDispatcher(context, values);
if (object == null) return await onNotFound(request);
if (authorizePredicate != null) {
let key = await context.getSignedKey();
key = key?.clone({}, {
// @ts-expect-error: $warning is not part of the type definition
$warning: {
category: ["fedify", "federation", "object"],
message: "The third parameter of ObjectAuthorizePredicate is " +
"deprecated in favor of RequestContext.getSignedKey() method. " +
"The third parameter will be removed in a future release.",
},
}) ?? null;
let keyOwner = await context.getSignedKeyOwner();
keyOwner = keyOwner?.clone({}, {
// @ts-expect-error: $warning is not part of the type definition
$warning: {
category: ["fedify", "federation", "object"],
message: "The fourth parameter of ObjectAuthorizePredicate is " +
"deprecated in favor of RequestContext.getSignedKeyOwner() method. " +
"The fourth parameter will be removed in a future release.",
},
}) ?? null;
if (!await authorizePredicate(context, values, key, keyOwner)) {
if (!await authorizePredicate(context, values)) {
return await onUnauthorized(request);
}
}
Expand Down Expand Up @@ -430,34 +390,8 @@ export async function handleCollection<
});
}
if (collectionCallbacks.authorizePredicate != null) {
let key = await context.getSignedKey();
key = key?.clone({}, {
// @ts-expect-error: $warning is not part of the type definition
$warning: {
category: ["fedify", "federation", "collection"],
message: "The third parameter of AuthorizePredicate is deprecated in " +
"favor of RequestContext.getSignedKey() method. The third " +
"parameter will be removed in a future release.",
},
}) ?? null;
let keyOwner = await context.getSignedKeyOwner();
keyOwner = keyOwner?.clone({}, {
// @ts-expect-error: $warning is not part of the type definition
$warning: {
category: ["fedify", "federation", "collection"],
message:
"The fourth parameter of AuthorizePredicate is deprecated in " +
"favor of RequestContext.getSignedKeyOwner() method. The fourth " +
"parameter will be removed in a future release.",
},
}) ?? null;
if (
!await collectionCallbacks.authorizePredicate(
context,
identifier,
key,
keyOwner,
)
!await collectionCallbacks.authorizePredicate(context, identifier)
) {
return await onUnauthorized(request);
}
Expand Down Expand Up @@ -1468,38 +1402,11 @@ const authIfNeeded = async <TContextData>(
},
): Promise<void | never> => {
if (authorize === undefined) return;
const key = (await context.getSignedKey())
// @ts-expect-error: $warning is not part of the type definition
?.clone({}, warning.key) ?? null;
const keyOwner = (await context.getSignedKeyOwner())
// @ts-expect-error: $warning is not part of the type definition
?.clone({}, warning.keyOwner) ?? null;
if (!await authorize(context, values, key, keyOwner)) {
if (!await authorize(context, values)) {
throw new UnauthorizedError();
}
};

/** Warning messages for `authIfNeeded`. */
const warning = {
key: {
$warning: {
category: ["fedify", "federation", "collection"],
message:
"The third parameter of AuthorizePredicate is deprecated in favor of " +
"RequestContext.getSignedKey() method. The third parameter will be " +
"removed in a future release.",
},
},
keyOwner: {
$warning: {
category: ["fedify", "federation", "collection"],
message: "The fourth parameter of AuthorizePredicate is deprecated in " +
"favor of RequestContext.getSignedKeyOwner() method. The fourth " +
"parameter will be removed in a future release.",
},
},
} as const;

/**
* Appends a cursor parameter to a URL if the cursor exists.
* @template Cursor The type of the cursor (string, null, or undefined).
Expand Down
Loading