@adonis-agora/media 0.22.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +27 -0
  3. package/dist/providers/media_provider.d.ts.map +1 -1
  4. package/dist/providers/media_provider.js +3 -0
  5. package/dist/providers/media_provider.js.map +1 -1
  6. package/dist/src/access.d.ts +103 -0
  7. package/dist/src/access.d.ts.map +1 -0
  8. package/dist/src/access.js +80 -0
  9. package/dist/src/access.js.map +1 -0
  10. package/dist/src/define_config.d.ts +14 -0
  11. package/dist/src/define_config.d.ts.map +1 -1
  12. package/dist/src/define_config.js.map +1 -1
  13. package/dist/src/errors.d.ts +14 -0
  14. package/dist/src/errors.d.ts.map +1 -1
  15. package/dist/src/errors.js +24 -0
  16. package/dist/src/errors.js.map +1 -1
  17. package/dist/src/index.d.ts +6 -2
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js +3 -1
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/integrations/authkit.d.ts +57 -0
  22. package/dist/src/integrations/authkit.d.ts.map +1 -0
  23. package/dist/src/integrations/authkit.js +71 -0
  24. package/dist/src/integrations/authkit.js.map +1 -0
  25. package/dist/src/integrations/authz.d.ts +36 -0
  26. package/dist/src/integrations/authz.d.ts.map +1 -0
  27. package/dist/src/integrations/authz.js +51 -0
  28. package/dist/src/integrations/authz.js.map +1 -0
  29. package/dist/src/media_access.d.ts +126 -0
  30. package/dist/src/media_access.d.ts.map +1 -0
  31. package/dist/src/media_access.js +197 -0
  32. package/dist/src/media_access.js.map +1 -0
  33. package/dist/src/media_collection.d.ts +11 -0
  34. package/dist/src/media_collection.d.ts.map +1 -1
  35. package/dist/src/media_collection.js.map +1 -1
  36. package/dist/src/media_library.js +15 -1
  37. package/dist/src/media_library.js.map +1 -1
  38. package/dist/src/media_manager.d.ts +6 -0
  39. package/dist/src/media_manager.d.ts.map +1 -1
  40. package/dist/src/media_manager.js +7 -0
  41. package/dist/src/media_manager.js.map +1 -1
  42. package/package.json +26 -4
package/dist/src/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // `node ace configure` reaches the hook through this entry point, not through the package's
3
3
  // `./configure` export path.
4
4
  export { configure } from '../configure.js';
5
+ export { adonisAuthIdentity, authorizers } from './access.js';
5
6
  export { Attachment, AttachmentManager, } from './attachment.js';
6
7
  // Real-content validation (magic-byte signatures behind `acceptsMimeTypes`)
7
8
  export { detectMimeType, isClosedSignatureWhitelist, isDetectableMimeType, SIGNATURE_HEAD_BYTES, verifyContentAgainstWhitelist, } from './content_type.js';
@@ -18,7 +19,7 @@ export { createDriveBackedResolver } from './disks/drive.js';
18
19
  // wiring presigned URLs outside the bundled disk)
19
20
  export { presignS3Url } from './disks/sigv4.js';
20
21
  // Errors
21
- export { ContentSignatureUnrecognizedError, ContentTypeMismatchError, ConversionArtifactMissingError, ConversionNotDefinedError, DeliverySignatureError, DirectUploadsNotConfiguredError, DriveNotReadyError, FfmpegFailedError, HlsSourceUnsupportedError, ImageProcessorMissingError, ImageSizeNotAllowedError, MediaNotFoundError, MediaObjectMissingError, MimeNotAllowedError, NotAnImageError, ResumableUploadsNotConfiguredError, StoreNotConfiguredError, TransformerConflictError, TransformerNotDefinedError, TransformerOutputError, TransformerRuntimeMissingError, TransformNotReadyError, UnsafeFileNameError, UploadNotSupportedError, UploadOffsetConflictError, UploadPartOutOfRangeError, UploadPartSizeError, UploadPartsIncompleteError, UploadSessionExpiredError, UploadSessionNotFoundError, UploadSessionStoreNotConfiguredError, VariantNotFoundError, } from './errors.js';
22
+ export { ContentSignatureUnrecognizedError, ContentTypeMismatchError, ConversionArtifactMissingError, ConversionNotDefinedError, DeliverySignatureError, DirectUploadsNotConfiguredError, DriveNotReadyError, FfmpegFailedError, HlsSourceUnsupportedError, ImageProcessorMissingError, ImageSizeNotAllowedError, MediaAccessDeniedError, MediaNotFoundError, MediaObjectMissingError, MimeNotAllowedError, NotAnImageError, ResumableUploadsNotConfiguredError, StoreNotConfiguredError, TransformerConflictError, TransformerNotDefinedError, TransformerOutputError, TransformerRuntimeMissingError, TransformNotReadyError, UnsafeFileNameError, UploadNotSupportedError, UploadOffsetConflictError, UploadPartOutOfRangeError, UploadPartSizeError, UploadPartsIncompleteError, UploadSessionExpiredError, UploadSessionNotFoundError, UploadSessionStoreNotConfiguredError, VariantNotFoundError, } from './errors.js';
22
23
  export { isExtendedDisk } from './extended_disk.js';
23
24
  // Validates a client-supplied upload file name before it reaches a storage key; the same guard the
24
25
  // built-in `keyFor` defaults and `MediaLibrary#layoutPath` run every file name through.
@@ -26,6 +27,7 @@ export { sanitizeFileName } from './file_name.js';
26
27
  export { HlsDeliveryHandler } from './hls/delivery.js';
27
28
  // HLS delivery (playlist rewriting + the framework-agnostic package handler)
28
29
  export { classifyUri, HLS_PLAYLIST_CONTENT_TYPE, HLS_SEGMENT_CONTENT_TYPE, hlsArtifactContentType, isRelativeUri, resolvePackagePath, rewriteHlsPlaylist, } from './hls/playlist.js';
30
+ export { MediaAccess } from './media_access.js';
29
31
  export { MediaCollectionRegistry } from './media_collection.js';
30
32
  export { MediaLibrary } from './media_library.js';
31
33
  // Manager + layers
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { MediaIdentityResolver } from '../access.js';
2
+ /**
3
+ * The bits of `@adonis-agora/authkit-server` this integration uses — typed structurally so the
4
+ * media package carries no dependency on it.
5
+ */
6
+ interface AuthkitServerModule {
7
+ getAccountId(ctx: unknown): string | null;
8
+ realAccountId(ctx: unknown): string | null;
9
+ }
10
+ /** The bits of `@adonis-agora/authkit-client` this integration uses. */
11
+ interface AuthkitClientModule {
12
+ getAuthkit(ctx: unknown): Promise<{
13
+ getIdentity(): Promise<{
14
+ userId: string;
15
+ globalRoles?: string[];
16
+ orgId?: string | null;
17
+ } | null>;
18
+ }>;
19
+ }
20
+ export interface AuthkitIdentityOptions {
21
+ /**
22
+ * Which side of authkit this app is:
23
+ * - `'server'` — the app IS the identity provider (`@adonis-agora/authkit-server`): the account
24
+ * comes from the console session (`getAccountId`, impersonation-aware; `realAccountId` for
25
+ * permission checks);
26
+ * - `'client'` — the app signs in against one (`@adonis-agora/authkit-client`): the identity
27
+ * comes from `ctx.authkit` (`getAuthkit(ctx).getIdentity()`), with its global roles and active
28
+ * organization;
29
+ * - `'auto'` (default) — the client when the request carries `ctx.authkit`, the server when its
30
+ * package is installed, the client otherwise.
31
+ */
32
+ side?: 'auto' | 'server' | 'client';
33
+ /** Subject type handed to authz. Default `'user'`. */
34
+ type?: string;
35
+ /** @internal test seams: how the authkit packages are loaded. */
36
+ load?: {
37
+ server?: () => Promise<AuthkitServerModule>;
38
+ client?: () => Promise<AuthkitClientModule>;
39
+ };
40
+ }
41
+ /**
42
+ * `access.identity` for apps on authkit — who the current user is, as authkit sees them:
43
+ *
44
+ * ```ts
45
+ * // config/media.ts
46
+ * import { authkitIdentity } from '@adonis-agora/media/authkit'
47
+ * export default defineConfig({ access: { identity: authkitIdentity() } })
48
+ * ```
49
+ *
50
+ * On the server side, `id` is the account the request acts as (an impersonating admin sees the
51
+ * user's files) and `realId` the human behind it (what `@adonis-agora/media/authz` checks
52
+ * permissions for). On the client side both are the token's `sub`, with its global roles and
53
+ * active organization as `roles` / `tenantId`.
54
+ */
55
+ export declare function authkitIdentity(options?: AuthkitIdentityOptions): MediaIdentityResolver;
56
+ export {};
57
+ //# sourceMappingURL=authkit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authkit.d.ts","sourceRoot":"","sources":["../../../src/integrations/authkit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAgB,MAAM,cAAc,CAAC;AAExE;;;GAGG;AACH,UAAU,mBAAmB;IAC3B,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED,wEAAwE;AACxE,UAAU,mBAAmB;IAC3B,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAChC,WAAW,IAAI,OAAO,CAAC;YACrB,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;YACvB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SACvB,GAAG,IAAI,CAAC,CAAC;KACX,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpC,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;KAC7C,CAAC;CACH;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,qBAAqB,CA+C3F"}
@@ -0,0 +1,71 @@
1
+ const importServer = () => import('@adonis-agora/authkit-server');
2
+ const importClient = () => import('@adonis-agora/authkit-client');
3
+ /**
4
+ * `access.identity` for apps on authkit — who the current user is, as authkit sees them:
5
+ *
6
+ * ```ts
7
+ * // config/media.ts
8
+ * import { authkitIdentity } from '@adonis-agora/media/authkit'
9
+ * export default defineConfig({ access: { identity: authkitIdentity() } })
10
+ * ```
11
+ *
12
+ * On the server side, `id` is the account the request acts as (an impersonating admin sees the
13
+ * user's files) and `realId` the human behind it (what `@adonis-agora/media/authz` checks
14
+ * permissions for). On the client side both are the token's `sub`, with its global roles and
15
+ * active organization as `roles` / `tenantId`.
16
+ */
17
+ export function authkitIdentity(options = {}) {
18
+ const side = options.side ?? 'auto';
19
+ const type = options.type;
20
+ const loadServer = options.load?.server ?? importServer;
21
+ const loadClient = options.load?.client ?? importClient;
22
+ const fromServer = async (ctx) => {
23
+ const server = await loadServer();
24
+ const id = server.getAccountId(ctx);
25
+ if (!id)
26
+ return null;
27
+ return { id, realId: server.realAccountId(ctx) ?? id, ...(type ? { type } : {}) };
28
+ };
29
+ const fromClient = async (ctx) => {
30
+ const client = await loadClient();
31
+ const identity = await (await client.getAuthkit(ctx)).getIdentity();
32
+ if (!identity)
33
+ return null;
34
+ return {
35
+ id: identity.userId,
36
+ realId: identity.userId,
37
+ roles: identity.globalRoles ?? [],
38
+ tenantId: identity.orgId ?? null,
39
+ ...(type ? { type } : {}),
40
+ };
41
+ };
42
+ return async (ctx) => {
43
+ if (side === 'server')
44
+ return fromServer(ctx);
45
+ if (side === 'client')
46
+ return fromClient(ctx);
47
+ if (ctx.authkit)
48
+ return fromClient(ctx);
49
+ try {
50
+ return await fromServer(ctx);
51
+ }
52
+ catch (error) {
53
+ if (!isModuleNotFound(error))
54
+ throw error;
55
+ }
56
+ try {
57
+ return await fromClient(ctx);
58
+ }
59
+ catch (error) {
60
+ if (!isModuleNotFound(error))
61
+ throw error;
62
+ throw new Error("authkitIdentity() needs '@adonis-agora/authkit-server' (an identity-provider app) or " +
63
+ "'@adonis-agora/authkit-client' (an app signing in against one), and neither is " +
64
+ 'installed. Install the one this app uses, or set `access.identity` to another resolver.', { cause: error });
65
+ }
66
+ };
67
+ }
68
+ function isModuleNotFound(error) {
69
+ return error?.code === 'ERR_MODULE_NOT_FOUND';
70
+ }
71
+ //# sourceMappingURL=authkit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authkit.js","sourceRoot":"","sources":["../../../src/integrations/authkit.ts"],"names":[],"mappings":"AA4CA,MAAM,YAAY,GAAG,GAAG,EAAE,CACxB,MAAM,CAAC,8BAAwC,CAAiC,CAAC;AACnF,MAAM,YAAY,GAAG,GAAG,EAAE,CACxB,MAAM,CAAC,8BAAwC,CAAiC,CAAC;AAEnF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,OAAO,GAA2B,EAAE;IAClE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,IAAI,YAAY,CAAC;IACxD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,IAAI,YAAY,CAAC;IAExD,MAAM,UAAU,GAAG,KAAK,EAAE,GAAY,EAAgC,EAAE;QACtE,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,MAAM,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACpF,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,KAAK,EAAE,GAAY,EAAgC,EAAE;QACtE,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACpE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3B,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,MAAM;YACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,KAAK,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE;YACjC,QAAQ,EAAE,QAAQ,CAAC,KAAK,IAAI,IAAI;YAChC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1B,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAK,GAA6B,CAAC,OAAO;YAAE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;QACnE,IAAI,CAAC;YACH,OAAO,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,uFAAuF;gBACrF,iFAAiF;gBACjF,yFAAyF,EAC3F,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAQ,KAA2B,EAAE,IAAI,KAAK,sBAAsB,CAAC;AACvE,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { MediaAccessRequest, MediaAuthorizer } from '../access.js';
2
+ /** The bits of `@adonis-agora/authz`'s service this integration uses (typed structurally). */
3
+ interface AuthzService {
4
+ can(user: unknown, permission: string, options?: {
5
+ scope?: unknown;
6
+ }): Promise<boolean>;
7
+ hasAnyRole(user: unknown, roles: string[], options?: {
8
+ scope?: unknown;
9
+ }): Promise<boolean>;
10
+ }
11
+ export interface AuthzRuleOptions {
12
+ /** Explicit authz tenant scope for the check. Default: authz's own resolvers (`resolveTenant`). */
13
+ scope?: (request: MediaAccessRequest) => unknown;
14
+ /** @internal test seam: how the authz service is loaded. */
15
+ load?: () => Promise<AuthzService>;
16
+ }
17
+ /**
18
+ * Media authorizers backed by `@adonis-agora/authz` (an optional peer, loaded on first use).
19
+ * Wildcards, roles → permissions, super-admins and tenancy all apply, as in any authz check.
20
+ *
21
+ * ```ts
22
+ * import { authorizers } from '@adonis-agora/media'
23
+ * import { authz } from '@adonis-agora/media/authz'
24
+ *
25
+ * { name: 'prescription', access: { read: authorizers.any(authorizers.owner(), authz.can('prescriptions.read')) } }
26
+ * { name: 'reports', access: { read: authz.can(({ collection, action }) => `media.${collection}.${action}`) } }
27
+ * ```
28
+ */
29
+ export declare const authz: {
30
+ /** The user holds `permission` (a string, or derived from the request). Anonymous ⇒ no. */
31
+ can(permission: string | ((request: MediaAccessRequest) => string), options?: AuthzRuleOptions): MediaAuthorizer;
32
+ /** The user has any of these roles in authz (store roles, `resolveRoles`, token roles). */
33
+ roles(roles: readonly string[], options?: AuthzRuleOptions): MediaAuthorizer;
34
+ };
35
+ export {};
36
+ //# sourceMappingURL=authz.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../../../src/integrations/authz.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AAEtF,8FAA8F;AAC9F,UAAU,YAAY;IACpB,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxF,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7F;AAED,MAAM,WAAW,gBAAgB;IAC/B,mGAAmG;IACnG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;CACpC;AAiBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK;IAChB,2FAA2F;IAC3F,GAAG,aACW,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,kBAAkB,KAAK,MAAM,CAAC,YACrD,gBAAgB,GACxB,eAAe;IAWlB,2FAA2F;IAC3F,KAAK,QAAQ,SAAS,MAAM,EAAE,YAAW,gBAAgB,GAAQ,eAAe;CAajF,CAAC"}
@@ -0,0 +1,51 @@
1
+ const importAuthz = async () => (await import('@adonis-agora/authz/services/main'))
2
+ .default;
3
+ /**
4
+ * What authz is asked about: the app's own user object when the identity has one (so a custom
5
+ * `resolveSubjectRef` keeps working), otherwise the `{ type, id }` reference of the human behind
6
+ * the request — `realId`, never the impersonated account: an admin acting as someone must not
7
+ * gain that someone's permissions, nor lose their own.
8
+ */
9
+ function authzSubject(subject) {
10
+ if (subject.user != null)
11
+ return subject.user;
12
+ return { type: subject.type ?? 'user', id: subject.realId ?? subject.id };
13
+ }
14
+ /**
15
+ * Media authorizers backed by `@adonis-agora/authz` (an optional peer, loaded on first use).
16
+ * Wildcards, roles → permissions, super-admins and tenancy all apply, as in any authz check.
17
+ *
18
+ * ```ts
19
+ * import { authorizers } from '@adonis-agora/media'
20
+ * import { authz } from '@adonis-agora/media/authz'
21
+ *
22
+ * { name: 'prescription', access: { read: authorizers.any(authorizers.owner(), authz.can('prescriptions.read')) } }
23
+ * { name: 'reports', access: { read: authz.can(({ collection, action }) => `media.${collection}.${action}`) } }
24
+ * ```
25
+ */
26
+ export const authz = {
27
+ /** The user holds `permission` (a string, or derived from the request). Anonymous ⇒ no. */
28
+ can(permission, options = {}) {
29
+ const load = options.load ?? importAuthz;
30
+ return async (request) => {
31
+ if (!request.subject)
32
+ return false;
33
+ const service = await load();
34
+ const name = typeof permission === 'function' ? permission(request) : permission;
35
+ const scope = options.scope?.(request);
36
+ return service.can(authzSubject(request.subject), name, scope !== undefined ? { scope } : {});
37
+ };
38
+ },
39
+ /** The user has any of these roles in authz (store roles, `resolveRoles`, token roles). */
40
+ roles(roles, options = {}) {
41
+ const load = options.load ?? importAuthz;
42
+ return async (request) => {
43
+ if (!request.subject)
44
+ return false;
45
+ const service = await load();
46
+ const scope = options.scope?.(request);
47
+ return service.hasAnyRole(authzSubject(request.subject), [...roles], scope !== undefined ? { scope } : {});
48
+ };
49
+ },
50
+ };
51
+ //# sourceMappingURL=authz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.js","sourceRoot":"","sources":["../../../src/integrations/authz.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,GAAG,KAAK,IAA2B,EAAE,CACnD,CAAC,MAAM,MAAM,CAAC,mCAA6C,CAAC,CAA+B;KACzF,OAAO,CAAC;AAEb;;;;;GAKG;AACH,SAAS,YAAY,CAAC,OAAqB;IACzC,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC;IAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,2FAA2F;IAC3F,GAAG,CACD,UAA8D,EAC9D,OAAO,GAAqB,EAAE;QAE9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;QACzC,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;YACvB,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,OAAO,KAAK,CAAC;YACnC,MAAM,OAAO,GAAG,MAAM,IAAI,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,OAAO,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;YACjF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC;YACvC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChG,CAAC,CAAC;IACJ,CAAC;IAED,2FAA2F;IAC3F,KAAK,CAAC,KAAwB,EAAE,OAAO,GAAqB,EAAE;QAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;QACzC,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;YACvB,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,OAAO,KAAK,CAAC;YACnC,MAAM,OAAO,GAAG,MAAM,IAAI,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC;YACvC,OAAO,OAAO,CAAC,UAAU,CACvB,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAC7B,CAAC,GAAG,KAAK,CAAC,EACV,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CACrC,CAAC;QACJ,CAAC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,126 @@
1
+ import type { Readable } from 'node:stream';
2
+ import { type MediaAction, type MediaAuthorizer, type MediaIdentityResolver, type MediaSubject } from './access.js';
3
+ import type { DeliveryMode } from './delivery.js';
4
+ import type { MediaLibrary } from './media_library.js';
5
+ import type { MediaRecord } from './media_record.js';
6
+ import type { ResponsiveImage, ResponsiveImageOptions } from './responsive_image.js';
7
+ /**
8
+ * The slice of an AdonisJS `HttpContext` the delivery route uses — typed structurally so the
9
+ * route works with the real context and with a test double alike.
10
+ */
11
+ export interface DeliveryRouteContext {
12
+ params: Record<string, unknown>;
13
+ request: {
14
+ qs(): Record<string, unknown>;
15
+ header(name: string): string | undefined | null;
16
+ };
17
+ response: {
18
+ status(code: number): {
19
+ json(body: unknown): unknown;
20
+ } | unknown;
21
+ header(name: string, value: string): unknown;
22
+ redirect(url: string): unknown;
23
+ stream(stream: Readable): unknown;
24
+ };
25
+ }
26
+ export interface MediaDeliveryRouteOptions {
27
+ /**
28
+ * Who may read. Default: the record's collection `access.read`. With neither, every request is
29
+ * refused — the route never serves by default.
30
+ */
31
+ authorize?: MediaAuthorizer;
32
+ /** Route parameter holding the media id. Default `'id'`. */
33
+ param?: string;
34
+ /**
35
+ * `Cache-Control` for what the route serves. Default `'private, max-age=60'`: a personal file
36
+ * must not be kept by a shared cache, and a minute absorbs a page rendering it many times.
37
+ */
38
+ cacheControl?: string;
39
+ /** Override `delivery.mode` for this route. */
40
+ mode?: DeliveryMode;
41
+ /** Signed-URL lifetime when the resolved mode is `signed`. */
42
+ signedTtlSeconds?: number;
43
+ }
44
+ /**
45
+ * Authorization for media, wired to the app's identity (`access.identity` — authkit, AdonisJS
46
+ * auth or your own) and the collections' `access` policies. `media.access` on the manager.
47
+ *
48
+ * The library still never decides authorization by itself: every rule comes from the app —
49
+ * a collection's `access`, or an `authorize` passed in. This is the plumbing that applies them.
50
+ */
51
+ export declare class MediaAccess {
52
+ #private;
53
+ constructor(options: {
54
+ library: MediaLibrary;
55
+ identity?: MediaIdentityResolver | undefined;
56
+ });
57
+ /** The current user per the configured identity resolver, or `null`. */
58
+ subject(ctx: unknown): Promise<MediaSubject | null>;
59
+ /**
60
+ * May the current user perform `action`? `target` is the record (read) or the collection a file
61
+ * is going into (write). The rule is `authorize` when given, otherwise the collection's
62
+ * `access[action]`; neither ⇒ `false`.
63
+ */
64
+ can(ctx: unknown, action: MediaAction, target: MediaRecord | {
65
+ collection: string;
66
+ }, authorize?: MediaAuthorizer): Promise<boolean>;
67
+ /**
68
+ * {@link can}, throwing {@link MediaAccessDeniedError} when refused — `401` for an anonymous
69
+ * request, `403` otherwise. Use it in your own controllers and upload policies:
70
+ *
71
+ * ```ts
72
+ * // DirectUploadPolicy.onInitiate
73
+ * await media.access.authorize(ctx, 'write', { collection: 'avatar' })
74
+ * ```
75
+ */
76
+ authorize(ctx: unknown, action: MediaAction, target: MediaRecord | {
77
+ collection: string;
78
+ }, authorize?: MediaAuthorizer): Promise<void>;
79
+ /**
80
+ * `media.library.image(id)` for the current user: checks read access first (the collection's
81
+ * `access.read`, or `authorize`), so the URLs it hands out — signed ones especially, which
82
+ * carry their authorization — only ever reach someone allowed to see the file.
83
+ */
84
+ image(ctx: unknown, id: string, options?: ResponsiveImageOptions & {
85
+ authorize?: MediaAuthorizer;
86
+ }): Promise<ResponsiveImage>;
87
+ /**
88
+ * Authorize and answer one media read — the core of {@link deliveryRoute}, for a controller that
89
+ * finds the record its own way (a stable `/profiles/:id/photo` URL, a record looked up by its
90
+ * owner):
91
+ *
92
+ * ```ts
93
+ * async show(ctx) {
94
+ * const [avatar] = await media.library.list('User', ctx.params.id, 'avatar')
95
+ * return media.access.serve(ctx, avatar, { authorize: canSeeProfile })
96
+ * }
97
+ * ```
98
+ *
99
+ * `target` is the record, its id, or `null`/`undefined` (answered 404). Everything else is as in
100
+ * {@link deliveryRoute}: the read is authorized unless the URL carries a valid signature, the query
101
+ * (`?conversion`, `?w`, `?q`, `?exp`, `?sig`) and `Accept` are honoured, the file is redirected or
102
+ * streamed with its headers, and the library's errors are answered as HTTP.
103
+ */
104
+ serve(ctx: DeliveryRouteContext, target: MediaRecord | string | null | undefined, options?: Omit<MediaDeliveryRouteOptions, 'param'>): Promise<unknown>;
105
+ /**
106
+ * A ready AdonisJS route handler that serves a media record — the delivery route every app was
107
+ * hand-writing:
108
+ *
109
+ * ```ts
110
+ * const serveMedia = media.access.deliveryRoute({ authorize: authorizers.owner() })
111
+ * router.get('/media/:id', serveMedia)
112
+ * ```
113
+ *
114
+ * - authorizes the read (`authorize`, or the collection's `access.read`; neither ⇒ refused),
115
+ * unless the URL carries a valid `delivery.signing` signature — that signature IS the
116
+ * authorization the app made when it issued the URL;
117
+ * - passes `?conversion`, `?w`, `?q`, `Accept`, `?exp`, `?sig` through (on-demand resize,
118
+ * format negotiation, signed URLs);
119
+ * - redirects or streams per `delivery.mode`, with `Content-Type`, `Content-Length`,
120
+ * `Content-Disposition: inline`, `Cache-Control` (private by default) and `Vary: Accept`;
121
+ * - answers the library's errors as HTTP: 404 (unknown record or conversion), 400 (size not
122
+ * allowed, not an image), 401/403 (not signed in / not allowed / bad signature).
123
+ */
124
+ deliveryRoute(options?: MediaDeliveryRouteOptions): (ctx: DeliveryRouteContext) => Promise<unknown>;
125
+ }
126
+ //# sourceMappingURL=media_access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media_access.d.ts","sourceRoot":"","sources":["../../src/media_access.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { adonisAuthIdentity, toAuthorizer, } from './access.js';
2
+ import { ConversionNotDefinedError, DeliverySignatureError, ImageSizeNotAllowedError, MediaAccessDeniedError, MediaNotFoundError, NotAnImageError, TransformNotReadyError, } from './errors.js';
3
+ /**
4
+ * Authorization for media, wired to the app's identity (`access.identity` — authkit, AdonisJS
5
+ * auth or your own) and the collections' `access` policies. `media.access` on the manager.
6
+ *
7
+ * The library still never decides authorization by itself: every rule comes from the app —
8
+ * a collection's `access`, or an `authorize` passed in. This is the plumbing that applies them.
9
+ */
10
+ export class MediaAccess {
11
+ #library;
12
+ #identity;
13
+ constructor(options) {
14
+ this.#library = options.library;
15
+ this.#identity = options.identity ?? adonisAuthIdentity;
16
+ }
17
+ /** The current user per the configured identity resolver, or `null`. */
18
+ async subject(ctx) {
19
+ return (await this.#identity(ctx)) ?? null;
20
+ }
21
+ /**
22
+ * May the current user perform `action`? `target` is the record (read) or the collection a file
23
+ * is going into (write). The rule is `authorize` when given, otherwise the collection's
24
+ * `access[action]`; neither ⇒ `false`.
25
+ */
26
+ async can(ctx, action, target, authorize) {
27
+ return (await this.#decide(ctx, action, target, authorize)).allowed;
28
+ }
29
+ /** Resolve the subject ONCE and run the rule — so a denial's 401/403 matches what the rule saw. */
30
+ async #decide(ctx, action, target, authorize) {
31
+ const collection = target.collection;
32
+ const record = 'id' in target ? target : undefined;
33
+ const rule = authorize ?? toAuthorizer(this.#library.collections.get(collection).access?.[action]);
34
+ const subject = await this.subject(ctx);
35
+ return { allowed: await rule({ ctx, action, collection, record, subject }), subject };
36
+ }
37
+ /**
38
+ * {@link can}, throwing {@link MediaAccessDeniedError} when refused — `401` for an anonymous
39
+ * request, `403` otherwise. Use it in your own controllers and upload policies:
40
+ *
41
+ * ```ts
42
+ * // DirectUploadPolicy.onInitiate
43
+ * await media.access.authorize(ctx, 'write', { collection: 'avatar' })
44
+ * ```
45
+ */
46
+ async authorize(ctx, action, target, authorize) {
47
+ const { allowed, subject } = await this.#decide(ctx, action, target, authorize);
48
+ if (allowed)
49
+ return;
50
+ throw new MediaAccessDeniedError(action, target.collection, 'id' in target ? target.id : undefined, subject === null);
51
+ }
52
+ /**
53
+ * `media.library.image(id)` for the current user: checks read access first (the collection's
54
+ * `access.read`, or `authorize`), so the URLs it hands out — signed ones especially, which
55
+ * carry their authorization — only ever reach someone allowed to see the file.
56
+ */
57
+ async image(ctx, id, options = {}) {
58
+ const { authorize, ...imageOptions } = options;
59
+ const record = await this.#library.find(id);
60
+ if (!record)
61
+ throw new MediaNotFoundError(id);
62
+ await this.authorize(ctx, 'read', record, authorize);
63
+ return this.#library.image(id, imageOptions);
64
+ }
65
+ /**
66
+ * Authorize and answer one media read — the core of {@link deliveryRoute}, for a controller that
67
+ * finds the record its own way (a stable `/profiles/:id/photo` URL, a record looked up by its
68
+ * owner):
69
+ *
70
+ * ```ts
71
+ * async show(ctx) {
72
+ * const [avatar] = await media.library.list('User', ctx.params.id, 'avatar')
73
+ * return media.access.serve(ctx, avatar, { authorize: canSeeProfile })
74
+ * }
75
+ * ```
76
+ *
77
+ * `target` is the record, its id, or `null`/`undefined` (answered 404). Everything else is as in
78
+ * {@link deliveryRoute}: the read is authorized unless the URL carries a valid signature, the query
79
+ * (`?conversion`, `?w`, `?q`, `?exp`, `?sig`) and `Accept` are honoured, the file is redirected or
80
+ * streamed with its headers, and the library's errors are answered as HTTP.
81
+ */
82
+ async serve(ctx, target, options = {}) {
83
+ const { request, response } = ctx;
84
+ const cacheControl = options.cacheControl ?? 'private, max-age=60';
85
+ try {
86
+ const record = typeof target === 'string' ? await this.#library.find(target) : (target ?? null);
87
+ if (!record)
88
+ throw new MediaNotFoundError(typeof target === 'string' ? target : '');
89
+ const id = record.id;
90
+ const qs = request.qs();
91
+ const signed = {
92
+ mediaId: id,
93
+ conversion: stringParam(qs.conversion),
94
+ width: stringParam(qs.w),
95
+ quality: stringParam(qs.q),
96
+ expires: stringParam(qs.exp),
97
+ signature: stringParam(qs.sig),
98
+ };
99
+ // A valid signature is a prior authorization; an invalid or expired one is refused
100
+ // (DeliverySignatureError). Unsigned requests are this caller's to authorize.
101
+ const trusted = this.#library.checkDeliverySignature(signed, { required: false });
102
+ if (!trusted)
103
+ await this.authorize(ctx, 'read', record, options.authorize);
104
+ const result = await this.#library.deliver(id, {
105
+ ...(signed.conversion !== undefined ? { conversion: signed.conversion } : {}),
106
+ ...(signed.width !== undefined && signed.width !== ''
107
+ ? {
108
+ width: signed.width,
109
+ quality: signed.quality,
110
+ accept: request.header('accept') ?? undefined,
111
+ }
112
+ : {}),
113
+ ...(trusted ? { trusted: true } : {}),
114
+ ...(options.mode !== undefined ? { mode: options.mode } : {}),
115
+ ...(options.signedTtlSeconds !== undefined
116
+ ? { signedTtlSeconds: options.signedTtlSeconds }
117
+ : {}),
118
+ });
119
+ response.header('cache-control', cacheControl);
120
+ if (result.vary)
121
+ response.header('vary', result.vary);
122
+ if (result.kind === 'redirect')
123
+ return response.redirect(result.url);
124
+ response.header('content-type', result.mimeType);
125
+ if (result.size !== undefined)
126
+ response.header('content-length', String(result.size));
127
+ if (result.fileName) {
128
+ response.header('content-disposition', inlineDisposition(result.fileName));
129
+ }
130
+ return response.stream(result.stream);
131
+ }
132
+ catch (error) {
133
+ const status = httpStatusOf(error);
134
+ if (status === undefined)
135
+ throw error;
136
+ const { code, message } = error;
137
+ const replied = response.status(status);
138
+ return replied.json?.({ code, message });
139
+ }
140
+ }
141
+ /**
142
+ * A ready AdonisJS route handler that serves a media record — the delivery route every app was
143
+ * hand-writing:
144
+ *
145
+ * ```ts
146
+ * const serveMedia = media.access.deliveryRoute({ authorize: authorizers.owner() })
147
+ * router.get('/media/:id', serveMedia)
148
+ * ```
149
+ *
150
+ * - authorizes the read (`authorize`, or the collection's `access.read`; neither ⇒ refused),
151
+ * unless the URL carries a valid `delivery.signing` signature — that signature IS the
152
+ * authorization the app made when it issued the URL;
153
+ * - passes `?conversion`, `?w`, `?q`, `Accept`, `?exp`, `?sig` through (on-demand resize,
154
+ * format negotiation, signed URLs);
155
+ * - redirects or streams per `delivery.mode`, with `Content-Type`, `Content-Length`,
156
+ * `Content-Disposition: inline`, `Cache-Control` (private by default) and `Vary: Accept`;
157
+ * - answers the library's errors as HTTP: 404 (unknown record or conversion), 400 (size not
158
+ * allowed, not an image), 401/403 (not signed in / not allowed / bad signature).
159
+ */
160
+ deliveryRoute(options = {}) {
161
+ const param = options.param ?? 'id';
162
+ return (ctx) => this.serve(ctx, String(ctx.params[param] ?? ''), options);
163
+ }
164
+ }
165
+ /** The HTTP status for an error the delivery route answers itself; `undefined` ⇒ rethrow. */
166
+ function httpStatusOf(error) {
167
+ if (error instanceof MediaAccessDeniedError)
168
+ return error.status;
169
+ if (error instanceof DeliverySignatureError)
170
+ return 403;
171
+ if (error instanceof MediaNotFoundError)
172
+ return 404;
173
+ if (error instanceof ConversionNotDefinedError)
174
+ return 404;
175
+ if (error instanceof TransformNotReadyError)
176
+ return 404;
177
+ if (error instanceof ImageSizeNotAllowedError)
178
+ return 400;
179
+ if (error instanceof NotAnImageError)
180
+ return 400;
181
+ return undefined;
182
+ }
183
+ function stringParam(value) {
184
+ const first = Array.isArray(value) ? value[0] : value;
185
+ if (first === undefined || first === null)
186
+ return undefined;
187
+ return String(first);
188
+ }
189
+ /** `inline; filename="…"; filename*=UTF-8''…` — ASCII fallback plus the exact name (RFC 6266). */
190
+ function inlineDisposition(fileName) {
191
+ const ascii = fileName.replace(/[^\x20-\x7e]/g, '_').replace(/["\\]/g, '_');
192
+ // RFC 5987 `attr-char` excludes ' ( ) * — which encodeURIComponent leaves alone; `'` would
193
+ // even end the value early.
194
+ const encoded = encodeURIComponent(fileName).replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
195
+ return `inline; filename="${ascii}"; filename*=UTF-8''${encoded}`;
196
+ }
197
+ //# sourceMappingURL=media_access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media_access.js","sourceRoot":"","sources":["../../src/media_access.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { MediaCollectionAccess } from './access.js';
1
2
  import type { ConversionPreset } from './image_processor.js';
2
3
  import type { Transformer } from './transformer.js';
3
4
  /** Widths a `responsive: true` collection generates (those below the original's width). */
@@ -43,6 +44,16 @@ export interface MediaCollectionConfig {
43
44
  * also on `attachExisting`. Only images with something to strip are rewritten.
44
45
  */
45
46
  stripMetadata?: boolean;
47
+ /**
48
+ * Who may read files of this collection and who may put files into it — applied by
49
+ * `media.access` (its `deliveryRoute()`, `image()`, `authorize()`). `'public'` allows everyone;
50
+ * an absent rule denies. Build rules with `authorizers` (and the authkit/authz integrations):
51
+ *
52
+ * ```ts
53
+ * { name: 'avatar', access: { read: authorizers.owner(), write: authorizers.authenticated() } }
54
+ * ```
55
+ */
56
+ access?: MediaCollectionAccess;
46
57
  }
47
58
  export declare class MediaCollectionRegistry {
48
59
  private readonly map;