summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/modules/sso.php')
-rw-r--r--plugins/jetpack/modules/sso.php978
1 files changed, 978 insertions, 0 deletions
diff --git a/plugins/jetpack/modules/sso.php b/plugins/jetpack/modules/sso.php
new file mode 100644
index 00000000..25203512
--- /dev/null
+++ b/plugins/jetpack/modules/sso.php
@@ -0,0 +1,978 @@
+<?php
+
+/**
+ * Module Name: Jetpack Single Sign On
+ * Module Description: Allow your users to log in using their WordPress.com accounts.
+ * Jumpstart Description: lets you login to all your Jetpack-enabled sites with one click using your WordPress.com account.
+ * Sort Order: 30
+ * Recommendation Order: 5
+ * First Introduced: 2.6
+ * Requires Connection: Yes
+ * Auto Activate: No
+ * Module Tags: Developers
+ * Feature: Jumpstart
+ */
+
+class Jetpack_SSO {
+ static $instance = null;
+
+ private function __construct() {
+
+ self::$instance = $this;
+
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
+ add_action( 'login_init', array( $this, 'login_init' ) );
+ add_action( 'delete_user', array( $this, 'delete_connection_for_user' ) );
+ add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
+ add_action( 'init', array( $this, 'maybe_logout_user' ), 5 );
+ add_action( 'jetpack_modules_loaded', array( $this, 'module_configure_button' ) );
+
+ // Adding this action so that on login_init, the action won't be sanitized out of the $action global.
+ add_action( 'login_form_jetpack-sso', '__return_true' );
+
+ if ( $this->should_hide_login_form() && apply_filters( 'jetpack_sso_display_disclaimer', true ) ) {
+ add_action( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
+ }
+ }
+
+ /**
+ * Returns the single instance of the Jetpack_SSO object
+ *
+ * @since 2.8
+ * @return Jetpack_SSO
+ **/
+ public static function get_instance() {
+ if( !is_null( self::$instance ) )
+ return self::$instance;
+
+ return self::$instance = new Jetpack_SSO;
+ }
+
+ /**
+ * Add configure button and functionality to the module card on the Jetpack screen
+ **/
+ public static function module_configure_button() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, array( __CLASS__, 'module_configuration_load' ) );
+ Jetpack::module_configuration_head( __FILE__, array( __CLASS__, 'module_configuration_head' ) );
+ Jetpack::module_configuration_screen( __FILE__, array( __CLASS__, 'module_configuration_screen' ) );
+ }
+
+ public static function module_configuration_load() {
+ // wp_safe_redirect( admin_url( 'options-general.php#configure-sso' ) );
+ // exit;
+ }
+
+ public static function module_configuration_head() {}
+
+ public static function module_configuration_screen() {
+ ?>
+ <form method="post" action="options.php">
+ <?php settings_fields( 'jetpack-sso' ); ?>
+ <?php do_settings_sections( 'jetpack-sso' ); ?>
+ <?php submit_button(); ?>
+ </form>
+ <?php
+ }
+
+ /**
+ * If jetpack_force_logout == 1 in current user meta the user will be forced
+ * to logout and reauthenticate with the site.
+ **/
+ public function maybe_logout_user() {
+ global $current_user;
+
+ if( 1 == $current_user->jetpack_force_logout ) {
+ delete_user_meta( $current_user->ID, 'jetpack_force_logout' );
+ self::delete_connection_for_user( $current_user->ID );
+ wp_logout();
+ wp_safe_redirect( wp_login_url() );
+ }
+ }
+
+
+ /**
+ * Adds additional methods the WordPress xmlrpc API for handling SSO specific features
+ *
+ * @param array $methods
+ * @return array
+ **/
+ public function xmlrpc_methods( $methods ) {
+ $methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' );
+ return $methods;
+ }
+
+ /**
+ * Marks a user's profile for disconnect from WordPress.com and forces a logout
+ * the next time the user visits the site.
+ **/
+ public function xmlrpc_user_disconnect( $user_id ) {
+ $user_query = new WP_User_Query(
+ array(
+ 'meta_key' => 'wpcom_user_id',
+ 'meta_value' => $user_id
+ )
+ );
+ $user = $user_query->get_results();
+ $user = $user[0];
+
+
+ if( $user instanceof WP_User ) {
+ $user = wp_set_current_user( $user->ID );
+ update_user_meta( $user->ID, 'jetpack_force_logout', '1' );
+ self::delete_connection_for_user( $user->ID );
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adds settings fields to Settings > General > Single Sign On that allows users to
+ * turn off the login form on wp-login.php
+ *
+ * @since 2.7
+ **/
+ public function register_settings() {
+
+ add_settings_section(
+ 'jetpack_sso_settings',
+ __( 'Jetpack Single Sign On' , 'jetpack' ),
+ '__return_false',
+ 'jetpack-sso'
+ );
+
+ /*
+ * Settings > General > Jetpack Single Sign On
+ * Checkbox for Remove default login form
+ */
+ /* Hide in 2.9
+ register_setting(
+ 'general',
+ 'jetpack_sso_remove_login_form',
+ array( $this, 'validate_settings_remove_login_form_checkbox' )
+ );
+
+ add_settings_field(
+ 'jetpack_sso_remove_login_form',
+ __( 'Remove default login form?' , 'jetpack' ),
+ array( $this, 'render_remove_login_form_checkbox' ),
+ 'general',
+ 'jetpack_sso_settings'
+ );
+ */
+
+ /*
+ * Settings > General > Jetpack Single Sign On
+ * Require two step authentication
+ */
+ register_setting(
+ 'jetpack-sso',
+ 'jetpack_sso_require_two_step',
+ array( $this, 'validate_jetpack_sso_require_two_step' )
+ );
+
+ add_settings_field(
+ 'jetpack_sso_require_two_step',
+ '', // __( 'Require Two-Step Authentication' , 'jetpack' ),
+ array( $this, 'render_require_two_step' ),
+ 'jetpack-sso',
+ 'jetpack_sso_settings'
+ );
+
+
+ /*
+ * Settings > General > Jetpack Single Sign On
+ */
+ register_setting(
+ 'jetpack-sso',
+ 'jetpack_sso_match_by_email',
+ array( $this, 'validate_jetpack_sso_match_by_email' )
+ );
+
+ add_settings_field(
+ 'jetpack_sso_match_by_email',
+ '', // __( 'Match by Email' , 'jetpack' ),
+ array( $this, 'render_match_by_email' ),
+ 'jetpack-sso',
+ 'jetpack_sso_settings'
+ );
+ }
+
+ /**
+ * Builds the display for the checkbox allowing user to require two step
+ * auth be enabled on WordPress.com accounts before login. Displays in Settings > General
+ *
+ * @since 2.7
+ **/
+ public function render_require_two_step() {
+ echo '<label>';
+ echo '<input type="checkbox" name="jetpack_sso_require_two_step" ' . checked( 1 == get_option( 'jetpack_sso_require_two_step' ), true, false ) . '> ';
+ esc_html_e( 'Require Two-Step Authentication' , 'jetpack' );
+ echo '</label>';
+ }
+
+ /**
+ * Validate the require two step checkbox in Settings > General
+ *
+ * @since 2.7
+ * @return boolean
+ **/
+ public function validate_jetpack_sso_require_two_step( $input ) {
+ return ( ! empty( $input ) ) ? 1 : 0;
+ }
+
+ /**
+ * Builds the display for the checkbox allowing the user to allow matching logins by email
+ * Displays in Settings > General
+ *
+ * @since 2.9
+ **/
+ public function render_match_by_email() {
+ echo '<label>';
+ echo '<input type="checkbox" name="jetpack_sso_match_by_email"' . checked( 1 == get_option( 'jetpack_sso_match_by_email' ), true, false) . '> ';
+ esc_html_e( 'Match by Email', 'jetpack' );
+ echo '</label>';
+ }
+
+ /**
+ * Validate the match by email check in Settings > General
+ *
+ * @since 2.9
+ * @return boolean
+ **/
+ public function validate_jetpack_sso_match_by_email( $input ) {
+ return ( ! empty( $input ) ) ? 1 : 0;
+ }
+
+ /**
+ * Builds the display for the checkbox allowing users to remove the default
+ * WordPress login form from wp-login.php. Displays in Settings > General
+ *
+ * @since 2.7
+ **/
+ public function render_remove_login_form_checkbox() {
+ if( $this->is_user_connected( get_current_user_id() ) ) {
+ echo '<a name="configure-sso"></a>';
+ echo '<input type="checkbox" name="jetpack_sso_remove_login_form[remove_login_form]" ' . checked( 1 == get_option( 'jetpack_sso_remove_login_form' ), true, false ) . '>';
+ echo '<p class="description">Removes default login form and disallows login via POST</p>';
+ } else {
+ echo 'Your account must be connected to WordPress.com before disabling the login form.';
+ echo '<br/>' . $this->button();
+ }
+ }
+
+ /**
+ * Validate settings input from Settings > General
+ *
+ * @since 2.7
+ * @return boolean
+ **/
+ public function validate_settings_remove_login_form_checkbox( $input ) {
+ return ( isset( $input['remove_login_form'] ) )? 1: 0;
+ }
+
+ /**
+ * Removes 'Lost your password?' text from the login form if user
+ * does not want to show the login form
+ *
+ * @since 2.7
+ * @return string
+ **/
+ public function remove_lost_password_text( $text ) {
+ if( 'Lost your password?' == $text )
+ $text = '';
+ return $text;
+ }
+
+ /**
+ * Checks to determine if the user wants to login on wp-login
+ *
+ * This function mostly exists to cover the exceptions to login
+ * that may exist as other parameters to $_GET[action] as $_GET[action]
+ * does not have to exist. By default WordPress assumes login if an action
+ * is not set, however this may not be true, as in the case of logout
+ * where $_GET[loggedout] is instead set
+ *
+ * @return boolean
+ **/
+ private function wants_to_login() {
+ $wants_to_login = false;
+
+ // Cover default WordPress behavior
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'login';
+
+ // And now the exceptions
+ $action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
+
+ if( 'login' == $action ) {
+ $wants_to_login = true;
+ }
+
+ return $wants_to_login;
+ }
+
+ private function bypass_login_forward_wpcom() {
+ return apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false );
+ }
+
+ function login_init() {
+ global $action;
+
+ /**
+ * If the user is attempting to logout AND the auto-forward to WordPress.com
+ * login is set then we need to ensure we do not auto-forward the user and get
+ * them stuck in an infinite logout loop.
+ */
+ if( isset( $_GET['loggedout'] ) && $this->bypass_login_forward_wpcom() ) {
+ add_filter( 'jetpack_remove_login_form', '__return_true' );
+ add_filter( 'gettext', array( $this, 'remove_lost_password_text' ) );
+ }
+
+ /**
+ * Check to see if the site admin wants to automagically forward the user
+ * to the WordPress.com login page AND that the request to wp-login.php
+ * is not something other than login (Like logout!)
+ */
+ if (
+ $this->wants_to_login()
+ && $this->bypass_login_forward_wpcom()
+ ) {
+ add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
+ wp_safe_redirect( $this->build_sso_url() );
+ }
+
+ if ( 'login' === $action ) {
+ wp_enqueue_script( 'jquery' );
+ wp_enqueue_style( 'genericons' );
+ add_action( 'login_footer', array( $this, 'login_form' ) );
+ add_action( 'login_footer', array( $this, 'login_footer' ) );
+/*
+ if ( get_option( 'jetpack_sso_remove_login_form' ) ) {
+ // Check to see if the user is attempting to login via the default login form.
+ // If so we need to deny it and forward elsewhere.
+ if( isset( $_REQUEST['wp-submit'] ) && 'Log In' == $_REQUEST['wp-submit'] ) {
+ wp_die( 'Login not permitted by this method. ');
+ }
+ add_filter( 'gettext', array( $this, 'remove_lost_password_text' ) );
+ }
+*/
+ } elseif ( 'jetpack-sso' === $action ) {
+ if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
+ $this->handle_login();
+ } else {
+ if ( Jetpack::check_identity_crisis() ) {
+ wp_die( __( "Error: This site's Jetpack connection is currently experiencing problems.", 'jetpack' ) );
+ } else {
+ $this->maybe_save_cookie_redirect();
+ // Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
+ add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
+ wp_safe_redirect( $this->build_sso_url() );
+ }
+ }
+ }
+ }
+
+ /**
+ * Conditionally save the redirect_to url as a cookie.
+ */
+ public static function maybe_save_cookie_redirect() {
+ if ( headers_sent() ) {
+ return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) );
+ }
+
+ // If we have something to redirect to
+ if ( ! empty( $_GET['redirect_to'] ) ) {
+ $url = esc_url_raw( $_GET['redirect_to'] );
+ setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
+ // Otherwise, if it's already set
+ } elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
+ // Purge it.
+ setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
+ }
+
+ if ( ! empty( $_GET['rememberme'] ) ) {
+ setcookie( 'jetpack_sso_remember_me', '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
+ } elseif ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
+ setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
+ }
+ }
+
+ /**
+ * Determine if the login form should be hidden or not
+ *
+ * Method is private only because it is only used in this class so far.
+ * Feel free to change it later
+ *
+ * @return bool
+ **/
+ private function should_hide_login_form() {
+ return apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) );
+ }
+
+ function login_form() {
+ $classes = '';
+
+ if( $this->should_hide_login_form() ) {
+ $classes .= ' forced-sso';
+ }
+ echo '<div class="jetpack-sso-wrap' . $classes . '">' . $this->button() . '</div>';
+ }
+
+ function login_footer() {
+ $hide_login_form = $this->should_hide_login_form();
+ ?>
+ <style>
+ #loginform {
+ overflow: hidden;
+ padding-bottom: 26px;
+ }
+ .jetpack-sso-wrap {
+ <?php if ( $hide_login_form ) : ?>
+ text-align: center;
+ <?php else : ?>
+ float: right;
+ <?php endif; ?>
+ margin: 1em 0 0;
+ clear: right;
+ display: block;
+ }
+
+ <?php if ( $hide_login_form ) : ?>
+ .forced-sso .jetpack-sso.button {
+ font-size: 16px;
+ line-height: 27px;
+ height: 37px;
+ padding: 5px 12px 6px 47px;
+ }
+ .forced-sso .jetpack-sso.button:before {
+ font-size: 28px !important;
+ height: 37px;
+ padding: 5px 5px 4px;
+ width: 37px;
+ }
+ <?php endif; ?>
+ </style>
+ <script>
+ jQuery(document).ready(function($){
+ <?php if ( $hide_login_form ) : ?>
+ $( '#loginform' ).empty();
+ <?php endif; ?>
+ $( '#loginform' ).append( $( '.jetpack-sso-wrap' ) );
+
+ var $rememberme = $( '#rememberme' ),
+ $ssoButton = $( 'a.jetpack-sso.button' );
+
+ $rememberme.on( 'change', function() {
+ var url = $ssoButton.prop( 'href' ),
+ isChecked = $rememberme.prop( 'checked' ) ? 1 : 0;
+
+ if ( url.match( /&rememberme=\d/ ) ) {
+ url = url.replace( /&rememberme=\d/, '&rememberme=' + isChecked );
+ } else {
+ url += '&rememberme=' + isChecked;
+ }
+
+ $ssoButton.prop( 'href', url );
+ } ).change();
+
+ });
+ </script>
+ <?php
+ }
+
+ static function delete_connection_for_user( $user_id ) {
+ if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
+ return;
+ }
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => $user_id
+ ) );
+ $xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
+
+ if ( $xml->isError() ) {
+ return false;
+ }
+
+ return $xml->getResponse();
+ }
+
+ static function request_initial_nonce() {
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => get_current_user_id()
+ ) );
+ $xml->query( 'jetpack.sso.requestNonce' );
+
+ if ( $xml->isError() ) {
+ wp_die( sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
+ }
+
+ return $xml->getResponse();
+ }
+
+ /**
+ * The function that actually handles the login!
+ */
+ function handle_login() {
+ $wpcom_nonce = sanitize_key( $_GET['sso_nonce'] );
+ $wpcom_user_id = (int) $_GET['user_id'];
+ $result = sanitize_key( $_GET['result'] );
+
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => get_current_user_id()
+ ) );
+ $xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
+
+ if ( $xml->isError() ) {
+ wp_die( sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
+ }
+
+ $user_data = $xml->getResponse();
+
+ if ( empty( $user_data ) ) {
+ wp_die( __( 'Error, invalid response data.', 'jetpack' ) );
+ }
+
+ $user_data = (object) $user_data;
+ $user = null;
+ do_action( 'jetpack_sso_pre_handle_login', $user_data );
+
+ // Check to see if having two step enable on wpcom is a requirement to login here
+ $require_two_step = apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) );
+ if( $require_two_step && 0 == (int) $user_data->two_step_enabled ) {
+ $this->user_data = $user_data;
+ do_action( 'wp_login_failed', $user_data->login );
+ add_action( 'login_message', array( $this, 'error_msg_enable_two_step' ) );
+ return;
+ }
+
+ if ( isset( $_GET['state'] ) && ( 0 < strpos( $_GET['state'], '|' ) ) ) {
+ list( $state, $nonce ) = explode( '|', $_GET['state'] );
+
+ if ( wp_verify_nonce( $nonce, $state ) ) {
+ if ( 'sso-link-user' == $state ) {
+ $user = wp_get_current_user();
+ update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
+ add_filter( 'login_redirect', array( __CLASS__, 'profile_page_url' ) );
+ }
+ } else wp_nonce_ays();
+ }
+
+ if ( empty( $user ) ) {
+ $user = $this->get_user_by_wpcom_id( $user_data->ID );
+ }
+
+ // If we don't have one by wpcom_user_id, try by the email?
+ if ( empty( $user ) && self::match_by_email() ) {
+ $user = get_user_by( 'email', $user_data->email );
+ if ( $user ) {
+ update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
+ }
+ }
+
+ // If we've still got nothing, create the user.
+ if ( empty( $user ) && ( get_option( 'users_can_register' ) || self::new_user_override() ) ) {
+ // If not matching by email we still need to verify the email does not exist
+ // or this blows up
+ /**
+ * If match_by_email is true, we know the email doesn't exist, as it would have
+ * been found in the first pass. If get_user_by( 'email' ) doesn't find the
+ * user, then we know that email is unused, so it's safe to add.
+ */
+ if ( self::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) {
+ $username = $user_data->login;
+
+ if ( username_exists( $username ) ) {
+ $username = $user_data->login . '_' . $user_data->ID;
+ }
+
+ $tries = 0;
+ while ( username_exists( $username ) ) {
+ $username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
+ if ( $tries++ >= 5 ) {
+ wp_die( __( "Error: Couldn't create suitable username.", 'jetpack' ) );
+ }
+ }
+
+ $password = wp_generate_password( 20 );
+ $user_id = wp_create_user( $username, $password, $user_data->email );
+ $user = get_userdata( $user_id );
+
+ $user->display_name = $user_data->display_name;
+ $user->first_name = $user_data->first_name;
+ $user->last_name = $user_data->last_name;
+ $user->url = $user_data->url;
+ $user->description = $user_data->description;
+ wp_update_user( $user );
+
+ update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
+ } else {
+ $this->user_data = $user_data;
+ // do_action( 'wp_login_failed', $user_data->login );
+ add_action( 'login_message', array( $this, 'error_msg_email_already_exists' ) );
+ return;
+ }
+ }
+
+ do_action( 'jetpack_sso_handle_login', $user, $user_data );
+
+ if ( $user ) {
+ // Cache the user's details, so we can present it back to them on their user screen.
+ update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
+
+ $remember = false;
+ if ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
+ $remember = true;
+ // And then purge it
+ setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
+ }
+ // Set remember me value
+ $remember = apply_filters( 'jetpack_remember_login', $remember );
+ wp_set_auth_cookie( $user->ID, $remember );
+
+ // Run the WP core login action
+ do_action( 'wp_login', $user->user_login, $user );
+
+ $_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
+ $redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
+
+ // If we have a saved redirect to request in a cookie
+ if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
+ // Set that as the requested redirect to
+ $redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
+ // And then purge it
+ setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
+ }
+
+ wp_safe_redirect( apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user ) );
+ exit;
+ }
+
+ $this->user_data = $user_data;
+ do_action( 'wp_login_failed', $user_data->login );
+ add_action( 'login_message', array( $this, 'cant_find_user' ) );
+ }
+
+ static function profile_page_url() {
+ return admin_url( 'profile.php' );
+ }
+
+ static function match_by_email() {
+ $match_by_email = ( 1 == get_option( 'jetpack_sso_match_by_email', true ) ) ? true: false;
+ $match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : $match_by_email;
+
+ return apply_filters( 'jetpack_sso_match_by_email', $match_by_email );
+ }
+
+ static function new_user_override() {
+ $new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false;
+ return apply_filters( 'jetpack_sso_new_user_override', $new_user_override );
+ }
+
+ function allowed_redirect_hosts( $hosts ) {
+ if ( empty( $hosts ) ) {
+ $hosts = array();
+ }
+ $hosts[] = 'wordpress.com';
+
+ return array_unique( $hosts );
+ }
+
+ function button( $args = array() ) {
+ $defaults = array(
+ 'action' => 'jetpack-sso',
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( ! empty( $_GET['redirect_to'] ) ) {
+ $args['redirect_to'] = esc_url_raw( $_GET['redirect_to'] );
+ }
+
+ $url = add_query_arg( $args, wp_login_url() );
+
+ $css = "<style>
+ .jetpack-sso.button {
+ position: relative;
+ padding-left: 37px;
+ }
+ .jetpack-sso.button:before {
+ display: block;
+ box-sizing: border-box;
+ padding: 7px 0 0;
+ text-align: center;
+ position: absolute;
+ top: -1px;
+ left: -1px;
+ border-radius: 2px 0 0 2px;
+ content: '\\f205';
+ background: #0074a2;
+ color: #fff;
+ -webkit-font-smoothing: antialiased;
+ width: 30px;
+ height: 107%;
+ height: calc( 100% + 2px );
+ font: normal 22px/1 Genericons !important;
+ text-shadow: none;
+ }
+ @media screen and (min-width: 783px) {
+ .jetpack-sso.button:before {
+ padding-top: 3px;
+ }
+ }
+ .jetpack-sso.button:hover {
+ border: 1px solid #aaa;
+ }";
+
+ if ( version_compare( $GLOBALS['wp_version'], '3.8-alpha', '<' ) ) {
+ $css .= "
+ .jetpack-sso.button:before {
+ width: 25px;
+ font-size: 18px !important;
+ }
+ ";
+ }
+
+ $css .= "</style>";
+
+ $button = sprintf( '<a href="%1$s" class="jetpack-sso button">%2$s</a>', esc_url( $url ), esc_html__( 'Log in with WordPress.com', 'jetpack' ) );
+ return $button . $css;
+ }
+
+ function build_sso_url( $args = array() ) {
+ $defaults = array(
+ 'action' => 'jetpack-sso',
+ 'site_id' => Jetpack_Options::get_option( 'id' ),
+ 'sso_nonce' => self::request_initial_nonce(),
+ );
+
+ if ( isset( $_GET['state'] ) && check_admin_referer( $_GET['state'] ) ) {
+ $defaults['state'] = rawurlencode( $_GET['state'] . '|' . $_GET['_wpnonce'] );
+ }
+
+ $args = wp_parse_args( $args, $defaults );
+ $url = add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
+
+ return $url;
+ }
+
+ function get_user_by_wpcom_id( $wpcom_user_id ) {
+ $user_query = new WP_User_Query( array(
+ 'meta_key' => 'wpcom_user_id',
+ 'meta_value' => intval( $wpcom_user_id ),
+ 'number' => 1,
+ ) );
+
+ $users = $user_query->get_results();
+ return $users ? array_shift( $users ) : null;
+ }
+
+ /**
+ * Error message displayed on the login form when two step is required and
+ * the user's account on WordPress.com does not have two step enabled.
+ *
+ * @since 2.7
+ * @param string $message
+ * @return string
+ **/
+ public function error_msg_enable_two_step( $message ) {
+ $err = __( sprintf( 'This site requires two step authentication be enabled for your user account on WordPress.com. Please visit the <a href="%1$s"> Security Settings</a> page to enable two step', 'https://wordpress.com/settings/security/' ) , 'jetpack' );
+
+ $message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
+
+ return $message;
+ }
+
+ /**
+ * Error message displayed when the user tries to SSO, but match by email
+ * is off and they already have an account with their email address on
+ * this site.
+ *
+ * @param string $message
+ * @return string
+ */
+ public function error_msg_email_already_exists( $message ) {
+ $err = __( sprintf( 'You already have an account on this site. Please visit your <a href="%1$s">profile page</a> page to link your account to WordPress.com!', admin_url( 'profile.php' ) ) , 'jetpack' );
+
+ $message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
+
+ return $message;
+ }
+
+ /**
+ * Message displayed when the site admin has disabled the default WordPress
+ * login form in Settings > General > Single Sign On
+ *
+ * @since 2.7
+ * @param string $message
+ * @return string
+ **/
+ public function msg_login_by_jetpack( $message ) {
+
+ $msg = __( sprintf( 'Jetpack authenticates through WordPress.com — to log in, enter your WordPress.com username and password, or <a href="%1$s">visit WordPress.com</a> to create a free account now.', 'http://wordpress.com/signup' ) , 'jetpack' );
+
+ $msg = apply_filters( 'jetpack_sso_disclaimer_message', $msg );
+
+ $message .= sprintf( '<p class="message">%s</p>', $msg );
+ return $message;
+ }
+
+ /**
+ * Error message displayed on the login form when the user attempts
+ * to post to the login form and it is disabled.
+ *
+ * @since 2.8
+ * @param string $message
+ * @param string
+ **/
+ public function error_msg_login_method_not_allowed( $message ) {
+ $err = __( 'Login method not allowed' , 'jetpack' );
+ $message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
+
+ return $message;
+ }
+ function cant_find_user( $message ) {
+ if ( self::match_by_email() ) {
+ $err_format = __( 'We couldn\'t find an account with the email <strong><code>%1$s</code></strong> to log you in with. If you already have an account on <strong>%2$s</strong>, please make sure that <strong><code>%1$s</code></strong> is configured as the email address, or that you have connected to WordPress.com on your profile page.', 'jetpack' );
+ } else {
+ $err_format = __( 'We couldn\'t find any account on <strong>%2$s</strong> that is linked to your WordPress.com account to log you in with. If you already have an account on <strong>%2$s</strong>, please make sure that you have connected to WordPress.com on your profile page.', 'jetpack' );
+ }
+ $err = sprintf( $err_format, $this->user_data->email, get_bloginfo( 'name' ) );
+ $message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
+ return $message;
+ }
+
+ /**
+ * Deal with user connections...
+ */
+ function admin_init() {
+ add_action( 'show_user_profile', array( $this, 'edit_profile_fields' ) ); // For their own profile
+ add_action( 'edit_user_profile', array( $this, 'edit_profile_fields' ) ); // For folks editing others profiles
+
+ if ( isset( $_GET['jetpack_sso'] ) && 'purge' == $_GET['jetpack_sso'] && check_admin_referer( 'jetpack_sso_purge' ) ) {
+ $user = wp_get_current_user();
+ // Remove the connection on the wpcom end.
+ self::delete_connection_for_user( $user->ID );
+ // Clear it locally.
+ delete_user_meta( $user->ID, 'wpcom_user_id' );
+ delete_user_meta( $user->ID, 'wpcom_user_data' );
+ // Forward back to the profile page.
+ wp_safe_redirect( remove_query_arg( array( 'jetpack_sso', '_wpnonce' ) ) );
+ }
+ }
+
+ /**
+ * Determines if a local user is connected to WordPress.com
+ *
+ * @since 2.8
+ * @param integer $user_id - Local user id
+ * @return boolean
+ **/
+ public function is_user_connected( $user_id ) {
+ return $this->get_user_data( $user_id ) ;
+ }
+
+ /**
+ * Retrieves a user's WordPress.com data
+ *
+ * @since 2.8
+ * @param integer $user_id - Local user id
+ * @return mixed null or stdClass
+ **/
+ public function get_user_data( $user_id ) {
+ return get_user_meta( $user_id, 'wpcom_user_data', true );
+ }
+
+ function edit_profile_fields( $user ) {
+ wp_enqueue_style( 'genericons' );
+ ?>
+
+ <h3><?php _e( 'WordPress.com Single Sign On', 'jetpack' ); ?></h3>
+ <p><?php _e( 'Connecting with WordPress.com SSO enables you to log in via your WordPress.com account.', 'jetpack' ); ?></p>
+
+ <?php if ( $this->is_user_connected( $user->ID ) ) : /* If the user is currently connected... */ ?>
+ <?php $user_data = $this->get_user_data( $user->ID ); ?>
+ <table class="form-table jetpack-sso-form-table">
+ <tbody>
+ <tr>
+ <td>
+ <div class="profile-card">
+ <?php echo get_avatar( $user_data->email ); ?>
+ <p class="connected"><strong><?php _e( 'Connected', 'jetpack' ); ?></strong></p>
+ <p><?php echo esc_html( $user_data->login ); ?></p>
+ <span class="two_step">
+ <?php
+ if( $user_data->two_step_enabled ) {
+ ?> <p class="enabled"><a href="https://wordpress.com/settings/security/"><?php _e( 'Two step Enabled', 'jetpack' ); ?></a></p> <?php
+ } else {
+ ?> <p class="disabled"><a href="https://wordpress.com/settings/security/"><?php _e( 'Two step Disabled', 'jetpack' ); ?></a></p> <?php
+ }
+ ?>
+ </span>
+
+ </div>
+ <p><a class="button button-secondary" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'jetpack_sso', 'purge' ), 'jetpack_sso_purge' ) ); ?>"><?php _e( 'Unlink This Account', 'jetpack' ); ?></a></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <style>
+ .jetpack-sso-form-table td {
+ padding-left: 0;
+ }
+
+ .jetpack-sso-form-table .profile-card {
+ padding: 10px;
+ background: #fff;
+ overflow: hidden;
+ max-width: 400px;
+ box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.1 );
+ margin-bottom: 1em;
+ }
+
+ .jetpack-sso-form-table .profile-card img {
+ float: left;
+ margin-right: 1em;
+ width: 48px;
+ height: 48px;
+ }
+
+ .jetpack-sso-form-table .profile-card .connected {
+ float: right;
+ margin-right: 0.5em;
+ color: #0a0;
+ }
+
+ .jetpack-sso-form-table .profile-card p {
+ margin-top: 0.7em;
+ font-size: 1.2em;
+ }
+
+ .jetpack-sso-form-table .profile-card .two_step .enabled a {
+ float: right;
+ color: #0a0;
+ }
+
+ .jetpack-sso-form-table .profile-card .two_step .disabled a {
+ float: right;
+ color: red;
+ }
+ </style>
+
+ <?php elseif ( get_current_user_id() == $user->ID ) : ?>
+
+ <?php echo $this->button( 'state=sso-link-user&_wpnonce=' . wp_create_nonce('sso-link-user') ); // update ?>
+
+ <?php else : ?>
+
+ <p><?php _e( 'This profile is not currently linked to a WordPress.com Profile.', 'jetpack' ); ?></p>
+
+ <?php endif;
+ }
+}
+
+Jetpack_SSO::get_instance();