UNPKG

@rnaga/wp-node

Version:

👉 **[View Full Documentation at rnaga.github.io/wp-node →](https://rnaga.github.io/wp-node/)**

627 lines (626 loc) • 27.6 kB
"use strict"; 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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UserCrud = void 0; const zod_1 = require("zod"); const common_1 = require("../common"); const diff_1 = require("../common/diff"); const config_1 = require("../config"); const components_1 = require("../core/components"); const current_1 = require("../core/current"); const roles_1 = require("../core/roles"); const query_util_1 = require("../core/utils/query.util"); const roles_util_1 = require("../core/utils/roles.util"); const user_util_1 = require("../core/utils/user.util"); const component_1 = require("../decorators/component"); const transactions_1 = require("../transactions"); const val = __importStar(require("../validators")); const crud_1 = require("./crud"); const error_1 = require("./error"); let UserCrud = class UserCrud extends crud_1.Crud { config; constructor(components, config) { super(components); this.config = config; } async getAsUpsert(userId) { const user = await this.get(userId, { context: "edit" }); const userUpsertMeta = val.trx.userUpsertMeta.parse(user.data.metas); const userUpsert = val.trx.userUpsert .merge(zod_1.z.object({ user_login: val.database.wpUsers.shape.user_login, user_pass: zod_1.z.string().transform(() => ""), })) .parse({ ...user.data, role: user.data.roles, meta_input: user.data.metas, }); return this.returnValue({ ...userUpsert, ...userUpsertMeta, }); } // check_role_update async checkRoleUpdatePermission(userId, role) { const { user: currentUser } = await this.getUser(); if (!currentUser.props?.ID) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Invalid User"); } const currentUserId = currentUser.props.ID; const currentRole = await currentUser.role(); // Changing role if (role.length > 0 && (0, diff_1.diffStringArray)(Array.from(currentRole.names), role).length > 0 && !(await currentUser.can("promote_user", userId))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit roles of this user"); } // Removing role if (0 >= role.length && !(await currentUser.can("remove_users"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit roles of this user"); } /* * Don't let anyone with 'edit_users' (admins) edit their own role to something without it. * Multisite super admins can freely edit their blog roles -- they possess all caps. */ if ((!this.config.isMultiSite() || !(await currentUser.can("manage_sites"))) && currentUserId === userId && !(await currentUser.can("edit_users"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to give users that role"); } } async get(userId = 0, options) { const userUtil = this.components.get(user_util_1.UserUtil); const { user: currentUser } = await this.getUser(); let targetUser = currentUser; if (userId > 0 && currentUser.props?.ID !== userId) { if (!(await currentUser.can("list_users"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to list users"); } targetUser = await userUtil.get(userId); } if (!targetUser.props) { throw new error_1.CrudError(error_1.StatusMessage.NOT_FOUND, "User not found"); } let data = {}; const targetRole = await targetUser.role(); if (options?.context == "edit" && (currentUser.props?.ID === userId || (await currentUser.can("edit_users")))) { data = { ...targetUser.props, metas: await targetUser.meta.props(), role: Array.from(targetRole.names), capabilities: targetRole.capabilities, }; } else { data = { ID: targetUser.props.ID, display_name: targetUser.props.display_name, user_nicename: targetUser.props.user_nicename, user_url: targetUser.props.user_url, }; } return this.returnValue(data); } async getBlogs(userId) { const { user: currentUser, userProps: currentUserProps } = await this.getUser(); if (!currentUserProps || !(await currentUser.can("manage_network_users"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Not permitted"); } const userUtil = this.components.get(user_util_1.UserUtil); const blogs = await userUtil.getBlogs(userId); if (0 >= blogs.length) { return this.returnValue([]); } const blogIds = blogs.map((blog) => blog.blog_id); if (!(await currentUser.can("list_blog_users", blogIds))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Not permitted"); } return this.returnValue(blogs.map((blog) => ({ site_id: blog.site_id, blog_id: blog.blog_id, blogname: blog.blogname, rolenames: blog.rolenames, }))); } async getAvailableSites() { const { user: currentUser, userProps: currentUserProps } = await this.getUser(); if (!currentUserProps) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Not permitted"); } const userUtil = this.components.get(user_util_1.UserUtil); const currentUserId = currentUserProps.ID; const currentSites = await userUtil.getSites(currentUserId); if (!currentSites.primary_blog) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Not permitted (no primary blog)"); } const getCapabilities = async (user, blogId, siteId, capabilities) => { const set = new Set(capabilities); if (await user.can("edit_user_roles", [blogId])) { set.add("edit_user_roles"); } if (await user.can("manage_roles", blogId)) { set.add("manage_roles"); } if (await user.can("list_blog_users", [blogId])) { set.add("list_blog_users"); } if (!(await user.can("manage_blog_users", [blogId]))) { set.delete("edit_users"); } (await user.can("create_users", [siteId])) ? set.add("create_users") : set.delete("create_users"); return Array.from(set); }; const getBlog = async (blog) => { const capabilities = await getCapabilities(currentUser, blog.blog_id, blog.site_id, blog.capabilities); const returnValue = { site_id: blog.site_id, blog_id: blog.blog_id, blogname: blog.blogname, rolenames: blog.rolenames, blog_roles: undefined, capabilities, }; if (await currentUser.can("manage_blog_users", [blog.blog_id])) { return { ...returnValue, blog_roles: blog.blog_roles, }; } return returnValue; }; const sites = []; for (const site of currentSites.sites ?? []) { const blogs = []; for (const blog of site.blogs) { blogs.push(await getBlog(blog)); } if (blogs.length > 0) { sites.push({ site_id: site.site_id, sitename: site.sitename, siteurl: site.siteurl, is_superadmin: site.is_superadmin, blogs, }); } } return this.returnValue({ user: currentSites.user, primary_blog: await getBlog(currentSites.primary_blog), is_multisite: this.config.isMultiSite(), sites: 0 >= sites.length ? undefined : sites, }); } async create(input) { const { user: currentUser } = await this.getUser(); if (!(await currentUser.can("create_users"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to create new users"); } const data = val.trx.userUpsert.parse(input); // For not to attach default role (subscriber) data.role = !input.role ? undefined : data.role; if (data.role) { const roleNames = Array.isArray(data.role) ? data.role .map((roleName) => roleName.trim()) .filter((roleName) => roleName.length > 0) : [data.role]; if (0 >= roleNames.length) { data.role = undefined; } else { const roles = this.components.get(roles_1.Roles); for (const roleName of roleNames) { if (!roles.get(roleName)) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, `The role does not exist. - ${data.role}`); } } } } data.ID = undefined; const userTrx = this.components.get(transactions_1.UserTrx); const newUserId = await userTrx.upsert(data, { attachRole: data.role ? true : false, }); const userUtil = this.components.get(user_util_1.UserUtil); const newUser = await userUtil.get(newUserId); if (!newUser.props) { throw new error_1.CrudError(error_1.StatusMessage.NOT_FOUND, "User not found"); } const newUserLogin = newUser.props.user_login; if (this.config.isMultiSite()) { const signupTrx = this.components.get(transactions_1.SignupTrx); await signupTrx.remove(newUserLogin); if (data.role) { const blogTrx = this.components.get(transactions_1.BlogTrx); const current = this.components.get(current_1.Current); await blogTrx.addUser(current.blogId, newUserId, data.role); } } return this.returnValue(newUser.props); } async updateSuperAdmin(userId, options) { if (!this.config.isMultiSite()) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "Not supported"); } try { const { blogId, siteId, remove = false } = options ?? {}; await this.switchBlog({ siteId, blogId, }); const userUtil = this.components.get(user_util_1.UserUtil); const user = await userUtil.get(userId); if (!user.props?.user_login) { throw new error_1.CrudError(error_1.StatusMessage.NOT_FOUND, "User not found"); } const { user: currentUser } = await this.getUser(); // Not allowing to remove own. if ((userId === currentUser.props?.ID && remove) || !(await currentUser.can("manage_network_users"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Not permitted"); } const userTrx = this.components.get(transactions_1.UserTrx); await userTrx.syncSuperAdmin(userId, { siteId, blogId, remove, }); return this.returnValue(true); } finally { await this.restoreBlog(); } } async updateRole(userId, role, options) { try { const { blogId, siteId } = options ?? {}; await this.switchBlog({ siteId, blogId, }); const parsedRole = zod_1.z.array(zod_1.z.string()).parse(role); await this.checkRoleUpdatePermission(userId, parsedRole); const userUtil = this.components.get(user_util_1.UserUtil); const user = await userUtil.get(userId); if (!user.props?.user_login) { throw new error_1.CrudError(error_1.StatusMessage.NOT_FOUND, "User not found"); } const userTrx = this.components.get(transactions_1.UserTrx); if (parsedRole.length > 0) { await userTrx.upsertRole(userId, parsedRole); } else { await userTrx.removeRole(userId, { removeSuperAdmin: false, }); } return this.returnValue(true); } finally { await this.restoreBlog(); } } async update(userId, data, options) { try { const { blogId, siteId, attachRole = false, removeRole = false, } = options ?? {}; await this.switchBlog({ siteId, blogId, }); data.ID = userId; const { user: currentUser } = await this.getUser(); const queryUtil = this.components.get(query_util_1.QueryUtil); const currentUserData = (await this.getAsUpsert(userId)).data; const diffData = (0, diff_1.diffObject)(data, currentUserData); if (!(await currentUser.can("edit_user", userId))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit this user"); } if (diffData.user_login || currentUserData.user_login !== data.user_login) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "User Login is not editable"); } if (typeof data.user_nicename == "string" && data.user_nicename.length > 0 && data.user_nicename !== currentUserData.user_nicename && (await queryUtil.users((query) => { query.where("user_nicename", data.user_nicename); }))) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "Invalid slug"); } const userTrx = this.components.get(transactions_1.UserTrx); if (data.role) { const roleNames = Array.isArray(data.role) ? data.role .map((roleName) => roleName.trim()) .filter((roleName) => roleName.length > 0) : [data.role]; await this.checkRoleUpdatePermission(userId, roleNames); if (0 >= roleNames.length) { data.role = undefined; } else { const roles = this.components.get(roles_1.Roles); for (const roleName of roleNames) { if (!roles.get(roleName)) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, `The role does not exist - ${data.role}`); } } } } return this.returnValue(await userTrx.upsert(data, { attachRole, removeRole, })); } finally { await this.restoreBlog(); } } async updatePassword(userId, newPassword) { const userUtil = this.components.get(user_util_1.UserUtil); const user = await userUtil.get(userId); if (!user.props || !user.props.user_login) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "User not found"); } const userLogin = user.props.user_login; const hashedPassword = (0, common_1.hashPassword)(newPassword); return await this.update(userId, { user_login: userLogin, user_pass: hashedPassword, }); } async delete(userId, options) { const { reassignList = undefined } = options ?? {}; let { reassign = undefined } = options ?? {}; const { user: currentUser } = await this.getUser(); const userTrx = this.components.get(transactions_1.UserTrx); if (!(await currentUser.can("delete_user", userId))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to delete this user"); } if (!this.config.isMultiSite()) { if (reassignList) { reassign = Object.values(reassignList)[0]; } return this.returnValue(await userTrx.remove(userId, reassign)); } return this.returnValue(await userTrx.removeFromAllBlogs(userId, reassignList)); } async list(args, options) { try { const { context = "view" } = options ?? {}; const queryUtil = this.components.get(query_util_1.QueryUtil); const userUtil = this.components.get(user_util_1.UserUtil); const rolesUtil = this.components.get(roles_util_1.RolesUtil); const parsedArgs = val.crud.userListParams.parse(args ?? {}); await this.switchBlog({ siteId: parsedArgs.site_id, blogId: parsedArgs.blog_id, }); const { user: currentUser } = await this.getUser(); const currentRole = await currentUser.role(); if ((parsedArgs.roles || parsedArgs.superadmins || parsedArgs.include_unregistered_users || context == "edit" || parsedArgs.orderby == "user_email" || parsedArgs.orderby == "user_registered") && (!currentUser.props?.ID || !(await currentUser.can("list_users")))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to filter users by role"); } let blogIds = []; let superAdmins = []; if (this.config.isMultiSite()) { const current = this.components.get(current_1.Current); superAdmins = (await rolesUtil.getSuperAdmins({ siteId: current.siteId, })) ?? []; if (parsedArgs.site_id) { const siteId = parsedArgs.site_id; const blogs = (await queryUtil.blogs((query) => { query.where("site_id", siteId); })) ?? []; // List all users in site blogs.map((blog) => blogIds?.push(blog.blog_id)); } else { const current = this.components.get(current_1.Current); blogIds = [parsedArgs.blog_id ?? current.blogId]; } } const includeAnonymous = Array.isArray(parsedArgs.roles) && parsedArgs.roles.filter((role) => role === "anonymous").length > 0; const roleNames = !Array.isArray(parsedArgs.roles) ? [] : parsedArgs.roles.filter((role) => role !== "anonymous"); const buildSearchQuery = (query) => { query.andWhere((query) => { const searchColumns = [ "user_login", "user_nicename", "display_name", "user_email", ]; for (const searchColumn of searchColumns) { parsedArgs.search && query.or.whereLike(searchColumn, parsedArgs.search); } }); }; const buildQuery = (query) => { const { column } = query.alias; const offset = parsedArgs.offset ?? (parsedArgs.page - 1) * parsedArgs.per_page; const limit = parsedArgs.per_page; if (includeAnonymous && !parsedArgs.exclude_anonymous) { query.hasNoRole(); } else if (blogIds.length > 0) { query.andWhere((query) => { query.withBlogIds(blogIds); }); } else if (!this.config.isMultiSite()) { // For single site query.hasRole(); } query.builder .offset(offset) .limit(limit) .groupBy(column("users", "ID")); if (parsedArgs.orderby) { query.builder.orderBy(column("users", parsedArgs.orderby), parsedArgs.order); } if (Array.isArray(parsedArgs.include)) { query.whereIn("ID", parsedArgs.include); } if (Array.isArray(parsedArgs.exclude)) { query.andWhereNot((query) => query.whereIn("ID", parsedArgs.exclude)); } if (parsedArgs.search) { buildSearchQuery(query); } if (Array.isArray(parsedArgs.slug) && parsedArgs.slug.length > 0) { query.where("user_nicename", parsedArgs.slug); } if (roleNames.length > 0) { query.withRoles(roleNames); } // SuperAdmins if (superAdmins.length > 0) { if (parsedArgs.site_id && currentRole.isSuperAdmin()) { query[parsedArgs.superadmins ? "andWhere" : "orWhere"]((query) => { query.whereIn("user_login", superAdmins); }); } } // Include anonymous users if no role is specified if (!includeAnonymous && !parsedArgs.exclude_anonymous && 0 >= roleNames.length && !parsedArgs.superadmins) { query.orWhere((query) => { query.hasNoRole(); if (parsedArgs.search) { buildSearchQuery(query); } }); } if (parsedArgs.has_published_posts) { query.withPublishedPosts(); } }; const users = (await queryUtil.users((query) => { buildQuery(query); })) ?? []; const counts = await queryUtil.users((query) => { buildQuery(query); query.count("users", "ID"); }, val.query.resultCount); const countPosts = context !== "edit" ? [] : await userUtil.countPosts(users.map((user) => user.ID)); const data = []; for (const user of await userUtil.toUsers(users)) { if (!user.props) continue; // const props = Object.entries(user.props) // .filter(([k]) => k !== "user_pass") // .reduce((a, b) => ({ ...a, [b[0]]: b[1] }), {}) as Exclude< // types.WpUsers, // "user_pass" // >; // eslint-disable-next-line @typescript-eslint/no-unused-vars const { user_pass, ...props } = user.props; const role = await user.role(); const posts = countPosts?.filter((v) => v.post_author == props.ID)[0]?.count ?? 0; if (this.config.isMultiSite() && !currentRole.isSuperAdmin()) { role.names.delete("superadmin"); } if (context === "edit") { data.push({ ...props, metas: await user.meta.props(), roles: Array.from(role.names), capabilities: role.capabilities, posts, }); } else { data.push({ ID: props.ID, display_name: props.display_name, user_url: props.user_url, user_nicename: props.user_nicename, user_login: props.user_login, posts, }); } } const pagination = this.pagination({ page: parsedArgs.page, limit: parsedArgs.per_page, count: counts?.count ?? 0, }); return this.returnValue(data, { pagination, context, }); } finally { await this.restoreBlog(); } } }; exports.UserCrud = UserCrud; exports.UserCrud = UserCrud = __decorate([ (0, component_1.component)(), __metadata("design:paramtypes", [components_1.Components, config_1.Config]) ], UserCrud);