2023-06-07 15:46:42 +00:00
|
|
|
import { AuthConfig, BaseAuth } from '@nocobase/auth';
|
|
|
|
import { PasswordField } from '@nocobase/database';
|
|
|
|
import crypto from 'crypto';
|
2023-06-30 03:20:35 +00:00
|
|
|
import { namespace } from '../preset';
|
2023-06-07 15:46:42 +00:00
|
|
|
|
|
|
|
export class BasicAuth extends BaseAuth {
|
|
|
|
constructor(config: AuthConfig) {
|
|
|
|
const userCollection = config.ctx.db.getCollection('users');
|
|
|
|
super({ ...config, userCollection });
|
|
|
|
}
|
|
|
|
|
|
|
|
async validate() {
|
|
|
|
const ctx = this.ctx;
|
2023-08-19 14:02:26 +00:00
|
|
|
const {
|
|
|
|
values: {
|
|
|
|
account, // Username or email
|
|
|
|
email, // Old parameter, compatible with old api
|
|
|
|
password,
|
|
|
|
},
|
|
|
|
} = ctx.action.params;
|
2023-06-07 15:46:42 +00:00
|
|
|
|
2023-08-19 14:02:26 +00:00
|
|
|
if (!account && !email) {
|
|
|
|
ctx.throw(400, ctx.t('Please enter your username or email', { ns: namespace }));
|
2023-06-07 15:46:42 +00:00
|
|
|
}
|
2023-08-23 10:38:57 +00:00
|
|
|
const filter = email
|
|
|
|
? { email }
|
|
|
|
: {
|
|
|
|
$or: [{ username: account }, { email: account }],
|
|
|
|
};
|
2023-06-30 03:20:35 +00:00
|
|
|
const user = await this.userRepository.findOne({
|
2023-08-23 10:38:57 +00:00
|
|
|
filter,
|
2023-06-07 15:46:42 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!user) {
|
2023-08-19 14:02:26 +00:00
|
|
|
ctx.throw(401, ctx.t('The username or email is incorrect, please re-enter', { ns: namespace }));
|
2023-06-07 15:46:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const field = this.userCollection.getField<PasswordField>('password');
|
2023-08-19 14:02:26 +00:00
|
|
|
const valid = await field.verify(password, user.password);
|
2023-06-07 15:46:42 +00:00
|
|
|
if (!valid) {
|
|
|
|
ctx.throw(401, ctx.t('The password is incorrect, please re-enter', { ns: namespace }));
|
|
|
|
}
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
async signUp() {
|
|
|
|
const ctx = this.ctx;
|
|
|
|
const options = this.authenticator.options?.public || {};
|
|
|
|
if (!options.allowSignUp) {
|
|
|
|
ctx.throw(403, ctx.t('Not allowed to sign up', { ns: namespace }));
|
|
|
|
}
|
|
|
|
const User = ctx.db.getRepository('users');
|
|
|
|
const { values } = ctx.action.params;
|
2023-08-19 14:02:26 +00:00
|
|
|
const { username } = values;
|
|
|
|
if (!/^[^@.<>"'/]{2,16}$/.test(username)) {
|
|
|
|
ctx.throw(400, ctx.t('Please enter a valid username', { ns: namespace }));
|
|
|
|
}
|
2023-06-07 15:46:42 +00:00
|
|
|
const user = await User.create({ values });
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
async lostPassword() {
|
|
|
|
const ctx = this.ctx;
|
|
|
|
const {
|
|
|
|
values: { email },
|
|
|
|
} = ctx.action.params;
|
|
|
|
if (!email) {
|
|
|
|
ctx.throw(400, ctx.t('Please fill in your email address', { ns: namespace }));
|
|
|
|
}
|
2023-06-30 03:20:35 +00:00
|
|
|
const user = await this.userRepository.findOne({
|
2023-06-07 15:46:42 +00:00
|
|
|
where: {
|
|
|
|
email,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (!user) {
|
|
|
|
ctx.throw(401, ctx.t('The email is incorrect, please re-enter', { ns: namespace }));
|
|
|
|
}
|
|
|
|
user.resetToken = crypto.randomBytes(20).toString('hex');
|
|
|
|
await user.save();
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
async resetPassword() {
|
|
|
|
const ctx = this.ctx;
|
|
|
|
const {
|
|
|
|
values: { email, password, resetToken },
|
|
|
|
} = ctx.action.params;
|
2023-06-30 03:20:35 +00:00
|
|
|
const user = await this.userRepository.findOne({
|
2023-06-07 15:46:42 +00:00
|
|
|
where: {
|
|
|
|
email,
|
|
|
|
resetToken,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (!user) {
|
|
|
|
ctx.throw(404);
|
|
|
|
}
|
|
|
|
user.token = null;
|
|
|
|
user.resetToken = null;
|
|
|
|
user.password = password;
|
|
|
|
await user.save();
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
async getUserByResetToken() {
|
|
|
|
const ctx = this.ctx;
|
|
|
|
const { token } = ctx.action.params;
|
2023-06-30 03:20:35 +00:00
|
|
|
const user = await this.userRepository.findOne({
|
2023-06-07 15:46:42 +00:00
|
|
|
where: {
|
|
|
|
resetToken: token,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (!user) {
|
|
|
|
ctx.throw(401);
|
|
|
|
}
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
async changePassword() {
|
|
|
|
const ctx = this.ctx;
|
|
|
|
const {
|
|
|
|
values: { oldPassword, newPassword },
|
|
|
|
} = ctx.action.params;
|
|
|
|
const currentUser = ctx.auth.user;
|
|
|
|
if (!currentUser) {
|
|
|
|
ctx.throw(401);
|
|
|
|
}
|
2023-09-16 05:55:29 +00:00
|
|
|
let key: string;
|
|
|
|
if (currentUser.username) {
|
|
|
|
key = 'username';
|
|
|
|
} else {
|
|
|
|
key = 'email';
|
|
|
|
}
|
2023-06-30 03:20:35 +00:00
|
|
|
const user = await this.userRepository.findOne({
|
2023-06-07 15:46:42 +00:00
|
|
|
where: {
|
2023-09-16 05:55:29 +00:00
|
|
|
[key]: currentUser[key],
|
2023-06-07 15:46:42 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
const pwd = this.userCollection.getField<PasswordField>('password');
|
|
|
|
const isValid = await pwd.verify(oldPassword, user.password);
|
|
|
|
if (!isValid) {
|
|
|
|
ctx.throw(401, ctx.t('The password is incorrect, please re-enter', { ns: namespace }));
|
|
|
|
}
|
|
|
|
user.password = newPassword;
|
|
|
|
await user.save();
|
|
|
|
return currentUser;
|
|
|
|
}
|
|
|
|
}
|