WordPress.org

Making WordPress.org

Changeset 6759


Ignore:
Timestamp:
02/26/2018 07:06:12 AM (3 years ago)
Author:
dd32
Message:

2FA: Enable Email, Slack, and Backup Codes as the backup 2FA method.

See #77.

Location:
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor/providers/class-wporg-two-factor-backup-codes.php

    r6735 r6759  
    1818    }
    1919
     20    public function validate_authentication( $user, $code = '' ) {
     21        return $this->validate_code( $user, $code );
     22    }
     23
    2024}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor/providers/class-wporg-two-factor-email.php

    r6735 r6759  
    1616        }
    1717        return $instance;
     18    }
     19
     20    public function validate_authentication( $user, $code = '' ) {
     21        if ( ! isset( $user->ID ) || ! $code ) {
     22            return false;
     23        }
     24
     25        return $this->validate_token( $user->ID, $code );
    1826    }
    1927
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor/providers/class-wporg-two-factor-primary.php

    r6753 r6759  
    2424        return _x( 'Time Based One-Time Password (Google Authenticator, Authy, etc)', 'Provider Label', 'wporg' );
    2525    }
     26
     27    public function validate_authentication( $user, $code = '' ) {
     28        $key = get_user_meta( $user->ID, self::SECRET_META_KEY, true );
     29
     30        if ( ! $code ) {
     31            $code = $_REQUEST['authcode'];
     32        }
     33
     34        return $this->is_valid_authcode( $key, $code );
     35    }
    2636}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor/providers/class-wporg-two-factor-secondary.php

    r6753 r6759  
    11<?php
    22
    3 require_once __DIR__ . '/class-wporg-two-factor-backup-codes.php';
    4 
    5 class WPORG_Two_Factor_Secondary extends WPORG_Two_Factor_Backup_Codes { // Temporarily
    6 // class WPORG_Two_Factor_Secondary extends Two_Factor_Provider { // When it's a proper wrapper.
     3class WPORG_Two_Factor_Secondary extends Two_Factor_Provider { // When it's a proper wrapper.
    74
    85    /**
     
    2421    }
    2522
    26     // protected $providers = [];
     23    public function authentication_page( $user ) {
     24        if ( ! $user ) {
     25            return;
     26        }
     27
     28        $this->send_codes_to_user( $user );
     29
     30        require_once( ABSPATH . '/wp-admin/includes/template.php' );
     31
     32        $email_enabled = isset( $this->providers['WPORG_Two_Factor_Email'] ) && $this->providers['WPORG_Two_Factor_Email']->is_available_for_user( $user );
     33        $slack_enabled = isset( $this->providers['WPORG_Two_Factor_Slack'] ) && $this->providers['WPORG_Two_Factor_Slack']->is_available_for_user( $user );
     34
     35        if ( $email_enabled && $slack_enabled ) {
     36            echo '<p>' . __( 'Enter the verification code sent to your Email, Slack, or a printed backup code.', 'wporg' ) . '</p>';
     37        } elseif ( $email_enabled ) {
     38            echo '<p>' . __( 'Enter the verification code sent to your Email, or a printed backup code.', 'wporg' ) . '</p>';
     39        } else {
     40            echo '<p>' . __( 'Enter a printed backup code.', 'wporg' ) . '</p>';
     41        }
     42        ?>
     43
     44        <p>
     45            <label for="authcode"><?php esc_html_e( 'Verification Code:', 'wporg' ); ?></label>
     46            <input type="tel" name="two-factor-backup-code" id="authcode" class="input" value="" size="20" pattern="[0-9]*" />
     47            <?php submit_button( __( 'Authenticate', 'wporg' ) ); ?>
     48        </p>
     49
     50        <?php if ( $email_enabled || $slack_enabled ) { ?>
     51            <p class="two-factor-email-resend">
     52                <input type="submit" class="button" name="two-factor-backup-resend" value="<?php esc_attr_e( 'Resend Code', 'wporg' ); ?>" />
     53            </p>
     54        <?php } ?>
     55
     56        <script type="text/javascript">
     57            setTimeout( function(){
     58                var d;
     59                try{
     60                    d = document.getElementById('authcode');
     61                    d.value = '';
     62                    d.focus();
     63                } catch(e){}
     64            }, 200);
     65        </script>
     66        <?php
     67    }
     68
     69    function is_available_for_user( $user ) { return true; }
     70
     71    protected $providers = [];
    2772
    2873    protected function __construct() {
    29         /*
    3074        $providers = [
    3175            'WPORG_Two_Factor_Email'        => __DIR__ . '/class-wporg-two-factor-email.php',
    3276            'WPORG_Two_Factor_Backup_Codes' => __DIR__ . '/class-wporg-two-factor-backup-codes.php',
    3377            'WPORG_Two_Factor_Slack'        => __DIR__ . '/class-wporg-two-factor-slack.php'
     78        ];
     79        $providers = apply_filters( 'wporg_two_factor_secondary_providers', $providers );
    3480
    35         ];
    36         */
     81        // Add some CSS for this clss.
     82        add_action( 'login_head', [ $this, 'add_styles' ] );
     83
     84        foreach ( $providers as $class => $path ) {
     85            include_once( $path );
     86
     87            if ( class_exists( $class ) ) {
     88                try {
     89                    $this->providers[ $class ] = call_user_func( array( $class, 'get_instance' ) );
     90                } catch ( Exception $e ) {
     91                    unset( $this->providers[ $class ] );
     92                }
     93            }
     94        }
     95
    3796        return parent::__construct();
    3897    }
     98
     99    // Add some specific styles for this class.
     100    public function add_styles() {
     101        if ( isset( $_GET['provider'] ) && $_GET['provider'] === __CLASS__ ) {
     102            echo '<style>
     103                body.login-action-backup_2fa .backup-methods-wrap {
     104                    display: none;
     105                }
     106                body.login-action-backup_2fa input[name="two-factor-backup-resend"] {
     107
     108                }
     109            </style>';
     110        }
     111    }
     112
     113    public function pre_process_authentication( $user ) {
     114        if ( isset( $_REQUEST['two-factor-backup-resend'] ) ) {
     115            return $this->send_codes_to_user( $user, true );
     116        }
     117
     118        return false;
     119    }
     120
     121    // Send codes to the user based on the providers available.
     122    //
     123    protected function send_codes_to_user( $user, $resend = false ) {
     124        $result = false;
     125
     126        if (
     127            isset( $this->providers['WPORG_Two_Factor_Email'] ) &&
     128            $this->providers['WPORG_Two_Factor_Email']->is_available_for_user( $user )
     129        ) {
     130            if (
     131                $resend |
     132                ! $this->providers['WPORG_Two_Factor_Email']->user_has_token( $user->ID )
     133            ) {
     134                $result = true;
     135                $this->providers['WPORG_Two_Factor_Email']->generate_and_email_token( $user );
     136            }
     137        }
     138
     139        if (
     140            isset( $this->providers['WPORG_Two_Factor_Slack'] ) &&
     141            $this->providers['WPORG_Two_Factor_Slack']->is_available_for_user( $user )
     142        ) {
     143            if (
     144                $resend ||
     145                ! $this->providers['WPORG_Two_Factor_Slack']->user_has_token( $user->ID )
     146            ) {
     147                $result = true;
     148                $this->providers['WPORG_Two_Factor_Slack']->generate_and_slack_token( $user );
     149            }
     150        }
     151
     152        return $result;
     153    }
     154
     155    function validate_authentication( $user ) {
     156        if ( empty( $_POST['two-factor-backup-code'] ) ) {
     157            return false;
     158        }
     159
     160        $backup_code = $_POST['two-factor-backup-code'];
     161
     162        $authenticated = false;
     163
     164        foreach ( $this->providers as $provider ) {
     165            if (
     166                $provider->is_available_for_user( $user ) &&
     167                $provider->validate_authentication( $user, $backup_code )
     168            ) {
     169                $authenticated = true;
     170                break;
     171            }
     172        }
     173
     174        // Also check the Primary method for the user just in case.
     175        $primary_provider = WPORG_Two_Factor_Primary::get_instance();
     176        if (
     177            ! $authenticated &&
     178            $primary_provider->is_available_for_user( $user ) &&
     179            $primary_provider->validate_authentication( $user, $backup_code )
     180        ) {
     181            $authenticated = true;
     182        }
     183
     184        if ( $authenticated ) {
     185            foreach ( $this->providers as $provider ) {
     186                if ( is_callable( [ $provider, 'delete_token' ] ) ) {
     187                    $provider->delete_token( $user->ID );
     188                }
     189            }
     190        }
     191
     192        return $authenticated;
     193    }
    39194}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor/providers/class-wporg-two-factor-slack.php

    r6735 r6759  
    11<?php
    22
    3 require_once TWO_FACTOR_DIR . 'providers/class.two-factor-email.php';
     3require_once __DIR__ . '/class-wporg-two-factor-email.php';
    44
    5 class WPORG_Two_Factor_Slack extends Two_Factor_Email {
     5class WPORG_Two_Factor_Slack extends WPORG_Two_Factor_Email {
    66
    77    /**
     
    1111     */
    1212    const TOKEN_META_KEY = '_two_factor_slack_token';
    13 
    14     /**
    15      * Name of the input field used for code resend.
    16      *
    17      * @var string
    18      */
    19     const INPUT_NAME_RESEND_CODE = 'two-factor-slack-code-resend';
    2013
    2114    /**
     
    3427
    3528    public function get_label() {
    36         return _x( 'Slack', 'Provider Label', 'wporg' );
     29        return 'Slack'; // Not marked for translation as this shouldn't be called/displayed.
    3730    }
    3831
    39     /**
    40      * Whether this Two Factor provider is configured and available for the user specified.
    41      *
    42      * @since 0.1-dev
    43      *
    44      * @param WP_User $user WP_User object of the logged-in user.
    45      * @return boolean
    46      */
     32    protected function get_slack_details( $user_id ) {
     33        global $wpdb;
     34
     35        static $cached_details = [];
     36        if ( isset( $cached_details[ $user_id ] ) ) {
     37            return $cached_details[ $user_id ];
     38        }
     39
     40        // TODO abstract this? memcache it?
     41        $user_details = $wpdb->get_var( $wpdb->prepare( "SELECT profiledata FROM slack_users WHERE user_id = %d LIMIT 1", $user_id ) );
     42        $user_details = $user_details ? json_decode( $user_details ) : false;
     43
     44        $cached_details[ $user_id ] = $user_details;
     45
     46        return $user_details;
     47    }
     48
    4749    public function is_available_for_user( $user ) {
    48         // TODO Check if the user has a 2FA slack account.
     50        $user_details = $this->get_slack_details( $user->ID );
     51
     52        // Require the Slack account to exist, and for the user to have 2FA enabled on Slack.
     53        return $user_detauls && empty( $user_details->deleted ) && ! empty( $user_details->has_2fa );
     54    }
     55
     56    public function generate_and_email_token( $user ) {
     57        return $this->generate_and_slack_token( $user );
     58    }
     59
     60    public function generate_and_slack_token( $user ) {
     61        $token = $this->generate_token( $user->ID );
     62
     63        $message = "Please enter the following verification code on WordPress.org to complete your login:\n{$token}";
     64
     65        $slack_details = $this->get_slack_details( $user->ID );
     66
     67        if ( $slack_details->id ) {
     68            // TODO: Replace this with a named Slack Bot.
     69            return slack_dm( $message, $slack_details->id );
     70        }
     71
    4972        return false;
    5073    }
    5174
    52     /**
    53      * Generate and email the user token.
    54      *
    55      * @since 0.1-dev
    56      *
    57      * @param WP_User $user WP_User object of the logged-in user.
    58      */
    59     public function generate_and_email_token( $user ) {
    60         $token = $this->generate_token( $user->ID );
    61 
    62         /* translators: %s: site name */
    63         $subject = wp_strip_all_tags( sprintf( __( 'Your login confirmation code for %s', 'wporg' ), get_bloginfo( 'name' ) ) );
    64         /* translators: %s: token */
    65         $message = wp_strip_all_tags( sprintf( __( 'Enter %s to log in.', 'wporg' ), $token ) );
    66 
    67         $who = '@dd32';
    68 
    69         return slack_dm( $subject . "\n" . $message, $who );
    70     }
    71 
    7275}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-two-factor/wporg-two-factor.php

    r6758 r6759  
    199199     */
    200200    public static function enable_two_factor( $user_id ) {
    201         // True if at least one provider method was set.
    202201        return (
    203202            update_user_meta( $user_id, self::PROVIDER_USER_META_KEY,          'WPORG_Two_Factor_Primary' ) &&
     
    223222    public static function user_two_factor_options( $user ) {
    224223        wp_enqueue_script( 'two-factor-edit', plugins_url( 'js/profile-edit.js' , __FILE__ ), [ 'jquery' ], 1, true );
    225         wp_localize_script( 'two-factor-edit', 'two_factor_edit', array( 
     224        wp_localize_script( 'two-factor-edit', 'two_factor_edit', array(
    226225            'ajaxurl' => admin_url( 'admin-ajax.php' ),
    227226        ) );
Note: See TracChangeset for help on using the changeset viewer.