najm-auth 4.0.9 → 4.2.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 +80 -25
- package/dist/index.d.ts +28 -3
- package/dist/index.js +103 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -508,44 +508,36 @@ export const Product = own(products)
|
|
|
508
508
|
join(products.userId, _users.id),
|
|
509
509
|
where(_users.id)
|
|
510
510
|
)
|
|
511
|
-
.writeBy(products.userId); //
|
|
511
|
+
.writeBy(products.userId); // Metadata for consumers; not an automatic write guard
|
|
512
512
|
```
|
|
513
513
|
|
|
514
514
|
### Using @Policy and @Owned
|
|
515
515
|
|
|
516
516
|
```typescript
|
|
517
|
-
import {
|
|
518
|
-
|
|
519
|
-
const config = configureOwnership({
|
|
520
|
-
adminRoles: ['admin'],
|
|
521
|
-
rules: {
|
|
522
|
-
'user': {
|
|
523
|
-
'products': Product.getRules()['user']
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
});
|
|
517
|
+
import { Owned, Policy, CanList, CanRead, CanCreate, CanUpdate, CanDelete } from 'najm-auth';
|
|
518
|
+
import { and, eq, type SQL } from 'drizzle-orm';
|
|
527
519
|
|
|
528
520
|
@Policy(Product)
|
|
529
521
|
@Controller('/api/products')
|
|
530
522
|
export class ProductController {
|
|
531
523
|
@Get('/')
|
|
532
|
-
@CanList() //
|
|
533
|
-
getAll(
|
|
524
|
+
@CanList() // Checks read permission; repository scopes rows
|
|
525
|
+
getAll() { /* delegate to a scoped service/repository read */ }
|
|
534
526
|
|
|
535
527
|
@Get('/:id')
|
|
536
|
-
@CanRead() //
|
|
528
|
+
@CanRead() // Checks read permission; lookup must scope the ID
|
|
537
529
|
getOne() { }
|
|
538
530
|
|
|
539
531
|
@Post('/')
|
|
540
|
-
@CanCreate() //
|
|
532
|
+
@CanCreate() // Service must validate/assign ownership
|
|
541
533
|
create(@Body() data: any) { }
|
|
542
534
|
|
|
543
535
|
@Put('/:id')
|
|
544
|
-
@CanUpdate() //
|
|
536
|
+
@CanUpdate() // Service must load an owned record before writing
|
|
545
537
|
update(@Body() data: any) { }
|
|
546
538
|
|
|
547
539
|
@Delete('/:id')
|
|
548
|
-
@CanDelete() //
|
|
540
|
+
@CanDelete() // Service must load an owned record before deleting
|
|
549
541
|
delete() { }
|
|
550
542
|
}
|
|
551
543
|
|
|
@@ -554,21 +546,84 @@ export class ProductController {
|
|
|
554
546
|
export class ProductRepository {
|
|
555
547
|
@DB() db!: Database;
|
|
556
548
|
|
|
557
|
-
//
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
549
|
+
// These methods are supplied by @Owned; do not define recursive wrappers.
|
|
550
|
+
declare ownershipCondition: () => SQL | undefined;
|
|
551
|
+
declare findMany: (opts?: { where?: SQL; limit?: number }) => Promise<ProductRow[]>;
|
|
552
|
+
declare findOne: (opts: { where: SQL }) => Promise<ProductRow | null>;
|
|
561
553
|
|
|
562
|
-
async
|
|
563
|
-
|
|
554
|
+
async getById(id: string) {
|
|
555
|
+
const [row] = await this.db.select().from(products)
|
|
556
|
+
.where(and(this.ownershipCondition(), eq(products.id, id))).limit(1);
|
|
557
|
+
return row ?? null;
|
|
564
558
|
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### Composable ownership (4.1+)
|
|
563
|
+
|
|
564
|
+
For custom queries, the property form puts the rules and type together:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
import { Owned, type OwnedWhere } from 'najm-auth';
|
|
565
568
|
|
|
566
|
-
|
|
567
|
-
|
|
569
|
+
@Repository('default')
|
|
570
|
+
export class ProductRepository {
|
|
571
|
+
@DB() db!: Database;
|
|
572
|
+
|
|
573
|
+
@Owned(Product, SharedProduct)
|
|
574
|
+
private ownedWhere!: OwnedWhere;
|
|
575
|
+
|
|
576
|
+
async getById(id: string) {
|
|
577
|
+
const [row] = await this.db.select().from(products)
|
|
578
|
+
.where(and(this.ownedWhere(), eq(products.id, id))).limit(1);
|
|
579
|
+
return row ?? null;
|
|
568
580
|
}
|
|
569
581
|
}
|
|
570
582
|
```
|
|
571
583
|
|
|
584
|
+
Use any instance property name; `OwnedWhere` is the shared function type.
|
|
585
|
+
Resolve the repository through Najm's container before calling it. Context
|
|
586
|
+
injection installs the function after construction, including when TypeScript
|
|
587
|
+
emits native class fields. The function reads the current request on every call,
|
|
588
|
+
so a singleton repository can safely serve different actors. Multiple decorated
|
|
589
|
+
properties may use independent rules, including on inherited repositories.
|
|
590
|
+
The class form and its `ownershipCondition`, `findMany`, `findOne`, and
|
|
591
|
+
`scopedQuery` helpers remain available. The property form supplies only the
|
|
592
|
+
decorated predicate; it does not rewrite queries or add CRUD helpers.
|
|
593
|
+
|
|
594
|
+
`@Owned(Product)` now provides `ownershipCondition()` for custom queries.
|
|
595
|
+
It returns a parameterized `id IN (authorized IDs)` predicate; join-based rules
|
|
596
|
+
do not multiply outer rows. Combine it with filters in one
|
|
597
|
+
`.where(and(this.ownershipCondition(), filter))`. The owned table must have an
|
|
598
|
+
`id` column. A standalone `ownershipCondition(db, tokens, context)` and the
|
|
599
|
+
`OwnershipReadContext` / `OwnershipConditionMethods` types are also exported.
|
|
600
|
+
|
|
601
|
+
`@Owned(Product, SharedProduct)` grants either rule (OR), then your query's
|
|
602
|
+
filters apply (AND). Every alternative must reference the same table object.
|
|
603
|
+
Alternatives work with `ownershipCondition`, `findMany`, `findOne`, and
|
|
604
|
+
`scopedQuery`. Single-token helpers retain their existing behavior.
|
|
605
|
+
|
|
606
|
+
Configure school-wide, tenant-wide, or other privileged roles in the app:
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
const Product = own(products, { adminRoles: ['admin', 'operations'] })
|
|
610
|
+
.for('member', where(products.userId));
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Those roles bypass row ownership only; route permissions still apply. Unknown
|
|
614
|
+
roles and anonymous active requests get no rows. Outside request context,
|
|
615
|
+
seed/job reads remain unscoped for compatibility; never treat a manually
|
|
616
|
+
constructed repository without request injection as an authenticated boundary.
|
|
617
|
+
|
|
618
|
+
**Migrating custom queries:** replace `this.scope(query).where(filter)` with
|
|
619
|
+
`query.where(and(this.ownershipCondition(), filter))`. Drizzle replaces an
|
|
620
|
+
earlier WHERE when another `.where()` is called. The legacy `scope()` and
|
|
621
|
+
`scopedQuery()` APIs remain for compatibility, but their result must not be
|
|
622
|
+
followed by `.where()`; for simple reads use `findMany({ where: filter })`.
|
|
623
|
+
An upgrade alone does not rewrite unsafe queries in consumers. Keep role
|
|
624
|
+
and cross-user integration tests in each app. `@Policy` and `@CanRead` check
|
|
625
|
+
permissions, not the ownership of a requested record ID.
|
|
626
|
+
|
|
572
627
|
### Advanced Ownership: Multi-Role Scoping
|
|
573
628
|
|
|
574
629
|
```typescript
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { CredentialSetupRequirement, CredentialSetupSession, NewCredentialSetupR
|
|
|
14
14
|
import { CookieService } from 'najm-cookies';
|
|
15
15
|
import { Context } from 'hono';
|
|
16
16
|
import { GuardResult } from 'najm-guard';
|
|
17
|
-
import 'drizzle-orm';
|
|
17
|
+
import { SQL } from 'drizzle-orm';
|
|
18
18
|
import 'drizzle-orm/pg-core';
|
|
19
19
|
|
|
20
20
|
interface CredentialSetupOptions {
|
|
@@ -2856,7 +2856,32 @@ declare class ScopeContext {
|
|
|
2856
2856
|
role: string;
|
|
2857
2857
|
} | null;
|
|
2858
2858
|
}
|
|
2859
|
-
|
|
2859
|
+
/** Decorate a repository class, or an `ownedWhere!: OwnedWhere` property. */
|
|
2860
|
+
declare function Owned(token: OwnershipToken, ...alternatives: OwnershipToken[]): ClassDecorator & PropertyDecorator;
|
|
2861
|
+
|
|
2862
|
+
/** Request-aware ownership predicate supplied by an @Owned property. */
|
|
2863
|
+
type OwnedWhere = () => SQL | undefined;
|
|
2864
|
+
/** Request context consumed by ownershipCondition; ScopeContext implements it. */
|
|
2865
|
+
interface OwnershipReadContext {
|
|
2866
|
+
hasActiveContext(): boolean;
|
|
2867
|
+
getUser(): {
|
|
2868
|
+
id: string;
|
|
2869
|
+
role: string;
|
|
2870
|
+
} | null;
|
|
2871
|
+
}
|
|
2872
|
+
/** Additional custom-query method installed by @Owned. */
|
|
2873
|
+
interface OwnershipConditionMethods {
|
|
2874
|
+
ownershipCondition(): SQL | undefined;
|
|
2875
|
+
}
|
|
2876
|
+
/**
|
|
2877
|
+
* Compose this predicate with application filters in ONE .where(and(...)).
|
|
2878
|
+
* Each token grants alternative access to the same table through an ID
|
|
2879
|
+
* subquery, so joins cannot duplicate outer rows or collide with outer joins.
|
|
2880
|
+
* The table must expose an `id` column. Roles and ownership rules remain
|
|
2881
|
+
* application-owned. Unknown roles and anonymous active requests deny all.
|
|
2882
|
+
* Outside a request, reads remain unscoped for seed/job compatibility.
|
|
2883
|
+
*/
|
|
2884
|
+
declare function ownershipCondition(db: any, tokens: readonly OwnershipToken[], context: OwnershipReadContext | undefined): SQL | undefined;
|
|
2860
2885
|
|
|
2861
2886
|
/**
|
|
2862
2887
|
* Shared query builders used across auth repositories.
|
|
@@ -3181,4 +3206,4 @@ declare const authSeed: (config: AuthSeedConfig) => Record<string, SeedEntry>;
|
|
|
3181
3206
|
*/
|
|
3182
3207
|
declare function seedAuthData(config: SeedAuthDataConfig): Promise<SeedAuthDataResult>;
|
|
3183
3208
|
|
|
3184
|
-
export { AUTH_CONFIG, AUTH_CORE_MODULE, en as AUTH_EN, AUTH_LOCALES, AUTH_LOGIN_RATE_LIMIT_ENV, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AdministrativeDelivery, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthLoginRateLimitConfig, type AuthPluginConfig, AuthQueries, type AuthRateLimitEnvironment, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, AuthSessionService, type AuthUser, CREDENTIAL_SETUP_CODES, CREDENTIAL_SETUP_MODULE, Can, CanCreate, CanDelete, CanList, CanRead, CanUpdate, type ChainableGuard, type ChangePasswordDto, type CheckPermissionDto, type ConfiguredOwnership, type ConfirmResetPasswordDto, type ConsumedSetPasswordToken, CookieManager, type CreatePermissionDto, type CreateRoleDto, type CreateTokenDto, type CreateUserDto, type CredentialSetupChangeDto, type CredentialSetupCode, type CredentialSetupConfig, CredentialSetupController, type CredentialSetupOptions, type CredentialSetupPasswordOptions, type CredentialSetupPending, CredentialSetupRepository, CredentialSetupRequirementRepository, type CredentialSetupRequirementRow, CredentialSetupRequirementService, CredentialSetupService, type CredentialSetupSessionInfo, type CredentialSetupStarted, DEFAULT_AUTH_LOGIN_RATE_LIMIT, DEFAULT_CREDENTIAL_SETUP_COOKIE_NAME, DEFAULT_CREDENTIAL_SETUP_TTL_MS, type DefineRolesOptions, type EmailParam, EncryptionService, type GitHubOAuthConfig, type GoogleOAuthConfig, IdentityConfig, type IdentityResolver, type InviteUserDto, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, type LoginResult, NewPermission, NewRoleEntity, NewUser, type OAuthConfig, type OAuthProvider, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, PASSWORD_SETUP_PURPOSE, PUBLIC_REGISTRATION_MODULE, PasswordSetupService, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, type ProvisionUserInput, type ProvisionUserWithPasswordInput, type ProvisionUserWithSetupInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, RegistrationController, type ResetPasswordDto, type ResolvedCredentialSetupConfig, ResolvedIdentityConfig, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, SessionInvalidationService, type SetPasswordToken, type SetPasswordTokenType, TOKEN_STATUS, TOKEN_TYPE, TemporaryCredentialInput, type TemporaryCredentialReset, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, type UserListQuery, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authEmailRateLimitKey, authIdentityRateLimitKey, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createIdentityResolver, createPermissionDto, createRoleDto, createTokenDto, createUserDto, credentialSetupChangeDto, credentialSetupError, defaultCredentialSetupPasswordSchema, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, inviteUserDto, isAdmin, isAdministrator, isAuth, isEmailIdentifier, isEmpty, isFile, isPath, join, languageParam, loginDto, normalizeAuthIdentifier, normalizeSetupPurpose, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, registerDto, resetPasswordDto, resolveAuthLoginRateLimitConfig, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
|
|
3209
|
+
export { AUTH_CONFIG, AUTH_CORE_MODULE, en as AUTH_EN, AUTH_LOCALES, AUTH_LOGIN_RATE_LIMIT_ENV, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AdministrativeDelivery, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthLoginRateLimitConfig, type AuthPluginConfig, AuthQueries, type AuthRateLimitEnvironment, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, AuthSessionService, type AuthUser, CREDENTIAL_SETUP_CODES, CREDENTIAL_SETUP_MODULE, Can, CanCreate, CanDelete, CanList, CanRead, CanUpdate, type ChainableGuard, type ChangePasswordDto, type CheckPermissionDto, type ConfiguredOwnership, type ConfirmResetPasswordDto, type ConsumedSetPasswordToken, CookieManager, type CreatePermissionDto, type CreateRoleDto, type CreateTokenDto, type CreateUserDto, type CredentialSetupChangeDto, type CredentialSetupCode, type CredentialSetupConfig, CredentialSetupController, type CredentialSetupOptions, type CredentialSetupPasswordOptions, type CredentialSetupPending, CredentialSetupRepository, CredentialSetupRequirementRepository, type CredentialSetupRequirementRow, CredentialSetupRequirementService, CredentialSetupService, type CredentialSetupSessionInfo, type CredentialSetupStarted, DEFAULT_AUTH_LOGIN_RATE_LIMIT, DEFAULT_CREDENTIAL_SETUP_COOKIE_NAME, DEFAULT_CREDENTIAL_SETUP_TTL_MS, type DefineRolesOptions, type EmailParam, EncryptionService, type GitHubOAuthConfig, type GoogleOAuthConfig, IdentityConfig, type IdentityResolver, type InviteUserDto, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, type LoginResult, NewPermission, NewRoleEntity, NewUser, type OAuthConfig, type OAuthProvider, Owned, type OwnedMethods, type OwnedWhere, type OwnershipConditionMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipReadContext, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, PASSWORD_SETUP_PURPOSE, PUBLIC_REGISTRATION_MODULE, PasswordSetupService, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, type ProvisionUserInput, type ProvisionUserWithPasswordInput, type ProvisionUserWithSetupInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, RegistrationController, type ResetPasswordDto, type ResolvedCredentialSetupConfig, ResolvedIdentityConfig, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, SessionInvalidationService, type SetPasswordToken, type SetPasswordTokenType, TOKEN_STATUS, TOKEN_TYPE, TemporaryCredentialInput, type TemporaryCredentialReset, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, type UserListQuery, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authEmailRateLimitKey, authIdentityRateLimitKey, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createIdentityResolver, createPermissionDto, createRoleDto, createTokenDto, createUserDto, credentialSetupChangeDto, credentialSetupError, defaultCredentialSetupPasswordSchema, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, inviteUserDto, isAdmin, isAdministrator, isAuth, isEmailIdentifier, isEmpty, isFile, isPath, join, languageParam, loginDto, normalizeAuthIdentifier, normalizeSetupPurpose, own, ownershipCondition, parseSchema, permissionIdParam, pickProps, refreshTokenDto, registerDto, resetPasswordDto, resolveAuthLoginRateLimitConfig, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
|
package/dist/index.js
CHANGED
|
@@ -5971,7 +5971,7 @@ var OwnershipToken = class {
|
|
|
5971
5971
|
symbol;
|
|
5972
5972
|
name;
|
|
5973
5973
|
table;
|
|
5974
|
-
_rules =
|
|
5974
|
+
_rules = /* @__PURE__ */ Object.create(null);
|
|
5975
5975
|
_writeScopeCol;
|
|
5976
5976
|
_adminRoles;
|
|
5977
5977
|
constructor(table, opts) {
|
|
@@ -6383,9 +6383,47 @@ __name(Policy, "Policy");
|
|
|
6383
6383
|
|
|
6384
6384
|
// src/ownership/OwnedDecorator.ts
|
|
6385
6385
|
import "reflect-metadata";
|
|
6386
|
-
import { sql as
|
|
6386
|
+
import { sql as sql6, and as and5 } from "drizzle-orm";
|
|
6387
6387
|
import { Injectable as Injectable17, Inject as Inject17, DI as DI3, Container as Container2, REQUEST_ID } from "najm-core";
|
|
6388
6388
|
import { USER as USER2 } from "najm-guard";
|
|
6389
|
+
|
|
6390
|
+
// src/ownership/ownershipCondition.ts
|
|
6391
|
+
import { inArray, or, sql as sql5 } from "drizzle-orm";
|
|
6392
|
+
function validateOwnershipAlternatives(tokens) {
|
|
6393
|
+
if (!tokens.length)
|
|
6394
|
+
throw new Error("Ownership requires at least one token");
|
|
6395
|
+
const table = tokens[0].table;
|
|
6396
|
+
if (tokens.some((token) => token.table !== table)) {
|
|
6397
|
+
throw new Error("Ownership alternatives must refer to the same table");
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
__name(validateOwnershipAlternatives, "validateOwnershipAlternatives");
|
|
6401
|
+
function ownershipCondition(db, tokens, context) {
|
|
6402
|
+
validateOwnershipAlternatives(tokens);
|
|
6403
|
+
const idColumn = tokens[0].table.id;
|
|
6404
|
+
if (!idColumn)
|
|
6405
|
+
throw new Error("ownershipCondition requires a table with an id column");
|
|
6406
|
+
if (!context?.hasActiveContext())
|
|
6407
|
+
return void 0;
|
|
6408
|
+
const user = context.getUser();
|
|
6409
|
+
if (!user)
|
|
6410
|
+
return sql5`1 = 0`;
|
|
6411
|
+
const conditions = [];
|
|
6412
|
+
for (const token of tokens) {
|
|
6413
|
+
const { query, condition } = token.applyScopeSplit(user.id, user.role, db.select({ id: idColumn }).from(token.table));
|
|
6414
|
+
if (condition === null)
|
|
6415
|
+
return void 0;
|
|
6416
|
+
if (!Object.prototype.hasOwnProperty.call(token.getRules(), user.role))
|
|
6417
|
+
continue;
|
|
6418
|
+
conditions.push(inArray(idColumn, query.where(condition)));
|
|
6419
|
+
}
|
|
6420
|
+
if (!conditions.length)
|
|
6421
|
+
return sql5`1 = 0`;
|
|
6422
|
+
return conditions.length === 1 ? conditions[0] : or(...conditions);
|
|
6423
|
+
}
|
|
6424
|
+
__name(ownershipCondition, "ownershipCondition");
|
|
6425
|
+
|
|
6426
|
+
// src/ownership/OwnedDecorator.ts
|
|
6389
6427
|
var __decorate33 = function(decorators, target, key, desc) {
|
|
6390
6428
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6391
6429
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -6397,6 +6435,43 @@ var __metadata33 = function(k, v) {
|
|
|
6397
6435
|
};
|
|
6398
6436
|
var _a21;
|
|
6399
6437
|
var OWNED_META = Symbol.for("najm:owned");
|
|
6438
|
+
var OWNED_PROPERTIES = Symbol.for("najm:owned-properties");
|
|
6439
|
+
var propertyContexts = /* @__PURE__ */ new WeakMap();
|
|
6440
|
+
function ownedProperty(proto, key, tokens) {
|
|
6441
|
+
const inherited = Reflect.getMetadata(OWNED_PROPERTIES, proto) ?? [];
|
|
6442
|
+
const properties = [...inherited.filter((property) => property.key !== key), { key, tokens }];
|
|
6443
|
+
Reflect.defineMetadata(OWNED_PROPERTIES, properties, proto);
|
|
6444
|
+
Object.defineProperty(proto, key, {
|
|
6445
|
+
value() {
|
|
6446
|
+
return ownershipCondition(this.db, tokens, this._scopeCtx);
|
|
6447
|
+
},
|
|
6448
|
+
configurable: true,
|
|
6449
|
+
enumerable: false
|
|
6450
|
+
});
|
|
6451
|
+
if (!Reflect.hasOwnMetadata(OWNED_PROPERTIES, proto, "_scopeCtx")) {
|
|
6452
|
+
Inject17(ScopeContext)(proto, "_scopeCtx");
|
|
6453
|
+
Reflect.defineMetadata(OWNED_PROPERTIES, true, proto, "_scopeCtx");
|
|
6454
|
+
Object.defineProperty(proto, "_scopeCtx", {
|
|
6455
|
+
get() {
|
|
6456
|
+
return propertyContexts.get(this);
|
|
6457
|
+
},
|
|
6458
|
+
set(context) {
|
|
6459
|
+
propertyContexts.set(this, context);
|
|
6460
|
+
const properties2 = Reflect.getMetadata(OWNED_PROPERTIES, this) ?? [];
|
|
6461
|
+
for (const property of properties2) {
|
|
6462
|
+
Object.defineProperty(this, property.key, {
|
|
6463
|
+
value: /* @__PURE__ */ __name(() => ownershipCondition(this.db, property.tokens, this._scopeCtx), "value"),
|
|
6464
|
+
configurable: true,
|
|
6465
|
+
enumerable: false
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6468
|
+
},
|
|
6469
|
+
configurable: true,
|
|
6470
|
+
enumerable: false
|
|
6471
|
+
});
|
|
6472
|
+
}
|
|
6473
|
+
}
|
|
6474
|
+
__name(ownedProperty, "ownedProperty");
|
|
6400
6475
|
var ScopeContext = class ScopeContext2 {
|
|
6401
6476
|
static {
|
|
6402
6477
|
__name(this, "ScopeContext");
|
|
@@ -6434,11 +6509,28 @@ __decorate33([
|
|
|
6434
6509
|
ScopeContext = __decorate33([
|
|
6435
6510
|
Injectable17()
|
|
6436
6511
|
], ScopeContext);
|
|
6437
|
-
function Owned(token) {
|
|
6438
|
-
|
|
6512
|
+
function Owned(token, ...alternatives) {
|
|
6513
|
+
const tokens = [token, ...alternatives];
|
|
6514
|
+
validateOwnershipAlternatives(tokens);
|
|
6515
|
+
return function(target, propertyKey) {
|
|
6516
|
+
if (propertyKey !== void 0) {
|
|
6517
|
+
if (typeof target === "function")
|
|
6518
|
+
throw new Error("@Owned requires an instance property");
|
|
6519
|
+
ownedProperty(target, propertyKey, tokens);
|
|
6520
|
+
return;
|
|
6521
|
+
}
|
|
6522
|
+
if (typeof target !== "function")
|
|
6523
|
+
throw new Error("@Owned requires a repository class");
|
|
6439
6524
|
Reflect.defineMetadata(OWNED_META, token, target);
|
|
6440
6525
|
const proto = target.prototype;
|
|
6441
6526
|
Inject17(ScopeContext)(proto, "_scopeCtx");
|
|
6527
|
+
Object.defineProperty(proto, "ownershipCondition", {
|
|
6528
|
+
value() {
|
|
6529
|
+
return ownershipCondition(this.db, tokens, this._scopeCtx);
|
|
6530
|
+
},
|
|
6531
|
+
configurable: true,
|
|
6532
|
+
enumerable: false
|
|
6533
|
+
});
|
|
6442
6534
|
function getUser(self) {
|
|
6443
6535
|
return self._scopeCtx?.getUser() ?? null;
|
|
6444
6536
|
}
|
|
@@ -6470,9 +6562,11 @@ function Owned(token) {
|
|
|
6470
6562
|
return (query) => {
|
|
6471
6563
|
if (!hasActiveContext(this))
|
|
6472
6564
|
return query;
|
|
6565
|
+
if (alternatives.length)
|
|
6566
|
+
return query.where(this.ownershipCondition());
|
|
6473
6567
|
const user = getUser(this);
|
|
6474
6568
|
if (!user)
|
|
6475
|
-
return query.where(
|
|
6569
|
+
return query.where(sql6`1 = 0`);
|
|
6476
6570
|
return token.applyScope(user.id, user.role, query);
|
|
6477
6571
|
};
|
|
6478
6572
|
},
|
|
@@ -6489,6 +6583,9 @@ function Owned(token) {
|
|
|
6489
6583
|
if (!user)
|
|
6490
6584
|
return [];
|
|
6491
6585
|
const base = this.db.select().from(token.table);
|
|
6586
|
+
if (alternatives.length) {
|
|
6587
|
+
return applyQueryOptions(base, opts, combineConditions(this.ownershipCondition(), opts.where));
|
|
6588
|
+
}
|
|
6492
6589
|
const { query: q, condition: scopeCondition } = token.applyScopeSplit(user.id, user.role, base);
|
|
6493
6590
|
return applyQueryOptions(q, opts, combineConditions(scopeCondition, opts.where));
|
|
6494
6591
|
};
|
|
@@ -8113,6 +8210,7 @@ export {
|
|
|
8113
8210
|
normalizeTunisianPhone,
|
|
8114
8211
|
oauthAccountsTable,
|
|
8115
8212
|
own,
|
|
8213
|
+
ownershipCondition,
|
|
8116
8214
|
parseSchema,
|
|
8117
8215
|
permissionIdParam,
|
|
8118
8216
|
permissionsTable,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "najm-auth",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Authentication and authorization library for najm framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"dependencies": {
|
|
93
93
|
"bcryptjs": "^3.0.2",
|
|
94
94
|
"najm-cookies": "^2.0.2",
|
|
95
|
-
"najm-core": "^2.0
|
|
95
|
+
"najm-core": "^2.1.0",
|
|
96
96
|
"najm-database": "^2.0.4",
|
|
97
97
|
"najm-guard": "^2.0.2",
|
|
98
98
|
"najm-i18n": "^2.1.2",
|