@rnaga/wp-node
Version:
👉 **[View Full Documentation at rnaga.github.io/wp-node →](https://rnaga.github.io/wp-node/)**
207 lines (206 loc) • 8.8 kB
JavaScript
"use strict";
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 __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.ApplicationPasswordsUtil = void 0;
const common_1 = require("../../common");
const config_1 = require("../../config");
const component_1 = require("../../decorators/component");
const transactions_1 = require("../../transactions");
const application_password_1 = require("../../validators/application-password");
const components_1 = require("../components");
const options_1 = require("../options");
const meta_util_1 = require("./meta.util");
const site_util_1 = require("./site.util");
const zod_1 = require("zod");
const user_util_1 = require("./user.util");
// Application Passwords should be 24 characters long.
// https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/#application-passwords
const PW_LENGTH = 24;
let ApplicationPasswordsUtil = class ApplicationPasswordsUtil {
config;
components;
constructor(config, components) {
this.config = config;
this.components = components;
}
getMetaKey() {
return "_application_passwords";
}
getOptionsKey() {
return "using_application_passwords";
}
isAvailable(checkSsl = true) {
return (this.config.config.useApplicationPasswords === true &&
(checkSsl === false || this.config.isSsl() === true));
}
async isInUse() {
const options = this.components.get(options_1.Options);
const siteUtil = this.components.get(site_util_1.SiteUtil);
const mainSiteId = await siteUtil.getMainSiteId();
const using = !this.config.isMultiSite()
? await options.get(this.getOptionsKey())
: await options.get(this.getOptionsKey(), { siteId: mainSiteId });
return (using === "1" ||
parseInt(`${using}`) === 1 ||
Boolean(`${using}`) === true);
}
// create_new_application_password
async createNewPassword(userId, options) {
const name = zod_1.z.string().min(2).max(100).parse(options.name);
const newPassword = (0, common_1.generatePassword)(PW_LENGTH, false);
const hashedPassword = (0, common_1.fastHash)(newPassword);
const newItem = {
uuid: (0, common_1.uuid4)(),
app_id: options.app_id || "",
name,
password: hashedPassword,
created: Math.floor(Date.now() / 1000),
last_used: null,
last_ip: null,
};
// Set Option Key
if (this.config.isMultiSite()) {
const siteId = await this.components.get(site_util_1.SiteUtil).getMainSiteId();
const metaTrx = this.components.get(transactions_1.MetaTrx);
await metaTrx.upsert("site", siteId, this.getOptionsKey(), "1");
}
else {
const optionsTrx = this.components.get(transactions_1.OptionsTrx);
await optionsTrx.insert(this.getOptionsKey(), "1", { upsert: true });
}
const passwords = await this.getUserPasswords(userId);
passwords.push(newItem);
const result = await this.setUserPasswords(userId, passwords);
if (!result) {
throw new Error("Failed to set user passwords");
}
return {
password: newPassword,
item: newItem,
};
}
// record_application_password_usage
async recordPasswordUsage(userId, uuid, options) {
const passwords = await this.getUserPasswords(userId);
for (const password of passwords) {
if (password.uuid !== uuid) {
continue;
}
const lastUsed = password.last_used || 0;
// Only record activity once a day.
if (lastUsed > 0 && lastUsed + 86400 > Math.floor(Date.now() / 1000)) {
return true;
}
// Update the password object in the array
password.last_used = Math.floor(Date.now() / 1000);
password.last_ip = options.ip || null;
// Save the updated passwords array
return await this.setUserPasswords(userId, passwords);
}
return false;
}
// update_application_password
async updatePasswordName(userId, uuid, newName) {
const name = zod_1.z.string().min(2).max(100).parse(newName);
const passwords = await this.getUserPasswords(userId);
for (const password of passwords) {
if (password.uuid !== uuid) {
continue;
}
// Update the password object in the array
password.name = name;
// Save the updated passwords array
return await this.setUserPasswords(userId, passwords);
}
return false;
}
// delete_application_password
async deletePassword(userId, uuid) {
const passwords = await this.getUserPasswords(userId);
const filtered = passwords.filter((password) => password.uuid !== uuid);
return await this.setUserPasswords(userId, filtered);
}
// delete_all_application_passwords
async deleteAllPasswords(userId) {
return await this.setUserPasswords(userId, []);
}
parsePasswords(passwords) {
return zod_1.z.array(application_password_1.applicationPassword).parse(passwords || []);
}
// get_user_application_password
async getUserPasswords(userId) {
const metaUtil = this.components.get(meta_util_1.MetaUtil);
const passwords = await metaUtil.getValue("user", userId, this.getMetaKey());
// Unserialize
const serialized = (0, common_1.phpUnserialize)(passwords) || [];
return this.parsePasswords(serialized);
}
// get_user_application_password
async getUserPasswordByUuid(userId, uuid) {
if (uuid.length !== 36) {
return null;
}
const passwords = await this.getUserPasswords(userId);
return passwords.find((password) => password.uuid === uuid) || null;
}
// set_user_application_passwords
async setUserPasswords(userId, passwords) {
const parsed = this.parsePasswords(passwords);
const metaTrx = this.components.get(transactions_1.MetaTrx);
return await metaTrx.upsert("user", userId, this.getMetaKey(), parsed, {
serialize: true,
});
}
chunkPassword(password) {
// Clean up first with cleanupPasswordHash
const cleanedUp = (0, common_1.cleanupPasswordHash)(password);
// trim( chunk_split( $raw_password, 4, ' ' ) );
return (cleanedUp
.match(/.{1,4}/g)
?.join(" ")
.trim() || "");
}
// wp_authenticate_application_password
async authenticate(userIdOrName, rawPassword, options) {
if ((await this.isInUse()) === false) {
return undefined;
}
// Get user
const userUtil = this.components.get(user_util_1.UserUtil);
const user = await userUtil.get(userIdOrName);
if (!user.props) {
return undefined;
}
const userId = user.props.ID;
const password = (0, common_1.cleanupPasswordHash)(rawPassword);
if (password.length !== PW_LENGTH) {
return undefined;
}
// Get all password objects
const passwords = await this.getUserPasswords(userId);
for (const item of passwords) {
if (item.password && (0, common_1.verifyFastHash)(password, item.password)) {
// Record usage
await this.recordPasswordUsage(userId, item.uuid, {
ip: options?.ip || null,
});
// Return user object
return user;
}
}
return undefined;
}
};
exports.ApplicationPasswordsUtil = ApplicationPasswordsUtil;
exports.ApplicationPasswordsUtil = ApplicationPasswordsUtil = __decorate([
(0, component_1.component)(),
__metadata("design:paramtypes", [config_1.Config, components_1.Components])
], ApplicationPasswordsUtil);