@things-factory/auth-base 10.1.16 → 10.1.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/auth-base",
3
- "version": "10.1.16",
3
+ "version": "10.1.18",
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": "aff86bc162b42c1d933711f7bc85a073f1ddcd29"
51
+ "gitHead": "608fe1a70f430ae9c3aa20497173cecc8121373e"
52
52
  }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * App binding lookups — against a real database.
3
+ *
4
+ * Both defects here are invisible to a pure test. One is about which tenant's rows come
5
+ * back; the other is about the order paging and filtering happen in, which only shows up
6
+ * once there are more rows than fit on a page. Neither can be seen by reading the function.
7
+ */
8
+
9
+ import { closeAuthDatabase, Domain, getRepository, openAuthDatabase, resetAuthDatabase } from './db'
10
+ import { loadCompiled } from './compiled'
11
+
12
+ const { User } = loadCompiled('service/user/user')
13
+ const { AppBindingQuery } = loadCompiled('service/app-binding/app-binding-query')
14
+
15
+ const query = new AppBindingQuery()
16
+
17
+ let ours: any
18
+ let theirs: any
19
+
20
+ function context(domain: any) {
21
+ return { state: { domain } } as any
22
+ }
23
+
24
+ async function makeUser(domain: any, username: string, userType: string) {
25
+ return await getRepository(User).save({
26
+ username,
27
+ email: `${username}@example.com`,
28
+ name: username,
29
+ userType,
30
+ salt: User.generateSalt(),
31
+ status: 'activated',
32
+ domains: [domain]
33
+ })
34
+ }
35
+
36
+ beforeAll(async () => {
37
+ await openAuthDatabase()
38
+ })
39
+
40
+ afterAll(async () => {
41
+ await closeAuthDatabase()
42
+ })
43
+
44
+ beforeEach(async () => {
45
+ await resetAuthDatabase()
46
+
47
+ const a = await getRepository(Domain).save({ name: 'Plant A', subdomain: 'plant-a' })
48
+ const b = await getRepository(Domain).save({ name: 'Plant B', subdomain: 'plant-b' })
49
+
50
+ ours = await getRepository(Domain).findOneBy({ id: a.id })
51
+ theirs = await getRepository(Domain).findOneBy({ id: b.id })
52
+ })
53
+
54
+ describe('one binding', () => {
55
+ it('answers for a binding in this domain', async () => {
56
+ const mine = await makeUser(ours, 'app-mine', 'application')
57
+
58
+ const found = await query.appBinding(mine.id, context(ours))
59
+
60
+ expect(found?.id).toBe(mine.id)
61
+ })
62
+
63
+ it('does not answer for a binding in another domain', async () => {
64
+ /*
65
+ * ⚠ The isolation case. The resolver read `domain` out of the context and never used
66
+ * it — `findOneBy({ id, userType })` searched every tenant, and a comment beside it
67
+ * said "TODO should check domain is available". An id from anywhere returned that
68
+ * application user's name, email and reference.
69
+ */
70
+ const foreign = await makeUser(theirs, 'app-theirs', 'application')
71
+
72
+ const found = await query.appBinding(foreign.id, context(ours))
73
+
74
+ expect(found).toBeFalsy()
75
+ })
76
+
77
+ it('answers the same for a foreign id as for one that does not exist', async () => {
78
+ /*
79
+ * Deliberate: a different answer would confirm that some id is real somewhere else.
80
+ */
81
+ const foreign = await makeUser(theirs, 'app-theirs', 'application')
82
+
83
+ const foreignAnswer = await query.appBinding(foreign.id, context(ours))
84
+ const nonsenseAnswer = await query.appBinding('00000000-0000-4000-8000-000000000000', context(ours))
85
+
86
+ expect(foreignAnswer).toEqual(nonsenseAnswer)
87
+ })
88
+
89
+ it('does not answer for a person, only for an application', async () => {
90
+ const person = await makeUser(ours, 'someone', 'user')
91
+
92
+ expect(await query.appBinding(person.id, context(ours))).toBeFalsy()
93
+ })
94
+ })
95
+
96
+ describe('the list', () => {
97
+ it('returns only the applications of this domain', async () => {
98
+ await makeUser(ours, 'app-one', 'application')
99
+ await makeUser(ours, 'a-person', 'user')
100
+ await makeUser(theirs, 'app-theirs', 'application')
101
+
102
+ const { items, total } = await query.appBindings({ filters: [] } as any, context(ours))
103
+
104
+ expect(items.map((one: any) => one.username)).toEqual(['app-one'])
105
+ expect(total).toBe(1)
106
+ })
107
+
108
+ it('counts every match, not the ones on this page', async () => {
109
+ /*
110
+ * ⚠ The paging case, and it needs rows to show at all.
111
+ *
112
+ * The filter used to run in JavaScript on whatever the paged query returned, and the
113
+ * total was `items.length` — the survivors of that one page. A grid asking for page 1
114
+ * of 2 was told the total was 2, so it never offered page 2 and the remaining bindings
115
+ * could not be reached from the screen at all.
116
+ */
117
+ for (let i = 0; i < 5; i++) {
118
+ await makeUser(ours, `app-${i}`, 'application')
119
+ }
120
+
121
+ const { items, total } = await query.appBindings(
122
+ { filters: [], pagination: { page: 1, limit: 2 } } as any,
123
+ context(ours)
124
+ )
125
+
126
+ expect(items).toHaveLength(2)
127
+ expect(total).toBe(5)
128
+ })
129
+
130
+ it('fills a page with matches rather than with whatever the page happened to hold', async () => {
131
+ /*
132
+ * The other half of the same defect. Interleaving people and applications used to leave
133
+ * a page of two carrying one row, or none, depending on the order rows came back.
134
+ */
135
+ for (let i = 0; i < 4; i++) {
136
+ await makeUser(ours, `person-${i}`, 'user')
137
+ await makeUser(ours, `app-${i}`, 'application')
138
+ }
139
+
140
+ const { items, total } = await query.appBindings(
141
+ { filters: [], pagination: { page: 1, limit: 3 } } as any,
142
+ context(ours)
143
+ )
144
+
145
+ expect(items).toHaveLength(3)
146
+ expect(items.every((one: any) => one.userType === 'application')).toBe(true)
147
+ expect(total).toBe(4)
148
+ })
149
+
150
+ it('reaches the last page', async () => {
151
+ for (let i = 0; i < 5; i++) {
152
+ await makeUser(ours, `app-${i}`, 'application')
153
+ }
154
+
155
+ const { items } = await query.appBindings(
156
+ { filters: [], pagination: { page: 3, limit: 2 } } as any,
157
+ context(ours)
158
+ )
159
+
160
+ expect(items).toHaveLength(1)
161
+ })
162
+ })
@@ -15,8 +15,8 @@
15
15
  "error.token or password is invalid": "token or password is invalid",
16
16
  "error.unavailable-domain": "unavailable domain",
17
17
  "error.user credential not found": "user credential not found. You need to register device to use biometric authentication.",
18
- "error.user credential registeration failed": "user credential registration failed. It may be an already registered credential.",
19
- "error.user credential registration not allowed": "user credential registration failed. The registration timed out or was not allowed.",
18
+ "error.user credential registration failed": "registration did not complete. the device was cancelled, or the server could not be reached.",
19
+ "error.user credential registration not allowed": "this device could not be registered. the registration timed out, or the credential is already registered.",
20
20
  "error.user duplicated.": "there is a user account using same email or user ID.",
21
21
  "error.user not activated": "user is not activated",
22
22
  "error.user not found": "user not found",
@@ -15,8 +15,8 @@
15
15
  "error.token or password is invalid": "トークンまたはパスワードが無効です.",
16
16
  "error.unavailable-domain": "使用できないドメインです.",
17
17
  "error.user credential not found": "ユーザー資格情報が見つかりません. 生体認証を使用するにはデバイスを登録する必要があります.",
18
- "error.user credential registeration failed": "ユーザー資格情報の登録に失敗しました。既に登録されている資格情報の可能性があります。",
19
- "error.user credential registration not allowed": "ユーザー資格情報の登録に失敗しました。登録のタイムアウトまたは登録が許可されていません。",
18
+ "error.user credential registration failed": "登録を完了できませんでした。デバイスでキャンセルされたか、サーバーに到達できませんでした。",
19
+ "error.user credential registration not allowed": "このデバイスは登録できませんでした。登録がタイムアウトしたか、すでに登録済みです。",
20
20
  "error.user duplicated.": "ユーザーが重複しています.",
21
21
  "error.user not activated": "ユーザーがアクティブ化されていません.",
22
22
  "error.user not found": "ユーザーが存在しません.",
@@ -15,8 +15,8 @@
15
15
  "error.token or password is invalid": "토큰 또는 비밀번호가 유효하지 않습니다.",
16
16
  "error.unavailable-domain": "사용할 수 없는 도메인입니다.",
17
17
  "error.user credential not found": "사용자 자격 증명을 찾을 수 없습니다. 바이오메트릭 인증을 사용하기 위해서는 먼저 기기를 등록해야 합니다.",
18
- "error.user credential registeration failed": "사용자 인증서 등록이 실패하였습니다. 이미 등록된 인증서일 수 있습니다.",
19
- "error.user credential registration not allowed": "사용자 인증서 등록이 실패하였습니다. 등록 시간이 초과되었거나 등록이 허용되지 않았습니다.",
18
+ "error.user credential registration failed": "등록을 마치지 못했습니다. 기기에서 취소했거나 서버에 닿지 못했습니다.",
19
+ "error.user credential registration not allowed": "이 기기를 등록할 수 없습니다. 등록 시간이 초과되었거나, 이미 등록된 기기입니다.",
20
20
  "error.user duplicated": "동일한 이메일이나 사용자아이디로 가입된 계정이 이미 존재합니다.",
21
21
  "error.user not activated": "사용자가 활성화되지 않았습니다.",
22
22
  "error.user not found": "사용자가 존재하지 않습니다.",
@@ -15,8 +15,8 @@
15
15
  "error.token or password is invalid": "Token atau kata laluan tidak sah",
16
16
  "error.unavailable-domain": "Domain tidak tersedia",
17
17
  "error.user credential not found": "kelayakan pengguna tidak ditemui. Anda perlu mendaftarkan peranti untuk menggunakan pengesahan biometrik.",
18
- "error.user credential registeration failed": "pendaftaran kelayakan pengguna gagal. Mungkin kelayakan tersebut sudah didaftarkan.",
19
- "error.user credential registration not allowed": "pendaftaran kelayakan pengguna gagal. Masa pendaftaran telah tamat atau pendaftaran tidak dibenarkan.",
18
+ "error.user credential registration failed": "pendaftaran tidak selesai. peranti dibatalkan, atau pelayan tidak dapat dihubungi.",
19
+ "error.user credential registration not allowed": "peranti ini tidak dapat didaftarkan. masa pendaftaran tamat, atau kelayakan sudah didaftarkan.",
20
20
  "error.user duplicated": "terdapat akaun pengguna yang menggunakan e-mel atau ID pengguna yang sama.",
21
21
  "error.user not activated": "Pengguna tidak diaktifkan",
22
22
  "error.user not found": "Pengguna tidak ditemui",
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "error.auth error": "认证错误。{message}",
3
3
  "error.authn verification failed": "用户认证失败。",
4
- "error.ownership required": "没有权限。需要域或系统所有者权限。",
5
- "error.user verification failed": "用户验证失败",
6
4
  "error.confirm password not matched": "新密码与确认密码不匹配!",
7
5
  "error.domain mismatch": "证书不适用于该域!",
8
6
  "error.domain not allowed": "用户无权限使用`{subdomain}`域!",
9
7
  "error.email already exists": "电子邮件已被其他用户使用!",
10
8
  "error.failed to find x": "查询{x}失败!",
11
9
  "error.invalid username": "无效的用户名: {username}",
10
+ "error.ownership required": "没有权限。需要域或系统所有者权限。",
12
11
  "error.password should be supported": "应支持初始密码或默认密码",
13
12
  "error.password should match the rule": "密码应符合以下规则。${rule}",
14
13
  "error.password used in the past": "使用过的密码!",
@@ -16,13 +15,14 @@
16
15
  "error.token or password is invalid": "令牌或密码无效!",
17
16
  "error.unavailable-domain": "不可用的域名",
18
17
  "error.user credential not found": "用户凭证未找到。您需要注册设备以使用生物识别认证。",
19
- "error.user credential registeration failed": "用户凭证注册失败。可能是已注册的凭证。",
20
- "error.user credential registration not allowed": "用户凭证注册失败。注册超时或注册不被允许。",
18
+ "error.user credential registration failed": "注册未完成。设备上已取消,或者无法连接到服务器。",
19
+ "error.user credential registration not allowed": "无法注册此设备。注册超时,或者该凭证已经注册过了。",
21
20
  "error.user duplicated": "存在一个用户帐户使用相同的电子邮件或用户ID。",
22
21
  "error.user not activated": "用户未激活!",
23
22
  "error.user not found": "找不到用户",
24
23
  "error.user or verification token not found": "找不到用户或验证令牌。",
25
24
  "error.user validation failed": "用户验证失败!",
25
+ "error.user verification failed": "用户验证失败",
26
26
  "error.username already exists": "用户名已被其他用户使用",
27
27
  "error.x is not a member of y": "{x}不是{y}的成员",
28
28
  "error.x privilege required": "没有权限。需要 {x} 权限。",