@gasboost/auth 0.4.1 → 0.6.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.
package/README.md CHANGED
@@ -90,6 +90,49 @@ const repository: AppsScriptAuthRepository = {
90
90
 
91
91
  将来的に SheetORM 向け adapter は auth-core とは別 package として提供する想定です。
92
92
 
93
+ ## Authorization
94
+
95
+ アプリケーション内部の認可には、GAS runtime に依存しない `AuthorizationPolicy` と、Backend service の `AppsScriptAuthorization` を利用できます。
96
+
97
+ ```ts
98
+ import {
99
+ AppsScriptAuthorization,
100
+ createAuthorizationTables,
101
+ } from "@gasboost/auth";
102
+ import { AuthorizationPolicy } from "@gasboost/auth/authorization";
103
+
104
+ export const authorizationPolicy = new AuthorizationPolicy({
105
+ project: ["read", "create", "update", "delete"],
106
+ authorization: ["manage"],
107
+ } as const)
108
+ .linkRole("admin", {
109
+ project: ["read", "create", "update", "delete"],
110
+ authorization: ["manage"],
111
+ })
112
+ .linkRole("member", {
113
+ project: ["read"],
114
+ });
115
+
116
+ const authorizationTables = createAuthorizationTables();
117
+
118
+ const authorization = new AppsScriptAuthorization({
119
+ policy: authorizationPolicy,
120
+ repository: authorizationRepository,
121
+ });
122
+ ```
123
+
124
+ `@gasboost/auth/authorization` は pure な subpath export です。Frontend / shared code では `AuthorizationPolicy` だけを import し、Backend で取得した effective permissions の表示制御に同じ `can()` を使えます。
125
+
126
+ ```ts
127
+ const permissions = await authorization.resolve(session.userId);
128
+
129
+ authorizationPolicy.can(permissions, {
130
+ project: ["update"],
131
+ });
132
+ ```
133
+
134
+ Role definition と permission vocabulary は application code が source of truth です。Repository には assignment だけを保存し、未知の role / permission は grant せず fail closed になります。
135
+
93
136
  ## Initialize
94
137
 
95
138
  `AppsScriptAuth` に Repository、GAS runtime、Session 設定、認証方式の設定を渡します。
@@ -215,6 +258,51 @@ User 検索
215
258
  Session 発行
216
259
  ```
217
260
 
261
+ ## Registration (without Session)
262
+
263
+ `auth.registration` は、Session を発行せずに
264
+ User と Account を登録するサーバー内部APIです。
265
+
266
+ 管理者によるユーザー登録など、
267
+ 登録対象ユーザーとしてサインインする必要がない場合に使用します。
268
+
269
+ ### Email / Password
270
+
271
+ ```ts
272
+ const user = await auth.registration.email({
273
+ name: "Taro",
274
+ email: "taro@example.com",
275
+ password: temporaryPassword,
276
+ });
277
+ ```
278
+
279
+ ### Apps Script
280
+
281
+ ```ts
282
+ const user = await auth.registration.appsScript({
283
+ name: "Taro",
284
+ });
285
+ ```
286
+
287
+ Apps Script方式では
288
+ `Session.getActiveUser().getEmail()` を使用するため、
289
+ 任意の他ユーザーを指定して登録することはできません。
290
+
291
+ ### Sign Upとの違い
292
+
293
+ | API | User + Account | Session発行 |
294
+ | -------------------------------- | -------------- | ----------- |
295
+ | `auth.registration.email()` | 作成 | なし |
296
+ | `auth.registration.appsScript()` | 作成 | なし |
297
+ | `auth.signUp.email()` | 作成 | あり |
298
+ | `auth.signUp.appsScript()` | 作成 | あり |
299
+
300
+ `isSignupEnabled: false` でも、
301
+ 認証方式自体が `enabled: true` ならRegistrationは利用できます。
302
+
303
+ Registrationはメール送信やRole付与を行いません。
304
+ 呼び出し元で認証・認可を実施してください。
305
+
218
306
  ## Apps Script Sign Up
219
307
 
220
308
  Apps Script 認証では `Session.getActiveUser().getEmail()` を Identity として利用します。
@@ -1,4 +1,5 @@
1
1
  import { AppsScriptAuthPassword } from "./api/AppsScriptAuthPassword";
2
+ import { AppsScriptAuthRegistration } from "./api/AppsScriptAuthRegistration";
2
3
  import { AppsScriptAuthSession } from "./api/AppsScriptAuthSession";
3
4
  import { AppsScriptAuthSignIn } from "./api/AppsScriptAuthSignIn";
4
5
  import { AppsScriptAuthSignOut } from "./api/AppsScriptAuthSignOut";
@@ -35,6 +36,7 @@ export declare class AppsScriptAuth<TEmailPassword extends EmailPasswordAuthOpti
35
36
  readonly session: AppsScriptAuthSession;
36
37
  readonly signOut: AppsScriptAuthSignOut;
37
38
  readonly password: AppsScriptAuthPasswordApi<TEmailPassword>;
39
+ readonly registration: AppsScriptAuthRegistration;
38
40
  constructor({ repository, session, runtime, emailPassword, appsScript, hooks, }: AppsScriptAuthConfig<TEmailPassword, THookResult>);
39
41
  }
40
42
  export {};
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AppsScriptAuth = void 0;
4
4
  const AppsScriptAuthPassword_1 = require("./api/AppsScriptAuthPassword");
5
+ const AppsScriptAuthRegistration_1 = require("./api/AppsScriptAuthRegistration");
5
6
  const AppsScriptAuthSession_1 = require("./api/AppsScriptAuthSession");
6
7
  const AppsScriptAuthSignIn_1 = require("./api/AppsScriptAuthSignIn");
7
8
  const AppsScriptAuthSignOut_1 = require("./api/AppsScriptAuthSignOut");
@@ -15,6 +16,7 @@ class AppsScriptAuth {
15
16
  session;
16
17
  signOut;
17
18
  password;
19
+ registration;
18
20
  constructor({ repository, session, runtime, emailPassword, appsScript, hooks, }) {
19
21
  const sessionStorage = (0, sessionStorageFactory_1.sessionStorageFactory)(session.storageType, {
20
22
  cacheService: runtime.cacheService,
@@ -61,6 +63,13 @@ class AppsScriptAuth {
61
63
  emailPassword: emailPasswordConfig,
62
64
  })
63
65
  : undefined);
66
+ this.registration = new AppsScriptAuthRegistration_1.AppsScriptAuthRegistration({
67
+ emailPassword: emailPasswordConfig,
68
+ appsScript: appsScriptConfig,
69
+ repository,
70
+ utilities: runtime.utilities,
71
+ session: runtime.session,
72
+ });
64
73
  }
65
74
  }
66
75
  exports.AppsScriptAuth = AppsScriptAuth;
@@ -0,0 +1,19 @@
1
+ import type { AppsScriptAuthenticationConfig } from "../authentication/AppsScriptAuthentication";
2
+ import type { EmailPasswordAuthConfig } from "../authentication/EmailPasswordAuthentication";
3
+ import type { User } from "../domain/User";
4
+ import { type AppsScriptRegistrationInput } from "../registration/AppsScriptRegistration";
5
+ import { type EmailPasswordRegistrationInput } from "../registration/EmailPasswordRegistration";
6
+ import type { AppsScriptAuthRepository } from "../storage/AppsScriptAuthRepository";
7
+ export declare class AppsScriptAuthRegistration {
8
+ private readonly emailPasswordRegistration?;
9
+ private readonly appsScriptRegistration?;
10
+ constructor({ emailPassword, appsScript, repository, utilities, session, }: {
11
+ emailPassword?: EmailPasswordAuthConfig;
12
+ appsScript?: AppsScriptAuthenticationConfig;
13
+ repository: AppsScriptAuthRepository;
14
+ utilities: GoogleAppsScript.Utilities.Utilities;
15
+ session: GoogleAppsScript.Base.Session;
16
+ });
17
+ email(input: EmailPasswordRegistrationInput): Promise<User>;
18
+ appsScript(input: AppsScriptRegistrationInput): Promise<User>;
19
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppsScriptAuthRegistration = void 0;
4
+ const AppsScriptRegistration_1 = require("../registration/AppsScriptRegistration");
5
+ const EmailPasswordRegistration_1 = require("../registration/EmailPasswordRegistration");
6
+ class AppsScriptAuthRegistration {
7
+ emailPasswordRegistration;
8
+ appsScriptRegistration;
9
+ constructor({ emailPassword, appsScript, repository, utilities, session, }) {
10
+ if (emailPassword?.enabled) {
11
+ this.emailPasswordRegistration = new EmailPasswordRegistration_1.EmailPasswordRegistration(repository, utilities, emailPassword);
12
+ }
13
+ if (appsScript?.enabled) {
14
+ this.appsScriptRegistration = new AppsScriptRegistration_1.AppsScriptRegistration(utilities, session, repository);
15
+ }
16
+ }
17
+ async email(input) {
18
+ if (!this.emailPasswordRegistration) {
19
+ throw new Error("Email and password authentication is disabled");
20
+ }
21
+ return this.emailPasswordRegistration.register(input);
22
+ }
23
+ async appsScript(input) {
24
+ if (!this.appsScriptRegistration) {
25
+ throw new Error("Apps Script authentication is disabled");
26
+ }
27
+ return this.appsScriptRegistration.register(input);
28
+ }
29
+ }
30
+ exports.AppsScriptAuthRegistration = AppsScriptAuthRegistration;
@@ -0,0 +1,33 @@
1
+ import type { AuthorizationPolicy, EffectivePermissions, PermissionOf, PermissionRequirement, PermissionStatement } from "./AuthorizationPolicy";
2
+ import type { AuthorizationRepository } from "./AuthorizationRepository";
3
+ type PolicyStatement<TPolicy> = TPolicy extends AuthorizationPolicy<infer TStatement, string> ? TStatement : never;
4
+ type PolicyRole<TPolicy> = TPolicy extends {
5
+ readonly $types: {
6
+ readonly role: infer TRole;
7
+ };
8
+ } ? Extract<TRole, string> : never;
9
+ type PolicyPermission<TPolicy> = TPolicy extends {
10
+ readonly $types: {
11
+ readonly permission: infer TPermission;
12
+ };
13
+ } ? Extract<TPermission, string> : never;
14
+ export declare class AppsScriptAuthorization<TPolicy extends AuthorizationPolicy<PermissionStatement, string>> {
15
+ readonly role: {
16
+ assign(userId: string, role: PolicyRole<TPolicy>): Promise<void>;
17
+ revoke(userId: string, role: PolicyRole<TPolicy>): Promise<void>;
18
+ };
19
+ readonly permission: {
20
+ allow(userId: string, permission: PolicyPermission<TPolicy>): Promise<void>;
21
+ deny(userId: string, permission: PolicyPermission<TPolicy>): Promise<void>;
22
+ revoke(userId: string, permission: PolicyPermission<TPolicy>): Promise<void>;
23
+ };
24
+ private readonly policy;
25
+ private readonly repository;
26
+ constructor({ policy, repository, }: {
27
+ policy: TPolicy;
28
+ repository: AuthorizationRepository;
29
+ });
30
+ resolve(userId: string): Promise<EffectivePermissions<PermissionOf<PolicyStatement<TPolicy>>>>;
31
+ can(userId: string, requirement: PermissionRequirement<PolicyStatement<TPolicy>>): Promise<boolean>;
32
+ }
33
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppsScriptAuthorization = void 0;
4
+ class AppsScriptAuthorization {
5
+ role;
6
+ permission;
7
+ policy;
8
+ repository;
9
+ constructor({ policy, repository, }) {
10
+ this.policy = policy;
11
+ this.repository = repository;
12
+ this.role = {
13
+ assign: (userId, role) => this.repository.role.assign(userId, role),
14
+ revoke: (userId, role) => this.repository.role.revoke(userId, role),
15
+ };
16
+ this.permission = {
17
+ allow: (userId, permission) => this.repository.permission.set(userId, permission, "allow"),
18
+ deny: (userId, permission) => this.repository.permission.set(userId, permission, "deny"),
19
+ revoke: (userId, permission) => this.repository.permission.revoke(userId, permission),
20
+ };
21
+ }
22
+ async resolve(userId) {
23
+ const [roles, permissions] = await Promise.all([
24
+ this.repository.role.findByUserId(userId),
25
+ this.repository.permission.findByUserId(userId),
26
+ ]);
27
+ return this.policy.resolve({
28
+ roles,
29
+ permissions,
30
+ });
31
+ }
32
+ async can(userId, requirement) {
33
+ return this.policy.can(await this.resolve(userId), requirement);
34
+ }
35
+ }
36
+ exports.AppsScriptAuthorization = AppsScriptAuthorization;
@@ -0,0 +1,32 @@
1
+ export type PermissionStatement = Record<string, readonly string[]>;
2
+ export type PermissionOf<TStatement extends PermissionStatement> = {
3
+ [TResource in Extract<keyof TStatement, string>]: `${TResource}:${Extract<TStatement[TResource][number], string>}`;
4
+ }[Extract<keyof TStatement, string>];
5
+ export type PermissionRequirement<TStatement extends PermissionStatement> = {
6
+ readonly [TResource in keyof TStatement]?: readonly TStatement[TResource][number][];
7
+ };
8
+ export type EffectivePermissions<TPermission extends string> = readonly TPermission[];
9
+ export type PermissionEffect = "allow" | "deny";
10
+ export type StoredPermissionOverride = {
11
+ readonly permission: string;
12
+ readonly effect: PermissionEffect;
13
+ };
14
+ export type AuthorizationResolutionInput = {
15
+ readonly roles: readonly string[];
16
+ readonly permissions: readonly StoredPermissionOverride[];
17
+ };
18
+ type RoleGrantMap = ReadonlyMap<string, readonly string[]>;
19
+ export declare class AuthorizationPolicy<const TStatement extends PermissionStatement, TRole extends string = never> {
20
+ readonly statement: TStatement;
21
+ readonly $types: {
22
+ readonly role: TRole;
23
+ readonly permission: PermissionOf<TStatement>;
24
+ };
25
+ private readonly permissions;
26
+ private readonly roleGrants;
27
+ constructor(statement: TStatement, roleGrants?: RoleGrantMap);
28
+ linkRole<const TName extends string, const TRequirement extends PermissionRequirement<TStatement>>(name: TName, requirement: TRequirement): AuthorizationPolicy<TStatement, TRole | TName>;
29
+ resolve(input: AuthorizationResolutionInput): EffectivePermissions<PermissionOf<TStatement>>;
30
+ can(permissions: EffectivePermissions<PermissionOf<TStatement>>, requirement: PermissionRequirement<TStatement>): boolean;
31
+ }
32
+ export {};
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthorizationPolicy = void 0;
4
+ function flattenRequirement(requirement) {
5
+ const permissions = [];
6
+ for (const [resource, actions] of Object.entries(requirement)) {
7
+ for (const action of actions ?? []) {
8
+ permissions.push(`${resource}:${action}`);
9
+ }
10
+ }
11
+ return permissions;
12
+ }
13
+ function unique(values) {
14
+ return [...new Set(values)];
15
+ }
16
+ class AuthorizationPolicy {
17
+ statement;
18
+ $types;
19
+ permissions;
20
+ roleGrants;
21
+ constructor(statement, roleGrants = new Map()) {
22
+ this.statement = statement;
23
+ this.permissions = new Set(flattenRequirement(statement));
24
+ this.roleGrants = roleGrants;
25
+ }
26
+ linkRole(name, requirement) {
27
+ return new AuthorizationPolicy(this.statement, new Map([
28
+ ...this.roleGrants,
29
+ [name, unique(flattenRequirement(requirement))],
30
+ ]));
31
+ }
32
+ resolve(input) {
33
+ const deniedPermissions = new Set(input.permissions
34
+ .filter(({ effect }) => effect === "deny")
35
+ .map(({ permission }) => permission)
36
+ .filter((permission) => this.permissions.has(permission)));
37
+ const resolved = new Set();
38
+ for (const { effect, permission } of input.permissions) {
39
+ if (effect === "allow" &&
40
+ this.permissions.has(permission) &&
41
+ !deniedPermissions.has(permission)) {
42
+ resolved.add(permission);
43
+ }
44
+ }
45
+ for (const role of input.roles) {
46
+ const permissions = this.roleGrants.get(role);
47
+ if (permissions === undefined) {
48
+ continue;
49
+ }
50
+ for (const permission of permissions) {
51
+ if (!deniedPermissions.has(permission)) {
52
+ resolved.add(permission);
53
+ }
54
+ }
55
+ }
56
+ return [...resolved];
57
+ }
58
+ can(permissions, requirement) {
59
+ const granted = new Set(permissions);
60
+ return flattenRequirement(requirement).every((permission) => granted.has(permission));
61
+ }
62
+ }
63
+ exports.AuthorizationPolicy = AuthorizationPolicy;
@@ -0,0 +1,13 @@
1
+ import type { StoredPermissionOverride } from "./AuthorizationPolicy";
2
+ export interface AuthorizationRepository {
3
+ readonly role: {
4
+ findByUserId(userId: string): Promise<readonly string[]>;
5
+ assign(userId: string, role: string): Promise<void>;
6
+ revoke(userId: string, role: string): Promise<void>;
7
+ };
8
+ readonly permission: {
9
+ findByUserId(userId: string): Promise<readonly StoredPermissionOverride[]>;
10
+ set(userId: string, permission: string, effect: StoredPermissionOverride["effect"]): Promise<void>;
11
+ revoke(userId: string, permission: string): Promise<void>;
12
+ };
13
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export { AuthorizationPolicy, type AuthorizationResolutionInput, type EffectivePermissions, type PermissionEffect, type PermissionOf, type PermissionRequirement, type PermissionStatement, type StoredPermissionOverride, } from "./AuthorizationPolicy";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthorizationPolicy = void 0;
4
+ var AuthorizationPolicy_1 = require("./AuthorizationPolicy");
5
+ Object.defineProperty(exports, "AuthorizationPolicy", { enumerable: true, get: function () { return AuthorizationPolicy_1.AuthorizationPolicy; } });
package/dist/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  export { AppsScriptAuth } from "./AppsScriptAuth";
2
+ export { AppsScriptAuthorization } from "./authorization/AppsScriptAuthorization";
2
3
  export type { AppsScriptAuthRepository } from "./storage/AppsScriptAuthRepository";
4
+ export type { AuthorizationRepository } from "./authorization/AuthorizationRepository";
5
+ export type { EffectivePermissions, PermissionEffect, PermissionOf, PermissionRequirement, PermissionStatement, StoredPermissionOverride, } from "./authorization/AuthorizationPolicy";
3
6
  export type { AuthSchema, AuthSchemaOptions } from "./schema/AuthSchema";
4
7
  export { createAuthTables } from "./schema/createAuthTables";
8
+ export type { AuthorizationSchema, AuthorizationSchemaOptions, } from "./schema/AuthorizationSchema";
9
+ export { createAuthorizationTables } from "./schema/createAuthorizationTables";
5
10
  export { authPattern, type AuthPattern } from "./AuthPattern";
6
11
  export { Account } from "./domain/Account";
7
12
  export { User } from "./domain/User";
package/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EmailPasswordIdentity = exports.AppsScriptIdentity = exports.PasswordReset = exports.PasswordCredential = exports.Password = exports.HashedPassword = exports.User = exports.Account = exports.authPattern = exports.createAuthTables = exports.AppsScriptAuth = void 0;
3
+ exports.EmailPasswordIdentity = exports.AppsScriptIdentity = exports.PasswordReset = exports.PasswordCredential = exports.Password = exports.HashedPassword = exports.User = exports.Account = exports.authPattern = exports.createAuthorizationTables = exports.createAuthTables = exports.AppsScriptAuthorization = exports.AppsScriptAuth = void 0;
4
4
  var AppsScriptAuth_1 = require("./AppsScriptAuth");
5
5
  Object.defineProperty(exports, "AppsScriptAuth", { enumerable: true, get: function () { return AppsScriptAuth_1.AppsScriptAuth; } });
6
+ var AppsScriptAuthorization_1 = require("./authorization/AppsScriptAuthorization");
7
+ Object.defineProperty(exports, "AppsScriptAuthorization", { enumerable: true, get: function () { return AppsScriptAuthorization_1.AppsScriptAuthorization; } });
6
8
  var createAuthTables_1 = require("./schema/createAuthTables");
7
9
  Object.defineProperty(exports, "createAuthTables", { enumerable: true, get: function () { return createAuthTables_1.createAuthTables; } });
10
+ var createAuthorizationTables_1 = require("./schema/createAuthorizationTables");
11
+ Object.defineProperty(exports, "createAuthorizationTables", { enumerable: true, get: function () { return createAuthorizationTables_1.createAuthorizationTables; } });
8
12
  var AuthPattern_1 = require("./AuthPattern");
9
13
  Object.defineProperty(exports, "authPattern", { enumerable: true, get: function () { return AuthPattern_1.authPattern; } });
10
14
  var Account_1 = require("./domain/Account");
@@ -0,0 +1,64 @@
1
+ export type AuthorizationSchemaOptions = {
2
+ role?: {
3
+ modelName?: string;
4
+ fields?: {
5
+ id?: string;
6
+ userId?: string;
7
+ role?: string;
8
+ };
9
+ };
10
+ permission?: {
11
+ modelName?: string;
12
+ fields?: {
13
+ id?: string;
14
+ userId?: string;
15
+ permission?: string;
16
+ effect?: string;
17
+ };
18
+ };
19
+ };
20
+ export type AuthorizationSchema = {
21
+ role: {
22
+ modelName: string;
23
+ fields: {
24
+ id: string;
25
+ userId: string;
26
+ role: string;
27
+ };
28
+ };
29
+ permission: {
30
+ modelName: string;
31
+ fields: {
32
+ id: string;
33
+ userId: string;
34
+ permission: string;
35
+ effect: string;
36
+ };
37
+ };
38
+ };
39
+ type Section<O, K extends PropertyKey> = K extends keyof O ? O[K] : undefined;
40
+ type Fields<T> = [T] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<T> ? NonNullable<T>["fields"] : undefined;
41
+ type PropertyOrDefault<T, K extends PropertyKey, Default extends string> = [
42
+ T
43
+ ] extends [null | undefined] ? Default : K extends keyof NonNullable<T> ? Exclude<NonNullable<T>[K], undefined> extends infer Value ? [Value] extends [never] ? Default : Value extends string ? Value : Default : Default : Default;
44
+ export type ResolveAuthorizationSchema<O extends AuthorizationSchemaOptions> = {
45
+ role: {
46
+ modelName: PropertyOrDefault<Section<O, "role">, "modelName", "authorizationRole">;
47
+ fields: {
48
+ id: PropertyOrDefault<Fields<Section<O, "role">>, "id", "id">;
49
+ userId: PropertyOrDefault<Fields<Section<O, "role">>, "userId", "userId">;
50
+ role: PropertyOrDefault<Fields<Section<O, "role">>, "role", "role">;
51
+ };
52
+ };
53
+ permission: {
54
+ modelName: PropertyOrDefault<Section<O, "permission">, "modelName", "authorizationPermission">;
55
+ fields: {
56
+ id: PropertyOrDefault<Fields<Section<O, "permission">>, "id", "id">;
57
+ userId: PropertyOrDefault<Fields<Section<O, "permission">>, "userId", "userId">;
58
+ permission: PropertyOrDefault<Fields<Section<O, "permission">>, "permission", "permission">;
59
+ effect: PropertyOrDefault<Fields<Section<O, "permission">>, "effect", "effect">;
60
+ };
61
+ };
62
+ };
63
+ export declare function resolveAuthorizationSchema<const O extends AuthorizationSchemaOptions>(options: O): ResolveAuthorizationSchema<O>;
64
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveAuthorizationSchema = resolveAuthorizationSchema;
4
+ function resolveAuthorizationSchema(options) {
5
+ return {
6
+ role: {
7
+ modelName: options.role?.modelName ?? "authorizationRole",
8
+ fields: {
9
+ id: options.role?.fields?.id ?? "id",
10
+ userId: options.role?.fields?.userId ?? "userId",
11
+ role: options.role?.fields?.role ?? "role",
12
+ },
13
+ },
14
+ permission: {
15
+ modelName: options.permission?.modelName ?? "authorizationPermission",
16
+ fields: {
17
+ id: options.permission?.fields?.id ?? "id",
18
+ userId: options.permission?.fields?.userId ?? "userId",
19
+ permission: options.permission?.fields?.permission ?? "permission",
20
+ effect: options.permission?.fields?.effect ?? "effect",
21
+ },
22
+ },
23
+ };
24
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import { type AuthorizationSchema, type AuthorizationSchemaOptions } from "./AuthorizationSchema";
3
+ type RoleShape<S extends AuthorizationSchema> = {
4
+ [K in S["role"]["fields"]["id"] | S["role"]["fields"]["userId"] | S["role"]["fields"]["role"]]: z.ZodString;
5
+ };
6
+ type PermissionShape<S extends AuthorizationSchema> = {
7
+ [K in S["permission"]["fields"]["id"] | S["permission"]["fields"]["userId"] | S["permission"]["fields"]["permission"] | S["permission"]["fields"]["effect"]]: K extends S["permission"]["fields"]["effect"] ? z.ZodEnum<{
8
+ allow: "allow";
9
+ deny: "deny";
10
+ }> : z.ZodString;
11
+ };
12
+ export declare function createAuthorizationTables<const O extends AuthorizationSchemaOptions = Record<never, never>>(options?: O): {
13
+ readonly schema: import("./AuthorizationSchema").ResolveAuthorizationSchema<O>;
14
+ readonly role: import("@gasboost/table").TableDefinition<["role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never] extends [null | undefined] ? "authorizationRole" : "modelName" extends infer T_1 ? T_1 extends "modelName" ? T_1 extends keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> ? Exclude<NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never>[T_1], undefined> extends infer Value ? [Value] extends [never] ? "authorizationRole" : Value extends string ? Value : "authorizationRole" : "authorizationRole" : "authorizationRole" : never : never, z.ZodObject<RoleShape<import("./AuthorizationSchema").ResolveAuthorizationSchema<O>> extends infer T_2 ? { -readonly [P in keyof T_2]: T_2[P]; } : never, z.core.$strip>> & {
15
+ readonly primaryKey: Extract<keyof z.core.$InferObjectOutput<RoleShape<import("./AuthorizationSchema").ResolveAuthorizationSchema<O>> extends infer T_2 ? { -readonly [P in keyof T_2]: T_2[P]; } : never, {}>, string>;
16
+ } & {
17
+ readonly primaryKey: [["role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> ? NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never>[keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> & "fields"] : undefined] extends [null | undefined] ? "id" : "id" extends infer T_3 ? T_3 extends "id" ? T_3 extends keyof NonNullable<["role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> ? NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never>[keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> & "fields"] : undefined> ? Exclude<NonNullable<["role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> ? NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never>[keyof NonNullable<"role" extends infer T ? T extends "role" ? T extends keyof O ? O[T] : undefined : never : never> & "fields"] : undefined>[T_3], undefined> extends infer Value ? [Value] extends [never] ? "id" : Value extends string ? Value : "id" : "id" : "id" : never : never;
18
+ };
19
+ readonly permission: import("@gasboost/table").TableDefinition<["permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never] extends [null | undefined] ? "authorizationPermission" : "modelName" extends infer T_5 ? T_5 extends "modelName" ? T_5 extends keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> ? Exclude<NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never>[T_5], undefined> extends infer Value ? [Value] extends [never] ? "authorizationPermission" : Value extends string ? Value : "authorizationPermission" : "authorizationPermission" : "authorizationPermission" : never : never, z.ZodObject<PermissionShape<import("./AuthorizationSchema").ResolveAuthorizationSchema<O>> extends infer T_6 ? { -readonly [P in keyof T_6]: T_6[P]; } : never, z.core.$strip>> & {
20
+ readonly primaryKey: Extract<keyof z.core.$InferObjectOutput<PermissionShape<import("./AuthorizationSchema").ResolveAuthorizationSchema<O>> extends infer T_6 ? { -readonly [P in keyof T_6]: T_6[P]; } : never, {}>, string>;
21
+ } & {
22
+ readonly primaryKey: [["permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> ? NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never>[keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> & "fields"] : undefined] extends [null | undefined] ? "id" : "id" extends infer T_7 ? T_7 extends "id" ? T_7 extends keyof NonNullable<["permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> ? NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never>[keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> & "fields"] : undefined> ? Exclude<NonNullable<["permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never] extends [null | undefined] ? undefined : "fields" extends keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> ? NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never>[keyof NonNullable<"permission" extends infer T_4 ? T_4 extends "permission" ? T_4 extends keyof O ? O[T_4] : undefined : never : never> & "fields"] : undefined>[T_7], undefined> extends infer Value ? [Value] extends [never] ? "id" : Value extends string ? Value : "id" : "id" : "id" : never : never;
23
+ };
24
+ };
25
+ export {};
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthorizationTables = createAuthorizationTables;
4
+ const zod_1 = require("zod");
5
+ const AuthorizationSchema_1 = require("./AuthorizationSchema");
6
+ function ensureUniqueFieldNames(modelName, fields) {
7
+ const fieldNames = Object.values(fields);
8
+ if (new Set(fieldNames).size !== fieldNames.length) {
9
+ throw new Error(`Field names for '${modelName}' must be unique.`);
10
+ }
11
+ }
12
+ function keyedTable(definition) {
13
+ return definition;
14
+ }
15
+ function createAuthorizationTables(options = {}) {
16
+ const schema = (0, AuthorizationSchema_1.resolveAuthorizationSchema)(options);
17
+ ensureUniqueFieldNames(schema.role.modelName, schema.role.fields);
18
+ ensureUniqueFieldNames(schema.permission.modelName, schema.permission.fields);
19
+ const roleSchema = zod_1.z.object({
20
+ [schema.role.fields.id]: zod_1.z.string(),
21
+ [schema.role.fields.userId]: zod_1.z.string(),
22
+ [schema.role.fields.role]: zod_1.z.string(),
23
+ });
24
+ const permissionSchema = zod_1.z.object({
25
+ [schema.permission.fields.id]: zod_1.z.string(),
26
+ [schema.permission.fields.userId]: zod_1.z.string(),
27
+ [schema.permission.fields.permission]: zod_1.z.string(),
28
+ [schema.permission.fields.effect]: zod_1.z.enum(["allow", "deny"]),
29
+ });
30
+ return {
31
+ schema,
32
+ role: keyedTable({
33
+ name: schema.role.modelName,
34
+ schema: roleSchema,
35
+ primaryKey: schema.role.fields.id,
36
+ }),
37
+ permission: keyedTable({
38
+ name: schema.permission.modelName,
39
+ schema: permissionSchema,
40
+ primaryKey: schema.permission.fields.id,
41
+ }),
42
+ };
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gasboost/auth",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Modern authentication for Google Apps Script with email/password, Apps Script authentication, sessions, and password reset.",
5
5
  "keywords": [
6
6
  "google-apps-script",
@@ -25,6 +25,10 @@
25
25
  ".": {
26
26
  "default": "./dist/index.js",
27
27
  "types": "./dist/index.d.ts"
28
+ },
29
+ "./authorization": {
30
+ "default": "./dist/authorization/index.js",
31
+ "types": "./dist/authorization/index.d.ts"
28
32
  }
29
33
  },
30
34
  "repository": {