mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
* feat: password policy * feat: password validator * fix(inbox): i18n of channel title * feat: atomic counter * fix: bug * fix: bloom * chore: i18n * fix: counter * fix: build * fix: bug * fix: z-index * fix: export * test: add redis cache test * fix: test * fix: test * fix: test * fix: bug * fix: form reset * fix: locale * fix: version * fix: separate cache for sub apps * chore: update
230 lines
6.8 KiB
TypeScript
230 lines
6.8 KiB
TypeScript
/**
|
|
* 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.
|
|
*/
|
|
|
|
import { Cache } from '@nocobase/cache';
|
|
import { Model } from '@nocobase/database';
|
|
import { InstallOptions, Plugin } from '@nocobase/server';
|
|
import { namespace, presetAuthType, presetAuthenticator } from '../preset';
|
|
import authActions from './actions/auth';
|
|
import authenticatorsActions from './actions/authenticators';
|
|
import { BasicAuth } from './basic-auth';
|
|
import { AuthModel } from './model/authenticator';
|
|
import { Storer } from './storer';
|
|
import { TokenBlacklistService } from './token-blacklist';
|
|
import { tval } from '@nocobase/utils';
|
|
|
|
export class PluginAuthServer extends Plugin {
|
|
cache: Cache;
|
|
|
|
afterAdd() {}
|
|
|
|
async beforeLoad() {
|
|
this.app.db.registerModels({ AuthModel });
|
|
}
|
|
|
|
async load() {
|
|
this.cache = await this.app.cacheManager.createCache({
|
|
name: 'auth',
|
|
prefix: 'auth',
|
|
store: 'memory',
|
|
});
|
|
// Set up auth manager
|
|
const storer = new Storer({
|
|
db: this.db,
|
|
cache: this.cache,
|
|
});
|
|
this.app.authManager.setStorer(storer);
|
|
|
|
if (!this.app.authManager.jwt.blacklist) {
|
|
// If blacklist service is not set, should configure default blacklist service
|
|
this.app.authManager.setTokenBlacklistService(new TokenBlacklistService(this));
|
|
}
|
|
// register preset auth type
|
|
this.app.authManager.registerTypes(presetAuthType, {
|
|
auth: BasicAuth,
|
|
title: tval('Password', { ns: namespace }),
|
|
getPublicOptions: (options) => {
|
|
const usersCollection = this.db.getCollection('users');
|
|
let signupForm = options?.public?.signupForm || [];
|
|
signupForm = signupForm.filter((item: { show: boolean }) => item.show);
|
|
if (
|
|
!(
|
|
signupForm.length &&
|
|
signupForm.some(
|
|
(item: { field: string; show: boolean; required: boolean }) =>
|
|
['username', 'email'].includes(item.field) && item.show && item.required,
|
|
)
|
|
)
|
|
) {
|
|
// At least one of the username or email fields is required
|
|
signupForm.unshift({ field: 'username', show: true, required: true });
|
|
}
|
|
signupForm = signupForm
|
|
.filter((field: { show: boolean }) => field.show)
|
|
.map((item: { field: string; required: boolean }) => {
|
|
const field = usersCollection.getField(item.field);
|
|
return {
|
|
...item,
|
|
uiSchema: {
|
|
...field.options?.uiSchema,
|
|
required: item.required,
|
|
},
|
|
};
|
|
});
|
|
return {
|
|
...options?.public,
|
|
signupForm,
|
|
};
|
|
},
|
|
});
|
|
// Register actions
|
|
Object.entries(authActions).forEach(
|
|
([action, handler]) => this.app.resourceManager.getResource('auth')?.addAction(action, handler),
|
|
);
|
|
Object.entries(authenticatorsActions).forEach(([action, handler]) =>
|
|
this.app.resourceManager.registerActionHandler(`authenticators:${action}`, handler),
|
|
);
|
|
// Set up ACL
|
|
['check', 'signIn', 'signUp'].forEach((action) => this.app.acl.allow('auth', action));
|
|
['signOut', 'changePassword'].forEach((action) => this.app.acl.allow('auth', action, 'loggedIn'));
|
|
this.app.acl.allow('authenticators', 'publicList');
|
|
this.app.acl.registerSnippet({
|
|
name: `pm.${this.name}.authenticators`,
|
|
actions: ['authenticators:*'],
|
|
});
|
|
|
|
// Change cache when user changed
|
|
this.app.db.on('users.afterSave', async (user: Model) => {
|
|
const cache = this.app.cache as Cache;
|
|
await cache.set(`auth:${user.id}`, user.toJSON());
|
|
});
|
|
this.app.db.on('users.afterDestroy', async (user: Model) => {
|
|
const cache = this.app.cache as Cache;
|
|
await cache.del(`auth:${user.id}`);
|
|
});
|
|
this.app.on('cache:del:auth', async ({ userId }) => {
|
|
await this.cache.del(`auth:${userId}`);
|
|
});
|
|
|
|
this.app.auditManager.registerActions([
|
|
{
|
|
name: 'auth:signIn',
|
|
getMetaData: async (ctx: any) => {
|
|
let body = {};
|
|
if (ctx.status === 200) {
|
|
body = {
|
|
data: {
|
|
...ctx.body.data,
|
|
token: undefined,
|
|
},
|
|
};
|
|
} else {
|
|
body = ctx.body;
|
|
}
|
|
return {
|
|
request: {
|
|
body: {
|
|
...ctx.request?.body,
|
|
password: undefined,
|
|
},
|
|
},
|
|
};
|
|
},
|
|
getUserInfo: async (ctx: any) => {
|
|
if (!ctx.body?.data?.user) {
|
|
return null;
|
|
}
|
|
// 查询用户角色
|
|
const userId = ctx.body.data.user.id;
|
|
const user = await ctx.db.getRepository('users').findOne({
|
|
filterByTk: userId,
|
|
});
|
|
const roles = await user?.getRoles();
|
|
if (!roles) {
|
|
return {
|
|
userId,
|
|
};
|
|
} else {
|
|
if (roles.length === 1) {
|
|
return {
|
|
userId,
|
|
roleName: roles[0].name,
|
|
};
|
|
} else {
|
|
// 多角色的情况下暂时不返回角色名
|
|
return {
|
|
userId,
|
|
};
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: 'auth:signUp',
|
|
getMetaData: async (ctx: any) => {
|
|
return {
|
|
request: {
|
|
body: {
|
|
...ctx.request?.body,
|
|
password: undefined,
|
|
confirm_password: undefined,
|
|
},
|
|
},
|
|
};
|
|
},
|
|
},
|
|
{
|
|
name: 'auth:changePassword',
|
|
getMetaData: async (ctx: any) => {
|
|
return {
|
|
request: {
|
|
body: {},
|
|
},
|
|
response: {
|
|
body: {},
|
|
},
|
|
};
|
|
},
|
|
getSourceAndTarget: async (ctx: any) => {
|
|
return {
|
|
targetCollection: 'users',
|
|
targetRecordUK: ctx.auth.user.id,
|
|
};
|
|
},
|
|
},
|
|
'auth:signOut',
|
|
]);
|
|
}
|
|
|
|
async install(options?: InstallOptions) {
|
|
const repository = this.db.getRepository('authenticators');
|
|
const exist = await repository.findOne({ filter: { name: presetAuthenticator } });
|
|
if (exist) {
|
|
return;
|
|
}
|
|
|
|
await repository.create({
|
|
values: {
|
|
name: presetAuthenticator,
|
|
authType: presetAuthType,
|
|
description: 'Sign in with username/email.',
|
|
enabled: true,
|
|
options: {
|
|
public: {
|
|
allowSignUp: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
async remove() {}
|
|
}
|
|
|
|
export default PluginAuthServer;
|