WordPress.org

Make WordPress Core

Ticket #31616: 31616.4.diff

File 31616.4.diff, 29.2 KB (added by jipmoors, 7 years ago)

Reformatted docs. Converted function parameters to optional args array. Fixed filter location. Organised tests.

  • src/wp-admin/includes/file.php

     
    910910
    911911        $context = trailingslashit( $context );
    912912
    913         if ( ! $method ) {
     913        static $results = array();
    914914
    915                 $temp_file_name = $context . 'temp-write-test-' . time();
    916                 $temp_handle = @fopen($temp_file_name, 'w');
    917                 if ( $temp_handle ) {
     915        // saving hash for static cache; file reads/writes are to be avoided when possible
     916        $hash = md5(serialize($args) . $context . $allow_relaxed_file_ownership);
     917        if ( ! isset($results[$hash])) {
    918918
    919                         // Attempt to determine the file owner of the WordPress files, and that of newly created files
    920                         $wp_file_owner = $temp_file_owner = false;
    921                         if ( function_exists('fileowner') ) {
    922                                 $wp_file_owner = @fileowner( __FILE__ );
    923                                 $temp_file_owner = @fileowner( $temp_file_name );
     919                if ( ! $method ) {
     920
     921                        $temp_file_name = $context . 'temp-write-test-' . time();
     922                        $temp_handle    = @fopen( $temp_file_name, 'w' );
     923                        if ( $temp_handle ) {
     924
     925                                // Attempt to determine the file owner of the WordPress files, and that of newly created files
     926                                $wp_file_owner = $temp_file_owner = false;
     927                                if ( function_exists( 'fileowner' ) ) {
     928                                        $wp_file_owner   = @fileowner( __FILE__ );
     929                                        $temp_file_owner = @fileowner( $temp_file_name );
     930                                }
     931
     932                                if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) {
     933                                        // WordPress is creating files as the same owner as the WordPress files,
     934                                        // this means it's safe to modify & create new files via PHP.
     935                                        $method                                  = 'direct';
     936                                        $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner';
     937                                } elseif ( $allow_relaxed_file_ownership ) {
     938                                        // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files
     939                                        // safely in this directory. This mode doesn't create new files, only alter existing ones.
     940                                        $method                                  = 'direct';
     941                                        $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership';
     942                                }
     943
     944                                @fclose( $temp_handle );
     945                                @unlink( $temp_file_name );
    924946                        }
     947                }
    925948
    926                         if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) {
    927                                 // WordPress is creating files as the same owner as the WordPress files,
    928                                 // this means it's safe to modify & create new files via PHP.
    929                                 $method = 'direct';
    930                                 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner';
    931                         } elseif ( $allow_relaxed_file_ownership ) {
    932                                 // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files
    933                                 // safely in this directory. This mode doesn't create new files, only alter existing ones.
    934                                 $method = 'direct';
    935                                 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership';
     949                if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
     950                if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext';
     951                if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread
     952
     953                /**
     954                 * Filter the filesystem method to use.
     955                 *
     956                 * @since 2.6.0
     957                 *
     958                 * @param string $method  Filesystem method to return.
     959                 * @param array  $args    An array of connection details for the method.
     960                 * @param string $context Full path to the directory that is tested for being writable.
     961                 * @param bool   $allow_relaxed_file_ownership Whether to allow Group/World writable.
     962                 */
     963                $results[$hash] = apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership );
     964        }
     965
     966        return $results[$hash];
     967}
     968
     969/**
     970 * Collects the filesystem credentials set in defines, $_POST data and database options.
     971 *
     972 * If no type is specified, a filesystem check will be done to see what
     973 * type is available to use.
     974 *
     975 * If direct file access is possible, no credentials are needed.
     976 *
     977 * The variables of the credentials are collected using this order:
     978 *
     979 *     1. Defines - Preceed everything if they are set they are used
     980 *     2. $_POST - Information submitted by the request form
     981 *     3. Saved - Previously entered data that is saved will be used last
     982 *
     983 * Note: this function does not check if a specified type is available on this system
     984 * it merly collects data based on available variables.
     985 *
     986 * @since 4.2.0
     987 *
     988 * @param array $args {
     989 *      Optional. Arguments to define the environment. Default empty array.
     990 *          @type string $form_post the URL to post the form to. Default ''. Accepts URL.
     991 *          @type string $type the chosen Filesystem method in use. Default ''.
     992 *              @type string $context The directory which is needed access to,
     993 *                                    the write-test will be performed on this
     994 *                                    directory by get_filesystem_method(). Default False.
     995 *              @type bool $allow_relaxed_file_ownership Whether to allow Group/World writable. Default False.
     996 * }
     997 * @return array {
     998 *    Optional. Credentials that are set on the system. Default empty array.
     999 *      @type string $connection_type Type of connection the credentials are meant for.
     1000 *      @type string $hostname Optional.
     1001 *      @type string $username Optional.
     1002 *      @type string $password Optional.
     1003 *      @type string $public_key Optional. SSH public key.
     1004 *      @type string $private_key Optional. SSH private key.
     1005 *      @type int $port Optional.
     1006 * }
     1007 */
     1008function get_filesystem_credentials( $args = array() ) {
     1009        $defaults = array(
     1010                'form_post' => '',
     1011                'type' => '',
     1012                'context' => false,
     1013                'allow_relaxed_file_ownership' => false
     1014        );
     1015
     1016        $args = wp_parse_args( $args, $defaults );
     1017
     1018        // extract items
     1019        $type = $args['type'];
     1020        $context = $args['context'];
     1021        $allow_relaxed_file_ownership = $args['allow_relaxed_file_ownership'];
     1022
     1023        // For the direct method no credentials are needed
     1024        if ( empty($type) ) {
     1025                $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership );
     1026        }
     1027
     1028        if ( 'direct' == $type ) {
     1029                return array( 'connection_type' => 'direct' );
     1030        }
     1031
     1032        // Retrieve saved information
     1033        $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => ''));
     1034
     1035        // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option)
     1036        $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']);
     1037        $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']);
     1038        $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : '');
     1039
     1040        // Check to see if we are setting the public/private keys for ssh
     1041        $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? wp_unslash( $_POST['public_key'] ) : '');
     1042        $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? wp_unslash( $_POST['private_key'] ) : '');
     1043
     1044        // Sanitize the hostname, Some people might pass in odd-data:
     1045        $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off
     1046
     1047        // If hostname contains a port, check it and separate it
     1048        if ( strpos($credentials['hostname'], ':') ) {
     1049                list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2);
     1050                if ( ! is_numeric($credentials['port']) )
     1051                        unset($credentials['port']);
     1052        } else {
     1053                unset($credentials['port']);
     1054        }
     1055
     1056        // Determine connection type available
     1057        if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) {
     1058                $credentials['connection_type'] = 'ssh';
     1059        } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL
     1060                $credentials['connection_type'] = 'ftps';
     1061        } elseif ( ! empty( $_POST['connection_type'] ) ) {
     1062                $credentials['connection_type'] = wp_unslash( $_POST['connection_type'] );
     1063        } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP
     1064                $credentials['connection_type'] = 'ftp';
     1065        }
     1066
     1067        return $credentials;
     1068}
     1069
     1070/**
     1071 * Check credentials for required parameters.
     1072 *
     1073 * Depending on the connection_type certain information is needed to be able to
     1074 * attempt a connection.
     1075 *
     1076 * Type of connection:
     1077 *
     1078 * - direct: needs no information
     1079 * - ssh: public and private keys
     1080 * - other: hostname(:port), username and password
     1081 *
     1082 * Plugins may override this check by returning true|false via the
     1083 * {@see 'request_filesystem_credentials'} filter.
     1084 *
     1085 * @since 4.2.0
     1086 *
     1087 * @param array $credentials the data to verify entered parameters on.
     1088 *
     1089 * @param array $args {
     1090 *      Optional. Arguments to feed to the filter. Default empty array.
     1091 *          @type string $form_post the URL to post the form to. Default ''. Accepts URL.
     1092 *          @type string $type the chosen Filesystem method in use. Default ''.
     1093 *              @type bool $error if the current request has failed to connect. Default False.
     1094 *              @type string $context The directory which is needed access to, the write-test will be performed on this
     1095 *                                    directory by get_filesystem_method(). Default False.
     1096 *              @type array $extra_fields Extra POST fields which should be checked for to be included in the post. Default null.
     1097 *              @type bool $allow_relaxed_file_ownership Whether to allow Group/World writable. Default False.
     1098 * }
     1099 *
     1100 * @return bool True when all required parameters have been set for supplied type,
     1101 *              False when parameter is missing or empty.
     1102 */
     1103function usable_filesystem_credentials($credentials, $args = array()) {
     1104        $defaults = array(
     1105                'form_post' => '',
     1106                'type' => '',
     1107                'error' => false,
     1108                'context' => false,
     1109                'extra_fields' => null,
     1110                'allow_relaxed_file_ownership' => false
     1111        );
     1112
     1113        $args = wp_parse_args( $args, $defaults );
     1114
     1115        // extract items
     1116        $form_post = $args['form_post'];
     1117        $type = $args['type'];
     1118        $error = $args['error'];
     1119        $context = $args['context'];
     1120        $extra_fields = $args['extra_fields'];
     1121        $allow_relaxed_file_ownership = $args['allow_relaxed_file_ownership'];
     1122
     1123        /** This filter is documented in wp-includes/file.php */
     1124        $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership );
     1125        if ( '' !== $req_cred )
     1126                return $req_cred;
     1127
     1128
     1129        // We need something to check against
     1130        if ( empty( $credentials ) ) {
     1131                return false;
     1132        }
     1133
     1134        // All the checks required an array for parameters; exit if we got something else
     1135        if ( ! is_array( $credentials ) ) {
     1136                return false;
     1137        }
     1138
     1139        if ( isset( $credentials['connection_type'] ) ) {
     1140                if ( 'direct' == $credentials['connection_type'] ) {
     1141                        return true;
     1142                }
     1143
     1144                // SSH needs public and private key
     1145                if ( 'ssh' == $credentials['connection_type'] ) {
     1146                        if ( ! isset( $credentials['public_key'], $credentials['private_key'] ) ) {
     1147                                return false;
    9361148                        }
    9371149
    938                         @fclose($temp_handle);
    939                         @unlink($temp_file_name);
     1150                        return ( ! empty( $credentials['public_key'] ) && ! empty( $credentials['private_key'] ) );
    9401151                }
    941         }
     1152        }
    9421153
    943         if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
    944         if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext';
    945         if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread
     1154        // Other connection methods need hostname, password and username
     1155        if ( ! isset( $credentials['password'], $credentials['username'], $credentials['hostname'] ) ) {
     1156                return false;
     1157        }
    9461158
    947         /**
    948          * Filter the filesystem method to use.
    949          *
    950          * @since 2.6.0
    951          *
    952          * @param string $method  Filesystem method to return.
    953          * @param array  $args    An array of connection details for the method.
    954          * @param string $context Full path to the directory that is tested for being writable.
    955          * @param bool   $allow_relaxed_file_ownership Whether to allow Group/World writable.
    956          */
    957         return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership );
     1159        return ( ! empty( $credentials['password'] ) && ! empty( $credentials['username'] ) && ! empty( $credentials['hostname'] ) );
    9581160}
    9591161
    9601162/**
     
    9711173 *
    9721174 * @since 2.5.
    9731175 *
    974  * @todo Properly mark optional arguments as such
    975  *
    976  * @param string $form_post the URL to post the form to
    977  * @param string $type the chosen Filesystem method in use
    978  * @param boolean $error if the current request has failed to connect
    979  * @param string $context The directory which is needed access to, The write-test will be performed on this directory by get_filesystem_method()
    980  * @param array $extra_fields Extra POST fields which should be checked for to be included in the post.
    981  * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable.
     1176 * @param string $form_post the URL to post the form to.
     1177 * @param string $type Optional. the chosen Filesystem method in use.
     1178 * @param boolean $error Optional. if the current request has failed to connect.
     1179 * @param string $context Optional. The directory which is needed access to,The write-test will be performed on this directory by get_filesystem_method().
     1180 * @param array $extra_fields Optional. Extra POST fields which should be checked for to be included in the post.
     1181 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable.
    9821182 * @return boolean False on failure. True on success.
    9831183 */
    9841184function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null, $allow_relaxed_file_ownership = false ) {
    985 
    9861185        /**
    9871186         * Filter the filesystem credentials form output.
    9881187         *
     
    10051204        if ( '' !== $req_cred )
    10061205                return $req_cred;
    10071206
    1008         if ( empty($type) ) {
     1207
     1208        // If no type is supplied, check to see if 'direct' method can be used.
     1209        if ( empty( $type ) ) {
    10091210                $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership );
    10101211        }
    10111212
    1012         if ( 'direct' == $type )
     1213        if ( 'direct' == $type ) {
    10131214                return true;
     1215        }
    10141216
    1015         if ( is_null( $extra_fields ) )
    1016                 $extra_fields = array( 'version', 'locale' );
     1217        $args = array(
     1218                'form_post' => $form_post,
     1219                'type' => $type,
     1220                'error' => $error,
     1221                'context' => $context,
     1222                'extra_fields' => $extra_fields,
     1223                'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership
     1224        );
    10171225
    1018         $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => ''));
     1226        $credentials = get_filesystem_credentials( $args );
    10191227
    1020         // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option)
    1021         $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']);
    1022         $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']);
    1023         $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : '');
     1228        if ( 'direct' == $credentials['connection_type'] ) {
     1229                return true;
     1230        }
    10241231
    1025         // Check to see if we are setting the public/private keys for ssh
    1026         $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? wp_unslash( $_POST['public_key'] ) : '');
    1027         $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? wp_unslash( $_POST['private_key'] ) : '');
     1232        if ( ! $error && usable_filesystem_credentials( $credentials ) ) {
    10281233
    1029         // Sanitize the hostname, Some people might pass in odd-data:
    1030         $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off
    1031 
    1032         if ( strpos($credentials['hostname'], ':') ) {
    1033                 list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2);
    1034                 if ( ! is_numeric($credentials['port']) )
    1035                         unset($credentials['port']);
    1036         } else {
    1037                 unset($credentials['port']);
    1038         }
    1039 
    1040         if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) {
    1041                 $credentials['connection_type'] = 'ssh';
    1042         } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL
    1043                 $credentials['connection_type'] = 'ftps';
    1044         } elseif ( ! empty( $_POST['connection_type'] ) ) {
    1045                 $credentials['connection_type'] = wp_unslash( $_POST['connection_type'] );
    1046         } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP
    1047                 $credentials['connection_type'] = 'ftp';
    1048         }
    1049         if ( ! $error &&
    1050                         (
    1051                                 ( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) ||
    1052                                 ( 'ssh' == $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']) )
    1053                         ) ) {
    10541234                $stored_credentials = $credentials;
    1055                 if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code.
     1235                if ( ! empty( $stored_credentials['port'] ) ) //save port as part of hostname to simplify retrievement code
     1236                {
    10561237                        $stored_credentials['hostname'] .= ':' . $stored_credentials['port'];
     1238                }
    10571239
    1058                 unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']);
     1240                unset( $stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key'] );
     1241
    10591242                if ( ! defined( 'WP_INSTALLING' ) ) {
    10601243                        update_option( 'ftp_credentials', $stored_credentials );
    10611244                }
     1245
    10621246                return $credentials;
    10631247        }
     1248
     1249        // Output the request for credentials to the user
     1250        $args = array(
     1251                'form_post' => $form_post,
     1252                'credentials' => $credentials,
     1253                'type' => $type,
     1254                'error' => $error,
     1255                'context' => $context,
     1256                'extra_fiels' => $extra_fields,
     1257                'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership
     1258        );
     1259
     1260        request_filesystem_credentials_form( $args );
     1261
     1262        // Nothing usable has been found yet.
     1263        return false;
     1264}
     1265
     1266/**
     1267 * Displays the form to request needed credentials in order to have sufficient
     1268 * filesystem control.
     1269 *
     1270 * @since 4.2.0
     1271 *
     1272 * @param array $args {
     1273 *   Optional. Arguments for pre-filling the form. Default empty array.
     1274 *      @type string $form_post the URL to post the form to. Default ''. Accepts URL.
     1275 *      @type array $credentials credentials that will be pre-filled in the form. Default empty array.
     1276 *      @type bool $error if the current request has failed to connect. Default False.
     1277 *      @type string $context The directory which is needed access to,
     1278 *                            The write-test will be performed on this directory
     1279 *                            by get_filesystem_method(). Default false.
     1280 *      @type array $extra_fields Extra POST fields which should be checked for to be included in the post. Default null.
     1281 *      @type bool $allow_relaxed_file_ownership Whether to allow Group/World writable. Default False.
     1282 * }
     1283 */
     1284function request_filesystem_credentials_form($args = array()) {
     1285        $defaults = array(
     1286                'form_post' => '',
     1287                'credentials' => array(),
     1288                'type' => '',
     1289                'error' => false,
     1290                'context' => false,
     1291                'extra_fields' => null,
     1292                'allow_relaxed_file_ownership' => false
     1293        );
     1294
     1295        $args = wp_parse_args( $args, $defaults );
     1296
     1297        // extract variables from args
     1298        $form_post = $args['form_post'];
     1299        $credentials = $args['credentials'];
     1300        $type = $args['type'];
     1301        $error = $args['error'];
     1302        $context = $args['context'];
     1303        $extra_fields = $args['extra_fields'];
     1304        $allow_relaxed_file_ownership = $args['allow_relaxed_file_ownership'];
     1305
     1306        if ( empty( $type ) ) {
     1307                $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership );
     1308        }
     1309
     1310        if ( ! is_array($credentials)) {
     1311                $credentials = array();
     1312        }
     1313
    10641314        $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : '';
    10651315        $username = isset( $credentials['username'] ) ? $credentials['username'] : '';
    10661316        $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : '';
     
    10681318        $port = isset( $credentials['port'] ) ? $credentials['port'] : '';
    10691319        $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : '';
    10701320
     1321        if ( is_null( $args['extra_fields'] ) )
     1322                $extra_fields = array( 'version', 'locale' );
     1323
    10711324        if ( $error ) {
    10721325                $error_string = __('<strong>ERROR:</strong> There was an error connecting to the server, Please verify the settings are correct.');
    10731326                if ( is_wp_error($error) )
     
    10751328                echo '<div id="message" class="error"><p>' . $error_string . '</p></div>';
    10761329        }
    10771330
     1331
     1332        // Detect available types by extension
    10781333        $types = array();
    10791334        if ( extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen') )
    10801335                $types[ 'ftp' ] = __('FTP');
     
    11901445</div>
    11911446</form>
    11921447<?php
    1193         return false;
     1448
    11941449}
  • tests/phpunit/tests/filesystem/credentials.php

     
     1<?php
     2
     3/**
     4 * @group filesystem
     5 * @group wp-filesystem
     6 */
     7
     8/**
     9 * Verification of filesystem credentials retrievement and validation.
     10 *
     11 * We cannot use WP_Filesystem_UnitTestCase because filesystem method 'MockFS' will influence test results.
     12 */
     13class Tests_Filesystem_Credentials extends WP_UnitTestCase {
     14
     15        public function tearDown() {
     16                $this->clear_credentials_option();
     17        }
     18
     19        /**
     20         * Direct type does not need credentials
     21         */
     22        function test_direct_credentials_retrievement() {
     23                // test direct type
     24                $credentials = get_filesystem_credentials( array('type' => 'direct' ) );
     25                $this->assertInternalType( 'array', $credentials );
     26                $this->assertArrayHasKey( 'connection_type', $credentials );
     27                $this->assertEquals( 'direct', $credentials['connection_type'] );
     28        }
     29
     30        function test_credentials_retrievement_filter_override() {
     31                add_filter( 'filesystem_method', array($this, 'filter_filesystem_method') );
     32
     33                $credentials = get_filesystem_credentials();
     34
     35                $this->assertInternalType( 'array', $credentials );
     36                $this->assertArrayHasKey( 'connection_type', $credentials );
     37                $this->assertNotEquals( 'direct', $credentials['connection_type'] );
     38
     39                remove_filter( 'filesystem_method', array($this, 'filter_filesystem_method') );
     40        }
     41
     42        /**
     43         * No variables set; test for expected parameters
     44         */
     45        function test_credentials_retrievement() {
     46                // setting type to anything but 'direct'
     47                $credentials = get_filesystem_credentials( array('type' => 'ssh') );
     48
     49                $this->assertInternalType( 'array', $credentials );
     50
     51                $this->assertArrayHasKey( 'connection_type', $credentials );
     52                $this->assertArrayHasKey( 'private_key', $credentials );
     53                $this->assertArrayHasKey( 'public_key', $credentials );
     54                $this->assertArrayHasKey( 'hostname', $credentials );
     55                $this->assertArrayHasKey( 'username', $credentials );
     56                $this->assertArrayHasKey( 'password', $credentials );
     57
     58                $this->assertEquals( '', $credentials['private_key'] );
     59                $this->assertEquals( '', $credentials['public_key'] );
     60                $this->assertEquals( '', $credentials['hostname'] );
     61                $this->assertEquals( '', $credentials['username'] );
     62                $this->assertEquals( '', $credentials['password'] );
     63        }
     64
     65        /**
     66         * When credentials are saved to options; these should be applied
     67         */
     68        function test_saved_credentials_retrievement() {
     69                // test saved variables
     70
     71                $this->set_credentials_option();
     72
     73                $credentials = get_filesystem_credentials( array('type' => 'ftp') );
     74
     75                $this->assertEquals( '', $credentials['private_key'] );
     76                $this->assertEquals( '', $credentials['public_key'] );
     77                $this->assertEquals( '', $credentials['password'] );
     78                $this->assertEquals( 'wordpress.org', $credentials['hostname'] );
     79                $this->assertEquals( 'wordpress', $credentials['username'] );
     80                $this->assertEquals( 'ftp', $credentials['connection_type'] );
     81        }
     82
     83        /**
     84         * When credentials are submitted to the page; these take precedence over saved variables;
     85         */
     86        function test_submitted_credentials_retrievement() {
     87                // test retrieving of submitted data
     88                $_POST['hostname'] = rand_str();
     89                $_POST['private_key'] = rand_str();
     90                $_POST['public_key'] = rand_str();
     91                $_POST['username'] = rand_str();
     92                $_POST['password'] = rand_str();
     93
     94                // set option variables; to see if they aren't used
     95                $this->set_credentials_option();
     96
     97                $credentials = get_filesystem_credentials( array('type' => 'ftp') );
     98
     99                // saved variables are set but should be ignored!
     100                $this->assertEquals( $_POST['private_key'], $credentials['private_key'] );
     101                $this->assertEquals( $_POST['public_key'], $credentials['public_key'] );
     102                $this->assertEquals( $_POST['hostname'], $credentials['hostname'] );
     103                $this->assertEquals( $_POST['username'], $credentials['username'] );
     104                $this->assertEquals( $_POST['password'], $credentials['password'] );
     105                $this->assertEquals( 'ftp', $credentials['connection_type'] );
     106
     107                unset($_POST['hostname'], $_POST['private_key'], $_POST['public_key'], $_POST['username'], $_POST['password']);
     108        }
     109
     110        /**
     111         * Direct connection does not need credentials, passing only the connection_type is sufficient
     112         */
     113        function test_usable_direct_credentials() {
     114                $credentials = array('connection_type' => 'direct');
     115                $this->assertTrue( usable_filesystem_credentials( $credentials ) );
     116        }
     117
     118        /**
     119         * Test for SSH parameter checks
     120         */
     121        function test_usable_ssh_credentials() {
     122                // ssh:
     123                $credentials = get_filesystem_credentials( array('type' => 'ssh' ) );
     124                $credentials['connection_type'] = 'ssh';
     125
     126                // missing private_key & public_key
     127                $this->assertFalse( usable_filesystem_credentials( $credentials ) );
     128
     129                // set private_key & public_key
     130                $credentials['private_key'] = 'private';
     131                $credentials['public_key'] = 'public';
     132                $this->assertTrue( usable_filesystem_credentials( $credentials ) );
     133        }
     134
     135        /**
     136         * Test for SSH2 parameter checks
     137         */
     138        function test_usable_ssh2_credentials() {
     139                // ssh2
     140                $credentials = array(
     141                        'connection_type' => 'ssh2'
     142                );
     143
     144                $this->assertFalse( usable_filesystem_credentials( $credentials ) );
     145
     146                $credentials = array(
     147                        'public_key' => 'a',
     148                        'private_key' => 'b',
     149                        'connection_type' => 'ssh2'
     150                );
     151                $this->assertFalse( usable_filesystem_credentials( $credentials ) );
     152
     153                $credentials = array(
     154                        'hostname' => 'a',
     155                        'username' => 'b',
     156                        'password' => 'c',
     157                        'connection_type' => 'ssh2'
     158                );
     159                $this->assertTrue( usable_filesystem_credentials( $credentials ) );
     160        }
     161
     162        /**
     163         * Test for expected results with FTP or other credentials
     164         */
     165        function test_usable_ftp_credentials() {
     166                // required elements are empty
     167                $credentials = array(
     168                        'hostname' => '',
     169                        'username' => '',
     170                        'password' => '',
     171                        'connection_type' => 'ftp'
     172                );
     173                $this->assertFalse( usable_filesystem_credentials( $credentials ) );
     174
     175                // required elements are missing
     176                $credentials = array(
     177                        'hostname' => 'localhost',
     178                        'connection_type' => 'ftp'
     179                );
     180                $this->assertFalse( usable_filesystem_credentials( $credentials ) );
     181
     182                // required elements are set
     183                $credentials = array(
     184                        'hostname' => 'a',
     185                        'username' => 'b',
     186                        'password' => 'c',
     187                        'connection_type' => 'ftp'
     188                );
     189                $this->assertTrue( usable_filesystem_credentials( $credentials ) );
     190        }
     191
     192        /**
     193         * Test if the 'request_filesystem_credentials' filter is applied properly
     194         */
     195        function test_usable_credentials_filter_override() {
     196                // sufficient credentials:
     197                $credentials = array(
     198                        'hostname' => 'a',
     199                        'username' => 'b',
     200                        'password' => 'c',
     201                        'connection_type' => 'ftp'
     202                );
     203
     204                $this->assertTrue( usable_filesystem_credentials( $credentials, array('type' => 'ftp') ) );
     205
     206                // force a failure
     207                add_filter( 'request_filesystem_credentials', '__return_false' );
     208
     209                $usable = usable_filesystem_credentials( $credentials, array('type' => 'ftp') );
     210                $this->assertFalse( $usable );
     211
     212                remove_filter( 'request_filesystem_credentials', '__return_false' );
     213
     214
     215
     216                // insufficient credentials:
     217                $credentials = array(
     218                        'public_key' => '',
     219                        'private_key' => '',
     220                        'connection_type' => 'ssh'
     221                );
     222
     223                $this->assertFalse( usable_filesystem_credentials( $credentials, array('type' => 'ssh') ) );
     224
     225                // force a success
     226                add_filter( 'request_filesystem_credentials', '__return_true' );
     227
     228                $usable = usable_filesystem_credentials( $credentials, array('type' => 'ssh') );
     229                $this->assertTrue( $usable );
     230
     231                // clean up filter
     232                remove_filter( 'request_filesystem_credentials', '__return_true' );
     233        }
     234
     235        /** Helper functions */
     236        function set_credentials_option() {
     237                $credentials = array();
     238                $credentials['username'] = 'wordpress';
     239                $credentials['hostname'] = 'wordpress.org';
     240                update_option('ftp_credentials', $credentials);
     241        }
     242
     243        function clear_credentials_option() {
     244                delete_option('ftp_credentials');
     245        }
     246
     247        public static function filter_filesystem_method() {
     248                // anything but 'direct'
     249                return 'ftp';
     250        }
     251}