@tachybase/plugin-password-policy 1.0.6
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 +1 -0
- package/client.d.ts +2 -0
- package/client.js +1 -0
- package/dist/client/IPFilterForm.d.ts +1 -0
- package/dist/client/PasswordAttemptForm.d.ts +1 -0
- package/dist/client/PasswordStrengthSettingsForm.d.ts +2 -0
- package/dist/client/SignInFailsTable.d.ts +2 -0
- package/dist/client/UserLocksTable.d.ts +2 -0
- package/dist/client/collections/signInFails.d.ts +2 -0
- package/dist/client/collections/userLocks.d.ts +2 -0
- package/dist/client/hooks/usePasswordStrength.d.ts +11 -0
- package/dist/client/hooks/usePasswordValidator.d.ts +16 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +4 -0
- package/dist/client/locale.d.ts +6 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.js +44 -0
- package/dist/externalVersion.js +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -0
- package/dist/locale/en-US.json +107 -0
- package/dist/locale/zh-CN.json +107 -0
- package/dist/node_modules/geoip-lite/LICENSE +50 -0
- package/dist/node_modules/geoip-lite/data/city.checksum +1 -0
- package/dist/node_modules/geoip-lite/data/country.checksum +1 -0
- package/dist/node_modules/geoip-lite/data/geoip-city-names.dat +0 -0
- package/dist/node_modules/geoip-lite/data/geoip-city.dat +0 -0
- package/dist/node_modules/geoip-lite/data/geoip-city6.dat +0 -0
- package/dist/node_modules/geoip-lite/data/geoip-country.dat +0 -0
- package/dist/node_modules/geoip-lite/data/geoip-country6.dat +0 -0
- package/dist/node_modules/geoip-lite/lib/fsWatcher.js +83 -0
- package/dist/node_modules/geoip-lite/lib/geoip.js +1 -0
- package/dist/node_modules/geoip-lite/lib/utils.js +98 -0
- package/dist/node_modules/geoip-lite/node_modules/.bin/rimraf +17 -0
- package/dist/node_modules/geoip-lite/package.json +1 -0
- package/dist/node_modules/geoip-lite/scripts/updatedb.js +685 -0
- package/dist/node_modules/geoip-lite/test/geo-lookup.js +56 -0
- package/dist/node_modules/geoip-lite/test/memory_usage.js +3 -0
- package/dist/node_modules/geoip-lite/test/tests.js +197 -0
- package/dist/server/actions/IpFilterController.d.ts +7 -0
- package/dist/server/actions/IpFilterController.js +124 -0
- package/dist/server/actions/PasswordAttemptController.d.ts +7 -0
- package/dist/server/actions/PasswordAttemptController.js +123 -0
- package/dist/server/actions/PasswordStrengthController.d.ts +7 -0
- package/dist/server/actions/PasswordStrengthController.js +123 -0
- package/dist/server/actions/SignInFailsController.d.ts +5 -0
- package/dist/server/actions/SignInFailsController.js +156 -0
- package/dist/server/actions/UserLocksController.d.ts +4 -0
- package/dist/server/actions/UserLocksController.js +102 -0
- package/dist/server/collections/ipFilter.d.ts +2 -0
- package/dist/server/collections/ipFilter.js +51 -0
- package/dist/server/collections/passwordAttempt.d.ts +2 -0
- package/dist/server/collections/passwordAttempt.js +55 -0
- package/dist/server/collections/passwordHistory.d.ts +2 -0
- package/dist/server/collections/passwordHistory.js +46 -0
- package/dist/server/collections/passwordStrengthConfig.d.ts +2 -0
- package/dist/server/collections/passwordStrengthConfig.js +59 -0
- package/dist/server/collections/signInFail.d.ts +2 -0
- package/dist/server/collections/signInFail.js +56 -0
- package/dist/server/collections/userLocks.d.ts +2 -0
- package/dist/server/collections/userLocks.js +51 -0
- package/dist/server/collections/users.d.ts +2 -0
- package/dist/server/collections/users.js +42 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +33 -0
- package/dist/server/plugin.d.ts +5 -0
- package/dist/server/plugin.js +129 -0
- package/dist/server/services/IPFilterService.d.ts +49 -0
- package/dist/server/services/IPFilterService.js +270 -0
- package/dist/server/services/PasswordAttemptService.d.ts +75 -0
- package/dist/server/services/PasswordAttemptService.js +595 -0
- package/dist/server/services/PasswordStrengthService.d.ts +28 -0
- package/dist/server/services/PasswordStrengthService.js +313 -0
- package/dist/types/geoip-lite.d.js +0 -0
- package/package.json +25 -0
- package/server.d.ts +2 -0
- package/server.js +1 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var server_exports = {};
|
|
29
|
+
__export(server_exports, {
|
|
30
|
+
default: () => import_plugin.default
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(server_exports);
|
|
33
|
+
var import_plugin = __toESM(require("./plugin"));
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
7
|
+
var __typeError = (msg) => {
|
|
8
|
+
throw TypeError(msg);
|
|
9
|
+
};
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
var __decoratorStart = (base) => [, , , __create((base == null ? void 0 : base[__knownSymbol("metadata")]) ?? null)];
|
|
26
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
27
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
28
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
29
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
30
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
31
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
32
|
+
return value;
|
|
33
|
+
};
|
|
34
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
35
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
36
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
37
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
38
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
39
|
+
return __privateGet(this, extra);
|
|
40
|
+
}, set [name](x) {
|
|
41
|
+
return __privateSet(this, extra, x);
|
|
42
|
+
} }, name));
|
|
43
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
44
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
45
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
46
|
+
if (k) {
|
|
47
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
48
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
49
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
50
|
+
}
|
|
51
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
52
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
53
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
54
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
55
|
+
}
|
|
56
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
57
|
+
};
|
|
58
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
59
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
60
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
61
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
62
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
63
|
+
var plugin_exports = {};
|
|
64
|
+
__export(plugin_exports, {
|
|
65
|
+
PluginPasswordPolicyServer: () => PluginPasswordPolicyServer,
|
|
66
|
+
default: () => plugin_default
|
|
67
|
+
});
|
|
68
|
+
module.exports = __toCommonJS(plugin_exports);
|
|
69
|
+
var import_server = require("@tachybase/server");
|
|
70
|
+
var import_IpFilterController = require("./actions/IpFilterController");
|
|
71
|
+
var import_PasswordAttemptController = require("./actions/PasswordAttemptController");
|
|
72
|
+
var import_PasswordStrengthController = require("./actions/PasswordStrengthController");
|
|
73
|
+
var import_SignInFailsController = require("./actions/SignInFailsController");
|
|
74
|
+
var import_UserLocksController = require("./actions/UserLocksController");
|
|
75
|
+
var import_IPFilterService = require("./services/IPFilterService");
|
|
76
|
+
var import_PasswordAttemptService = require("./services/PasswordAttemptService");
|
|
77
|
+
var import_PasswordStrengthService = require("./services/PasswordStrengthService");
|
|
78
|
+
var _PluginPasswordPolicyServer_decorators, _init, _a;
|
|
79
|
+
_PluginPasswordPolicyServer_decorators = [(0, import_server.InjectedPlugin)({
|
|
80
|
+
Controllers: [
|
|
81
|
+
import_PasswordAttemptController.PasswordAttemptController,
|
|
82
|
+
import_UserLocksController.UserLocksController,
|
|
83
|
+
import_IpFilterController.IpFilterController,
|
|
84
|
+
import_PasswordStrengthController.PasswordStrengthController,
|
|
85
|
+
import_SignInFailsController.SignInFailsController
|
|
86
|
+
],
|
|
87
|
+
Services: [import_PasswordAttemptService.PasswordAttemptService, import_IPFilterService.IPFilterService, import_PasswordStrengthService.PasswordStrengthService]
|
|
88
|
+
})];
|
|
89
|
+
class PluginPasswordPolicyServer extends (_a = import_server.Plugin) {
|
|
90
|
+
async load() {
|
|
91
|
+
this.app.acl.registerSnippet({
|
|
92
|
+
name: `pm.security.password-attempt`,
|
|
93
|
+
actions: ["passwordAttempt:*"]
|
|
94
|
+
});
|
|
95
|
+
this.app.acl.registerSnippet({
|
|
96
|
+
name: `pm.security.user-lock`,
|
|
97
|
+
actions: ["userLocks:*"]
|
|
98
|
+
});
|
|
99
|
+
this.app.acl.registerSnippet({
|
|
100
|
+
name: `pm.security.ip-filter`,
|
|
101
|
+
actions: ["ipFilter:*"]
|
|
102
|
+
});
|
|
103
|
+
this.app.acl.registerSnippet({
|
|
104
|
+
name: `pm.security.password-strength`,
|
|
105
|
+
actions: ["passwordStrengthConfig:*"]
|
|
106
|
+
});
|
|
107
|
+
this.app.acl.registerSnippet({
|
|
108
|
+
name: `pm.security.sign-in-fails`,
|
|
109
|
+
actions: ["signInFails:*"]
|
|
110
|
+
});
|
|
111
|
+
this.app.acl.addFixedParams("userLocks", "list", () => {
|
|
112
|
+
return {
|
|
113
|
+
filter: {
|
|
114
|
+
expireAt: {
|
|
115
|
+
$gt: /* @__PURE__ */ new Date()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
_init = __decoratorStart(_a);
|
|
123
|
+
PluginPasswordPolicyServer = __decorateElement(_init, 0, "PluginPasswordPolicyServer", _PluginPasswordPolicyServer_decorators, PluginPasswordPolicyServer);
|
|
124
|
+
__runInitializers(_init, 1, PluginPasswordPolicyServer);
|
|
125
|
+
var plugin_default = PluginPasswordPolicyServer;
|
|
126
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
127
|
+
0 && (module.exports = {
|
|
128
|
+
PluginPasswordPolicyServer
|
|
129
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Database from '@tachybase/database';
|
|
2
|
+
import { Application } from '@tachybase/server';
|
|
3
|
+
export declare class IPFilterService {
|
|
4
|
+
db: Database;
|
|
5
|
+
app: Application;
|
|
6
|
+
private logger;
|
|
7
|
+
private readonly CACHE_KEY;
|
|
8
|
+
private readonly CACHE_TTL;
|
|
9
|
+
private config;
|
|
10
|
+
load(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* 初始化IP过滤器配置
|
|
13
|
+
*/
|
|
14
|
+
private initIPFilterConfig;
|
|
15
|
+
private loadConfigFromDBAtStart;
|
|
16
|
+
/**
|
|
17
|
+
* 从数据库加载IP过滤器配置
|
|
18
|
+
*/
|
|
19
|
+
private loadConfigFromDB;
|
|
20
|
+
/**
|
|
21
|
+
* 创建默认配置
|
|
22
|
+
*/
|
|
23
|
+
private createDefaultConfig;
|
|
24
|
+
/**
|
|
25
|
+
* 解析IP列表文本为数组
|
|
26
|
+
*/
|
|
27
|
+
private parseIPList;
|
|
28
|
+
/**
|
|
29
|
+
* 设置监听ipFilter表变动的事件
|
|
30
|
+
*/
|
|
31
|
+
private setupIPFilterListener;
|
|
32
|
+
/**
|
|
33
|
+
* 添加IP过滤中间件
|
|
34
|
+
*/
|
|
35
|
+
private addMiddleWare;
|
|
36
|
+
/**
|
|
37
|
+
* 检查IP是否被允许访问
|
|
38
|
+
* @param ip 客户端IP地址
|
|
39
|
+
* @returns 如果IP被允许访问,返回true;否则返回false
|
|
40
|
+
*/
|
|
41
|
+
isIPAllowed(ip: string): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* 检查IP是否在列表中
|
|
44
|
+
* @param ip 解析后的IP地址
|
|
45
|
+
* @param list IP地址列表
|
|
46
|
+
* @returns 如果IP在列表中,返回true;否则返回false
|
|
47
|
+
*/
|
|
48
|
+
private isInList;
|
|
49
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
8
|
+
var __typeError = (msg) => {
|
|
9
|
+
throw TypeError(msg);
|
|
10
|
+
};
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
34
|
+
var __decoratorStart = (base) => [, , , __create((base == null ? void 0 : base[__knownSymbol("metadata")]) ?? null)];
|
|
35
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
36
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
37
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
38
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
39
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
40
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
41
|
+
return value;
|
|
42
|
+
};
|
|
43
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
44
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
45
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
46
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
47
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
48
|
+
return __privateGet(this, extra);
|
|
49
|
+
}, set [name](x) {
|
|
50
|
+
return __privateSet(this, extra, x);
|
|
51
|
+
} }, name));
|
|
52
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
53
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
54
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
55
|
+
if (k) {
|
|
56
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
57
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
58
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
59
|
+
}
|
|
60
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
61
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
62
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
63
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
64
|
+
}
|
|
65
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
66
|
+
};
|
|
67
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
68
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
69
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
70
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
71
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
72
|
+
var IPFilterService_exports = {};
|
|
73
|
+
__export(IPFilterService_exports, {
|
|
74
|
+
IPFilterService: () => IPFilterService
|
|
75
|
+
});
|
|
76
|
+
module.exports = __toCommonJS(IPFilterService_exports);
|
|
77
|
+
var import_utils = require("@tachybase/utils");
|
|
78
|
+
var ipaddr = __toESM(require("ipaddr.js"));
|
|
79
|
+
var import_constants = require("../../constants");
|
|
80
|
+
var _logger_dec, _app_dec, _db_dec, _IPFilterService_decorators, _init;
|
|
81
|
+
_IPFilterService_decorators = [(0, import_utils.Service)()], _db_dec = [(0, import_utils.Db)()], _app_dec = [(0, import_utils.App)()], _logger_dec = [(0, import_utils.InjectLog)()];
|
|
82
|
+
class IPFilterService {
|
|
83
|
+
constructor() {
|
|
84
|
+
this.db = __runInitializers(_init, 8, this), __runInitializers(_init, 11, this);
|
|
85
|
+
this.app = __runInitializers(_init, 12, this), __runInitializers(_init, 15, this);
|
|
86
|
+
this.logger = __runInitializers(_init, 16, this), __runInitializers(_init, 19, this);
|
|
87
|
+
// 缓存前缀
|
|
88
|
+
this.CACHE_KEY = "ipFilter:config";
|
|
89
|
+
// 缓存过期时间(毫秒)
|
|
90
|
+
this.CACHE_TTL = 5 * 60 * 1e3;
|
|
91
|
+
// 5分钟
|
|
92
|
+
// 内存缓存
|
|
93
|
+
this.config = {
|
|
94
|
+
allowList: [],
|
|
95
|
+
blockList: [],
|
|
96
|
+
allowFirst: true,
|
|
97
|
+
lastUpdated: /* @__PURE__ */ new Date(0)
|
|
98
|
+
// 初始设置为过期
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
async load() {
|
|
102
|
+
this.addMiddleWare();
|
|
103
|
+
this.setupIPFilterListener();
|
|
104
|
+
this.app.on("afterStart", async () => {
|
|
105
|
+
await this.initIPFilterConfig();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 初始化IP过滤器配置
|
|
110
|
+
*/
|
|
111
|
+
async initIPFilterConfig() {
|
|
112
|
+
try {
|
|
113
|
+
const cachedConfig = await this.app.cache.get(this.CACHE_KEY);
|
|
114
|
+
if (cachedConfig) {
|
|
115
|
+
this.config = cachedConfig;
|
|
116
|
+
this.logger.info("Loaded IP filter config from cache");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
await this.loadConfigFromDBAtStart();
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.logger.error("Failed to initialize IP filter config:", error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async loadConfigFromDBAtStart() {
|
|
125
|
+
try {
|
|
126
|
+
const ipFilterRepo = this.db.getRepository("ipFilter");
|
|
127
|
+
const ipFilter = await ipFilterRepo.findOne();
|
|
128
|
+
if (ipFilter) {
|
|
129
|
+
await this.loadConfigFromDB(ipFilter);
|
|
130
|
+
} else {
|
|
131
|
+
await this.createDefaultConfig();
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.logger.error("Failed to init ip filter:", error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 从数据库加载IP过滤器配置
|
|
139
|
+
*/
|
|
140
|
+
async loadConfigFromDB(ipFilter) {
|
|
141
|
+
try {
|
|
142
|
+
const allowList = this.parseIPList(ipFilter.get("allowList") || "");
|
|
143
|
+
const blockList = this.parseIPList(ipFilter.get("blockList") || "");
|
|
144
|
+
const allowFirst = ipFilter.get("allowFirst") !== false;
|
|
145
|
+
this.config = {
|
|
146
|
+
allowList,
|
|
147
|
+
blockList,
|
|
148
|
+
allowFirst,
|
|
149
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
150
|
+
};
|
|
151
|
+
await this.app.cache.set(this.CACHE_KEY, this.config, this.CACHE_TTL);
|
|
152
|
+
this.logger.info(`Loaded IP filter config: ${allowList.length} allowed, ${blockList.length} blocked`);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.logger.error("Failed to load IP filter config from database:", error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 创建默认配置
|
|
159
|
+
*/
|
|
160
|
+
async createDefaultConfig() {
|
|
161
|
+
try {
|
|
162
|
+
const ipFilterRepo = this.db.getRepository("ipFilter");
|
|
163
|
+
await ipFilterRepo.create({
|
|
164
|
+
values: {
|
|
165
|
+
allowList: "",
|
|
166
|
+
blockList: "",
|
|
167
|
+
allowFirst: true
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
this.config = {
|
|
171
|
+
allowList: [],
|
|
172
|
+
blockList: [],
|
|
173
|
+
allowFirst: true,
|
|
174
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
175
|
+
};
|
|
176
|
+
await this.app.cache.set(this.CACHE_KEY, this.config, this.CACHE_TTL);
|
|
177
|
+
this.logger.info("Created default IP filter config");
|
|
178
|
+
} catch (error) {
|
|
179
|
+
this.logger.error("Failed to create default IP filter config:", error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 解析IP列表文本为数组
|
|
184
|
+
*/
|
|
185
|
+
parseIPList(ipListText) {
|
|
186
|
+
if (!ipListText) return [];
|
|
187
|
+
return ipListText.split("\n").map((ip) => ip.trim()).filter((ip) => ip && !ip.startsWith("#"));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 设置监听ipFilter表变动的事件
|
|
191
|
+
*/
|
|
192
|
+
setupIPFilterListener() {
|
|
193
|
+
this.app.db.on("ipFilter.afterUpdate", async (model) => {
|
|
194
|
+
await this.loadConfigFromDB(model);
|
|
195
|
+
this.logger.info("IP filter config updated");
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* 添加IP过滤中间件
|
|
200
|
+
*/
|
|
201
|
+
addMiddleWare() {
|
|
202
|
+
this.app.resourcer.use(
|
|
203
|
+
async (ctx, next) => {
|
|
204
|
+
const clientIP = ctx.state.clientIp;
|
|
205
|
+
const isAllowed = await this.isIPAllowed(clientIP);
|
|
206
|
+
if (!isAllowed) {
|
|
207
|
+
ctx.throw(403, ctx.t("Your IP address is not allowed to access this resource", { ns: import_constants.NAMESPACE }));
|
|
208
|
+
}
|
|
209
|
+
await next();
|
|
210
|
+
},
|
|
211
|
+
{ tag: "ipFilterControl", before: "lockUserByPasswordPolicy" }
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 检查IP是否被允许访问
|
|
216
|
+
* @param ip 客户端IP地址
|
|
217
|
+
* @returns 如果IP被允许访问,返回true;否则返回false
|
|
218
|
+
*/
|
|
219
|
+
async isIPAllowed(ip) {
|
|
220
|
+
try {
|
|
221
|
+
const parsedIP = ipaddr.parse(ip);
|
|
222
|
+
const inAllowList = this.isInList(parsedIP, this.config.allowList);
|
|
223
|
+
const inBlockList = this.isInList(parsedIP, this.config.blockList);
|
|
224
|
+
if (this.config.allowFirst) {
|
|
225
|
+
return inAllowList || !inBlockList;
|
|
226
|
+
} else {
|
|
227
|
+
return !inBlockList && (inAllowList || this.config.allowList.length === 0);
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
this.logger.error(`Error checking IP ${ip}:`, error);
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* 检查IP是否在列表中
|
|
236
|
+
* @param ip 解析后的IP地址
|
|
237
|
+
* @param list IP地址列表
|
|
238
|
+
* @returns 如果IP在列表中,返回true;否则返回false
|
|
239
|
+
*/
|
|
240
|
+
isInList(ip, list) {
|
|
241
|
+
for (const entry of list) {
|
|
242
|
+
try {
|
|
243
|
+
if (entry.includes("/")) {
|
|
244
|
+
const cidr = ipaddr.parseCIDR(entry);
|
|
245
|
+
if (ip.kind() === cidr[0].kind() && ip.match(cidr)) {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
const listIP = ipaddr.parse(entry);
|
|
250
|
+
if (ip.kind() === listIP.kind() && ip.toString() === listIP.toString()) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
this.logger.error(`Invalid IP or CIDR in list: ${entry}`, error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
_init = __decoratorStart(null);
|
|
262
|
+
__decorateElement(_init, 5, "db", _db_dec, IPFilterService);
|
|
263
|
+
__decorateElement(_init, 5, "app", _app_dec, IPFilterService);
|
|
264
|
+
__decorateElement(_init, 5, "logger", _logger_dec, IPFilterService);
|
|
265
|
+
IPFilterService = __decorateElement(_init, 0, "IPFilterService", _IPFilterService_decorators, IPFilterService);
|
|
266
|
+
__runInitializers(_init, 1, IPFilterService);
|
|
267
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
268
|
+
0 && (module.exports = {
|
|
269
|
+
IPFilterService
|
|
270
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Database from '@tachybase/database';
|
|
2
|
+
import { Application } from '@tachybase/server';
|
|
3
|
+
export declare class PasswordAttemptService {
|
|
4
|
+
db: Database;
|
|
5
|
+
app: Application;
|
|
6
|
+
private logger;
|
|
7
|
+
private config;
|
|
8
|
+
private failureRecords;
|
|
9
|
+
private readonly CACHE_PREFIX;
|
|
10
|
+
private readonly CACHE_TTL;
|
|
11
|
+
load(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* 获取用户锁定信息的缓存键
|
|
14
|
+
* @param userId 用户ID
|
|
15
|
+
* @returns 缓存键
|
|
16
|
+
*/
|
|
17
|
+
private getUserLockCacheKey;
|
|
18
|
+
/**
|
|
19
|
+
* 初始化被锁定用户的缓存
|
|
20
|
+
*/
|
|
21
|
+
private initLockedUsersCache;
|
|
22
|
+
/**
|
|
23
|
+
* 设置监听userLocks表变动的事件
|
|
24
|
+
*/
|
|
25
|
+
private setupLockedUsersListener;
|
|
26
|
+
/**
|
|
27
|
+
* 清空用户的登录失败记录
|
|
28
|
+
* @param userId 用户ID
|
|
29
|
+
*/
|
|
30
|
+
private clearUserFailRecords;
|
|
31
|
+
/**
|
|
32
|
+
* 检查用户是否被锁定
|
|
33
|
+
* @param userId 用户ID
|
|
34
|
+
* @returns 如果用户被锁定,返回true;否则返回false
|
|
35
|
+
*/
|
|
36
|
+
private isUserLocked;
|
|
37
|
+
/**
|
|
38
|
+
* 从数据库检查并缓存用户锁定状态
|
|
39
|
+
* @param userId 用户ID
|
|
40
|
+
* @returns 如果用户被锁定,返回true;否则返回false
|
|
41
|
+
*/
|
|
42
|
+
private checkAndCacheLockedUser;
|
|
43
|
+
/**
|
|
44
|
+
* 在后台刷新用户锁定缓存
|
|
45
|
+
* @param userId 用户ID
|
|
46
|
+
*/
|
|
47
|
+
private refreshLockedUserCache;
|
|
48
|
+
refreshConfig(config: any): Promise<void>;
|
|
49
|
+
addMiddleWare(): void;
|
|
50
|
+
/**
|
|
51
|
+
* 从数据库加载最近的失败记录到内存
|
|
52
|
+
*/
|
|
53
|
+
private loadRecentRecords;
|
|
54
|
+
/**
|
|
55
|
+
* 获取IP地址的地理位置信息
|
|
56
|
+
* @param ip IP地址
|
|
57
|
+
* @returns 地理位置信息
|
|
58
|
+
*/
|
|
59
|
+
private getGeoLocation;
|
|
60
|
+
/**
|
|
61
|
+
* 记录登录失败
|
|
62
|
+
* @param username 用户名
|
|
63
|
+
*/
|
|
64
|
+
recordFailedAttempt(user: any, ip: string): Promise<void>;
|
|
65
|
+
private recordFailedAttemptToDb;
|
|
66
|
+
/**
|
|
67
|
+
* 获取最近的失败次数(从内存缓存中获取)
|
|
68
|
+
*/
|
|
69
|
+
private getRecentFailureCount;
|
|
70
|
+
/**
|
|
71
|
+
* 重置用户的失败记录
|
|
72
|
+
* @param username 用户名
|
|
73
|
+
*/
|
|
74
|
+
resetFailedAttempts(userId: number): Promise<void>;
|
|
75
|
+
}
|