vona-module-captcha-simple 5.1.13 → 5.1.15
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/dist/.metadata/index.d.ts.map +1 -1
- package/dist/bean/captchaProvider.imageText.d.ts +12 -2
- package/dist/bean/captchaProvider.imageText.d.ts.map +1 -1
- package/dist/index.js +102 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/.metadata/index.ts +16 -17
- package/src/bean/captchaProvider.imageText.ts +123 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/.metadata/index.ts"],"names":[],"mappings":"AACA,6BAA6B;AAC7B,cAAc,sCAAsC,CAAC;AACrD,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,sCAAsC,CAAC;AAC7F,OAAO,uBAAuB,CAAC;AAC/B,OAAO,QAAQ,uBAAuB,CAAC;IAEnC,UAAiB,sBAAsB;QACrC,0BAA0B,EAAE,gCAAgC,CAAC;KAC9D;CAGJ;AACD,OAAO,QAAQ,4BAA4B,CAAC;IAEpC,UAAiB,wBAAwB;KAGxC;IAEC,UAAiB,wBAAwB;QACvC,IAAI,aAAa,IAAI,0CAA0C,CAAC;QAChE,IAAI,UAAU,IAAI,0BAA0B,CAAC;QAC7C,IAAI,aAAa,IAAI,gCAAgC,CAAC;KACvD;CACV;AACD,2BAA2B;AAC3B,0BAA0B;AAC1B,cAAc,gCAAgC,CAAC;AAE/C,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,QAAQ,uBAAuB,CAAC;IAEnC,UAAiB,mBAAmB;QAClC,uBAAuB,EAAE,6BAA6B,CAAC;KACxD;CAGJ;AACD,OAAO,QAAQ,4BAA4B,CAAC;IAEpC,UAAiB,kBAAkB;KAGlC;IAEC,UAAiB,kBAAkB;QACjC,IAAI,aAAa,IAAI,oCAAoC,CAAC;QAC1D,IAAI,UAAU,IAAI,uBAAuB,CAAC;QAC1C,IAAI,aAAa,IAAI,6BAA6B,CAAC;KACpD;CACV;AACD,wBAAwB;AACxB,kBAAkB;AAClB,cAAc,uBAAuB,CAAC;AAEtC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,QAAQ,oBAAoB,CAAC;IAEhC,UAAiB,WAAW;QAC1B,sBAAsB,EAAE,KAAK,CAAC;KAC/B;CAGJ;AACD,OAAO,QAAQ,4BAA4B,CAAC;IAEpC,UAAiB,SAAS;KAGzB;IAEC,UAAiB,SAAS;QACxB,IAAI,aAAa,IAAI,2BAA2B,CAAC;QACjD,IAAI,UAAU,IAAI,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/.metadata/index.ts"],"names":[],"mappings":"AACA,6BAA6B;AAC7B,cAAc,sCAAsC,CAAC;AACrD,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,sCAAsC,CAAC;AAC7F,OAAO,uBAAuB,CAAC;AAC/B,OAAO,QAAQ,uBAAuB,CAAC;IAEnC,UAAiB,sBAAsB;QACrC,0BAA0B,EAAE,gCAAgC,CAAC;KAC9D;CAGJ;AACD,OAAO,QAAQ,4BAA4B,CAAC;IAEpC,UAAiB,wBAAwB;KAGxC;IAEC,UAAiB,wBAAwB;QACvC,IAAI,aAAa,IAAI,0CAA0C,CAAC;QAChE,IAAI,UAAU,IAAI,0BAA0B,CAAC;QAC7C,IAAI,aAAa,IAAI,gCAAgC,CAAC;KACvD;CACV;AACD,2BAA2B;AAC3B,0BAA0B;AAC1B,cAAc,gCAAgC,CAAC;AAE/C,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,QAAQ,uBAAuB,CAAC;IAEnC,UAAiB,mBAAmB;QAClC,uBAAuB,EAAE,6BAA6B,CAAC;KACxD;CAGJ;AACD,OAAO,QAAQ,4BAA4B,CAAC;IAEpC,UAAiB,kBAAkB;KAGlC;IAEC,UAAiB,kBAAkB;QACjC,IAAI,aAAa,IAAI,oCAAoC,CAAC;QAC1D,IAAI,UAAU,IAAI,uBAAuB,CAAC;QAC1C,IAAI,aAAa,IAAI,6BAA6B,CAAC;KACpD;CACV;AACD,wBAAwB;AACxB,kBAAkB;AAClB,cAAc,uBAAuB,CAAC;AAEtC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,QAAQ,oBAAoB,CAAC;IAEhC,UAAiB,WAAW;QAC1B,sBAAsB,EAAE,KAAK,CAAC;KAC/B;CAGJ;AACD,OAAO,QAAQ,4BAA4B,CAAC;IAEpC,UAAiB,SAAS;KAGzB;IAEC,UAAiB,SAAS;QACxB,IAAI,aAAa,IAAI,2BAA2B,CAAC;QACjD,IAAI,UAAU,IAAI,sBAAsB,CAAC;KAC1C;CACV;AACD,gBAAgB;AAChB,wBAAwB;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,sBAAsB;AACtB,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,MAAM,CAAC;AAGzD,qBACa,wBAAyB,SAAQ,aAAa;CAAG;AAE9D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,SAAS,CAAC;CAChB;AAED,OAAO,MAAM,CAAC;AACd,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAiB,gBAAgB;QAC/B,gBAAgB,EAAE,wBAAwB,CAAC;KAC5C;IAED,UAAiB,mBAAmB;QAClC,aAAa,EAAE,wBAAwB,CAAC;KACzC;CAOF;AACD,iBAAiB"}
|
|
@@ -4,6 +4,12 @@ import { BeanBase } from 'vona';
|
|
|
4
4
|
export type TypeCaptchaProviderImageTextToken = string;
|
|
5
5
|
export type TypeCaptchaProviderImageTextPayload = string;
|
|
6
6
|
export type TypeCaptchaProviderImageTextData = ICaptchaProviderData<TypeCaptchaProviderImageTextToken, TypeCaptchaProviderImageTextPayload>;
|
|
7
|
+
export interface ICaptchaImageTextState {
|
|
8
|
+
token?: string;
|
|
9
|
+
token2?: string;
|
|
10
|
+
revision: string;
|
|
11
|
+
ttlSecondary: number;
|
|
12
|
+
}
|
|
7
13
|
export type TypeCaptchaProviderImageTextType = 'char' | 'math';
|
|
8
14
|
export interface ICaptchaProviderOptionsImageText extends IDecoratorCaptchaProviderOptions {
|
|
9
15
|
type?: TypeCaptchaProviderImageTextType;
|
|
@@ -11,8 +17,12 @@ export interface ICaptchaProviderOptionsImageText extends IDecoratorCaptchaProvi
|
|
|
11
17
|
opts: ConfigObject;
|
|
12
18
|
}
|
|
13
19
|
export declare class CaptchaProviderImageText extends BeanBase implements ICaptchaProviderExecute<TypeCaptchaProviderImageTextToken, TypeCaptchaProviderImageTextPayload> {
|
|
14
|
-
create(options: ICaptchaProviderOptionsImageText): Promise<TypeCaptchaProviderImageTextData>;
|
|
15
|
-
verify(token:
|
|
20
|
+
create(options: ICaptchaProviderOptionsImageText, _payload?: unknown, previousToken?: string): Promise<TypeCaptchaProviderImageTextData>;
|
|
21
|
+
verify(token: string, tokenInput: unknown, _options: ICaptchaProviderOptionsImageText): Promise<boolean>;
|
|
22
|
+
verifyImmediate(token: string, tokenInput: unknown, _options: ICaptchaProviderOptionsImageText): Promise<false | string>;
|
|
23
|
+
updateToken(token: string, tokenInput: unknown, options: ICaptchaProviderOptionsImageText): Promise<TypeCaptchaProviderImageTextData>;
|
|
24
|
+
private _matches;
|
|
25
|
+
private _policy;
|
|
16
26
|
private _confirmFont;
|
|
17
27
|
}
|
|
18
28
|
//# sourceMappingURL=captchaProvider.imageText.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captchaProvider.imageText.d.ts","sourceRoot":"","sources":["../../src/bean/captchaProvider.imageText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,uBAAuB,EACvB,gCAAgC,EACjC,MAAM,uBAAuB,CAAC;AAG/B,OAAmB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAIjE,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"captchaProvider.imageText.d.ts","sourceRoot":"","sources":["../../src/bean/captchaProvider.imageText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,uBAAuB,EACvB,gCAAgC,EACjC,MAAM,uBAAuB,CAAC;AAG/B,OAAmB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAIjE,OAAO,EAAE,QAAQ,EAAgB,MAAM,MAAM,CAAC;AAG9C,MAAM,MAAM,iCAAiC,GAAG,MAAM,CAAC;AACvD,MAAM,MAAM,mCAAmC,GAAG,MAAM,CAAC;AACzD,MAAM,MAAM,gCAAgC,GAAG,oBAAoB,CACjE,iCAAiC,EACjC,mCAAmC,CACpC,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,gCAAgC,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,gCAAiC,SAAQ,gCAAgC;IACxF,IAAI,CAAC,EAAE,gCAAgC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,qBAMa,wBACX,SAAQ,QACR,YACE,uBAAuB,CAAC,iCAAiC,EAAE,mCAAmC,CAAC;IAE3F,MAAM,CACV,OAAO,EAAE,gCAAgC,EACzC,QAAQ,CAAC,EAAE,OAAO,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,gCAAgC,CAAC;IAkCtC,MAAM,CACV,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,OAAO,EACnB,QAAQ,EAAE,gCAAgC,GACzC,OAAO,CAAC,OAAO,CAAC;IAmBb,eAAe,CACnB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,OAAO,EACnB,QAAQ,EAAE,gCAAgC,GACzC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;IAmBpB,WAAW,CACf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,OAAO,EACnB,OAAO,EAAE,gCAAgC,GACxC,OAAO,CAAC,gCAAgC,CAAC;IAwB5C,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;YAgBD,YAAY;CAQ3B"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BeanInfo, BeanBase, cast, BeanScopeBase } from 'vona';
|
|
1
|
+
import { BeanInfo, BeanBase, uuidv4, cast, BeanScopeBase } from 'vona';
|
|
2
2
|
import { __decorate } from 'tslib';
|
|
3
3
|
import { getRandomInt } from '@cabloy/utils';
|
|
4
4
|
import svgCaptcha from '@zhennann/svg-captcha';
|
|
@@ -12,20 +12,117 @@ import { Scope } from 'vona-module-a-bean';
|
|
|
12
12
|
|
|
13
13
|
const CaptchaProviderImageTextTypes = ['char', 'math'];
|
|
14
14
|
let CaptchaProviderImageText = class CaptchaProviderImageText extends BeanBase {
|
|
15
|
-
async create(options) {
|
|
15
|
+
async create(options, _payload, previousToken) {
|
|
16
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
17
|
+
const snapshot = previousToken ? await cache.snapshot(previousToken) : undefined;
|
|
18
|
+
if (previousToken !== undefined && !previousToken.startsWith('imageText:')) {
|
|
19
|
+
return this.app.throw(403);
|
|
20
|
+
}
|
|
21
|
+
const {
|
|
22
|
+
ttl,
|
|
23
|
+
ttlSecondary
|
|
24
|
+
} = this._policy(options);
|
|
16
25
|
await this._confirmFont(options);
|
|
17
26
|
let type = options.type;
|
|
18
27
|
if (!type) {
|
|
19
28
|
type = CaptchaProviderImageTextTypes[getRandomInt(2, 0)];
|
|
20
29
|
}
|
|
21
30
|
const captcha = type === 'char' ? svgCaptcha.create(options.opts) : svgCaptcha.createMathExpr(options.opts);
|
|
22
|
-
|
|
31
|
+
const token = snapshot ? previousToken : `imageText:${uuidv4()}`;
|
|
32
|
+
const written = await cache.writeProof(token, {
|
|
23
33
|
token: captcha.text,
|
|
24
|
-
|
|
34
|
+
revision: uuidv4(),
|
|
35
|
+
ttlSecondary
|
|
36
|
+
}, ttl, snapshot);
|
|
37
|
+
if (!written.changed) return this.app.throw(403);
|
|
38
|
+
return {
|
|
39
|
+
token,
|
|
40
|
+
tokenPublic: captcha.text,
|
|
41
|
+
payload: svg64(captcha.data),
|
|
42
|
+
serverNow: written.serverNow,
|
|
43
|
+
expiresAt: written.expiresAt,
|
|
44
|
+
routingExpiresAt: written.expiresAt + ttlSecondary
|
|
25
45
|
};
|
|
26
46
|
}
|
|
27
47
|
async verify(token, tokenInput, _options) {
|
|
28
|
-
|
|
48
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
49
|
+
const snapshot = await cache.snapshot(token);
|
|
50
|
+
if (!snapshot || !token.startsWith('imageText:')) return false;
|
|
51
|
+
const data = snapshot.data;
|
|
52
|
+
if (data.token2) {
|
|
53
|
+
const consumed = await cache.compareAndSet(token, snapshot);
|
|
54
|
+
return consumed && data.token2 === tokenInput;
|
|
55
|
+
}
|
|
56
|
+
if (!data.token) return false;
|
|
57
|
+
const verified = this._matches(data.token, tokenInput);
|
|
58
|
+
const changed = await cache.compareAndSet(token, snapshot, verified ? undefined : {
|
|
59
|
+
...data,
|
|
60
|
+
token: undefined,
|
|
61
|
+
revision: uuidv4()
|
|
62
|
+
});
|
|
63
|
+
return verified && changed;
|
|
64
|
+
}
|
|
65
|
+
async verifyImmediate(token, tokenInput, _options) {
|
|
66
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
67
|
+
const snapshot = await cache.snapshot(token);
|
|
68
|
+
if (!snapshot || !token.startsWith('imageText:') || !snapshot.data.token) return false;
|
|
69
|
+
const data = snapshot.data;
|
|
70
|
+
if (!this._matches(data.token, tokenInput)) {
|
|
71
|
+
await cache.compareAndSet(token, snapshot, {
|
|
72
|
+
...data,
|
|
73
|
+
token: undefined,
|
|
74
|
+
revision: uuidv4()
|
|
75
|
+
});
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const tokenSecondary = uuidv4();
|
|
79
|
+
const changed = await cache.compareAndSet(token, snapshot, {
|
|
80
|
+
...data,
|
|
81
|
+
token: undefined,
|
|
82
|
+
token2: tokenSecondary,
|
|
83
|
+
revision: uuidv4()
|
|
84
|
+
}, data.ttlSecondary);
|
|
85
|
+
return changed ? tokenSecondary : false;
|
|
86
|
+
}
|
|
87
|
+
async updateToken(token, tokenInput, options) {
|
|
88
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
89
|
+
const snapshot = await cache.snapshot(token);
|
|
90
|
+
if (!snapshot || !token.startsWith('imageText:') || typeof tokenInput !== 'string') {
|
|
91
|
+
return this.app.throw(403);
|
|
92
|
+
}
|
|
93
|
+
const {
|
|
94
|
+
ttl,
|
|
95
|
+
ttlSecondary
|
|
96
|
+
} = this._policy(options);
|
|
97
|
+
const written = await cache.writeProof(token, {
|
|
98
|
+
token: tokenInput,
|
|
99
|
+
revision: uuidv4(),
|
|
100
|
+
ttlSecondary
|
|
101
|
+
}, ttl, snapshot);
|
|
102
|
+
if (!written.changed) return this.app.throw(403);
|
|
103
|
+
return {
|
|
104
|
+
token,
|
|
105
|
+
tokenPublic: tokenInput,
|
|
106
|
+
payload: '',
|
|
107
|
+
serverNow: written.serverNow,
|
|
108
|
+
expiresAt: written.expiresAt,
|
|
109
|
+
routingExpiresAt: written.expiresAt + ttlSecondary
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
_matches(token, input) {
|
|
113
|
+
return typeof input === 'string' && !!input && input.toLowerCase() === token.toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
_policy(options) {
|
|
116
|
+
const defaults = this.$scope.captcha.config.captchaProvider;
|
|
117
|
+
const ttl = options.ttl ?? defaults.ttl;
|
|
118
|
+
const ttlSecondary = options.ttlSecondary ?? defaults.ttlSecondary;
|
|
119
|
+
if (!Number.isSafeInteger(ttl) || ttl <= 0 || !Number.isSafeInteger(ttlSecondary) || ttlSecondary <= 0 || !Number.isSafeInteger(Date.now() + ttl + ttlSecondary)) {
|
|
120
|
+
throw new Error('invalid captcha ttl');
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
ttl,
|
|
124
|
+
ttlSecondary
|
|
125
|
+
};
|
|
29
126
|
}
|
|
30
127
|
async _confirmFont(options) {
|
|
31
128
|
if (cast(svgCaptcha.options).font) return;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/bean/captchaProvider.imageText.ts","../src/bean/captchaScene.simple.ts","../src/bean/meta.asset.ts","../src/.metadata/index.ts"],"sourcesContent":[null,null,null,null],"names":["CaptchaProviderImageTextTypes","CaptchaProviderImageText","BeanBase","create","options","
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/bean/captchaProvider.imageText.ts","../src/bean/captchaScene.simple.ts","../src/bean/meta.asset.ts","../src/.metadata/index.ts"],"sourcesContent":[null,null,null,null],"names":["CaptchaProviderImageTextTypes","CaptchaProviderImageText","BeanBase","create","options","_payload","previousToken","cache","$scope","captcha","cacheRedis","snapshot","undefined","startsWith","app","throw","ttl","ttlSecondary","_policy","_confirmFont","type","getRandomInt","svgCaptcha","opts","createMathExpr","token","uuidv4","written","writeProof","text","revision","changed","tokenPublic","payload","svg64","data","serverNow","expiresAt","routingExpiresAt","verify","tokenInput","_options","token2","consumed","compareAndSet","verified","_matches","verifyImmediate","tokenSecondary","updateToken","input","toLowerCase","defaults","config","captchaProvider","Number","isSafeInteger","Date","now","Error","cast","font","fontFile","fontPath","scope","asset","get","_loadFont","ascender","descender","__decorate","CaptchaProvider","size","color","__z_BeanInfo","module","buffer","fse","readFile","opentypeParse","_nodeBufferToArrayBuffer","ab","ArrayBuffer","length","view","Uint8Array","i","CaptchaSceneSimple","CaptchaScene","resolver","_ctx","_providers","providers","MetaAsset","BeanAssetBase","Meta","ScopeModuleCaptchaSimple","BeanScopeBase","Scope"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/.metadata/index.ts
CHANGED
|
@@ -4,15 +4,15 @@ export * from '../bean/captchaProvider.imageText.ts';
|
|
|
4
4
|
import type { ICaptchaProviderOptionsImageText } from '../bean/captchaProvider.imageText.ts';
|
|
5
5
|
import 'vona-module-a-captcha';
|
|
6
6
|
declare module 'vona-module-a-captcha' {
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
export interface ICaptchaProviderRecord {
|
|
9
9
|
'captcha-simple:imageText': ICaptchaProviderOptionsImageText;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
}
|
|
14
14
|
declare module 'vona-module-captcha-simple' {
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
export interface CaptchaProviderImageText {
|
|
17
17
|
/** @internal */
|
|
18
18
|
get scope(): ScopeModuleCaptchaSimple;
|
|
@@ -22,7 +22,7 @@ declare module 'vona-module-captcha-simple' {
|
|
|
22
22
|
get $beanFullName(): 'captcha-simple.captchaProvider.imageText';
|
|
23
23
|
get $onionName(): 'captcha-simple:imageText';
|
|
24
24
|
get $onionOptions(): ICaptchaProviderOptionsImageText;
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
}
|
|
27
27
|
/** captchaProvider: end */
|
|
28
28
|
/** captchaScene: begin */
|
|
@@ -30,15 +30,15 @@ export * from '../bean/captchaScene.simple.ts';
|
|
|
30
30
|
|
|
31
31
|
import { type IDecoratorCaptchaSceneOptions } from 'vona-module-a-captcha';
|
|
32
32
|
declare module 'vona-module-a-captcha' {
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
export interface ICaptchaSceneRecord {
|
|
35
35
|
'captcha-simple:simple': IDecoratorCaptchaSceneOptions;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
}
|
|
40
40
|
declare module 'vona-module-captcha-simple' {
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
export interface CaptchaSceneSimple {
|
|
43
43
|
/** @internal */
|
|
44
44
|
get scope(): ScopeModuleCaptchaSimple;
|
|
@@ -48,7 +48,7 @@ declare module 'vona-module-captcha-simple' {
|
|
|
48
48
|
get $beanFullName(): 'captcha-simple.captchaScene.simple';
|
|
49
49
|
get $onionName(): 'captcha-simple:simple';
|
|
50
50
|
get $onionOptions(): IDecoratorCaptchaSceneOptions;
|
|
51
|
-
}
|
|
51
|
+
}
|
|
52
52
|
}
|
|
53
53
|
/** captchaScene: end */
|
|
54
54
|
/** meta: begin */
|
|
@@ -56,15 +56,15 @@ export * from '../bean/meta.asset.ts';
|
|
|
56
56
|
|
|
57
57
|
import 'vona-module-a-meta';
|
|
58
58
|
declare module 'vona-module-a-meta' {
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
export interface IMetaRecord {
|
|
61
61
|
'captcha-simple:asset': never;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
}
|
|
66
66
|
declare module 'vona-module-captcha-simple' {
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
export interface MetaAsset {
|
|
69
69
|
/** @internal */
|
|
70
70
|
get scope(): ScopeModuleCaptchaSimple;
|
|
@@ -73,8 +73,7 @@ declare module 'vona-module-captcha-simple' {
|
|
|
73
73
|
export interface MetaAsset {
|
|
74
74
|
get $beanFullName(): 'captcha-simple.meta.asset';
|
|
75
75
|
get $onionName(): 'captcha-simple:asset';
|
|
76
|
-
|
|
77
|
-
}
|
|
76
|
+
}
|
|
78
77
|
}
|
|
79
78
|
/** meta: end */
|
|
80
79
|
/** meta asset: begin */
|
|
@@ -101,11 +100,11 @@ declare module 'vona' {
|
|
|
101
100
|
export interface IBeanScopeContainer {
|
|
102
101
|
captchaSimple: ScopeModuleCaptchaSimple;
|
|
103
102
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
103
|
|
|
107
|
-
|
|
108
104
|
|
|
109
|
-
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
110
109
|
}
|
|
111
110
|
/** scope: end */
|
|
@@ -9,7 +9,7 @@ import svgCaptcha, { ConfigObject } from '@zhennann/svg-captcha';
|
|
|
9
9
|
import fse from 'fs-extra';
|
|
10
10
|
import { parse as opentypeParse } from 'opentype.js/dist/opentype.mjs';
|
|
11
11
|
import svg64 from 'svg64';
|
|
12
|
-
import { BeanBase, cast } from 'vona';
|
|
12
|
+
import { BeanBase, cast, uuidv4 } from 'vona';
|
|
13
13
|
import { CaptchaProvider } from 'vona-module-a-captcha';
|
|
14
14
|
|
|
15
15
|
export type TypeCaptchaProviderImageTextToken = string;
|
|
@@ -19,6 +19,13 @@ export type TypeCaptchaProviderImageTextData = ICaptchaProviderData<
|
|
|
19
19
|
TypeCaptchaProviderImageTextPayload
|
|
20
20
|
>;
|
|
21
21
|
|
|
22
|
+
export interface ICaptchaImageTextState {
|
|
23
|
+
token?: string;
|
|
24
|
+
token2?: string;
|
|
25
|
+
revision: string;
|
|
26
|
+
ttlSecondary: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
export type TypeCaptchaProviderImageTextType = 'char' | 'math';
|
|
23
30
|
const CaptchaProviderImageTextTypes = ['char', 'math'] as const;
|
|
24
31
|
export interface ICaptchaProviderOptionsImageText extends IDecoratorCaptchaProviderOptions {
|
|
@@ -40,7 +47,17 @@ export class CaptchaProviderImageText
|
|
|
40
47
|
{
|
|
41
48
|
async create(
|
|
42
49
|
options: ICaptchaProviderOptionsImageText,
|
|
50
|
+
_payload?: unknown,
|
|
51
|
+
previousToken?: string,
|
|
43
52
|
): Promise<TypeCaptchaProviderImageTextData> {
|
|
53
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
54
|
+
const snapshot = previousToken
|
|
55
|
+
? await cache.snapshot<ICaptchaImageTextState>(previousToken)
|
|
56
|
+
: undefined;
|
|
57
|
+
if (previousToken !== undefined && !previousToken.startsWith('imageText:')) {
|
|
58
|
+
return this.app.throw(403);
|
|
59
|
+
}
|
|
60
|
+
const { ttl, ttlSecondary } = this._policy(options);
|
|
44
61
|
await this._confirmFont(options);
|
|
45
62
|
let type = options.type;
|
|
46
63
|
if (!type) {
|
|
@@ -48,15 +65,116 @@ export class CaptchaProviderImageText
|
|
|
48
65
|
}
|
|
49
66
|
const captcha =
|
|
50
67
|
type === 'char' ? svgCaptcha.create(options.opts) : svgCaptcha.createMathExpr(options.opts);
|
|
51
|
-
|
|
68
|
+
const token = snapshot ? previousToken! : `imageText:${uuidv4()}`;
|
|
69
|
+
const written = await cache.writeProof(
|
|
70
|
+
token,
|
|
71
|
+
{ token: captcha.text, revision: uuidv4(), ttlSecondary },
|
|
72
|
+
ttl,
|
|
73
|
+
snapshot,
|
|
74
|
+
);
|
|
75
|
+
if (!written.changed) return this.app.throw(403);
|
|
76
|
+
return {
|
|
77
|
+
token,
|
|
78
|
+
tokenPublic: captcha.text,
|
|
79
|
+
payload: svg64(captcha.data),
|
|
80
|
+
serverNow: written.serverNow,
|
|
81
|
+
expiresAt: written.expiresAt,
|
|
82
|
+
routingExpiresAt: written.expiresAt + ttlSecondary,
|
|
83
|
+
};
|
|
52
84
|
}
|
|
53
85
|
|
|
54
86
|
async verify(
|
|
55
|
-
token:
|
|
56
|
-
tokenInput:
|
|
87
|
+
token: string,
|
|
88
|
+
tokenInput: unknown,
|
|
57
89
|
_options: ICaptchaProviderOptionsImageText,
|
|
58
90
|
): Promise<boolean> {
|
|
59
|
-
|
|
91
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
92
|
+
const snapshot = await cache.snapshot<ICaptchaImageTextState>(token);
|
|
93
|
+
if (!snapshot || !token.startsWith('imageText:')) return false;
|
|
94
|
+
const data = snapshot.data;
|
|
95
|
+
if (data.token2) {
|
|
96
|
+
const consumed = await cache.compareAndSet(token, snapshot);
|
|
97
|
+
return consumed && data.token2 === tokenInput;
|
|
98
|
+
}
|
|
99
|
+
if (!data.token) return false;
|
|
100
|
+
const verified = this._matches(data.token, tokenInput);
|
|
101
|
+
const changed = await cache.compareAndSet(
|
|
102
|
+
token,
|
|
103
|
+
snapshot,
|
|
104
|
+
verified ? undefined : { ...data, token: undefined, revision: uuidv4() },
|
|
105
|
+
);
|
|
106
|
+
return verified && changed;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async verifyImmediate(
|
|
110
|
+
token: string,
|
|
111
|
+
tokenInput: unknown,
|
|
112
|
+
_options: ICaptchaProviderOptionsImageText,
|
|
113
|
+
): Promise<false | string> {
|
|
114
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
115
|
+
const snapshot = await cache.snapshot<ICaptchaImageTextState>(token);
|
|
116
|
+
if (!snapshot || !token.startsWith('imageText:') || !snapshot.data.token) return false;
|
|
117
|
+
const data = snapshot.data;
|
|
118
|
+
if (!this._matches(data.token!, tokenInput)) {
|
|
119
|
+
await cache.compareAndSet(token, snapshot, { ...data, token: undefined, revision: uuidv4() });
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const tokenSecondary = uuidv4();
|
|
123
|
+
const changed = await cache.compareAndSet(
|
|
124
|
+
token,
|
|
125
|
+
snapshot,
|
|
126
|
+
{ ...data, token: undefined, token2: tokenSecondary, revision: uuidv4() },
|
|
127
|
+
data.ttlSecondary,
|
|
128
|
+
);
|
|
129
|
+
return changed ? tokenSecondary : false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async updateToken(
|
|
133
|
+
token: string,
|
|
134
|
+
tokenInput: unknown,
|
|
135
|
+
options: ICaptchaProviderOptionsImageText,
|
|
136
|
+
): Promise<TypeCaptchaProviderImageTextData> {
|
|
137
|
+
const cache = this.$scope.captcha.cacheRedis.captcha;
|
|
138
|
+
const snapshot = await cache.snapshot<ICaptchaImageTextState>(token);
|
|
139
|
+
if (!snapshot || !token.startsWith('imageText:') || typeof tokenInput !== 'string') {
|
|
140
|
+
return this.app.throw(403);
|
|
141
|
+
}
|
|
142
|
+
const { ttl, ttlSecondary } = this._policy(options);
|
|
143
|
+
const written = await cache.writeProof(
|
|
144
|
+
token,
|
|
145
|
+
{ token: tokenInput, revision: uuidv4(), ttlSecondary },
|
|
146
|
+
ttl,
|
|
147
|
+
snapshot,
|
|
148
|
+
);
|
|
149
|
+
if (!written.changed) return this.app.throw(403);
|
|
150
|
+
return {
|
|
151
|
+
token,
|
|
152
|
+
tokenPublic: tokenInput,
|
|
153
|
+
payload: '',
|
|
154
|
+
serverNow: written.serverNow,
|
|
155
|
+
expiresAt: written.expiresAt,
|
|
156
|
+
routingExpiresAt: written.expiresAt + ttlSecondary,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private _matches(token: string, input: unknown) {
|
|
161
|
+
return typeof input === 'string' && !!input && input.toLowerCase() === token.toLowerCase();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private _policy(options: ICaptchaProviderOptionsImageText) {
|
|
165
|
+
const defaults = this.$scope.captcha.config.captchaProvider;
|
|
166
|
+
const ttl = options.ttl ?? defaults.ttl;
|
|
167
|
+
const ttlSecondary = options.ttlSecondary ?? defaults.ttlSecondary;
|
|
168
|
+
if (
|
|
169
|
+
!Number.isSafeInteger(ttl) ||
|
|
170
|
+
ttl <= 0 ||
|
|
171
|
+
!Number.isSafeInteger(ttlSecondary) ||
|
|
172
|
+
ttlSecondary <= 0 ||
|
|
173
|
+
!Number.isSafeInteger(Date.now() + ttl + ttlSecondary)
|
|
174
|
+
) {
|
|
175
|
+
throw new Error('invalid captcha ttl');
|
|
176
|
+
}
|
|
177
|
+
return { ttl, ttlSecondary };
|
|
60
178
|
}
|
|
61
179
|
|
|
62
180
|
private async _confirmFont(options: ICaptchaProviderOptionsImageText) {
|