Making WordPress.org

Changeset 6759


Ignore:
Timestamp:
02/26/2018 07:06:12 AM (9 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.