@things-factory/auth-base 10.1.167 → 10.1.176

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.
@@ -1,5 +1,17 @@
1
1
  import { EntityManager, SelectQueryBuilder } from 'typeorm';
2
2
  import { Domain } from '@things-factory/shell';
3
3
  import { User } from '../service/user/user.js';
4
+ import { DomainOwnershipTarget } from './domain-ownership.js';
4
5
  export declare function getDomainUsers(domain: Partial<Domain>, trxMgr?: EntityManager): Promise<User[]>;
5
6
  export declare function buildDomainUsersQueryBuilder(domainId: string, alias?: string, trxMgr?: EntityManager): SelectQueryBuilder<User>;
7
+ /**
8
+ * The people work in a domain may be given to: activated users who enter it — its members (`users_domains`) and its
9
+ * owners, who enter by ownership (`checkin-domain-rule`). The one answer for every 「who may hold this here」 list —
10
+ * a license's recipient, a sales opportunity's owner. By name.
11
+ */
12
+ export declare function getDomainAssignees(domain: DomainOwnershipTarget, trxMgr?: EntityManager): Promise<User[]>;
13
+ /**
14
+ * Whether a user enters the domain as a member or an owner — the membership half of `getDomainAssignees`. The account's
15
+ * status is the caller's to refuse first, in its own words.
16
+ */
17
+ export declare function entersDomainAsAssignee(domain: DomainOwnershipTarget, user: Pick<User, 'id'>, trxMgr?: EntityManager): Promise<boolean>;
@@ -2,17 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getDomainUsers = getDomainUsers;
4
4
  exports.buildDomainUsersQueryBuilder = buildDomainUsersQueryBuilder;
5
+ exports.getDomainAssignees = getDomainAssignees;
6
+ exports.entersDomainAsAssignee = entersDomainAsAssignee;
7
+ const typeorm_1 = require("typeorm");
5
8
  const shell_1 = require("@things-factory/shell");
6
9
  const user_js_1 = require("../service/user/user.js");
10
+ const domain_ownership_js_1 = require("./domain-ownership.js");
7
11
  async function getDomainUsers(domain, trxMgr) {
8
12
  const domainRepo = trxMgr?.getRepository(shell_1.Domain) || (0, shell_1.getRepository)(shell_1.Domain);
13
+ /* A domain given by its other fields (subdomain, name …) is found by them first. */
9
14
  if (!domain.id) {
10
- const foundDomain = await domainRepo.findOne({ where: { id: domain.id } });
15
+ const { id: _id, ...condition } = domain;
16
+ const foundDomain = Object.keys(condition).length ? await domainRepo.findOne({ where: condition }) : null;
11
17
  if (!foundDomain)
12
- throw new Error(`Failed to find domain by passed condition, ${domain}`);
18
+ throw new Error(`Failed to find domain by passed condition, ${JSON.stringify(condition)}`);
13
19
  domain = foundDomain;
14
20
  }
15
- const qb = buildDomainUsersQueryBuilder(domain.id);
21
+ const qb = buildDomainUsersQueryBuilder(domain.id, 'USER', trxMgr);
16
22
  return await qb.getMany();
17
23
  }
18
24
  function buildDomainUsersQueryBuilder(domainId, alias = 'USER', trxMgr) {
@@ -26,4 +32,25 @@ function buildDomainUsersQueryBuilder(domainId, alias = 'USER', trxMgr) {
26
32
  .getQuery()}`);
27
33
  return qb;
28
34
  }
35
+ /**
36
+ * The people work in a domain may be given to: activated users who enter it — its members (`users_domains`) and its
37
+ * owners, who enter by ownership (`checkin-domain-rule`). The one answer for every 「who may hold this here」 list —
38
+ * a license's recipient, a sales opportunity's owner. By name.
39
+ */
40
+ async function getDomainAssignees(domain, trxMgr) {
41
+ const members = await buildDomainUsersQueryBuilder(domain.id, 'USER', trxMgr).andWhere('USER.status = :status', { status: user_js_1.UserStatus.ACTIVATED }).getMany();
42
+ const ownerIds = [...(await (0, domain_ownership_js_1.findDomainOwnerUserIds)(domain, trxMgr))].filter(id => !members.some(one => one.id === id));
43
+ const owners = ownerIds.length ? await (trxMgr?.getRepository(user_js_1.User) || (0, shell_1.getRepository)(user_js_1.User)).find({ where: { id: (0, typeorm_1.In)(ownerIds), status: user_js_1.UserStatus.ACTIVATED } }) : [];
44
+ return [...members, ...owners].sort((a, b) => (a.name ?? '').localeCompare(b.name ?? ''));
45
+ }
46
+ /**
47
+ * Whether a user enters the domain as a member or an owner — the membership half of `getDomainAssignees`. The account's
48
+ * status is the caller's to refuse first, in its own words.
49
+ */
50
+ async function entersDomainAsAssignee(domain, user, trxMgr) {
51
+ if (!domain?.id || !user?.id)
52
+ return false;
53
+ const member = await buildDomainUsersQueryBuilder(domain.id, 'USER', trxMgr).andWhere('USER.id = :userId', { userId: user.id }).getExists();
54
+ return member || (await (0, domain_ownership_js_1.isDomainOwner)(domain, user, trxMgr));
55
+ }
29
56
  //# sourceMappingURL=get-domain-users.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-domain-users.js","sourceRoot":"","sources":["../../server/utils/get-domain-users.ts"],"names":[],"mappings":";;AAMA,wCAYC;AAED,oEAiBC;AAnCD,iDAA6D;AAE7D,qDAA8C;AAEvC,KAAK,UAAU,cAAc,CAAC,MAAuB,EAAE,MAAsB;IAClF,MAAM,UAAU,GAAuB,MAAM,EAAE,aAAa,CAAC,cAAM,CAAC,IAAI,IAAA,qBAAa,EAAC,cAAM,CAAC,CAAA;IAE7F,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,WAAW,GAAW,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAClF,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,MAAM,EAAE,CAAC,CAAA;QAEzF,MAAM,GAAG,WAAW,CAAA;IACtB,CAAC;IAED,MAAM,EAAE,GAA6B,4BAA4B,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC5E,OAAO,MAAM,EAAE,CAAC,OAAO,EAAE,CAAA;AAC3B,CAAC;AAED,SAAgB,4BAA4B,CAC1C,QAAgB,EAChB,QAAgB,MAAM,EACtB,MAAsB;IAEtB,MAAM,QAAQ,GAAqB,MAAM,EAAE,aAAa,CAAC,cAAI,CAAC,IAAI,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAA;IACrF,MAAM,EAAE,GAA6B,QAAQ,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAA;IACvE,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,CAClB,GAAG,KAAK,UAAU,EAAE;SACjB,QAAQ,EAAE;SACV,MAAM,CAAC,wBAAwB,CAAC;SAChC,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC;SACtC,KAAK,CAAC,sCAAsC,EAAE,EAAE,QAAQ,EAAE,CAAC;SAC3D,QAAQ,EAAE,EAAE,CAChB,CAAA;IAED,OAAO,EAAE,CAAA;AACX,CAAC","sourcesContent":["import { EntityManager, Repository, SelectQueryBuilder } from 'typeorm'\n\nimport { Domain, getRepository } from '@things-factory/shell'\n\nimport { User } from '../service/user/user.js'\n\nexport async function getDomainUsers(domain: Partial<Domain>, trxMgr?: EntityManager): Promise<User[]> {\n const domainRepo: Repository<Domain> = trxMgr?.getRepository(Domain) || getRepository(Domain)\n\n if (!domain.id) {\n const foundDomain: Domain = await domainRepo.findOne({ where: { id: domain.id } })\n if (!foundDomain) throw new Error(`Failed to find domain by passed condition, ${domain}`)\n\n domain = foundDomain\n }\n\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id)\n return await qb.getMany()\n}\n\nexport function buildDomainUsersQueryBuilder(\n domainId: string,\n alias: string = 'USER',\n trxMgr?: EntityManager\n): SelectQueryBuilder<User> {\n const userRepo: Repository<User> = trxMgr?.getRepository(User) || getRepository(User)\n const qb: SelectQueryBuilder<User> = userRepo.createQueryBuilder(alias)\n qb.select().andWhere(\n `${alias}.id IN ${qb\n .subQuery()\n .select('USERS_DOMAINS.users_id')\n .from('users_domains', 'USERS_DOMAINS')\n .where('USERS_DOMAINS.domains_id = :domainId', { domainId })\n .getQuery()}`\n )\n\n return qb\n}\n"]}
1
+ {"version":3,"file":"get-domain-users.js","sourceRoot":"","sources":["../../server/utils/get-domain-users.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In, Repository, SelectQueryBuilder } from 'typeorm'\n\nimport { Domain, getRepository } from '@things-factory/shell'\n\nimport { User, UserStatus } from '../service/user/user.js'\nimport { DomainOwnershipTarget, findDomainOwnerUserIds, isDomainOwner } from './domain-ownership.js'\n\nexport async function getDomainUsers(domain: Partial<Domain>, trxMgr?: EntityManager): Promise<User[]> {\n const domainRepo: Repository<Domain> = trxMgr?.getRepository(Domain) || getRepository(Domain)\n\n /* A domain given by its other fields (subdomain, name …) is found by them first. */\n if (!domain.id) {\n const { id: _id, ...condition } = domain\n const foundDomain: Domain = Object.keys(condition).length ? await domainRepo.findOne({ where: condition as any }) : null\n if (!foundDomain) throw new Error(`Failed to find domain by passed condition, ${JSON.stringify(condition)}`)\n\n domain = foundDomain\n }\n\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER', trxMgr)\n return await qb.getMany()\n}\n\nexport function buildDomainUsersQueryBuilder(\n domainId: string,\n alias: string = 'USER',\n trxMgr?: EntityManager\n): SelectQueryBuilder<User> {\n const userRepo: Repository<User> = trxMgr?.getRepository(User) || getRepository(User)\n const qb: SelectQueryBuilder<User> = userRepo.createQueryBuilder(alias)\n qb.select().andWhere(\n `${alias}.id IN ${qb\n .subQuery()\n .select('USERS_DOMAINS.users_id')\n .from('users_domains', 'USERS_DOMAINS')\n .where('USERS_DOMAINS.domains_id = :domainId', { domainId })\n .getQuery()}`\n )\n\n return qb\n}\n\n/**\n * The people work in a domain may be given to: activated users who enter it — its members (`users_domains`) and its\n * owners, who enter by ownership (`checkin-domain-rule`). The one answer for every 「who may hold this here」 list —\n * a license's recipient, a sales opportunity's owner. By name.\n */\nexport async function getDomainAssignees(domain: DomainOwnershipTarget, trxMgr?: EntityManager): Promise<User[]> {\n const members = await buildDomainUsersQueryBuilder(domain.id, 'USER', trxMgr).andWhere('USER.status = :status', { status: UserStatus.ACTIVATED }).getMany()\n const ownerIds = [...(await findDomainOwnerUserIds(domain, trxMgr))].filter(id => !members.some(one => one.id === id))\n const owners = ownerIds.length ? await (trxMgr?.getRepository(User) || getRepository(User)).find({ where: { id: In(ownerIds), status: UserStatus.ACTIVATED } }) : []\n return [...members, ...owners].sort((a, b) => (a.name ?? '').localeCompare(b.name ?? ''))\n}\n\n/**\n * Whether a user enters the domain as a member or an owner — the membership half of `getDomainAssignees`. The account's\n * status is the caller's to refuse first, in its own words.\n */\nexport async function entersDomainAsAssignee(domain: DomainOwnershipTarget, user: Pick<User, 'id'>, trxMgr?: EntityManager): Promise<boolean> {\n if (!domain?.id || !user?.id) return false\n const member = await buildDomainUsersQueryBuilder(domain.id, 'USER', trxMgr).andWhere('USER.id = :userId', { userId: user.id }).getExists()\n return member || (await isDomainOwner(domain, user, trxMgr))\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/auth-base",
3
- "version": "10.1.167",
3
+ "version": "10.1.176",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -48,5 +48,5 @@
48
48
  "passport-jwt": "^4.0.0",
49
49
  "passport-local": "^1.0.0"
50
50
  },
51
- "gitHead": "86283c5d35042880be6827f777067fc5e6417eed"
51
+ "gitHead": "24928f127d73de0e9946423bd4c3295f54cce03d"
52
52
  }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Who work in a domain may be given to - its activated members and its activated owners (ADR-0138, architect's
3
+ * ruling 2026-10-17). A license's recipient and a sales opportunity's owner ask the same question; the answer is here
4
+ * once. Before it, Office listed members and owners, sales members only, and an owner without a membership row could
5
+ * take a license but not an opportunity.
6
+ *
7
+ * And `getDomainUsers`: it read the users outside the caller's transaction, and looked a domain given without an id
8
+ * up by the id it did not have.
9
+ */
10
+
11
+ import { closeAuthDatabase, Domain, getRepository, openAuthDatabase, resetAuthDatabase, ENTITIES } from './db'
12
+ import { loadCompiled } from './compiled'
13
+
14
+ const { DataSource } = require('typeorm')
15
+ const shell = require('@things-factory/shell')
16
+
17
+ const { User } = loadCompiled('service/user/user')
18
+ const { DomainOwner } = loadCompiled('service/domain-owner/domain-owner')
19
+ const { getDomainAssignees, entersDomainAsAssignee, getDomainUsers } = loadCompiled('utils/get-domain-users')
20
+
21
+ let domain: any
22
+ let other: any
23
+
24
+ async function makeUser(name: string, domains: any[], status = 'activated') {
25
+ return await getRepository(User).save({ username: name, email: `${name}@example.com`, name, salt: User.generateSalt(), status, domains })
26
+ }
27
+
28
+ beforeAll(async () => {
29
+ await openAuthDatabase()
30
+ })
31
+
32
+ afterAll(async () => {
33
+ await closeAuthDatabase()
34
+ })
35
+
36
+ beforeEach(async () => {
37
+ await resetAuthDatabase()
38
+ domain = await getRepository(Domain).save({ name: 'hatiolab', subdomain: 'hatiolab' })
39
+ other = await getRepository(Domain).save({ name: 'other', subdomain: 'other' })
40
+ })
41
+
42
+ describe('the people work in a domain may be given to', () => {
43
+ it('are its activated members and its activated owners, by name, each once', async () => {
44
+ await makeUser('bora', [domain])
45
+ await makeUser('chul', [domain], 'inactive')
46
+ const owner = await makeUser('ahn', [other])
47
+ await getRepository(DomainOwner).save({ domain, user: owner })
48
+ const memberOwner = await makeUser('dana', [domain])
49
+ await getRepository(DomainOwner).save({ domain, user: memberOwner })
50
+ const columnOwner = await makeUser('eun', [])
51
+ await getRepository(Domain).update({ id: domain.id }, { owner: columnOwner.id })
52
+ const lockedOwner = await makeUser('fin', [], 'locked')
53
+ await getRepository(DomainOwner).save({ domain, user: lockedOwner })
54
+ await makeUser('gil', [other])
55
+
56
+ const read = await getRepository(Domain).findOneBy({ id: domain.id })
57
+ expect((await getDomainAssignees(read)).map((one: any) => one.name)).toEqual(['ahn', 'bora', 'dana', 'eun'])
58
+ })
59
+
60
+ it('a member or an owner enters; the status is the caller\'s to refuse', async () => {
61
+ const read = async () => await getRepository(Domain).findOneBy({ id: domain.id })
62
+ const inactive = await makeUser('chul', [domain], 'inactive')
63
+ const owner = await makeUser('ahn', [other])
64
+ await getRepository(DomainOwner).save({ domain, user: owner })
65
+ const stranger = await makeUser('gil', [other])
66
+
67
+ expect(await entersDomainAsAssignee(await read(), inactive)).toBe(true)
68
+ expect(await entersDomainAsAssignee(await read(), owner)).toBe(true)
69
+ expect(await entersDomainAsAssignee(await read(), stranger)).toBe(false)
70
+ expect(await entersDomainAsAssignee(await read(), { id: undefined })).toBe(false)
71
+ })
72
+ })
73
+
74
+ describe('getDomainUsers', () => {
75
+ it('reads through the manager it is given, not past it', async () => {
76
+ /* A second database the framework registry does not know: only the manager reaches it. */
77
+ const elsewhere = await new DataSource({ type: 'sqlite', database: ':memory:', synchronize: true, namingStrategy: new shell.NamingStrategy(), entities: ENTITIES }).initialize()
78
+ try {
79
+ const there = await elsewhere.getRepository(Domain).save({ name: 'there', subdomain: 'there' })
80
+ await elsewhere.getRepository(User).save({ username: 'han', email: 'han@example.com', name: 'han', salt: User.generateSalt(), status: 'activated', domains: [there] })
81
+
82
+ expect((await getDomainUsers(there, elsewhere.manager)).map((one: any) => one.name)).toEqual(['han'])
83
+ expect((await getDomainAssignees(there, elsewhere.manager)).map((one: any) => one.name)).toEqual(['han'])
84
+ } finally {
85
+ await elsewhere.destroy()
86
+ }
87
+ })
88
+
89
+ it('finds a domain given without its id by the fields it has', async () => {
90
+ await makeUser('bora', [domain])
91
+ expect((await getDomainUsers({ subdomain: 'hatiolab' })).map((one: any) => one.name)).toEqual(['bora'])
92
+ await expect(getDomainUsers({ subdomain: 'nowhere' })).rejects.toThrow(/Failed to find domain/)
93
+ await expect(getDomainUsers({})).rejects.toThrow(/Failed to find domain/)
94
+ })
95
+ })