Custom XML-RPC authentication
-
I use a modified simpleSAMLphp SSO authentication plugin for my blog network to allows a single sign-on using my main user database and then users are automatically authenticated/created in WordPress. The simpleSAMLphp plugin breaks XML-RPC authenticate, I know why and I’m happy enough with this BUT to provide a fix for users who want to use the WordPress mobile apps and other XML-RPC tools I have written a plug-in which allows users to set up a separate password for XML-RPC use which is stored in the user meta data table.
The problem is I can’t figure our how to extend the
login_pass_ok
orlogin
functions in class-wp-xmlrpc-server.php to allow my intended additional user password to be used. My plugin code looks like this at them moment.<?php /* Plugin Name: XML RPC Separate Password Plugin URI: https://dropdesign.co.uk/wordpress/xml_rpc_separate_password/ Description: Creates XML-RPC Password pages under Users menu to allow a separate password to be set for XML-RPC use. This is useful for WordPress setups where the main autheticate is external such as using SAML, LDAP or similar. Version: 1.0 Author: Charlie Love Author URI: https://charlielove.org Text Domain: xml-rpc-separate-password This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ include_once(ABSPATH . WPINC . '/class-IXR.php'); include_once(ABSPATH . WPINC . '/class-wp-xmlrpc-server.php'); // for future use with localisation //load_plugin_textdomain('xml-rpc-separate-password', false, basename(dirname(__FILE__)) . 'languages/'); //add the actions for the dashboard menus add_action('admin_menu', 'xml_rpc_separate_password_init'); add_action('admin_init', 'xml_rpc_separate_password_check'); //Add a filter for XML RPC class - we'll replace the login check in the class with our one add_filter( 'wp_xmlrpc_server_class', 'replace_xmlrpc_server_class' ); function xml_rpc_separate_password_init () { add_submenu_page('users.php', __('Set Password for Mobile/XML-RPC App', 'xml-rpc-separate-password'), __('XML-RPC Password', 'xml-rpc-separate-password'), 0, 'change-password', 'xml_rpc_separate_password_pw'); } function xml_rpc_separate_password_check () { if (isset($_GET['page']) && ($_GET['page'] == 'change-password' )) { wp_enqueue_script('user-profile'); wp_enqueue_script('password-strength-meter'); if (!empty($_POST)) { global $wpdb, $user_ID, $user_email; get_currentuserinfo(); $user = get_userdata($user_ID); //write this password into the user meta data - DO NOT TOUCH THE CORE WORDPRESS USER PASSWORD if (isset($_POST['pass1']) && isset($_POST['pass2']) && !empty($_POST['pass1']) && $_POST['pass1'] == $_POST['pass2']) { //write the new xml-rpc authentication password into the user meta using the standard wp encryption for passwords $update = update_user_meta( $user_ID, 'xml_rpc_password', array(wp_hash_password($_POST['pass1']))); //if it hasn't screwed up say it's all super-duper! if (!is_wp_error($update)) { ob_start(); ?> <div id="message" class="updated fade"> <p><strong><?php _e('Password updated.') ?></strong></p> </div> <?php $_POST['post_msg'] = ob_get_clean(); } //eck, screwed up - fail! if (is_wp_error($update)) { ob_start(); ?> <div class="error"> <ul> <?php foreach ($update->get_error_messages() as $message) { ?> <li><?php echo $message; ?></li> <?php } ?> </ul> </div> <?php $_POST['post_msg'] = ob_get_clean(); } } } } } function xml_rpc_separate_password_pw () { global $wpdb, $user_ID; $title = __('Update Password for Mobile/XML-RPC Services'); $what = 'change-password'; $user = get_userdata($user_ID); if (isset($_POST['post_msg'])) echo $_POST['post_msg']; ?> <div class="wrap" id="profile-page"> <?php screen_icon(); ?> <h2><?php echo esc_html($title); ?></h2> <form id="your-profile" action="" method="post"> <table class="form-table"> <tr id="password"> <th><label for="pass1"><?php _e('New Password'); ?></label></th> <td><input type="password" name="pass1" id="pass1" size="16" value="" autocomplete="off" /> <span class="description"><?php _e("If you would like to change the password type a new one. Otherwise leave this blank."); ?></span><br /> <input type="password" name="pass2" id="pass2" size="16" value="" autocomplete="off" /> <span class="description"><?php _e("Type your new password again."); ?></span><br /> <div id="pass-strength-result"><?php _e('Strength indicator'); ?></div> <p class="description indicator-hint"><?php _e('Hint: The password should be at least seven characters long. To make it stronger, use upper and lower case letters, numbers and symbols like ! " ? $ % ^ & ).'); ?></p> </td> </tr> </table> <p class="submit"> <input type="hidden" name="user_login" id="user_login" value="<?php echo $user->user_login; ?>" /> <input type="submit" class="button-primary" value="<?php esc_attr_e('Update Password') ?>" name="submit" /> </p> </form> </div> <?php } //now the serious stuff to authenticate XML-RPC calls using our saved user-meta password rather than the default WP user one! /** * Generate the Response * * @param methods Array - list of existing XMLRPC methods * @return methods Array - list of updated XMLRPC methods */ function replace_xmlrpc_server_class( $class_name ) { // only replace the default XML-RPC class if another plug-in hasn't already changed it if ( $class_name === 'wp_xmlrpc_server' ) return 'wp_xmlrpc_server_ext'; else return $class_name; } class wp_xmlrpc_server_ext extends wp_xmlrpc_server { function __construct() { // hook filter to add the new methods after the existing ones are added in the parent constructor add_filter( 'xmlrpc_methods' , array( &$this, 'xmlrpc_methods' ) ); parent::__construct(); } function xmlrpc_methods ( $methods ) { $new_methods = array(); // array_merge will take the values defined in later arguments, so // the plugin will not overwrite any methods defined by WP core // (i.e., plugin will be forward-compatible with future releases of WordPress // that include these methods built-in) return array_merge( $new_methods, $methods ); } //replace the login functions /** * Check user's credentials. * * @param string $user_login User's username. * @param string $user_pass User's password. * @return bool Whether authentication passed. * @deprecated use wp_xmlrpc_server::login * @see wp_xmlrpc_server::login */ function login_pass_ok($user_login, $user_pass) { if ( !get_option( 'enable_xmlrpc' ) ) { $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'), admin_url('options-writing.php') ) ); return false; } // Let's run a check to see if credentials are okay if (!function_exists('get_userdatabylogin')) return new IXR_Error( 403, __( 'No user validation available.' ) ); //retrieve the user using the username $user = get_userdatabylogin($user_login); if(!$user) return new IXR_Error( 403, __( 'User does not exist.' ) ); //get the user id from the user data $user_id = $user->ID; //retreive the stored hashed password array from the usermeta $password_array = get_user_meta($user_id,'xml_rpc_password'); //stored the hashed password in an array so extract the hashed password from the array $hashed_stored_password = $password_array[0]; //validate the hashed password with the user supplied one from XML-RPC if ( !wp_check_password($user_pass,$hashed_stored_password) ) { $this->error = new IXR_Error(403, __('Bad login/pass combination.')); return false; } return true; } /** * Log user in. * * @since 2.8 * * @param string $username User's username. * @param string $password User's password. * @return mixed WP_User object if authentication passed, false otherwise */ function login($username, $password) { if ( !get_option( 'enable_xmlrpc' ) ) { $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'), admin_url('options-writing.php') ) ); return false; } // Let's run a check to see if credentials are okay if (!function_exists('get_userdatabylogin')) return new IXR_Error( 403, __( 'No user validation available.' ) ); //retrieve the user using the username $user = get_userdatabylogin($user_login); if(!$user) return new IXR_Error( 403, __( 'User does not exist.' ) ); //get the user id from the user data $user_id = $user->ID; //retreive the stored hashed password array from the usermeta $password_array = get_user_meta($user_id,'xml_rpc_password'); //stored the hashed password in an array so extract the hashed password from the array $hashed_stored_password = $password_array[0]; //validate the hashed password with the user supplied one from XML-RPC if ( !wp_check_password($user_pass,$hashed_stored_password) ) { $this->error = new IXR_Error(403, __('Bad login/pass combination.')); return false; } wp_set_current_user( $user->ID ); return $user; } } ?>
I could just hack the class-wp-xmlrpc-server.php file and replace the two functions but I would much rather have something that works correctly as a plug. Any ideas how I can replace the functionality of these two functions?
- The topic ‘Custom XML-RPC authentication’ is closed to new replies.