vona-module-a-user 5.0.49 → 5.1.2

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 (112) hide show
  1. package/LICENSE +0 -0
  2. package/dist/.metadata/index.d.ts +17 -1
  3. package/dist/.metadata/index.d.ts.map +1 -0
  4. package/dist/.metadata/locales.d.ts +11 -0
  5. package/dist/.metadata/locales.d.ts.map +1 -0
  6. package/dist/.metadata/this.d.ts +1 -0
  7. package/dist/.metadata/this.d.ts.map +1 -0
  8. package/dist/bean/bean.passport.d.ts +5 -2
  9. package/dist/bean/bean.passport.d.ts.map +1 -0
  10. package/dist/bean/bean.role.d.ts +3 -1
  11. package/dist/bean/bean.role.d.ts.map +1 -0
  12. package/dist/bean/bean.user.d.ts +4 -3
  13. package/dist/bean/bean.user.d.ts.map +1 -0
  14. package/dist/bean/cacheRedis.authToken.d.ts +1 -0
  15. package/dist/bean/cacheRedis.authToken.d.ts.map +1 -0
  16. package/dist/bean/event.activate.d.ts +2 -1
  17. package/dist/bean/event.activate.d.ts.map +1 -0
  18. package/dist/bean/event.createAnonymous.d.ts +2 -1
  19. package/dist/bean/event.createAnonymous.d.ts.map +1 -0
  20. package/dist/bean/event.register.d.ts +2 -1
  21. package/dist/bean/event.register.d.ts.map +1 -0
  22. package/dist/bean/event.signin.d.ts +2 -1
  23. package/dist/bean/event.signin.d.ts.map +1 -0
  24. package/dist/bean/event.signout.d.ts +2 -1
  25. package/dist/bean/event.signout.d.ts.map +1 -0
  26. package/dist/bean/guard.passport.d.ts +1 -0
  27. package/dist/bean/guard.passport.d.ts.map +1 -0
  28. package/dist/bean/guard.roleName.d.ts +2 -1
  29. package/dist/bean/guard.roleName.d.ts.map +1 -0
  30. package/dist/bean/guard.userName.d.ts +2 -1
  31. package/dist/bean/guard.userName.d.ts.map +1 -0
  32. package/dist/bean/meta.printTip.d.ts +1 -0
  33. package/dist/bean/meta.printTip.d.ts.map +1 -0
  34. package/dist/bean/meta.runtime.d.ts +1 -0
  35. package/dist/bean/meta.runtime.d.ts.map +1 -0
  36. package/dist/config/config.d.ts +1 -0
  37. package/dist/config/config.d.ts.map +1 -0
  38. package/dist/config/errors.d.ts +4 -0
  39. package/dist/config/errors.d.ts.map +1 -0
  40. package/dist/config/locale/en-us.d.ts +5 -0
  41. package/dist/config/locale/en-us.d.ts.map +1 -0
  42. package/dist/config/locale/zh-cn.d.ts +5 -0
  43. package/dist/config/locale/zh-cn.d.ts.map +1 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +65 -20
  47. package/dist/index.js.map +1 -0
  48. package/dist/lib/auth.d.ts +1 -0
  49. package/dist/lib/auth.d.ts.map +1 -0
  50. package/dist/lib/index.d.ts +1 -0
  51. package/dist/lib/index.d.ts.map +1 -0
  52. package/dist/lib/passport.d.ts +1 -0
  53. package/dist/lib/passport.d.ts.map +1 -0
  54. package/dist/lib/user.d.ts +1 -0
  55. package/dist/lib/user.d.ts.map +1 -0
  56. package/dist/main.d.ts +1 -0
  57. package/dist/main.d.ts.map +1 -0
  58. package/dist/service/authTokenAdapter.d.ts +2 -1
  59. package/dist/service/authTokenAdapter.d.ts.map +1 -0
  60. package/dist/service/redisToken.d.ts +1 -0
  61. package/dist/service/redisToken.d.ts.map +1 -0
  62. package/dist/types/auth.d.ts +1 -0
  63. package/dist/types/auth.d.ts.map +1 -0
  64. package/dist/types/authProfile.d.ts +1 -0
  65. package/dist/types/authProfile.d.ts.map +1 -0
  66. package/dist/types/authToken.d.ts +1 -0
  67. package/dist/types/authToken.d.ts.map +1 -0
  68. package/dist/types/index.d.ts +1 -0
  69. package/dist/types/index.d.ts.map +1 -0
  70. package/dist/types/passport.d.ts +1 -0
  71. package/dist/types/passport.d.ts.map +1 -0
  72. package/dist/types/role.d.ts +2 -0
  73. package/dist/types/role.d.ts.map +1 -0
  74. package/dist/types/user.d.ts +1 -0
  75. package/dist/types/user.d.ts.map +1 -0
  76. package/package.json +21 -16
  77. package/src/.metadata/index.ts +382 -0
  78. package/src/.metadata/locales.ts +13 -0
  79. package/src/.metadata/this.ts +2 -0
  80. package/src/bean/bean.passport.ts +249 -0
  81. package/src/bean/bean.role.ts +39 -0
  82. package/src/bean/bean.user.ts +88 -0
  83. package/src/bean/cacheRedis.authToken.ts +10 -0
  84. package/src/bean/event.activate.ts +10 -0
  85. package/src/bean/event.createAnonymous.ts +10 -0
  86. package/src/bean/event.register.ts +14 -0
  87. package/src/bean/event.signin.ts +10 -0
  88. package/src/bean/event.signout.ts +10 -0
  89. package/src/bean/guard.passport.ts +53 -0
  90. package/src/bean/guard.roleName.ts +35 -0
  91. package/src/bean/guard.userName.ts +35 -0
  92. package/src/bean/meta.printTip.ts +18 -0
  93. package/src/bean/meta.runtime.ts +19 -0
  94. package/src/config/config.ts +28 -0
  95. package/src/config/errors.ts +3 -0
  96. package/src/config/locale/en-us.ts +3 -0
  97. package/src/config/locale/zh-cn.ts +3 -0
  98. package/src/index.ts +4 -0
  99. package/src/lib/auth.ts +7 -0
  100. package/src/lib/index.ts +3 -0
  101. package/src/lib/passport.ts +41 -0
  102. package/src/lib/user.ts +7 -0
  103. package/src/main.ts +25 -0
  104. package/src/service/authTokenAdapter.ts +42 -0
  105. package/src/service/redisToken.ts +68 -0
  106. package/src/types/auth.ts +26 -0
  107. package/src/types/authProfile.ts +23 -0
  108. package/src/types/authToken.ts +12 -0
  109. package/src/types/index.ts +6 -0
  110. package/src/types/passport.ts +29 -0
  111. package/src/types/role.ts +19 -0
  112. package/src/types/user.ts +35 -0
@@ -0,0 +1,249 @@
1
+ import type { IJwtClientRecord, IJwtSignOptions, IJwtToken, IJwtVerifyOptions, IPayloadData } from 'vona-module-a-jwt';
2
+
3
+ import { catchError, isNil } from '@cabloy/utils';
4
+ import { BeanBase, beanFullNameFromOnionName } from 'vona';
5
+ import { Bean } from 'vona-module-a-bean';
6
+
7
+ import type { IAuth, IAuthIdRecord, ISigninOptions } from '../types/auth.ts';
8
+ import type { IAuthTokenAdapter } from '../types/authToken.ts';
9
+ import type { IPassport, IPassportAdapter } from '../types/passport.ts';
10
+ import type { IRole, IRoleNameRecord } from '../types/role.ts';
11
+ import type { IUser, IUserNameRecord } from '../types/user.ts';
12
+
13
+ import { $getAuthIdSystem } from '../lib/auth.ts';
14
+
15
+ @Bean()
16
+ export class BeanPassport extends BeanBase {
17
+ private _authTokenAdapter: IAuthTokenAdapter;
18
+ private _passportAdapter: IPassportAdapter;
19
+ private _mockCounter: number = 0;
20
+
21
+ private get authTokenAdapter(): IAuthTokenAdapter {
22
+ if (!this._authTokenAdapter) {
23
+ const beanFullName = beanFullNameFromOnionName(this.scope.config.adapter.authToken, 'service');
24
+ this._authTokenAdapter = this.bean._getBean(beanFullName) as IAuthTokenAdapter;
25
+ }
26
+ return this._authTokenAdapter;
27
+ }
28
+
29
+ private get passportAdapter(): IPassportAdapter {
30
+ if (!this._passportAdapter) {
31
+ const beanFullName = beanFullNameFromOnionName(this.scope.config.adapter.passport, 'service');
32
+ this._passportAdapter = this.bean._getBean(beanFullName) as IPassportAdapter;
33
+ }
34
+ return this._passportAdapter;
35
+ }
36
+
37
+ public get isAuthenticated(): boolean {
38
+ const user = this.currentUser;
39
+ return !!user && !user.anonymous;
40
+ }
41
+
42
+ public get isActivated(): boolean {
43
+ const user = this.currentUser;
44
+ return !!user && !!user.activated;
45
+ }
46
+
47
+ public async isAdmin(): Promise<boolean> {
48
+ const passport = this.current;
49
+ return await this.passportAdapter.isAdmin(passport);
50
+ }
51
+
52
+ public async setCurrent(passport: IPassport | undefined) {
53
+ this.ctx.state.passport = await this.passportAdapter.setCurrent(passport);
54
+ }
55
+
56
+ public get current(): IPassport | undefined {
57
+ return this.ctx.state.passport;
58
+ }
59
+
60
+ public get currentUser(): IUser | undefined {
61
+ return this.ctx.state.passport?.user;
62
+ }
63
+
64
+ public get currentAuth(): IAuth | undefined {
65
+ return this.ctx.state.passport?.auth;
66
+ }
67
+
68
+ public get currentRoles(): IRole[] | undefined {
69
+ return this.ctx.state.passport?.roles;
70
+ }
71
+
72
+ public async signin(passport: IPassport, options?: ISigninOptions): Promise<IJwtToken> {
73
+ if (isNil(this.ctx.instanceName)) throw new Error('should specify instance');
74
+ // current
75
+ await this.setCurrent(passport);
76
+ // event
77
+ await this.scope.event.signin.emit(passport);
78
+ // serialize: payloadData for client certificate
79
+ const payloadData = await this._passportSerialize(passport, options);
80
+ // jwt token
81
+ return await this.bean.jwt.create(payloadData, { dev: passport.auth?.id.toString() === '-1' });
82
+ }
83
+
84
+ public async signout(): Promise<void> {
85
+ // current
86
+ const passport = this.current;
87
+ if (!passport) return;
88
+ // removeAuthToken
89
+ const payloadData = await this.passportAdapter.serialize(passport);
90
+ await this.authTokenAdapter.remove(payloadData);
91
+ // event
92
+ await this.scope.event.signout.emit(passport);
93
+ // ok
94
+ await this.setCurrent(undefined);
95
+ }
96
+
97
+ public async signinSystem<K extends keyof IAuthIdRecord>(
98
+ authName: IAuthIdRecord[K],
99
+ authId: K,
100
+ name?: string,
101
+ options?: ISigninOptions,
102
+ ): Promise<IJwtToken> {
103
+ if (isNil(this.ctx.instanceName)) throw new Error('should specify instance');
104
+ const user = await this.bean.user.findOneByName(name ?? 'admin');
105
+ if (!user) return this.app.throw(401);
106
+ const auth = { id: $getAuthIdSystem(authName, authId) };
107
+ const roles = await this.bean.role.findAllByUserId(user.id);
108
+ const passport = { user, auth, roles };
109
+ return await this.signin(passport, options);
110
+ }
111
+
112
+ public async signinMock(name?: keyof IUserNameRecord, options?: ISigninOptions): Promise<IJwtToken> {
113
+ return await this.signinSystem('mock', (-10000 - ++this._mockCounter) as any, name, options);
114
+ }
115
+
116
+ public async signinWithAnonymous(): Promise<void> {
117
+ const userAnonymous = await this.bean.user.createAnonymous();
118
+ const passport = { user: userAnonymous, auth: undefined };
119
+ await this.setCurrent(passport);
120
+ }
121
+
122
+ public async kickOut(user?: IUser) {
123
+ if (!user) return;
124
+ await this.authTokenAdapter.removeAll(user);
125
+ }
126
+
127
+ public async checkAuthToken(accessToken?: string, clientName?: keyof IJwtClientRecord, options?: IJwtVerifyOptions) {
128
+ clientName = clientName ?? 'access';
129
+ const [payloadData, err] = await catchError(() => {
130
+ return this.bean.jwt.get(clientName).verify(accessToken, options);
131
+ });
132
+ if (err) {
133
+ if (['access', 'refresh'].includes(clientName)) {
134
+ err.code = 401;
135
+ }
136
+ throw err;
137
+ }
138
+ if (!payloadData) return; // no jwt token
139
+ const verified = await this.authTokenAdapter.verify(payloadData);
140
+ if (!verified) return this.app.throw(401);
141
+ const passport = await this.passportAdapter.deserialize(payloadData);
142
+ if (!passport) return this.app.throw(401);
143
+ await this.setCurrent(passport);
144
+ return payloadData;
145
+ }
146
+
147
+ public async refreshAuthToken(refreshToken: string) {
148
+ // checkAuthToken by code
149
+ let payloadData = await this.checkAuthToken(refreshToken, 'refresh');
150
+ if (!payloadData) return this.app.throw(401);
151
+ // refreshAuthToken
152
+ const configRefreshAuthToken = this.scope.config.passport.refreshAuthToken;
153
+ payloadData = await this._handlePayloadData(payloadData, { authToken: configRefreshAuthToken });
154
+ // jwt token
155
+ return await this.bean.jwt.create(payloadData);
156
+ }
157
+
158
+ // only created by accessToken
159
+ public async createTempAuthToken(options?: IJwtSignOptions) {
160
+ // current
161
+ const passport = this.current;
162
+ if (!passport) return this.app.throw(401);
163
+ // payloadData
164
+ const payloadData = await this._passportSerialize(passport, { authToken: 'nochange' });
165
+ // jwt token
166
+ return await this.bean.jwt.createTempAuthToken(payloadData, options);
167
+ }
168
+
169
+ public async createOauthAuthToken(options?: IJwtSignOptions) {
170
+ // current
171
+ const passport = this.current;
172
+ if (!passport) return this.app.throw(401);
173
+ // payloadData
174
+ const payloadData = await this._passportSerialize(passport, { authToken: 'nochange' });
175
+ // jwt token
176
+ return await this.bean.jwt.createOauthAuthToken(payloadData, options);
177
+ }
178
+
179
+ public async createOauthCode(options?: IJwtSignOptions) {
180
+ // current
181
+ const passport = this.current;
182
+ if (!passport) return this.app.throw(401);
183
+ // payloadData
184
+ const payloadData = await this._passportSerialize(passport, { authToken: 'nochange' });
185
+ // code
186
+ return await this.bean.jwt.createOauthCode(payloadData, options);
187
+ }
188
+
189
+ public async createOauthCodeFromOauthAuthToken(accessToken: string, options?: IJwtSignOptions) {
190
+ // payloadData
191
+ const payloadData = await this.bean.jwt.get('access').verify(accessToken);
192
+ if (!payloadData) return this.app.throw(401);
193
+ // code
194
+ return await this.bean.jwt.createOauthCode(payloadData, options);
195
+ }
196
+
197
+ public async createAuthTokenFromOauthCode(code: string) {
198
+ // checkAuthToken by code
199
+ const payloadData = await this.checkAuthToken(code, 'code');
200
+ if (!payloadData) return this.app.throw(401);
201
+ // jwt token
202
+ return await this.bean.jwt.create(payloadData);
203
+ }
204
+
205
+ public checkRoleName(name: keyof IRoleNameRecord | (keyof IRoleNameRecord)[]) {
206
+ const user = this.bean.passport.currentUser;
207
+ if (!user || user.anonymous) return false;
208
+ const roles = this.bean.passport.currentRoles;
209
+ if (!roles) return false;
210
+ const roleNames = roles?.map(item => item.name as keyof IRoleNameRecord);
211
+ const optionsName = Array.isArray(name) ? name : [name];
212
+ if (!roleNames.some(roleName => optionsName.includes(roleName))) return false;
213
+ return true;
214
+ }
215
+
216
+ public checkUserName(name: keyof IUserNameRecord | (keyof IUserNameRecord)[]) {
217
+ const user = this.bean.passport.currentUser;
218
+ if (!user || user.anonymous) return false;
219
+ const userName = user.name as keyof IUserNameRecord;
220
+ const optionsName = Array.isArray(name) ? name : [name];
221
+ if (!optionsName.includes(userName)) return false;
222
+ return true;
223
+ }
224
+
225
+ private async _passportSerialize(passport: IPassport, options?: ISigninOptions) {
226
+ // serialize
227
+ const payloadData = await this.passportAdapter.serialize(passport);
228
+ return await this._handlePayloadData(payloadData, options);
229
+ }
230
+
231
+ private async _handlePayloadData(payloadData: IPayloadData, options?: ISigninOptions) {
232
+ // auth token
233
+ const authToken = options?.authToken ?? 'refresh';
234
+ if (authToken === 'recreate') {
235
+ return await this.authTokenAdapter.create(payloadData);
236
+ } else {
237
+ const payloadData2 = await this.authTokenAdapter.retrieve(payloadData);
238
+ if (!payloadData2) {
239
+ return await this.authTokenAdapter.create(payloadData);
240
+ }
241
+ if (authToken === 'refresh') {
242
+ await this.authTokenAdapter.refresh(payloadData2);
243
+ } else if (authToken === 'nochange') {
244
+ // do nothing
245
+ }
246
+ return payloadData2;
247
+ }
248
+ }
249
+ }
@@ -0,0 +1,39 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+
3
+ import { BeanBase, beanFullNameFromOnionName } from 'vona';
4
+ import { Bean } from 'vona-module-a-bean';
5
+
6
+ import type { IRole, IRoleAdapter } from '../types/role.ts';
7
+
8
+ @Bean()
9
+ export class BeanRole extends BeanBase {
10
+ private _roleAdapter: IRoleAdapter;
11
+
12
+ private get roleAdapter(): IRoleAdapter {
13
+ if (!this._roleAdapter) {
14
+ const beanFullName = beanFullNameFromOnionName(this.scope.config.adapter.role, 'service');
15
+ this._roleAdapter = this.bean._getBean(beanFullName) as IRoleAdapter;
16
+ }
17
+ return this._roleAdapter;
18
+ }
19
+
20
+ findOneByName(name: string): Promise<IRole | undefined> {
21
+ return this.roleAdapter.findOneByName(name);
22
+ }
23
+
24
+ findOneById(id: TableIdentity): Promise<IRole | undefined> {
25
+ return this.roleAdapter.findOne({ id });
26
+ }
27
+
28
+ findOne(role: Partial<IRole>): Promise<IRole | undefined> {
29
+ return this.roleAdapter.findOne(role);
30
+ }
31
+
32
+ findAllByUserId(userId: TableIdentity): Promise<IRole[] | undefined> {
33
+ return this.roleAdapter.findAllByUserId(userId);
34
+ }
35
+
36
+ addUserId(id: TableIdentity, userId: TableIdentity): Promise<TableIdentity> {
37
+ return this.roleAdapter.addUserId(id, userId);
38
+ }
39
+ }
@@ -0,0 +1,88 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+
3
+ import { BeanBase, beanFullNameFromOnionName } from 'vona';
4
+ import { Bean } from 'vona-module-a-bean';
5
+
6
+ import type { IAuthUserProfile } from '../types/authProfile.ts';
7
+ import type { IUser, IUserAdapter } from '../types/user.ts';
8
+
9
+ @Bean()
10
+ export class BeanUser extends BeanBase {
11
+ private _userAdapter: IUserAdapter;
12
+
13
+ private get userAdapter(): IUserAdapter {
14
+ if (!this._userAdapter) {
15
+ const beanFullName = beanFullNameFromOnionName(this.scope.config.adapter.user, 'service');
16
+ this._userAdapter = this.bean._getBean(beanFullName) as IUserAdapter;
17
+ }
18
+ return this._userAdapter;
19
+ }
20
+
21
+ async activate(user: IUser) {
22
+ await this.scope.event.activate.emit(user, async user => {
23
+ await this.userAdapter.setActivated(user.id, true);
24
+ });
25
+ }
26
+
27
+ async register(user: Partial<IUser>, confirmed?: boolean, randomUsername?: boolean): Promise<IUser> {
28
+ // config.user.autoActivate > confirmed
29
+ const autoActivate = this.scope.config.user.autoActivate ? true : confirmed;
30
+ const data = { user, confirmed, autoActivate };
31
+ return (await this.scope.event.register.emit(data, async data => {
32
+ let name = data.user.name;
33
+ if (!name) this.app.throw(403);
34
+ // check if exists
35
+ const userCheck = await this.userAdapter.findOneByName(name);
36
+ if (userCheck) {
37
+ if (!randomUsername) this.scope.error.UserExists.throw();
38
+ name = `${name}_${userCheck.id}${Date.now().toString().substring(8)}`;
39
+ }
40
+ // user
41
+ const userData = { ...data.user, name };
42
+ const userNew = await this.userAdapter.create(userData);
43
+ if (data.autoActivate) {
44
+ await this.activate(userNew);
45
+ }
46
+ return userNew;
47
+ })) as IUser;
48
+ }
49
+
50
+ async registerByProfile(profile: IAuthUserProfile, randomUsername?: boolean): Promise<IUser> {
51
+ const user = await this.userAdapter.userOfProfile(profile);
52
+ return await this.register(user, profile.confirmed, randomUsername);
53
+ }
54
+
55
+ async createAnonymous(): Promise<IUser> {
56
+ return (await this.scope.event.createAnonymous.emit(undefined, async () => {
57
+ return await this.userAdapter.createAnonymous();
58
+ })) as Promise<IUser>;
59
+ }
60
+
61
+ async findOneByName(name: string): Promise<IUser | undefined> {
62
+ return this.userAdapter.findOneByName(name);
63
+ }
64
+
65
+ async findOneById(id: TableIdentity): Promise<IUser | undefined> {
66
+ return this.userAdapter.findOne({ id });
67
+ }
68
+
69
+ async findOne(user: Partial<IUser>): Promise<IUser | undefined> {
70
+ return this.userAdapter.findOne(user);
71
+ }
72
+
73
+ async updateById(id: TableIdentity, user: Partial<IUser>): Promise<void> {
74
+ return this.userAdapter.update({ ...user, id });
75
+ }
76
+
77
+ async update(user: Partial<IUser>): Promise<void> {
78
+ return this.userAdapter.update(user);
79
+ }
80
+
81
+ async removeById(id: TableIdentity): Promise<void> {
82
+ return this.userAdapter.remove({ id });
83
+ }
84
+
85
+ async remove(user: Partial<IUser>): Promise<void> {
86
+ return this.userAdapter.remove(user);
87
+ }
88
+ }
@@ -0,0 +1,10 @@
1
+ import { BeanCacheRedisBase, CacheRedis } from 'vona-module-a-cache';
2
+
3
+ export type TCacheRedisAuthTokenKey = string;
4
+ export type TCacheRedisAuthTokenData = string;
5
+
6
+ @CacheRedis({
7
+ ttl: 30 * 24 * 60 * 60 * 1000,
8
+ disableTransactionCompensate: true,
9
+ })
10
+ export class CacheRedisAuthToken extends BeanCacheRedisBase<TCacheRedisAuthTokenKey, TCacheRedisAuthTokenData> {}
@@ -0,0 +1,10 @@
1
+ import { BeanEventBase, Event } from 'vona-module-a-event';
2
+
3
+ import type { IUser } from '../types/user.ts';
4
+
5
+ export type TypeEventActivateData = IUser;
6
+
7
+ export type TypeEventActivateResult = void;
8
+
9
+ @Event()
10
+ export class EventActivate extends BeanEventBase<TypeEventActivateData, TypeEventActivateResult> {}
@@ -0,0 +1,10 @@
1
+ import { BeanEventBase, Event } from 'vona-module-a-event';
2
+
3
+ import type { IUser } from '../types/user.ts';
4
+
5
+ export type TypeEventCreateAnonymousData = undefined;
6
+
7
+ export type TypeEventCreateAnonymousResult = Partial<IUser>;
8
+
9
+ @Event()
10
+ export class EventCreateAnonymous extends BeanEventBase<TypeEventCreateAnonymousData, TypeEventCreateAnonymousResult> {}
@@ -0,0 +1,14 @@
1
+ import { BeanEventBase, Event } from 'vona-module-a-event';
2
+
3
+ import type { IUser } from '../types/user.ts';
4
+
5
+ export interface TypeEventRegisterData {
6
+ user: Partial<IUser>;
7
+ confirmed?: boolean;
8
+ autoActivate?: boolean;
9
+ }
10
+
11
+ export type TypeEventRegisterResult = Partial<IUser>;
12
+
13
+ @Event()
14
+ export class EventRegister extends BeanEventBase<TypeEventRegisterData, TypeEventRegisterResult> {}
@@ -0,0 +1,10 @@
1
+ import { BeanEventBase, Event } from 'vona-module-a-event';
2
+
3
+ import type { IPassport } from '../types/passport.ts';
4
+
5
+ export type TypeEventSigninData = IPassport;
6
+
7
+ export type TypeEventSigninResult = void;
8
+
9
+ @Event()
10
+ export class EventSignin extends BeanEventBase<TypeEventSigninData, TypeEventSigninResult> {}
@@ -0,0 +1,10 @@
1
+ import { BeanEventBase, Event } from 'vona-module-a-event';
2
+
3
+ import type { IPassport } from '../types/passport.ts';
4
+
5
+ export type TypeEventSignoutData = IPassport;
6
+
7
+ export type TypeEventSignoutResult = void;
8
+
9
+ @Event()
10
+ export class EventSignout extends BeanEventBase<TypeEventSignoutData, TypeEventSignoutResult> {}
@@ -0,0 +1,53 @@
1
+ import type { Next } from 'vona';
2
+ import type { IDecoratorGuardOptionsGlobal, IGuardExecute } from 'vona-module-a-aspect';
3
+
4
+ import { catchError } from '@cabloy/utils';
5
+ import { BeanBase, Global } from 'vona';
6
+ import { Guard } from 'vona-module-a-aspect';
7
+ import { checkErrorJwtExpired } from 'vona-module-a-jwt';
8
+
9
+ export interface IGuardOptionsPassport extends IDecoratorGuardOptionsGlobal {
10
+ public: boolean;
11
+ activated?: boolean;
12
+ checkAuthToken: boolean; // default is true
13
+ }
14
+
15
+ @Guard<IGuardOptionsPassport>({ public: false, activated: true, checkAuthToken: true })
16
+ @Global()
17
+ export class GuardPassport extends BeanBase implements IGuardExecute {
18
+ async execute(options: IGuardOptionsPassport, next: Next): Promise<boolean> {
19
+ // auth token
20
+ if (!this.bean.passport.current) {
21
+ if (options.checkAuthToken) {
22
+ // will return undefined if no accessToken, so not check options.public
23
+ const [_, err] = await catchError(() => {
24
+ return this.bean.passport.checkAuthToken();
25
+ });
26
+ if (err && !options.public) throw err;
27
+ // throw error only when ErrorMessageJwtExpired
28
+ checkErrorJwtExpired(err, this.ctx.headers);
29
+ }
30
+ }
31
+ // check current
32
+ if (!this.bean.passport.current) {
33
+ await this.bean.passport.signinWithAnonymous();
34
+ }
35
+ if (!options.public && !this.bean.passport.isAuthenticated) {
36
+ // return false;
37
+ // 401 for this guard, 403 for the next guards
38
+ return this.app.throw(401);
39
+ }
40
+ if (this.bean.passport.isAuthenticated) {
41
+ if (options.activated === true && !this.bean.passport.isActivated) {
42
+ return this.app.throw(403);
43
+ }
44
+ if (options.activated === false && this.bean.passport.isActivated) {
45
+ return this.app.throw(403);
46
+ }
47
+ }
48
+ // check innerAccess
49
+ if (this.ctx.innerAccess) return true;
50
+ // next
51
+ return next();
52
+ }
53
+ }
@@ -0,0 +1,35 @@
1
+ import type { Next } from 'vona';
2
+ import type { IDecoratorGuardOptions, IGuardExecute } from 'vona-module-a-aspect';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { Guard } from 'vona-module-a-aspect';
6
+
7
+ import type { IRoleNameRecord } from '../types/role.ts';
8
+
9
+ export interface IGuardOptionsRoleName extends IDecoratorGuardOptions {
10
+ name?: keyof IRoleNameRecord | (keyof IRoleNameRecord)[];
11
+ passWhenMatched: boolean;
12
+ rejectWhenDismatched: boolean;
13
+ }
14
+
15
+ @Guard<IGuardOptionsRoleName>({
16
+ passWhenMatched: true,
17
+ rejectWhenDismatched: true,
18
+ })
19
+ export class GuardRoleName extends BeanBase implements IGuardExecute {
20
+ async execute(options: IGuardOptionsRoleName, next: Next): Promise<boolean> {
21
+ const result = await this._check(options);
22
+ if (!result) {
23
+ if (options.rejectWhenDismatched) return this.app.throw(403);
24
+ } else {
25
+ if (options.passWhenMatched) return true;
26
+ }
27
+ // next
28
+ return next();
29
+ }
30
+
31
+ private async _check(options: IGuardOptionsRoleName) {
32
+ if (!options.name) return false;
33
+ return this.bean.passport.checkRoleName(options.name);
34
+ }
35
+ }
@@ -0,0 +1,35 @@
1
+ import type { Next } from 'vona';
2
+ import type { IDecoratorGuardOptions, IGuardExecute } from 'vona-module-a-aspect';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { Guard } from 'vona-module-a-aspect';
6
+
7
+ import type { IUserNameRecord } from '../types/user.ts';
8
+
9
+ export interface IGuardOptionsUserName extends IDecoratorGuardOptions {
10
+ name?: keyof IUserNameRecord | (keyof IUserNameRecord)[];
11
+ passWhenMatched: boolean;
12
+ rejectWhenDismatched: boolean;
13
+ }
14
+
15
+ @Guard<IGuardOptionsUserName>({
16
+ passWhenMatched: true,
17
+ rejectWhenDismatched: true,
18
+ })
19
+ export class GuardUserName extends BeanBase implements IGuardExecute {
20
+ async execute(options: IGuardOptionsUserName, next: Next): Promise<boolean> {
21
+ const result = await this._check(options);
22
+ if (!result) {
23
+ if (options.rejectWhenDismatched) return this.app.throw(403);
24
+ } else {
25
+ if (options.passWhenMatched) return true;
26
+ }
27
+ // next
28
+ return next();
29
+ }
30
+
31
+ private async _check(options: IGuardOptionsUserName) {
32
+ if (!options.name) return false;
33
+ return this.bean.passport.checkUserName(options.name);
34
+ }
35
+ }
@@ -0,0 +1,18 @@
1
+ import type { IMetaPrintTipExecute, TypeMetaPrintTipResult } from 'vona-module-a-printtip';
2
+
3
+ import { BeanBase } from 'vona';
4
+ import { Meta } from 'vona-module-a-meta';
5
+
6
+ @Meta()
7
+ export class MetaPrintTip extends BeanBase implements IMetaPrintTipExecute {
8
+ async execute(): Promise<TypeMetaPrintTipResult> {
9
+ if (!this.app.meta.isDev) return;
10
+ // signin
11
+ const jwt = await this.bean.passport.signinSystem('dev', '-1');
12
+ const accessToken = jwt.accessToken;
13
+ return {
14
+ title: 'access token [admin] [dev]',
15
+ message: `Bearer ${accessToken}`,
16
+ };
17
+ }
18
+ }
@@ -0,0 +1,19 @@
1
+ import type { IMetaRuntimeExecute } from 'vona-module-a-runtime';
2
+
3
+ import { BeanBase } from 'vona';
4
+ import { Meta } from 'vona-module-a-meta';
5
+
6
+ export type TypeMetaRuntimeResult = { accessToken?: string } | undefined;
7
+
8
+ @Meta()
9
+ export class MetaRuntime extends BeanBase implements IMetaRuntimeExecute {
10
+ async execute(): Promise<TypeMetaRuntimeResult> {
11
+ if (this.app.meta.isProd) return;
12
+ // signin
13
+ const jwt = await this.bean.passport.signinSystem('dev', '-1');
14
+ const accessToken = jwt.accessToken;
15
+ return {
16
+ accessToken,
17
+ };
18
+ }
19
+ }
@@ -0,0 +1,28 @@
1
+ import type { VonaApplication } from 'vona';
2
+ import type { IServiceRecord } from 'vona-module-a-bean';
3
+
4
+ import type { TypeAuthToken } from '../types/auth.ts';
5
+
6
+ export function config(_app: VonaApplication) {
7
+ return {
8
+ user: {
9
+ autoActivate: false,
10
+ },
11
+ passport: {
12
+ refreshAuthToken: 'recreate' as TypeAuthToken,
13
+ },
14
+ adapter: {
15
+ authToken: 'a-user:authTokenAdapter' as keyof IServiceRecord,
16
+ passport: 'home-user:passportAdapter' as keyof IServiceRecord,
17
+ user: 'home-user:userAdapter' as keyof IServiceRecord,
18
+ role: 'home-user:roleAdapter' as keyof IServiceRecord,
19
+ },
20
+ payloadData: {
21
+ fields: {
22
+ authId: 'authId',
23
+ userId: 'userId',
24
+ token: 'token',
25
+ },
26
+ },
27
+ };
28
+ }
@@ -0,0 +1,3 @@
1
+ export const errors = {
2
+ UserExists: 1001,
3
+ } as const;
@@ -0,0 +1,3 @@
1
+ export default {
2
+ UserExists: 'User Exists',
3
+ };
@@ -0,0 +1,3 @@
1
+ export default {
2
+ UserExists: '用户已存在',
3
+ };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './.metadata/index.ts';
2
+ export * from './.metadata/locales.ts';
3
+ export * from './lib/index.ts';
4
+ export * from './types/index.ts';
@@ -0,0 +1,7 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+
3
+ import type { IAuthIdRecord } from '../types/auth.ts';
4
+
5
+ export function $getAuthIdSystem<K extends keyof IAuthIdRecord>(_authName: IAuthIdRecord[K], authId: K): TableIdentity {
6
+ return authId;
7
+ }
@@ -0,0 +1,3 @@
1
+ export * from './auth.ts';
2
+ export * from './passport.ts';
3
+ export * from './user.ts';