@nocobase/plugin-auth
Version:
User authentication management, including password, SMS, and support for Single Sign-On (SSO) protocols, with extensibility.
334 lines (332 loc) • 11.5 kB
JavaScript
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var plugin_exports = {};
__export(plugin_exports, {
PluginAuthServer: () => PluginAuthServer,
default: () => plugin_default
});
module.exports = __toCommonJS(plugin_exports);
var import_server = require("@nocobase/server");
var import_utils = require("@nocobase/utils");
var import_constants = require("../constants");
var import_preset = require("../preset");
var import_auth = __toESM(require("./actions/auth"));
var import_authenticators = __toESM(require("./actions/authenticators"));
var import_basic_auth = require("./basic-auth");
var import_authenticator = require("./model/authenticator");
var import_storer = require("./storer");
var import_token_blacklist = require("./token-blacklist");
var import_token_controller = require("./token-controller");
class PluginAuthServer extends import_server.Plugin {
cache;
afterAdd() {
this.app.on("afterLoad", async () => {
if (this.app.authManager.tokenController) {
return;
}
const cache = await this.app.cacheManager.createCache({
name: "auth-token-controller",
prefix: "auth-token-controller"
});
const tokenController = new import_token_controller.TokenController({ cache, app: this.app, logger: this.app.log });
this.app.authManager.setTokenControlService(tokenController);
const tokenPolicyRepo = this.app.db.getRepository(import_constants.tokenPolicyCollectionName);
try {
const res = await tokenPolicyRepo.findOne({ filterByTk: import_constants.tokenPolicyRecordKey });
if (res) {
this.app.authManager.tokenController.setConfig(res.config);
}
} catch (error) {
this.app.logger.warn("access control config not exist, use default value");
}
});
}
async beforeLoad() {
this.app.db.registerModels({ AuthModel: import_authenticator.AuthModel });
}
async load() {
this.cache = await this.app.cacheManager.createCache({
name: "auth",
prefix: "auth",
store: "memory"
});
const storer = new import_storer.Storer({
app: this.app,
db: this.db,
cache: this.cache,
authManager: this.app.authManager
});
this.app.authManager.setStorer(storer);
if (!this.app.authManager.jwt.blacklist) {
this.app.authManager.setTokenBlacklistService(new import_token_blacklist.TokenBlacklistService(this));
}
this.app.authManager.registerTypes(import_preset.presetAuthType, {
auth: import_basic_auth.BasicAuth,
title: (0, import_utils.tval)("Password", { ns: import_preset.namespace }),
getPublicOptions: (options) => {
var _a;
const usersCollection = this.db.getCollection("users");
let signupForm = ((_a = options == null ? void 0 : options.public) == null ? void 0 : _a.signupForm) || [];
signupForm = signupForm.filter((item) => item.show);
if (!(signupForm.length && signupForm.some(
(item) => ["username", "email"].includes(item.field) && item.show && item.required
))) {
signupForm.unshift({ field: "username", show: true, required: true });
}
signupForm = signupForm.filter((field) => field.show).map((item) => {
var _a2;
const field = usersCollection.getField(item.field);
return {
...item,
uiSchema: {
...(_a2 = field == null ? void 0 : field.options) == null ? void 0 : _a2.uiSchema,
required: item.required
}
};
});
return {
...options == null ? void 0 : options.public,
signupForm
};
}
});
Object.entries(import_auth.default).forEach(
([action, handler]) => {
var _a;
return (_a = this.app.resourceManager.getResource("auth")) == null ? void 0 : _a.addAction(action, handler);
}
);
Object.entries(import_authenticators.default).forEach(
([action, handler]) => this.app.resourceManager.registerActionHandler(`authenticators:${action}`, handler)
);
["signIn", "signUp"].forEach((action) => this.app.acl.allow("auth", action));
["check", "signOut", "changePassword"].forEach((action) => this.app.acl.allow("auth", action, "loggedIn"));
["lostPassword", "resetPassword", "checkResetToken"].forEach(
(action) => this.app.acl.allow("auth", action, "public")
);
this.app.acl.allow("authenticators", "publicList");
this.app.acl.registerSnippet({
name: `pm.${this.name}.authenticators`,
actions: ["authenticators:*"]
});
this.app.db.on("users.afterSave", async (user) => {
const cache = this.app.cache;
await cache.set(`auth:${user.id}`, user.toJSON());
});
this.app.db.on("users.afterDestroy", async (user) => {
const cache = this.app.cache;
await cache.del(`auth:${user.id}`);
});
this.app.on("cache:del:auth", async ({ userId }) => {
await this.cache.del(`auth:${userId}`);
});
this.app.on("ws:message:auth:token", async ({ clientId, payload }) => {
if (!payload || !payload.token) {
this.app.emit(`ws:removeTag`, {
clientId,
tagKey: "userId"
});
return;
}
const auth = await this.app.authManager.get(payload.authenticator || "basic", {
getBearerToken: () => payload.token,
app: this.app,
db: this.app.db,
cache: this.app.cache,
logger: this.app.logger,
log: this.app.log,
throw: (...args) => {
throw new Error(...args);
},
t: this.app.i18n.t
});
let user;
try {
user = (await auth.checkToken()).user;
} catch (error) {
if (!user) {
this.app.logger.error(error);
this.app.emit(`ws:removeTag`, {
clientId,
tagKey: "userId"
});
return;
}
}
this.app.emit(`ws:setTag`, {
clientId,
tagKey: "userId",
tagValue: user.id
});
this.app.emit(`ws:authorized`, {
clientId,
userId: user.id
});
});
this.app.auditManager.registerActions([
{
name: "auth:signIn",
getMetaData: async (ctx) => {
var _a;
let body = {};
if (ctx.status === 200) {
body = {
data: {
...ctx.body.data,
token: void 0
}
};
} else {
body = ctx.body;
}
return {
request: {
body: {
...(_a = ctx.request) == null ? void 0 : _a.body,
password: void 0
}
}
};
},
getUserInfo: async (ctx) => {
var _a, _b;
if (!((_b = (_a = ctx.body) == null ? void 0 : _a.data) == null ? void 0 : _b.user)) {
return null;
}
const userId = ctx.body.data.user.id;
const user = await ctx.db.getRepository("users").findOne({
filterByTk: userId
});
const roles = await (user == null ? void 0 : user.getRoles());
if (roles && roles.length === 1) {
return {
userId,
roleName: roles[0].name
};
}
return {
userId
};
}
},
{
name: "auth:signUp",
getMetaData: async (ctx) => {
var _a;
return {
request: {
body: {
...(_a = ctx.request) == null ? void 0 : _a.body,
password: void 0,
confirm_password: void 0
}
}
};
}
},
{
name: "auth:changePassword",
getMetaData: async (ctx) => {
return {
request: {
body: {}
},
response: {
body: {}
}
};
},
getSourceAndTarget: async (ctx) => {
return {
targetCollection: "users",
targetRecordUK: ctx.auth.user.id
};
}
},
"auth:signOut"
]);
this.app.acl.registerSnippet({
name: `pm.security.token-policy`,
actions: [`${import_constants.tokenPolicyCollectionName}:*`]
});
this.app.db.on(`${import_constants.tokenPolicyCollectionName}.afterSave`, async (model) => {
var _a;
(_a = this.app.authManager.tokenController) == null ? void 0 : _a.setConfig(model.config);
});
}
async install(options) {
const authRepository = this.db.getRepository("authenticators");
const exist = await authRepository.findOne({ filter: { name: import_preset.presetAuthenticator } });
if (!exist) {
await authRepository.create({
values: {
name: import_preset.presetAuthenticator,
authType: import_preset.presetAuthType,
description: "Sign in with username/email.",
enabled: true,
options: {
public: {
allowSignUp: true
}
}
}
});
}
const tokenPolicyRepo = this.app.db.getRepository(import_constants.tokenPolicyCollectionName);
const res = await tokenPolicyRepo.findOne({ filterByTk: import_constants.tokenPolicyRecordKey });
if (res) {
return;
}
const config = {
tokenExpirationTime: "1d",
sessionExpirationTime: "7d",
expiredTokenRenewLimit: "1d"
};
await tokenPolicyRepo.create({
values: {
key: import_constants.tokenPolicyRecordKey,
config
}
});
}
async remove() {
}
}
var plugin_default = PluginAuthServer;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PluginAuthServer
});