WordPress.org

Plugin Directory

Changeset 604793


Ignore:
Timestamp:
09/27/12 18:36:40 (19 months ago)
Author:
andy
Message:

Jetpack notes: merge json-api branch into notes

Location:
jetpack/branches/notes
Files:
5 edited
3 copied

Legend:

Unmodified
Added
Removed
  • jetpack/branches/notes

  • jetpack/branches/notes/class.jetpack-signature.php

    r585858 r604793  
    1818    } 
    1919 
    20     function sign_current_request( $override = null ) { 
     20    function sign_current_request( $override = array() ) { 
     21        if ( isset( $override['scheme'] ) ) { 
     22            $scheme = $override['scheme']; 
     23            if ( !in_array( $scheme, array( 'http', 'https' ) ) ) { 
     24                return new Jetpack_Error( 'invalid_sheme', 'Invalid URL scheme' ); 
     25            } 
     26        } else { 
     27            if ( is_ssl() ) { 
     28                $scheme = 'https'; 
     29            } else { 
     30                $scheme = 'http'; 
     31            } 
     32        } 
     33 
    2134        if ( is_ssl() ) { 
    22             $scheme = 'https'; 
    2335            $port = JETPACK_SIGNATURE__HTTPS_PORT == $_SERVER['SERVER_PORT'] ? '' : $_SERVER['SERVER_PORT']; 
    2436        } else { 
    25             $scheme = 'http'; 
    2637            $port = JETPACK_SIGNATURE__HTTP_PORT  == $_SERVER['SERVER_PORT'] ? '' : $_SERVER['SERVER_PORT']; 
    2738        } 
     
    2940        $url = "{$scheme}://{$_SERVER['HTTP_HOST']}:{$port}" . stripslashes( $_SERVER['REQUEST_URI'] ); 
    3041 
    31         if ( isset( $override['body'] ) && !is_null( $override['body'] ) ) { 
     42        if ( array_key_exists( 'body', $override ) && !is_null( $override['body'] ) ) { 
    3243            $body = $override['body']; 
    3344        } else if ( 'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { 
     
    4657        } 
    4758 
    48         return $this->sign_request( $a['token'], $a['timestamp'], $a['nonce'], $a['body-hash'], $_SERVER['REQUEST_METHOD'], $url, $body, true ); 
     59        $method = isset( $override['method'] ) ? $override['method'] : $_SERVER['REQUEST_METHOD'];  
     60        return $this->sign_request( $a['token'], $a['timestamp'], $a['nonce'], $a['body-hash'], $method, $url, $body, true ); 
    4961    } 
    5062 
  • jetpack/branches/notes/class.jetpack-xmlrpc-server.php

    r585858 r604793  
    1515     * so they will get a "does not exist" error. 
    1616     */ 
    17     function xmlrpc_methods() { 
     17    function xmlrpc_methods( $core_methods ) { 
    1818        if ( !$user = $this->login() ) { 
    1919            return array(); 
     
    2222        return apply_filters( 'jetpack_xmlrpc_methods', array( 
    2323            'jetpack.testConnection'    => array( $this, 'test_connection' ), 
     24            'jetpack.testAPIUserCode'   => array( $this, 'test_api_user_code' ), 
    2425            'jetpack.featuresAvailable' => array( $this, 'features_available' ), 
    2526            'jetpack.featuresEnabled'   => array( $this, 'features_enabled' ), 
    2627            'jetpack.getPost'           => array( $this, 'get_post' ), 
    2728            'jetpack.getComment'        => array( $this, 'get_comment' ),   
    28         ) ); 
     29            'jetpack.jsonAPI'           => array( $this, 'json_api' ), 
     30        ), $core_methods ); 
    2931    } 
    3032 
     
    133135     */ 
    134136    function test_connection() { 
    135         return true; 
     137        return JETPACK__VERSION; 
     138    } 
     139     
     140    function test_api_user_code( $args ) { 
     141        $client_id = (int) $args[0]; 
     142        $user_id   = (int) $args[1]; 
     143        $nonce     = (string) $args[2]; 
     144        $verify    = (string) $args[3]; 
     145 
     146        if ( !$client_id || !$user_id || !strlen( $nonce ) || 32 !== strlen( $verify ) ) { 
     147            return false; 
     148        } 
     149 
     150        if ( !get_user_by( 'id', $user_id ) ) { 
     151            return false; 
     152        } 
     153 
     154        error_log( "CLIENT: $client_id" ); 
     155        error_log( "USER:   $user_id" ); 
     156        error_log( "NONCE:  $nonce" ); 
     157        error_log( "VERIFY: $verify" ); 
     158 
     159        $jetpack_token = Jetpack_Data::get_access_token( 1 ); 
     160 
     161        $api_user_code = get_user_meta( $user_id, "jetpack_json_api_$client_id", true ); 
     162        if ( !$api_user_code ) { 
     163            return false; 
     164        } 
     165 
     166        $hmac = hash_hmac( 'md5', json_encode( (object) array( 
     167            'client_id' => (int) $client_id, 
     168            'user_id'   => (int) $user_id, 
     169            'nonce'     => (string) $nonce, 
     170            'code'      => (string) $api_user_code, 
     171        ) ), $jetpack_token->secret ); 
     172 
     173        if ( $hmac !== $verify ) { 
     174            return false; 
     175        } 
     176 
     177        return $user_id; 
    136178    } 
    137179 
     
    196238        return $comment; 
    197239    } 
     240     
     241    function json_api( $args = array() ) { 
     242        $json_api_args = $args[0]; 
     243        $verify_api_user_args = $args[1]; 
     244 
     245        $method    = $json_api_args[0]; 
     246        $url       = $json_api_args[1]; 
     247        $post_body = $json_api_args[2]; 
     248        $my_id     = $json_api_args[3]; 
     249 
     250        $user_id = call_user_func( array( $this, 'test_api_user_code' ), $verify_api_user_args ); 
     251 
     252        /* debugging 
     253        error_log( "-- begin json api via jetpack debugging -- " ); 
     254        error_log( "METHOD: $method" ); 
     255        error_log( "URL: $url" ); 
     256        error_log( "POST BODY: $post_body" ); 
     257        error_log( "MY JETPACK ID: $my_id" ); 
     258        error_log( "VERIFY_ARGS: " . print_r( $verify_api_user_args, 1 ) ); 
     259        error_log( "VERIFIED USER_ID: " . (int) $user_id ); 
     260        error_log( "-- end json api via jetpack debugging -- " ); 
     261        */ 
     262 
     263        if ( !$user_id ) { 
     264            return false; 
     265        } 
     266 
     267        $old_user = wp_get_current_user(); 
     268        wp_set_current_user( $user_id ); 
     269 
     270        define( 'REST_API_REQUEST', true ); 
     271        define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' ); 
     272 
     273        // needed? 
     274        require_once ABSPATH . 'wp-admin/includes/admin.php'; 
     275 
     276        require_once dirname( __FILE__ ) . '/class.json-api.php'; 
     277        $api = WPCOM_JSON_API::init( $method, $url, $post_body ); 
     278        require_once dirname( __FILE__ ) . '/class.json-api-endpoints.php'; 
     279 
     280        $display_errors = ini_set( 'display_errors', 0 ); 
     281        ob_start(); 
     282        $content_type = $api->serve( false ); 
     283        $output = ob_get_clean(); 
     284        ini_set( 'display_errors', $display_errors ); 
     285 
     286        $nonce = wp_generate_password( 10, false ); 
     287        $token = Jetpack_Data::get_access_token( 1 ); 
     288        $hmac  = hash_hmac( 'md5', $nonce . $output, $token->secret ); 
     289 
     290        wp_set_current_user( isset( $old_user->ID ) ? $old_user->ID : 0 ); 
     291 
     292        return array( 
     293            (string) $output, 
     294            (string) $nonce, 
     295            (string) $hmac, 
     296        ); 
     297    } 
    198298} 
  • jetpack/branches/notes/jetpack.php

    r604765 r604793  
    9191     */ 
    9292    var $sync; 
     93     
     94    /** 
     95     * Verified data for JSON authorization request 
     96     */ 
     97    var $json_api_authorization_request = array(); 
    9398 
    9499    /** 
     
    162167    function Jetpack() { 
    163168        $this->sync = new Jetpack_Sync; 
    164  
     169         
    165170        require_once dirname( __FILE__ ) . '/class.jetpack-user-agent.php'; 
    166171 
     
    190195            // Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on. 
    191196            add_filter( 'pre_option_enable_xmlrpc', '__return_true' ); 
    192         } 
     197        } else { 
     198            if ( $this->is_active() ) {  
     199                add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );  
     200            }  
     201        } 
    193202 
    194203        add_action( 'jetpack_clean_nonces', array( $this, 'clean_nonces' ) ); 
     
    25582567        } 
    25592568 
    2560         if ( !$this->add_nonce( $_GET['timestamp'], $_GET['nonce'] ) ) { 
     2569        $timestamp = (int) $_GET['timestamp']; 
     2570        $nonce     = stripslashes( (string) $_GET['nonce'] ); 
     2571 
     2572        if ( !$this->add_nonce( $timestamp, $nonce ) ) { 
    25612573            return $user; 
    25622574        } 
     
    25692581    function add_nonce( $timestamp, $nonce ) { 
    25702582        global $wpdb; 
     2583        static $nonces_used_this_request = array(); 
     2584 
     2585        if ( isset( $nonces_used_this_request["$timestamp:$nonce"] ) ) { 
     2586            return $nonces_used_this_request["$timestamp:$nonce"]; 
     2587        } 
    25712588 
    25722589        // This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp an $nonce 
     2590        $timestamp = (int) $timestamp; 
     2591        $nonce     = $wpdb->escape( $nonce ); 
    25732592 
    25742593        // Raw query so we can avoid races: add_option will also update 
     
    25812600        ) ); 
    25822601        $wpdb->show_errors( $show_errors ); 
     2602 
     2603        $nonces_used_this_request["$timestamp:$nonce"] = $return; 
     2604 
    25832605        return $return; 
    25842606    } 
     
    27492771 
    27502772        return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url ); 
     2773    } 
     2774     
     2775/* JSON API Authorization */ 
     2776 
     2777    /** 
     2778     * Handles the login action for Authorizing the JSON API 
     2779     */ 
     2780    function login_form_json_api_authorization() { 
     2781        $this->verify_json_api_authorization_request(); 
     2782 
     2783        add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 ); 
     2784 
     2785        add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) ); 
     2786        add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) ); 
     2787        add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 ); 
     2788    } 
     2789 
     2790    // Make sure the login form is POSTed to the signed URL so we can reverify the request 
     2791    function post_login_form_to_signed_url( $url, $path, $scheme ) { 
     2792        if ( 'wp-login.php' !== $path || 'login_post' !== $scheme ) { 
     2793            return $url; 
     2794        } 
     2795 
     2796        return "$url?{$_SERVER['QUERY_STRING']}"; 
     2797    } 
     2798 
     2799    // Make sure the POSTed request is handled by the same action 
     2800    function preserve_action_in_login_form_for_json_api_authorization() { 
     2801        echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n"; 
     2802    } 
     2803 
     2804    // If someone logs in to approve API access, store the Access Code in usermeta 
     2805    function store_json_api_authorization_token( $user_login, $user ) { 
     2806        add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 ); 
     2807        add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) ); 
     2808        $token = wp_generate_password( 32, false ); 
     2809        update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token ); 
     2810    } 
     2811 
     2812    // Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access 
     2813    function allow_wpcom_public_api_domain( $domains ) { 
     2814        $domains[] = 'public-api.wordpress.com'; 
     2815        return $domains; 
     2816    } 
     2817 
     2818    // Add the Access Code details to the public-api.wordpress.com redirect 
     2819    function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) { 
     2820        return add_query_arg( urlencode_deep( array( 
     2821            'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ), 
     2822            'jetpack-user-id' => (int) $user->ID, 
     2823            'jetpack-state'   => $this->json_api_authorization_request['state'], 
     2824        ) ), $redirect_to ); 
     2825    } 
     2826 
     2827    // Verifies the request by checking the signature 
     2828    function verify_json_api_authorization_request() { 
     2829        require_once dirname( __FILE__ ) . '/class.jetpack-signature.php'; 
     2830 
     2831        $token = Jetpack_Data::get_access_token( 1 ); 
     2832        if ( !$token || empty( $token->secret ) ) { 
     2833            wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.' ) ); 
     2834        } 
     2835 
     2836        $die_error = __( 'Someone may be trying to trick you into giving them access to your site.  Or it could be you just encountered a bug :).  Either way, please close this window.', 'jetpack' ); 
     2837 
     2838        $jetpack_signature =& new Jetpack_Signature( $token->secret, (int) Jetpack::get_option( 'time_diff' ) ); 
     2839        $signature = $jetpack_signature->sign_current_request( array( 'body' => null, 'method' => 'GET' ) ); 
     2840        if ( !$signature ) { 
     2841            wp_die( $die_error ); 
     2842        } else if ( is_wp_error( $signature ) ) { 
     2843            wp_die( $die_error ); 
     2844        } else if ( $signature !== $_GET['signature'] ) { 
     2845            if ( is_ssl() ) { 
     2846                // If we signed an HTTP request on the Jetpack Servers, but got redirected to HTTPS by the local blog, check the HTTP signature as well 
     2847                $signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', 'body' => null, 'method' => 'GET' ) ); 
     2848                if ( !$signature || is_wp_error( $signature ) || $signature !== $_GET['signature'] ) { 
     2849                    wp_die( $die_error ); 
     2850                } 
     2851            } else { 
     2852                wp_die( $die_error ); 
     2853            } 
     2854        } 
     2855 
     2856        $timestamp = (int) $_GET['timestamp']; 
     2857        $nonce     = stripslashes( (string) $_GET['nonce'] ); 
     2858 
     2859        if ( !$this->add_nonce( $timestamp, $nonce ) ) { 
     2860            // De-nonce the nonce, at least for 5 minutes. 
     2861            // We have to reuse this nonce at least once (used the first time when the initial request is made, used a second time when the login form is POSTed) 
     2862            $old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" ); 
     2863            if ( $old_nonce_time < time() - 300 ) { 
     2864                wp_die( __( 'The authorization process expired.  Please go back and try again.' ) ); 
     2865            } 
     2866        } 
     2867 
     2868        $data = json_decode( base64_decode( stripslashes( $_GET['data'] ) ) ); 
     2869        $data_filters = array( 
     2870            'state'        => 'opaque', 
     2871            'client_id'    => 'int', 
     2872            'client_title' => 'string', 
     2873            'client_image' => 'url', 
     2874        ); 
     2875 
     2876        foreach ( $data_filters as $key => $sanitation ) { 
     2877            if ( !isset( $data->$key ) ) { 
     2878                wp_die( $die_error ); 
     2879            } 
     2880 
     2881            switch ( $sanitation ) { 
     2882            case 'int' : 
     2883                $this->json_api_authorization_request[$key] = (int) $data->$key; 
     2884                break; 
     2885            case 'opaque' : 
     2886                $this->json_api_authorization_request[$key] = (string) $data->$key; 
     2887                break; 
     2888            case 'string' : 
     2889                $this->json_api_authorization_request[$key] = wp_kses( (string) $data->$key, array() ); 
     2890                break; 
     2891            case 'url' : 
     2892                $this->json_api_authorization_request[$key] = esc_url_raw( (string) $data->$key ); 
     2893                break; 
     2894            } 
     2895        } 
     2896 
     2897        if ( empty( $this->json_api_authorization_request['client_id'] ) ) { 
     2898            wp_die( $die_error ); 
     2899        } 
     2900    } 
     2901 
     2902    function login_message_json_api_authorization( $message ) { 
     2903        return '<p class="message">' . sprintf( 
     2904            esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.' ), 
     2905            '<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>' 
     2906        ) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>'; 
    27512907    } 
    27522908} 
  • jetpack/branches/notes/modules/module-info.php

    r604765 r604793  
    434434add_action( 'jetpack_learn_more_button_enhanced-distribution', 'jetpack_enhanced_distribution_more_link' ); 
    435435 
     436// External Applications 
     437function jetpack_external_applications_more_info() { ?> 
     438    <h4><?php esc_html_e( 'External Applications' , 'jetpack' ); ?></h4> 
     439 
     440    <p><?php esc_html_e( 'Jetpack will allow you to authorize third-party services to securely connect to your blog and allow them to use your content in new ways and offer you new functionality.', 'jetpack' ); ?></p> 
     441 
     442<?php 
     443} 
     444 
     445add_action( 'jetpack_module_more_info_external-applications', 'jetpack_external_applications_more_info' ); 
     446add_action( 'jetpack_module_more_info_connected_external-applications', 'jetpack_external_applications_more_info' ); 
     447 
     448function jetpack_external_applications_more_link() { 
     449    echo '<a class="button more-info-link" href="http://en.wordpress.com/firehose/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>'; 
     450} 
     451add_action( 'jetpack_learn_more_button_external-applications', 'jetpack_external_applications_more_link' ); 
     452 
    436453// Contact Form: START 
    437454function jetpack_contact_form_learn_more_button() { 
Note: See TracChangeset for help on using the changeset viewer.