@rnaga/wp-node
Version:
👉 **[View Full Documentation at rnaga.github.io/wp-node →](https://rnaga.github.io/wp-node/)**
551 lines (550 loc) • 22.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var UserUtil_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserUtil = void 0;
const zod_1 = require("zod");
const common_1 = require("../../common");
const config_1 = require("../../config");
const component_1 = require("../../decorators/component");
const transactions_1 = require("../../transactions");
const val = __importStar(require("../../validators"));
const components_1 = require("../components");
const current_1 = require("../current");
const logger_1 = require("../logger");
const options_1 = require("../options");
const user_1 = require("../user");
const vars_1 = require("../vars");
const blog_util_1 = require("./blog.util");
const query_util_1 = require("./query.util");
const roles_util_1 = require("./roles.util");
let UserUtil = UserUtil_1 = class UserUtil {
config;
logger;
components;
vars;
constructor(config, logger, components, vars) {
this.config = config;
this.logger = logger;
this.components = components;
this.vars = vars;
}
async get(userRef) {
return await this.components.asyncGet(user_1.User, [userRef]);
}
async toUsers(users) {
const arr = [];
for (const user of users) {
arr.push(await this.components.asyncGet(user_1.User, [user.ID, user]));
}
return arr;
}
// count_many_users_posts
async countPosts(userIds, postType = "post", options) {
const { publicOnly = false } = options ?? {};
const queryUtil = this.components.get(query_util_1.QueryUtil);
const counts = await queryUtil.posts((query) => {
query.where("post_type", postType).whereIn("post_author", userIds);
if (publicOnly) {
query.where("post_status", "publish");
}
query.countGroupby("posts", "post_author");
}, val.query.resultCountGroupBy("post_author"));
return counts;
}
// reset_password
async resetPassword(user, newPassword) {
if (!user.props) {
return;
}
const userId = user.props.ID;
const userTrx = this.components.get(transactions_1.UserTrx);
await userTrx.upsert({
ID: userId,
user_pass: (0, common_1.hashPassword)(newPassword),
});
const metaTrx = this.components.get(transactions_1.MetaTrx);
await metaTrx.upsert("user", userId, "default_password_nag", false);
}
// wp_is_password_reset_allowed_for_user
isPasswordResetAllowed(user) {
if (!this.config.isMultiSite()) {
return true;
}
// is_user_spammy
return 1 === user.props?.spam ? false : true;
}
// check_password_reset_key
async checkPasswordResetKey(resetKey, userRef) {
resetKey = resetKey.replace(/[^a-z0-9]/gi, "");
if (0 >= resetKey.length) {
this.logger.info(`Invalid resetKey: ${resetKey}`);
return false;
}
const user = await this.get(userRef);
if (!user.props ||
!user.props.user_activation_key ||
0 >= user.props.user_activation_key.length) {
this.logger.info(`Invalid user or activation key - ${user.props?.ID} ${user.props?.user_activation_key}`);
return false;
}
// Revoke activation key
const userTrx = this.components.get(transactions_1.UserTrx);
await userTrx.revokeActivationKey(user);
const activationKey = user.props.user_activation_key;
const [passRequestTime, storedHash] = activationKey.split(":");
if (!passRequestTime.match(/^[0-9]+$/) || 0 >= storedHash.length) {
this.logger.info(`Invalid passRequestTime or storedHash - ${passRequestTime} ${storedHash}`);
return false;
}
const expirationDuration = parseInt(passRequestTime) + 60 * 60; // 3600sec
if (Math.floor(Date.now() / 1000) > expirationDuration) {
this.logger.info(`Reset key expired: ${expirationDuration}`);
return false;
}
if (!(0, common_1.checkPassword)(resetKey, storedHash)) {
return false;
}
return user.props.ID;
}
// Handles sending a password retrieval email to a user.
// retrieve_password
async getPasswordResetKey(userLogin, options) {
const { registration = false } = options ?? {};
userLogin = common_1.formatting.unslash(userLogin);
const user = await this.get(userLogin);
if (!user.props) {
throw new Error(`There is no account with that username or email address.' - ${userLogin}`);
}
// Generate something random for a password reset key.
const userTrx = this.components.get(transactions_1.UserTrx);
const resetKey = await userTrx.resetActivationKey(user);
let siteName = "";
if (this.config.isMultiSite()) {
const current = this.components.get(current_1.Current);
if (current.site?.props.blog.domain) {
siteName = current.site?.props.blog.domain;
}
}
else {
const options = this.components.get(options_1.Options);
const blogName = await options.get("blogname");
if (blogName) {
siteName = common_1.formatting.specialcharsDecode(blogName);
}
}
// invoke action hook
const context = this.vars.CONTEXT;
context.hooks.action.do("core_reset_password", resetKey, user, siteName, registration, context);
return resetKey;
}
roleNamesToCapabilities(roleNames, roles) {
return (roleNames?.map((roleName) => roles[roleName].capabilities).flat() ?? []);
}
// Retrieves sites and blogs associated with a user.
async getSites(userRef) {
if (this.config.isMultiSite()) {
return await this.getMultiSites(userRef);
}
return await this.getSingleSite(userRef);
}
async getSingleSite(userRef) {
const rolesUtil = this.components.get(roles_util_1.RolesUtil);
const user = await this.get(userRef);
if (!user.props?.user_login) {
throw new Error("Invalid user");
}
const primaryBlog = (await this.getBlogs(userRef))[0];
const roles = await rolesUtil.get();
const capabilities = this.roleNamesToCapabilities(primaryBlog.rolenames, roles);
return {
user: user.props,
primary_blog: {
...primaryBlog,
capabilities,
},
is_multisite: false,
sites: undefined,
};
}
async getMultiSites(userRef) {
const options = this.components.get(options_1.Options);
const user = await this.get(userRef);
if (!user.props?.user_login) {
throw new Error("Invalid user");
}
const userLogin = user.props.user_login;
// Blogs belonged to a user
const blogsOfUser = await this.getBlogs(userRef);
const siteIds = new Set(blogsOfUser.map((blog) => blog.site_id));
// User's primary blog
const primaryBlogId = await this.getPrimaryBlogId(userRef);
let primaryBlog;
let primarySite;
const result = [];
for (const siteId of Array.from(siteIds)) {
const blogs = [];
for (const blogOfUser of blogsOfUser.filter((blog) => blog.site_id == siteId)) {
blogs.push({
...blogOfUser,
capabilities: this.roleNamesToCapabilities(blogOfUser.rolenames, blogOfUser.blog_roles),
});
}
primaryBlog = blogs.filter((blogs) => blogs.blog_id == primaryBlogId)[0];
const optionValues = await options.get(["site_name", "siteurl"], {
siteId,
});
const sitename = (optionValues.get("site_name") ?? "");
const siteurl = (optionValues.get("siteurl") ?? "");
if (primaryBlog) {
primarySite = {
sitename,
siteurl,
};
}
const rolesUtil = this.components.get(roles_util_1.RolesUtil);
const superAdmins = await rolesUtil.getSuperAdmins({ siteId });
result.push({
site_id: siteId,
sitename,
siteurl,
is_superadmin: superAdmins.includes(userLogin),
blogs,
});
}
if (!primaryBlog && result.length > 0) {
primaryBlog = result[0].blogs[0];
}
if (!primarySite) {
primarySite = result[0];
}
return {
user: user.props,
primary_blog: !primaryBlog
? undefined
: {
...primarySite,
blog_id: primaryBlog.blog_id,
site_id: primaryBlog.site_id,
blog_roles: primaryBlog.blog_roles,
blogname: primaryBlog.blogname ?? "",
rolenames: primaryBlog.rolenames ?? [],
capabilities: primaryBlog.capabilities,
},
is_multisite: true,
sites: result,
};
}
async getPrimaryBlogId(userRef) {
const user = await this.get(userRef);
if (!user.props?.user_login) {
throw new Error("Invalid user");
}
if (!this.config.isMultiSite()) {
return 1;
}
return await user.meta.get("primary_blog");
}
// get role names of user (across all blogs)
async getRoleNames(userRefOrUser) {
const user = typeof userRefOrUser == "string" || typeof userRefOrUser == "number"
? await this.get(userRefOrUser)
: userRefOrUser;
if (!user.props?.user_login) {
throw new Error("Invalid user");
}
const userLogin = user.props.user_login;
const metas = (await user.meta.props()) ?? {};
const rolesMap = new Map();
for (const [key, value] of Object.entries(metas)) {
if (!key.startsWith(this.config.config.tablePrefix) ||
!key.endsWith("_capabilities")) {
continue;
}
let blogId;
const roleNames = Object.entries(value)
.filter(([, value]) => value)
.map(([roleName]) => roleName)
.flat();
if (key === `${this.config.config.tablePrefix}capabilities`) {
blogId = 1;
}
else {
const blogIdString = key.replace(new RegExp(`^${this.config.config.tablePrefix}|_capabilities$`, "g"), "");
if (!blogIdString.match(/^[0-9]+$/)) {
continue;
}
blogId = parseInt(blogIdString);
}
rolesMap.set(blogId, roleNames);
}
// Super Admins
if (this.config.isMultiSite()) {
const queryUtil = this.components.get(query_util_1.QueryUtil);
const siteIds = ((await queryUtil.meta("site", (query) => {
query.whereLike("site_admins", `:"${userLogin}";`);
})) ?? []).map((site) => site.site_id);
if (siteIds.length > 0) {
const blogIds = ((await queryUtil.blogs((query) => {
query.whereIn("site_id", siteIds);
})) ?? []).map((blog) => blog.blog_id) ?? [];
for (const blogId of blogIds) {
rolesMap.set(blogId, [...(rolesMap.get(blogId) ?? []), "superadmin"]);
}
}
}
return rolesMap;
}
// get site ids of user
async getSiteIds(userRef) {
const blogIds = await this.getBlogIds(userRef);
const queryUtil = this.components.get(query_util_1.QueryUtil);
const blogs = (await queryUtil.blogs((query) => {
const { column } = query.alias;
query
.whereIn("blog_id", blogIds)
.select(["site_id"])
.builder.groupBy(column("blogs", "site_id"));
}, zod_1.z.array(zod_1.z.object({
site_id: zod_1.z.number(),
})))) ?? [];
return blogs.map((blog) => blog.site_id);
}
// get blog ids of user
async getBlogIds(userRef) {
if (!this.config.isMultiSite()) {
return [1];
}
const user = await this.get(userRef);
if (!user.props || !user.props.user_login) {
throw new Error("Invalid user");
}
const userId = user.props.ID;
const rolesMap = await this.getRoleNames(userId);
const blogIds = [...rolesMap.keys()];
return blogIds;
}
// get_blogs_of_user
async getBlogs(userRef, options) {
const { siteId, blogId: targetBlogId } = options ?? {};
const user = await this.get(userRef);
if (!user.props) {
throw new Error("Invalid user");
}
const userId = user.props.ID;
const rolesUtil = this.components.get(roles_util_1.RolesUtil);
const rolesMap = await this.getRoleNames(userId);
if (!this.config.isMultiSite()) {
const options = this.components.get(options_1.Options);
const vars = this.components.get(vars_1.Vars);
return [
{
blog_id: 1,
domain: "",
path: "",
site_id: 0,
siteurl: await options.get("siteurl"),
archived: 0,
spam: 0,
deleted: 0,
rolenames: rolesMap.get(1) ?? [],
blog_roles: vars.USER_ROLES,
blogname: await options.get("blogname"),
},
];
}
let blogIds = [...rolesMap.keys()];
// Filter if blogId is specified
if (targetBlogId) {
blogIds = blogIds.filter((blogId) => blogId === targetBlogId);
}
if (0 >= blogIds.length) {
return [];
}
const queryUtil = this.components.get(query_util_1.QueryUtil);
const blogs = (await queryUtil.blogs((query) => {
query.whereIn("blog_id", blogIds);
if (siteId) {
query.where("site_id", siteId);
}
})) ?? [];
const blogUtil = this.components.get(blog_util_1.BlogUtil);
const result = [];
for (const blogItem of blogs) {
if (0 >= blogItem.blog_id || 0 >= blogItem.site_id) {
this.logger.warn(`Invalid blog - blog_id: ${blogItem.blog_id} domain: ${blogItem.domain} path: ${blogItem.path}`);
continue;
}
const blog = blogUtil.toBlog(blogItem);
result.push({
...blogItem,
rolenames: rolesMap.get(blogItem.blog_id),
blog_roles: await rolesUtil.get(blogItem.blog_id),
siteurl: await blog.options("siteurl"),
blogname: await blog.options("blogname"),
});
}
return result;
}
async checkSuperAdminStatus(userRefOrUser, args) {
const user = typeof userRefOrUser === "string" || typeof userRefOrUser === "number"
? await this.get(userRefOrUser)
: userRefOrUser;
if (!user.props?.user_login) {
throw new Error("Invalid user");
}
const userLogin = user.props.user_login;
if (!this.config.isMultiSite()) {
const role = await user.role();
return [role.isAdmin(), [1]];
}
const { blogIds = [] } = args ?? {};
let { siteIds = [] } = args ?? {};
const queryUtil = this.components.get(query_util_1.QueryUtil);
if (0 >= blogIds.length && 0 >= siteIds.length) {
// Check superadmin across all sites
siteIds = ((await queryUtil.sites((query) => {
query;
})) ?? []).map((site) => siteIds.push(site.id));
}
else if (blogIds.length > 0) {
siteIds = ((await queryUtil.blogs((query) => {
query.whereIn("blog_id", blogIds);
})) ?? []).map((blog) => siteIds.push(blog.site_id));
}
if (0 >= siteIds.length) {
return [false, []];
}
const rolesUtil = this.components.get(roles_util_1.RolesUtil);
for (const siteId of siteIds) {
const superAdmins = await rolesUtil.getSuperAdmins({
siteId,
});
if (!Array.isArray(superAdmins) || !superAdmins.includes(userLogin)) {
return [false, []];
}
}
return [true, siteIds];
}
async hasCapabilities(userRefOrUser, targetCapabilities, options) {
const user = typeof userRefOrUser == "string" || typeof userRefOrUser == "number"
? await this.get(userRefOrUser)
: userRefOrUser;
if (!user.props?.user_login || !user.props.ID) {
throw new Error("Invalid user");
}
const { blogIds = [] } = options ?? {};
if (0 >= blogIds.length) {
const current = this.components.get(current_1.Current);
blogIds.push(current.blogId);
}
const rolesUtil = this.components.get(roles_util_1.RolesUtil);
const roleNamesMap = await this.getRoleNames(user);
for (const blogId of blogIds) {
if (!roleNamesMap.get(blogId)) {
return false;
}
const roleNames = roleNamesMap.get(blogId);
const roles = await rolesUtil.get(blogId);
let capabilities = [];
for (const roleName of roleNames) {
capabilities = [
...capabilities,
...(roles[roleName]?.capabilities ?? []),
];
}
if (targetCapabilities.filter((v) => capabilities.includes(v)).length !==
targetCapabilities.length) {
return false;
}
}
return true;
}
async getUniqueNicename(nicename, userLogin, maxSuffix = 10) {
const queryUtil = this.components.get(query_util_1.QueryUtil);
nicename = common_1.formatting.slug(nicename);
if (50 < nicename.length) {
throw new Error(`user_nicename is too long - ${nicename}`);
}
for (let suffix = 0; suffix < maxSuffix; suffix++) {
const newNicename = 0 >= suffix ? nicename : `${nicename}-${suffix + 1}`;
const users = await queryUtil.users((query) => {
query
.where("user_nicename", newNicename)
.builder.not.where("user_login", userLogin);
});
if (!users) {
return newNicename;
}
}
return `${nicename}-${Math.floor(Math.random() * (maxSuffix + 999990010 - maxSuffix + 1) + maxSuffix + 1)}`;
}
async getUniqueUserLogin(maxSuffix = 10) {
const queryUtil = this.components.get(query_util_1.QueryUtil);
const prefix = "user-";
// Generate a random 6 character string
const randomString = Math.random().toString(36).substring(2, 8);
let userLogin = `${prefix}${randomString}`;
const context = this.vars.CONTEXT;
userLogin = await context.hooks.filter.asyncApply("core_unigue_user_login", userLogin);
for (let suffix = 0; suffix < maxSuffix; suffix++) {
const users = await queryUtil.users((query) => {
query.where("user_login", userLogin);
});
if (!users) {
return userLogin;
}
userLogin = 0 >= suffix ? userLogin : `${userLogin}${suffix + 1}`;
}
return `${userLogin}-${Math.floor(Math.random() * (maxSuffix + 999990010 - maxSuffix + 1) + maxSuffix + 1)}`;
}
};
exports.UserUtil = UserUtil;
exports.UserUtil = UserUtil = UserUtil_1 = __decorate([
(0, component_1.component)(),
__metadata("design:paramtypes", [config_1.Config,
logger_1.Logger,
components_1.Components,
vars_1.Vars])
], UserUtil);