: // where type can be one of the following: string, integer, bool, image, time, timestamp // thumbnailphoto:image // whencreated:time protected $_additional_user_attributes = ''; // Merged array of _user_attributes and _additional_user_attributes protected $_all_user_attributes = array(); // Add all user attributes from AD to WP table usermeta protected $_write_usermeta = true; // Prefix for user meta Data from AD protected $_usermeta_prefix = 'adi_'; // Overwrite local values even if values in Active Directory are empty. protected $_usermeta_empty_overwrite = false; // Enable Sync Back protected $_syncback = false; // Use global Sync Back User protected $_syncback_use_global_user = false; // Account name of global sync back user protected $_syncback_global_user = ''; // Password of global sync back user protected $_syncback_global_pwd = ''; // Show AD attributes in user profile protected $_show_attributes = false; // List of AD attributes in the order they should appear on users profile page // Attributes are separated by semicolon or linefeed / newline and have to format: // : // is used on the profile page protected $_attributes_to_show = ''; // Use the real password when a user is created protected $_no_random_password = false; // Update password on every successfull login protected $_auto_update_password = false; // Enable lost password recovery protected $_enable_lost_password_recovery = false; // enable Bulk Import protected $_bulkimport_enabled = false; // AUTHCODE for Bulk Import. Bulk Import will only work, if this AUTHCODE is send as as get-parameter to bulkimport.php protected $_bulkimport_authcode = ''; // generate a new AUTHCODE for Bulk Import protected $_bulkimport_new_authcode = false; // Import members of these security groups (separated by semicolons) protected $_bulkimport_security_groups = ''; // name of Bulk Import User in Active Directory protected $_bulkimport_user = ''; // password for Bulk Import User (will be stored encrypted) protected $_bulkimport_pwd = ''; // use user disabling protected $_disable_users = false; // use local (WordPress) password as fallback if authentication against AD fails protected $_fallback_to_local_password = false; // show disabled and ADI user status on user list protected $_show_user_status = true; // Prevent email change by ADI Users (not for admins) protected $_prevent_email_change = false; // protected $_sso_enabled = false; // TODO: for auto login/SSO feature, has to be added to _load_options(), admin.php etc. // All options and its types // Has to be static for static call of method uninstall() protected static $_all_options = array( array('name' => 'AD_Integration_version', 'type' => 'string'), // Server array('name' => 'AD_Integration_domain_controllers', 'type' => 'string'), array('name' => 'AD_Integration_port', 'type' => 'int'), array('name' => 'AD_Integration_use_tls', 'type' => 'bool'), array('name' => 'AD_Integration_network_timeout', 'type' => 'integer'), array('name' => 'AD_Integration_base_dn', 'type' => 'string'), // User array('name' => 'AD_Integration_account_suffix', 'type' => 'string'), array('name' => 'AD_Integration_append_suffix_to_new_users', 'type' => 'bool'), array('name' => 'AD_Integration_auto_create_user', 'type' => 'bool'), array('name' => 'AD_Integration_auto_update_user', 'type' => 'bool'), array('name' => 'AD_Integration_auto_update_description', 'type' => 'bool'), array('name' => 'AD_Integration_default_email_domain', 'type' => 'string'), array('name' => 'AD_Integration_duplicate_email_prevention', 'type' => 'string'), array('name' => 'AD_Integration_prevent_email_change', 'type' => 'bool'), array('name' => 'AD_Integration_display_name', 'type' => 'string'), array('name' => 'AD_Integration_show_user_status', 'type' => 'bool'), array('name' => 'AD_Integration_enable_password_change', 'type' => 'bool'), array('name' => 'AD_Integration_no_random_password', 'type' => 'bool'), array('name' => 'AD_Integration_auto_update_password', 'type' => 'bool'), // Authorization array('name' => 'AD_Integration_authorize_by_group', 'type' => 'bool'), array('name' => 'AD_Integration_authorization_group', 'type' => 'string'), array('name' => 'AD_Integration_role_equivalent_groups', 'type' => 'string'), // Security array('name' => 'AD_Integration_fallback_to_local_password', 'type' => 'bool'), array('name' => 'AD_Integration_enable_lost_password_recovery', 'type' => 'bool'), array('name' => 'AD_Integration_max_login_attempts', 'type' => 'int'), array('name' => 'AD_Integration_block_time', 'type' => 'int'), array('name' => 'AD_Integration_user_notification', 'type' => 'bool'), array('name' => 'AD_Integration_admin_notification', 'type' => 'bool'), array('name' => 'AD_Integration_admin_email', 'type' => 'string'), // User Meta array('name' => 'AD_Integration_additional_user_attributes', 'type' => 'string'), array('name' => 'AD_Integration_usermeta_empty_overwrite', 'type' => 'bool'), array('name' => 'AD_Integration_show_attributes', 'type' => 'bool'), array('name' => 'AD_Integration_attributes_to_show', 'type' => 'bool'), array('name' => 'AD_Integration_syncback', 'type' => 'bool'), array('name' => 'AD_Integration_syncback_use_global_user', 'type' => 'bool'), array('name' => 'AD_Integration_syncback_global_user', 'type' => 'string'), array('name' => 'AD_Integration_syncback_global_pwd', 'type' => 'string'), // Bulk Import array('name' => 'AD_Integration_bulkimport_enabled', 'type' => 'bool'), array('name' => 'AD_Integration_bulkimport_authcode', 'type' => 'string'), array('name' => 'AD_Integration_bulkimport_new_authcode', 'type' => 'bool'), array('name' => 'AD_Integration_bulkimport_security_groups', 'type' => 'string'), array('name' => 'AD_Integration_bulkimport_user', 'type' => 'string'), array('name' => 'AD_Integration_bulkimport_pwd', 'type' => 'string'), array('name' => 'AD_Integration_disable_users', 'type' => 'bool') ); public $errors = false; /** * Constructor */ public function __construct() { global $wp_version, $wpdb; // define folder constant if (!defined('ADINTEGRATION_FOLDER')) { define('ADINTEGRATION_FOLDER', basename(dirname(__FILE__))); } if (is_multisite()) { $this->setLogFile(dirname(__FILE__) . '/adi_blog-'. $wpdb->blogid . '.log'); } else { $this->setLogFile(dirname(__FILE__).'/adi.log'); } $this->errors = new WP_Error(); // Load Options $this->_load_options(); // Generate authcode if necessary if (strlen($this->_bulkimport_authcode) < 20) { $this->_generate_authcode(); } add_action('admin_init', array(&$this, 'register_adi_settings')); // Adds our Options page if ( is_multisite() ) { add_action( 'network_admin_menu', array(&$this, 'add_options_page')); } else { add_action('admin_menu', array(&$this, 'add_options_page')); } add_filter('contextual_help', array(&$this, 'contextual_help'), 10, 2); // DO WE HAVE LDAP SUPPORT? if (function_exists('ldap_connect')) { add_filter('authenticate', array(&$this, 'authenticate'), 10, 3); if (!$this->_enable_lost_password_recovery) { add_action('lost_password', array(&$this, 'disable_function')); add_action('retrieve_password', array(&$this, 'disable_function')); add_action('password_reset', array(&$this, 'disable_function')); } add_action('admin_print_styles', array(&$this, 'load_styles')); add_action('admin_print_scripts', array(&$this, 'load_scripts')); // Add new column to the user list if ($this->_show_user_status) { add_filter( 'manage_users_columns', array( &$this, 'manage_users_columns' ) ); add_filter( 'manage_users_custom_column', array( &$this, 'manage_users_custom_column' ), 10, 3 ); } // actions for user disabling add_action('personal_options_update', array(&$this, 'profile_update_disable_user')); add_action('edit_user_profile_update', array(&$this, 'profile_update_disable_user')); add_action('edit_user_profile', array(&$this, 'show_user_profile_disable_user')); add_action('show_user_profile', array(&$this, 'show_user_profile_disable_user')); // Sync Back? if ($this->_syncback === true) { add_action('personal_options_update', array(&$this, 'profile_update')); add_action('edit_user_profile_update', array(&$this, 'profile_update')); } // TODO: auto_login feature must be tested /* if ($this->_auto_login) { add_action('init', array(&$this, 'auto_login')); } */ add_filter('check_password', array(&$this, 'override_password_check'), 10, 4); // Is local password change disallowed? if (!$this->_enable_password_change) { // disable password fields add_filter('show_password_fields', array(&$this, 'disable_password_fields')); // generate a random password for manually added users add_action('check_passwords', array(&$this, 'generate_password'), 10, 3); } if (!class_exists('adLDAP')) { require 'ad_ldap/adLDAP.php'; } } else { $this->_log(ADI_LOG_WARN,'openLDAP not installed or activated in PHP.'); } // Adding AD attributes to profile page if ($this->_show_attributes) { add_action( 'edit_user_profile', array(&$this, 'show_AD_attributes')); add_action( 'show_user_profile', array(&$this, 'show_AD_attributes')); } $this->_all_user_attributes = $this->_get_user_attributes(); // Prevent email change if ($this->_prevent_email_change) { add_action( 'edit_user_profile', array(&$this, 'user_profile_prevent_email_change')); // cosmetic add_action( 'show_user_profile', array(&$this, 'user_profile_prevent_email_change')); // cosmetic add_action( 'user_profile_update_errors', array(&$this, 'prevent_email_change'), 10, 3 ); // true prevention } } public function load_styles() { wp_register_style('adintegration', plugins_url('css/adintegration.css', __FILE__ ) ,false, '1.7.1', 'screen'); wp_enqueue_style('adintegration'); } public function load_scripts() { wp_enqueue_script('jquery-ui-tabs'); // this is a wp default script wp_enqueue_script('jquery-ui-dialog'); // this is a wp default script } /************************************************************* * Plugin hooks *************************************************************/ /** * Add options for this plugin to the database. */ public function initialize_options() { if ( is_multisite() ) { if (is_super_admin()) { add_site_option('AD_Integration_account_suffix', ''); add_site_option('AD_Integration_auto_create_user', false); add_site_option('AD_Integration_auto_update_user', false); add_site_option('AD_Integration_append_suffix_to_new_users', false); add_site_option('AD_Integration_domain_controllers', ''); add_site_option('AD_Integration_base_dn', ''); add_site_option('AD_Integration_role_equivalent_groups', ''); add_site_option('AD_Integration_default_email_domain', ''); add_site_option('AD_Integration_port', '389'); add_site_option('AD_Integration_use_tls', false); add_site_option('AD_Integration_network_timeout', 5); // User add_site_option('AD_Integration_authorize_by_group', false); add_site_option('AD_Integration_authorization_group', ''); add_site_option('AD_Integration_display_name', ''); add_site_option('AD_Integration_enable_password_change', false); add_site_option('AD_Integration_duplicate_email_prevention', ADI_DUPLICATE_EMAIL_ADDRESS_PREVENT); add_site_option('AD_Integration_prevent_email_change', false); add_site_option('AD_Integration_auto_update_description', false); add_site_option('AD_Integration_show_user_status', false); add_site_option('AD_Integration_show_attributes', false); add_site_option('AD_Integration_attributes_to_show', ''); add_site_option('AD_Integration_additionl_user_attributes', ''); add_site_option('AD_Integration_usermeta_empty_overwrite', false); add_site_option('AD_Integration_no_random_password', false); add_site_option('AD_Integration_auto_update_password', false); add_site_option('AD_Integration_max_login_attempts', '3'); add_site_option('AD_Integration_block_time', '30'); add_site_option('AD_Integration_user_notification', false); add_site_option('AD_Integration_admin_notification', false); add_site_option('AD_Integration_admin_email', ''); add_site_option('AD_Integration_disable_users', false); add_site_option('AD_Integration_fallback_to_local_password', false); add_site_option('AD_Integration_enable_lost_password_recovery', false); add_site_option('AD_Integration_syncback', false); add_site_option('AD_Integration_syncback_use_global_user', false); add_site_option('AD_Integration_syncback_global_user', ''); add_site_option('AD_Integration_syncback_global_pwd', ''); add_site_option('AD_Integration_bulkimport_enabled', false); add_site_option('AD_Integration_bulkimport_authcode', ''); add_site_option('AD_Integration_bulkimport_new_authcode', false); add_site_option('AD_Integration_bulkimport_security_groups', ''); add_site_option('AD_Integration_bulkimport_user', ''); add_site_option('AD_Integration_bulkimport_pwd', ''); } } else { if (current_user_can('manage_options')) { add_option('AD_Integration_account_suffix', ''); add_option('AD_Integration_auto_create_user', false); add_option('AD_Integration_auto_update_user', false); add_option('AD_Integration_append_suffix_to_new_users', false); add_option('AD_Integration_domain_controllers', ''); add_option('AD_Integration_base_dn', ''); add_option('AD_Integration_role_equivalent_groups', ''); add_option('AD_Integration_default_email_domain', ''); add_option('AD_Integration_port', '389'); add_option('AD_Integration_use_tls', false); add_option('AD_Integration_network_timeout', 5); add_option('AD_Integration_authorize_by_group', false); add_option('AD_Integration_authorization_group', ''); add_option('AD_Integration_display_name', ''); add_option('AD_Integration_enable_password_change', false); add_option('AD_Integration_duplicate_email_prevention', ADI_DUPLICATE_EMAIL_ADDRESS_PREVENT); add_option('AD_Integration_prevent_email_change', false); add_option('AD_Integration_auto_update_description', false); add_option('AD_Integration_show_user_status', false); add_option('AD_Integration_show_attributes', false); add_option('AD_Integration_attributes_to_show', ''); add_option('AD_Integration_additional_user_attributes', ''); add_option('AD_Integration_usermeta_empty_overwrite', false); add_option('AD_Integration_no_random_password', false); add_option('AD_Integration_auto_update_password', false); add_option('AD_Integration_max_login_attempts', '3'); add_option('AD_Integration_block_time', '30'); add_option('AD_Integration_user_notification', false); add_option('AD_Integration_admin_notification', false); add_option('AD_Integration_admin_email', ''); add_option('AD_Integration_fallback_to_local_password', false); add_option('AD_Integration_enable_lost_password_recovery', false); add_option('AD_Integration_syncback', false); add_option('AD_Integration_syncback_use_global_user', false); add_option('AD_Integration_syncback_global_user', ''); add_option('AD_Integration_syncback_global_pwd', ''); add_option('AD_Integration_bulkimport_enabled', false); add_option('AD_Integration_bulkimport_authcode', ''); add_option('AD_Integration_bulkimport_new_authcode', false); add_option('AD_Integration_bulkimport_security_groups', ''); add_option('AD_Integration_bulkimport_user', ''); add_option('AD_Integration_bulkimport_pwd', ''); add_option('AD_Integration_disable_users', false); } } } public function register_adi_settings() { // Server register_setting('ADI-server-settings', 'AD_Integration_domain_controllers'); register_setting('ADI-server-settings', 'AD_Integration_port', array(&$this, 'sanitize_port')); register_setting('ADI-server-settings', 'AD_Integration_use_tls', array(&$this, 'sanitize_bool')); register_setting('ADI-server-settings', 'AD_Integration_base_dn'); register_setting('ADI-server-settings', 'AD_Integration_network_timeout', array(&$this, 'sanitize_network_timeout')); // User register_setting('ADI-user-settings', 'AD_Integration_auto_create_user', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_auto_update_user', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_auto_update_description', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_default_email_domain', array(&$this, 'sanitize_default_email_domain')); register_setting('ADI-user-settings', 'AD_Integration_account_suffix', array(&$this, 'sanitize_account_suffix')); register_setting('ADI-user-settings', 'AD_Integration_append_suffix_to_new_users', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_display_name'); register_setting('ADI-user-settings', 'AD_Integration_enable_password_change', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_duplicate_email_prevention'); register_setting('ADI-user-settings', 'AD_Integration_prevent_email_change', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_no_random_password', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_auto_update_password', array(&$this, 'sanitize_bool')); register_setting('ADI-user-settings', 'AD_Integration_show_user_status', array(&$this, 'sanitize_bool')); // Authorization register_setting('ADI-auth-settings', 'AD_Integration_authorize_by_group', array(&$this, 'sanitize_bool')); register_setting('ADI-auth-settings', 'AD_Integration_authorization_group'); register_setting('ADI-auth-settings', 'AD_Integration_role_equivalent_groups', array(&$this, 'sanitize_role_equivalent_groups')); // Security register_setting('ADI-security-settings', 'AD_Integration_fallback_to_local_password', array(&$this, 'sanitize_bool')); register_setting('ADI-security-settings', 'AD_Integration_enable_lost_password_recovery', array(&$this, 'sanitize_bool')); register_setting('ADI-security-settings', 'AD_Integration_max_login_attempts', array(&$this, 'sanitize_max_login_attempts')); register_setting('ADI-security-settings', 'AD_Integration_block_time', array(&$this, 'sanitize_block_time')); register_setting('ADI-security-settings', 'AD_Integration_user_notification', array(&$this, 'sanitize_bool')); register_setting('ADI-security-settings', 'AD_Integration_admin_notification', array(&$this, 'sanitize_bool')); register_setting('ADI-security-settings', 'AD_Integration_admin_email', array(&$this, 'sanitize_admin_email')); // User Meta register_setting('ADI-usermeta-settings', 'AD_Integration_show_attributes', array(&$this, 'sanitize_bool')); register_setting('ADI-usermeta-settings', 'AD_Integration_attributes_to_show', array(&$this, 'sanitize_attributes_to_show')); register_setting('ADI-usermeta-settings', 'AD_Integration_additional_user_attributes', array(&$this, 'sanitize_additional_user_attributes')); register_setting('ADI-usermeta-settings', 'AD_Integration_usermeta_empty_overwrite', array(&$this, 'sanitize_bool')); register_setting('ADI-usermeta-settings', 'AD_Integration_syncback', array(&$this, 'sanitize_bool')); register_setting('ADI-usermeta-settings', 'AD_Integration_syncback_use_global_user', array(&$this, 'sanitize_bool')); register_setting('ADI-usermeta-settings', 'AD_Integration_syncback_global_user', array(&$this, 'sanitize_syncback_global_user')); register_setting('ADI-usermeta-settings', 'AD_Integration_syncback_global_pwd', array(&$this, 'sanitize_syncback_global_user_pwd')); // Bulk Import register_setting('ADI-bulkimport-settings', 'AD_Integration_bulkimport_enabled', array(&$this, 'sanitize_bool')); register_setting('ADI-bulkimport-settings', 'AD_Integration_bulkimport_new_authcode', array(&$this, 'sanitize_new_authcode')); register_setting('ADI-bulkimport-settings', 'AD_Integration_bulkimport_security_groups'); register_setting('ADI-bulkimport-settings', 'AD_Integration_bulkimport_user', array(&$this, 'sanitize_bulkimport_user')); register_setting('ADI-bulkimport-settings', 'AD_Integration_bulkimport_pwd', array(&$this, 'sanitize_bulkimport_user_pwd')); register_setting('ADI-bulkimport-settings', 'AD_Integration_disable_users', array(&$this, 'sanitize_bool')); } /** * Add an options pane for this plugin. */ public function add_options_page() { if ( is_multisite() ) { // WordPress Multisite if (function_exists('add_submenu_page')) { add_submenu_page('settings.php', __('Active Directory Integration'), __('Active Directory Integration'), 'manage_network', 'active-directory-integration', array(&$this, 'display_options_page')); } } else { // Standard WordPress if (function_exists('add_options_page')) { //add_options_page('Active Directory Integration', 'Active Directory Integration', 'manage_options', __FILE__, array(&$this, 'display_options_page')); add_options_page('Active Directory Integration', 'Active Directory Integration', 'manage_options', 'active-directory-integration', array(&$this, 'display_options_page')); } } } /** * If the REMOTE_USER evironment is set, use it as the username. * This assumes that you have externally authenticated the user. */ public function authenticate($user = NULL, $username = '', $password = '') { global $wp_version; $this->_log(ADI_LOG_INFO,'method authenticate() called'); // log debug informations $this->_log(ADI_LOG_INFO,"------------------------------------------\n". 'PHP version: '.phpversion()."\n". 'WP version: '.$wp_version."\n". 'ADI version: '.ADIntegrationPlugin::ADI_VERSION."\n". 'OS Info : '.php_uname()."\n". 'Web Server : '.php_sapi_name()."\n". 'adLDAP ver.: '.adLDAP::VERSION."\n". '------------------------------------------'); if (function_exists('ldap_control_paged_result')) { $this->_log(ADI_LOG_INFO, 'LDAP paging: enabled'); } else { $this->_log(ADI_LOG_INFO, 'LDAP paging: not available'); } // IMPORTANT! $this->_authenticated = false; $user_id = NULL; $username = strtolower($username); $password = stripslashes($password); // Stop if username is empty if (empty($username)) { $this->_log(ADI_LOG_ERROR,'Empty username. Authentication failed.'); return false; } // Don't use Active Directory for admin user (ID 1) // $user = get_userdatabylogin($username); // deprecated $user = get_user_by('login', $username); if (is_object($user) && ($user->ID == 1)) { $this->_log(ADI_LOG_NOTICE,'User with ID 1 will never be authenticated by Active Directory Integration.'); return false; } // extract account suffix from username if not set // (after loading of options) // Extended for issue #0043 if (strpos($username,'@') !== false) { $this->_log(ADI_LOG_NOTICE,'@domain found.'); $parts = explode('@',trim($username)); $puser = $parts[0]; $pdomain = '@'.$parts[1]; if (trim($this->_account_suffix) == '') { // without Account Suffix $username = $puser; $this->_account_suffix = $pdomain; $this->_append_suffix_to_new_users = true; $this->_log(ADI_LOG_NOTICE,'No account suffix set. Using user domain "' . $pdomain . '" as account suffix.'); } else { // with Account Suffix // let's see if users domain is in the list of all account suffixes $account_suffix_found = false; $account_suffixes = explode(";", $this->_account_suffix); foreach($account_suffixes AS $account_suffix) { if (trim($account_suffix) == $pdomain) { // user domain same as _account_suffix (leave _append_suffix_to_new_users untouched) $username = $puser; $account_suffix_found = true; $this->_log(ADI_LOG_NOTICE,'user domain "' . $pdomain . '" in list of account suffixes.'); break; } } if ($account_suffix_found === false) { // $this->_append_suffix_to_new_users = false; $this->_account_suffix = ''; $this->_log(ADI_LOG_NOTICE,'user domain "' . $pdomain . '" NOT in list of account suffixes.'); } } } $this->_log(ADI_LOG_NOTICE,'username: '.$username); $this->_log(ADI_LOG_NOTICE,'password: **not shown**'); // Log informations $this->_log(ADI_LOG_INFO,"Options for adLDAP connection:\n". "- account_suffix: $this->_account_suffix\n". "- base_dn: $this->_base_dn\n". "- domain_controllers: $this->_domain_controllers\n". "- ad_port: $this->_port\n". "- use_tls: ".(int) $this->_use_tls."\n". "- network timeout: ". $this->_network_timeout); // Check if the domain controllers are reachable if ($this->_loglevel == ADI_LOG_DEBUG) { // Let's check if AD is reachable if (function_exists('fsockopen')) { $this->_log(ADI_LOG_INFO,'Checking domain controller ports:'); $arrDCs = explode(";",$this->_domain_controllers); foreach($arrDCs AS $dc) { if($fp = @fsockopen($dc,$this->_port,$errCode,$errStr,2)) { $this->_log(ADI_LOG_INFO,'- ' . $dc . ':' . $this->_port . ' - OK'); fclose($fp); } else { $this->_log(ADI_LOG_ERROR,'- ' . $dc . ':' . $this->_port . ' - FAILED'); } } unset($arrDCs, $dc, $fp); } else { $this->_log(ADI_LOG_NOTICE,'Function fsockopen() not available. Can not check server ports.'); } } // Connect to Active Directory try { $this->_adldap = @new adLDAP(array( "base_dn" => $this->_base_dn, "domain_controllers" => explode(';', $this->_domain_controllers), "ad_port" => $this->_port, // AD port "use_tls" => $this->_use_tls, // secure? "network_timeout" => $this->_network_timeout // network timeout*/ )); } catch (Exception $e) { $this->_log(ADI_LOG_ERROR,'adLDAP exception: ' . $e->getMessage()); return false; } $this->_log(ADI_LOG_NOTICE,'adLDAP object created.'); // Check for maximum login attempts $this->_log(ADI_LOG_INFO,'max_login_attempts: '.$this->_max_login_attempts); if ($this->_max_login_attempts > 0) { $failed_logins = $this->_get_failed_logins_within_block_time($username); $this->_log(ADI_LOG_INFO,'users failed logins: '.$failed_logins); if ($failed_logins >= $this->_max_login_attempts) { $this->_authenticated = false; $this->_log(ADI_LOG_ERROR,'Authentication failed again'); $this->_log(ADI_LOG_ERROR,"Account '$username' blocked for $this->_block_time seconds"); // e-mail notfications if user is blocked if ($this->_user_notification) { $this->_notify_user($username); $this->_log(ADI_LOG_NOTICE,"Notification send to user."); } if ($this->_admin_notification) { $this->_notify_admin($username); $this->_log(ADI_LOG_NOTICE,"Notification send to admin(s)."); } // Show the blocking page to the user (only if we are not in debug/log mode) if ($this->_loglevel == ADI_LOG_NONE) { $this->_display_blocking_page($username); } die(); // important ! } } // This is where the action is. $account_suffixes = explode(";",$this->_account_suffix); foreach($account_suffixes AS $account_suffix) { $account_suffix = trim($account_suffix); $this->_log(ADI_LOG_NOTICE,'trying account suffix "'.$account_suffix.'"'); $this->_adldap->set_account_suffix($account_suffix); if ( $this->_adldap->authenticate($username, $password) === true ) // Authenticate { $this->_log(ADI_LOG_NOTICE,'Authentication successfull for "' . $username . $account_suffix.'"'); $this->_authenticated = true; break; } } if ( $this->_authenticated == false ) { $this->_log(ADI_LOG_ERROR,'Authentication failed'); $this->_authenticated = false; $this->_store_failed_login($username); return false; } // Cleanup old database entries $this->_cleanup_failed_logins($username); // Check the authorization if ($this->_authorize_by_group) { if ($this->_check_authorization_by_group($username)) { $this->_authenticated = true; } else { $this->_authenticated = false; return false; } } $ad_username = $username; // should the account suffix be used for the new username? if ($this->_append_suffix_to_new_users) { $username .= $account_suffix; } // getting user data (again!) // $user = get_userdatabylogin($username); // deprecated $user = get_user_by('login', $username); // role $user_role = $this->_get_user_role_equiv($ad_username); // important: use $ad_username not $username // userinfo from AD $this->_log(ADI_LOG_DEBUG, 'ATTRIBUTES TO LOAD: '.print_r($this->_all_user_attributes, true)); $userinfo = $this->_adldap->user_info($username, $this->_all_user_attributes); // Issue #0081 $username instead of $ad_username $userinfo = $userinfo[0]; $this->_log(ADI_LOG_DEBUG,"USERINFO[0]: \n".print_r($userinfo,true)); // get display name $display_name = $this->_get_display_name_from_AD($username, $userinfo); // Create new users automatically, if configured if (!$user OR ($user->user_login != $username)) { if ($this->_auto_create_user || trim($user_role) != '' ) { // create user $user_id = $this->_create_user($ad_username, $userinfo, $display_name, $user_role, $password); } else { // Bail out to avoid showing the login form $this->_log(ADI_LOG_ERROR,'This user exists in Active Directory, but has not been granted access to this installation of WordPress.'); return new WP_Error('invalid_username', __('ERROR: This user exists in Active Directory, but has not been granted access to this installation of WordPress.')); } } else { // Update known users if configured if ($this->_auto_create_user AND $this->_auto_update_user) { // Update users role $user_id = $this->_update_user($ad_username, $userinfo, $display_name, $user_role, $password); } } // load user object if (!$user_id) { if (version_compare($wp_version, '3.1', '<')) { require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php'); } $user_id = username_exists($username); $this->_log(ADI_LOG_NOTICE,'user_id: '.$user_id); } $user = new WP_User($user_id); // user disabled? if (get_user_meta($user_id, 'adi_user_disabled', true)) { $this->_log(ADI_LOG_WARN,'User with ID ' . $user_id .' is disabled.'); $this->_authenticated = false; return false; } $this->_log(ADI_LOG_NOTICE,'FINISHED'); return $user; } /* * Use local (WordPress) password check if needed and allowed */ public function override_password_check($check, $password, $hash, $user_id) { // Always use local password handling for user_id 1 (admin) if ($user_id == 1) { $this->_log(ADI_LOG_DEBUG,'UserID 1: using local (WordPress) password check.'); return $check; } // return true for users authenticated by ADI (should never happen, but who knows?) if ( $this->_authenticated == true ) { $this->_log(ADI_LOG_DEBUG,'User successfully authenticated by ADI: override local (WordPress) password check.'); return true; } // return false if user is disabled if (get_user_meta($user_id,'adi_user_disabled', true)) { $reason = get_user_meta($user_id,'adi_user_disabled_reason', true); $this->_log(ADI_LOG_DEBUG,'User is disabled. Reason: '.$reason); return false; } // Only check for local password if this is not an AD user and if fallback to local password is active $usercheck = get_user_meta($user_id,'adi_samaccountname', true); if ($usercheck != '') { if ($this->_fallback_to_local_password) { $this->_log(ADI_LOG_DEBUG,'User from AD. Falling back to local (WordPress) password check.'); return $check; } else { $this->_log(ADI_LOG_DEBUG,'User from AD and fallback to local (WordPress) password deactivated. Authentication failed.'); return false; } } // use local password check in all other cases $this->_log(ADI_LOG_DEBUG,'Using local (WordPress) password check.'); return $check; } /** * Auto Login / Single Sign On * Code by Alex Nolan */ public function auto_login() { // TODO: This has to be tested - carefully // looks pretty insecure /* if (!is_user_logged_in() && isset($_SERVER['LOGON_USER'])) { $user_login = substr($_SERVER['LOGON_USER'], strrpos($_SERVER['LOGON_USER'],'\\')+1, strlen($_SERVER['LOGON_USER'])-strrpos($_SERVER['LOGON_USER'],'\\')); $user = get_userdatabylogin($user_login); // TODO: deprecated $user_id = $user->ID; wp_set_current_user($user_id, $user_login); wp_set_auth_cookie($user_id); do_action('wp_login', $user_login); } */ } /* * Generate a password for the user. This plugin does not * require the user to enter this value, but we want to set it * to something nonobvious. */ public function generate_password($username, $password1, $password2) { $password1 = $password2 = $this->_get_password(); } /* * Used to disable certain display elements, e.g. password * fields on profile screen. */ public function disable_password_fields($show_password_fields) { return false; } /* * Used to disable certain login functions, e.g. retrieving a * user's password. */ public function disable_function() { die('Disabled'); } /** * Shows the contexual help on the options/admin screen. * * @param $help * @param $screen * @return string help message */ public function contextual_help ($help, $screen) { if ($screen == 'settings_page_' . ADINTEGRATION_FOLDER . '/ad-integration' || $screen == 'settings_page_active-directory-integration-network' ) { $help .= '
' . __('Active Directory Integration Help','ad-integration') . '
'; } return $help; } /** * Generate an error message for the users if Sync Back failed. */ public function generate_error(&$errors, $update, &$user) { $errors = $this->errors; } public function setLogLevel($level = 0) { $this->_loglevel = (int)$level; } public function setLogFile($filename) { $this->_logfile = $filename; } public function disableDebug() { echo '
';
		$this->debug = false;
		echo '
'; } /** * HOOKS: Actions and Filters */ /** * Show the disable user checkbox if needed * Action(s): edit_user_profile, show_user_profile * * @param object $user */ public function show_user_profile_disable_user($user) { global $user_ID; // User disabled only visible for admins and not for user with ID 1 (admin) and not for ourselves if (current_user_can('level_10') && ($user->ID != 1) && ($user->ID != $user_ID)) { // Load up the localization file if we're using WordPress in a different language // Place it in this plugin's folder and name it "ad-integration-[value in wp-config].mo" load_plugin_textdomain( 'ad-integration', false, dirname( plugin_basename( __FILE__ ) ) ); $user_disabled = get_user_meta($user->ID, 'adi_user_disabled', true); ?>
value="1" />

ID, 'adi_user_disabled_reason', true);?>

_disable_user($user_id, sprintf(__('User manually disabled by "%s".', 'ad-integration'), $user_login)); } } else { // Reenable if user was disabled only if (get_user_meta($user_id, 'adi_user_disabled', true) == true) { $this->_enable_user($user_id); } } } } /* * Display the options for this plugin. */ public function display_options_page() { include dirname( __FILE__ ) .'/admin.php'; } /** * Show defined AD attributes on profile page */ public function show_AD_attributes($user) { global $user_ID; // Load up the localization file if we're using WordPress in a different language // Place it in this plugin's folder and name it "ad-integration-[value in wp-config].mo" load_plugin_textdomain( 'ad-integration', false, dirname( plugin_basename( __FILE__ ) ) ); // Additional Attributes if ($this->_show_attributes) { $all_attributes = $this->_get_attributes_array(); $list = str_replace(";", "\n", $this->_attributes_to_show); $list = explode("\n", $list); $attributes = array(); foreach($list AS $line) { $parts = explode(':',$line); if (isset($parts[0])) { if (trim($parts[0] != '')) { $attributes[] = trim($parts[0]); } } } if (count($attributes) > 0) { wp_nonce_field('ADI_UserProfileUpdate','ADI_UserProfileUpdate_NONCE'); echo '

' . __('Additional Informations', 'ad-integration').'

'; $adi_samaccountname = get_user_meta($user->ID, 'adi_samaccountname', true); ?> ID, $metakey, true); } else { $value = ''; $no_attribute = true; } if (isset($all_attributes[$attribute]['description'])) { $description = trim($all_attributes[$attribute]['description']); } else { // if value is empty and we've found no description then this is no attribute if ($value == '') { $no_attribute = true; } } ?> _syncback_use_global_user === false) && ($user->ID == $user_ID) && ($this->_syncback == true) && ($adi_samaccountname != '')) { ?>
_syncback == true) && isset($all_attributes[$attribute]['sync']) && ($all_attributes[$attribute]['sync'] == true) && (($user->ID == $user_ID) || ($this->_syncback_use_global_user === true))) { // use textarea if we have a list if (isset($all_attributes[$attribute]['type']) && ($all_attributes[$attribute]['type'] == 'list')) { echo ''; } else { echo ''; } } else { echo nl2br(esc_html($value)); } ?>
_log(ADI_LOG_DEBUG,'SyncBack: Start of profile update'); $attributes_to_sync = array(); // check if synback is on and we have a user from AD and not local user if ($this->_syncback === true) { // Go through attributes and update user meta if necessary. $attributes = $this->_get_attributes_array(); foreach($attributes AS $key => $attribute) { if ($attribute['sync'] == true) { if (isset($_POST[$attribute['metakey']])) { if ($attribute['type'] == 'list') { // List $list = explode("\n",str_replace("\r",'',$_POST[$attribute['metakey']])); $i=0; foreach ($list AS $line) { if (trim($line) != '') { $attributes_to_sync[$key][$i] = $line; $i++; } } if ($i == 0) { $attributes_to_sync[$key][0] = ' '; // Use a SPACE !!! } } else { // single value if ($_POST[$attribute['metakey']] == '') { $attributes_to_sync[$key][0] = ' '; // Use a SPACE !!!! } else { $attributes_to_sync[$key][0] = $_POST[$attribute['metakey']]; } } update_user_meta($user_id, $attribute['metakey'], $_POST[$attribute['metakey']]); } } } // Only SyncBack if we have an AD-user and not a local user if (isset($_POST['adi_samaccountname']) && ($_POST['adi_samaccountname'] != '')) { // Get User Data $userinfo = get_userdata($_POST['user_id']); $username = $userinfo->user_login; // use personal_account_suffix $personal_account_suffix = trim(get_user_meta($user_id,'ad_integration_account_suffix', true)); if ($personal_account_suffix != '') { $account_suffix = $personal_account_suffix; } else { // extract account suffix from username if not set // (after loading of options) if (trim($this->_account_suffix) == '') { if (strpos($username,'@') !== false) { $parts = explode('@',$username); $username = $parts[0]; $this->_account_suffix = '@'.$parts[1]; $this->_append_suffix_to_new_users = true; // TODO not really, hm? } } else { // choose first possible account suffix (this should never happen) $suffixes = explode(';',$this->_account_suffix); $account_suffix = $suffixes[0]; $this->_log(ADI_LOG_WARN,'No personal account suffix found. Now using first account suffix "'.$account_suffix.'".'); } } // establish adLDAP connection // Connect to Active Directory if ($this->_syncback_use_global_user === true) { $ad_username = $this->_syncback_global_user; $ad_password = $this->_decrypt($this->_syncback_global_pwd); } else { if (isset($_POST['adi_synback_password']) && ($_POST['adi_synback_password'] != '')) { $ad_username = $username.$account_suffix; $ad_password = stripslashes($_POST['adi_synback_password']); } else { // No Global Sync User and no password given, so stop here. $this->errors->add('syncback_no_password',__('No password given, so additional attributes are not written back to Active Directory','ad-integration')); return false; } } // Log informations $this->_log(ADI_LOG_INFO,"SyncBack: Options for adLDAP connection:\n". "- base_dn: $this->_base_dn\n". "- domain_controllers: $this->_domain_controllers\n". "- ad_username: $ad_username\n". "- ad_password: **not shown**\n". "- ad_port: $this->_port\n". "- use_tls: ".(int) $this->_use_tls."\n". "- network timeout: ". $this->_network_timeout); try { $ad = @new adLDAP(array( "base_dn" => $this->_base_dn, "domain_controllers" => explode(';', $this->_domain_controllers), "ad_username" => $ad_username, // AD Bind User "ad_password" => $ad_password, // password "ad_port" => $this->_port, // AD port "use_tls" => $this->_use_tls, // secure? "network_timeout" => $this->_network_timeout // network timeout )); } catch (Exception $e) { $this->_log(ADI_LOG_ERROR,'adLDAP exception: ' . $e->getMessage()); $this->errors->add('syncback_wrong_password',__('Error on writing additional attributes back to Active Directory. Wrong password?','ad-integration'),''); return false; } $this->_log(ADI_LOG_DEBUG,'Connected to AD'); // Now we can modify the user $this->_log(ADI_LOG_DEBUG,'attributes to sync: '.print_r($attributes_to_sync, true)); $this->_log(ADI_LOG_DEBUG,'modifying user: '.$username); $modified = @$ad->user_modify_without_schema($username, $attributes_to_sync); if (!$modified) { $this->_log(ADI_LOG_WARN,'SyncBack: modifying user failed'); $this->_log(ADI_LOG_DEBUG,$ad->get_last_errno().': '.$ad->get_last_error()); $this->errors->add('syncback_modify_failed',__('Error on writing additional attributes back to Active Directory. Please contact your administrator.','ad-integration') . "
[Error " . $ad->get_last_errno().'] '.$ad->get_last_error(),''); return false; } else { $this->_log(ADI_LOG_NOTICE,'SyncBack: User successfully modified.'); return true; } } else { return true; } } } /** * Disable email field in user profile if needed (actions edit_user_profile and show_user_profile) * This is not safe and only for cosmetic reasons, but we also have the method prevent_email_change() (see below). * * @param object $user */ public function user_profile_prevent_email_change($user) { // disable email field if needed (dirty hack) if ($this->_prevent_email_change && $this->_is_adi_user($user->ID) && (!current_user_can('level_10'))) { ?> _prevent_email_change && ($this->_is_adi_user($user->ID)) && (!current_user_can('level_10'))) { $old = get_user_by('id', $user->ID); if( $user->user_email != $old->user_email ) { // reset to old email $this->_log(ADI_LOG_DEBUG, 'Prevent email change on profile update for user "'.$user->user_login.'" ('.$user->ID.').'); $user->user_email = $old->user_email; } } } /** * Add new column to the user list page * * @param array $columns */ public function manage_users_columns($columns) { global $wp_version; $columns['adi_user'] = __('ADI User', 'ad-integration'); $columns['adi_user_disabled'] = __('Disabled', 'ad-integration'); return $columns; } /** * Add column content for each user on user list * * @param mixed $value Value to show * @param string $column_name Name of column in user table * @param integer $user_id ID of user (the row) */ public function manage_users_custom_column( $value, $column_name, $user_id ) { // Column "Disabled" if ( $column_name == 'adi_user' ) { $sam = get_user_meta($user_id, 'adi_samaccountname', true); if ($sam != '') { $value = '
 
'; } else { $value = ''; } } // Column "Reason" if ( $column_name == 'adi_user_disabled' ) { if (get_user_meta($user_id, 'adi_user_disabled', true)) { //$value = __('Yes', 'ad-integration'). ' - '; $value = '
' . __(get_user_meta($user_id, 'adi_user_disabled_reason', true), 'ad-integration') . '
'; } else { $value = ''; } } return $value; } /**************************************************************** * STATIC FUNCTIONS ****************************************************************/ /** * Adding the needed table to database and store the db version in the * options table on plugin activation. */ public static function activate() { global $wpdb; $table_name = $wpdb->base_prefix . ADIntegrationPlugin::TABLE_NAME; // get current version and write version of plugin to options table if ( is_multisite() ) { $version_installed = get_site_option('AD_Integration_version'); update_site_option('AD_Integration_version', ADIntegrationPlugin::ADI_VERSION); } else { $version_installed = get_option('AD_Integration_version'); update_option('AD_Integration_version', ADIntegrationPlugin::ADI_VERSION); } // get current db version if ( is_multisite() ) { $db_version = get_site_option('AD_Integration_db_version'); } else { $db_version = get_option('AD_Integration_db_version'); } if (($wpdb->get_var("show tables like '$table_name'") != $table_name) OR ($db_version != ADIntegrationPlugin::DB_VERSION)) { $sql = 'CREATE TABLE ' . $table_name . ' ( id bigint(20) NOT NULL AUTO_INCREMENT, user_login varchar(60), failed_login_time bigint(11), UNIQUE KEY id (id) );'; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // store db version in the options if ( is_multisite() ) { add_site_option('AD_Integration_db_version', ADIntegrationPlugin::DB_VERSION); } else { add_option('AD_Integration_db_version', ADIntegrationPlugin::DB_VERSION); } } // Upgrade? if (version_compare(ADIntegrationPlugin::ADI_VERSION, $version_installed,'>')) { if (version_compare('1.0.1', $version_installed, '>') || ($version_installed == false)) { // remove old needless options if ( is_multisite() ) { delete_site_option('AD_Integration_bind_user'); delete_site_option('AD_Integration_bind_pwd'); } else { delete_option('AD_Integration_bind_user'); delete_option('AD_Integration_bind_pwd'); } } } } /** * Delete the table from database and delete the db version from the * options table on plugin deactivation. */ public static function deactivate() { global $wpdb; $table_name = $wpdb->base_prefix . ADIntegrationPlugin::TABLE_NAME; // drop table $wpdb->query('DROP TABLE IF EXISTS '.$table_name); // delete option if ( is_multisite() ) { delete_site_option('AD_Integration_db_version'); } else { delete_option('AD_Integration_db_version'); } } /** * removes the plugin options from options table. * * @param bool $echo print results as HTML */ public static function uninstall($echo=false) { foreach(self::$_all_options as $option) { $delete_setting = delete_option($option['name']); if ($echo) { if($delete_setting) { echo ''; printf(__('Setting Key \'%s\' has been deleted.', 'ad-integration'), "{$option['name']}"); echo '
'; } else { echo ''; printf(__('Error deleting Setting Key \'%s\'.', 'ad-integration'), "{$option['name']}"); echo '
'; } } } } /******************************************** * Sanitize methods for register_settings ********************************************/ /** * Sanitize AD Servers port * * @param string $port * @return integer sanitized port number */ public function sanitize_port($port) { $port = intval($port); if (($port < 0) || ($port > 65535)) { $port = 389; } return $port; } /** * Sanitize default email domain * trim, strip possible @ * * @param string $domain * @return string sanitized domain */ public function sanitize_default_email_domain($domain) { $domain = preg_replace('/[^\A-Za-z0-9-\.]/', '', $domain); return $domain; } /** * Strip spaces from beginning or end of suffixes * Our seperator (;) is an allowed character in UPN suffixes but not recommended, so fuck the shit. * * @param string $suffix * @return string */ public function sanitize_account_suffix($suffix) { $parts = explode(';', $suffix); $results = array(); foreach($parts as $part) { $results[] = trim($part); } return implode(';', $results); } /** * Sanitize Additional User Attributes * trim, delete empty line * * @param string $text * @return string */ public function sanitize_attributes_to_show($text) { $lines = explode("\n", $text); $sanitized_lines = array(); foreach ($lines AS $line) { $line = trim($line); if ($line != '') { $sanitized_lines[] = $line; } } return implode("\n", $sanitized_lines); } /** * Strips out wrong entries from role_equivalent_groups and converts the WP role to lowercase. * * @param string $text * @return string */ public function sanitize_role_equivalent_groups($text) { $groups = explode(";", $text); $sanitized_groups = array(); foreach ($groups AS $group) { $group = trim($group); $pos = strpos($group, '='); if ($pos != 0) { // yes != 0, since int 0 is also unwanted $ad_group = substr($group,0,$pos); $role = strtolower(substr($group,$pos+1)); // roles are always lowercase / Issue #0055 if ($role != '') { $sanitized_groups[] = $ad_group . '=' . $role; } } } return implode(";", $sanitized_groups); } /** * Sanitize Additional User Attributes * trim, delete empty line, all to lowercase. * * @param string $text * @return string */ public function sanitize_additional_user_attributes($text) { $lines = explode("\n", $text); $sanitized_lines = array(); foreach ($lines AS $line) { $line = trim($line); if ($line != '') { $sanitized_lines[] = strtolower($line); // all in lower case } } return implode("\n", $sanitized_lines); } /** * Maximum number of login attempts must be a postive integer. * * @param integer $attempts * @return integer 3 if $attempts is lower than 1 */ public function sanitize_max_login_attempts($attempts) { $attempts = intval($attempts); if ($attempts < 1) { $attempts = 3; } return $attempts; } /** * Block time must be a postive integer. * * @param integer $seconds * @return integer 30 if $seconds is lower than 1 */ public function sanitize_block_time($seconds) { $seconds = intval($seconds); if ($seconds < 1) { $seconds = 30; } return $seconds; } /** * Check if $email is a correct email address. * * @param string $email * @return string if we have no correct email address we return an empty string */ public function sanitize_admin_email($email) { if (!is_email($email)) { return ''; } return $email; } /** * If $value is true (as expression) returns true, otherwise false * * @param mixed $value * @return bool */ public function sanitize_bool($value) { return ($value == true); } /** * Sanitize Global Sync User * * @param string $user * @return string sanitized username */ public function sanitize_syncback_global_user($user) { return trim($user); } /** * Encrypts the Sync Back User Password * * @param string $pwd unencrypted password * @return encrypted (sanitized) password */ public function sanitize_syncback_global_user_pwd($pwd) { // Password left unchanged so get it from $db if ($pwd == '') { if ( is_multisite() ) { $pwd = get_site_option('AD_Integration_syncback_global_pwd'); } else { $pwd = get_option('AD_Integration_syncback_global_pwd'); } } else { $pwd = $this->_encrypt($pwd); } return $pwd; } /** * Sanitize Buk Import User * * @param string $user * @return string sanitized username */ public function sanitize_bulkimport_user($user) { return trim($user); } /** * Encrypts the Bulk Import User Password * * @param string $pwd unencrypted password * @return encrypted (sanitized) password */ public function sanitize_bulkimport_user_pwd($pwd) { // Password left unchanged so get it from $db if ($pwd == '') { if ( is_multisite() ) { $pwd = get_site_option('AD_Integration_bulkimport_pwd'); } else { $pwd = get_option('AD_Integration_bulkimport_pwd'); } } else { $pwd = $this->_encrypt($pwd); } return $pwd; } /** * Sanitize new authcode * new_authcode is always resetted to false after a new authcode is generated * * @param bool $new */ public function sanitize_new_authcode($new) { if ($new) { $this->_generate_authcode(); } return false; } /** * LDAP network timeout must be a postive integer. * * @param $seconds * @return integer 5 if $seconds is lower than 1 */ public function sanitize_network_timeout($seconds) { $seconds = intval($seconds); if ($seconds < 1) { $seconds = 5; } return $seconds; } /************************************************************* * Protected Methods *************************************************************/ /** * Loads the options from WordPress-DB */ protected function _load_options() { if ( is_multisite() ) { $this->_log(ADI_LOG_INFO,'loading options (WPMU) ...'); // Server (5) $this->_domain_controllers = get_site_option('AD_Integration_domain_controllers'); $this->_port = get_site_option('AD_Integration_port'); $this->_use_tls = get_site_option('AD_Integration_use_tls'); $this->_network_timeout = (int)get_site_option('AD_Integration_network_timeout'); $this->_base_dn = get_site_option('AD_Integration_base_dn'); // User (13) $this->_account_suffix = get_site_option('AD_Integration_account_suffix'); $this->_append_suffix_to_new_users = get_site_option('AD_Integration_append_suffix_to_new_users'); $this->_auto_create_user = (bool)get_site_option('AD_Integration_auto_create_user'); $this->_auto_update_user = (bool)get_site_option('AD_Integration_auto_update_user'); $this->_auto_update_description = (bool)get_site_option('AD_Integration_auto_update_description'); $this->_default_email_domain = get_site_option('AD_Integration_default_email_domain'); $this->_duplicate_email_prevention = get_site_option('AD_Integration_duplicate_email_prevention'); $this->_prevent_email_change = (bool)get_site_option('AD_Integration_prevent_email_change'); $this->_display_name = get_site_option('AD_Integration_display_name'); $this->_show_user_status = (bool)get_site_option('AD_Integration_show_user_status'); $this->_enable_password_change = get_site_option('AD_Integration_enable_password_change'); $this->_no_random_password = (bool)get_site_option('AD_Integration_no_random_password'); $this->_auto_update_password = (bool)get_site_option('AD_Integration_auto_update_password'); // Authorization (3) $this->_authorize_by_group = (bool)get_site_option('AD_Integration_authorize_by_group'); $this->_authorization_group = get_site_option('AD_Integration_authorization_group'); $this->_role_equivalent_groups = get_site_option('AD_Integration_role_equivalent_groups'); // Security (7) $this->_fallback_to_local_password = get_site_option('AD_Integration_fallback_to_local_password'); $this->_enable_lost_password_recovery = (bool)get_site_option('AD_Integration_enable_lost_password_recovery'); $this->_max_login_attempts = (int)get_site_option('AD_Integration_max_login_attempts'); $this->_block_time = (int)get_site_option('AD_Integration_block_time'); $this->_user_notification = (bool)get_site_option('AD_Integration_user_notification'); $this->_admin_notification = (bool)get_site_option('AD_Integration_admin_notification'); $this->_admin_email = get_site_option('AD_Integration_admin_email'); // User Meta (8) $this->_additional_user_attributes = get_site_option('AD_Integration_additional_user_attributes'); $this->_usermeta_empty_overwrite = (bool)get_site_option('AD_Integration_usermeta_empty_overwrite'); $this->_show_attributes = (bool)get_site_option('AD_Integration_show_attributes'); $this->_attributes_to_show = get_site_option('AD_Integration_attributes_to_show'); $this->_syncback = (bool)get_site_option('AD_Integration_syncback'); $this->_syncback_use_global_user = (bool)get_site_option('AD_Integration_syncback_use_global_user'); $this->_syncback_global_user = get_site_option('AD_Integration_syncback_global_user'); $this->_syncback_global_pwd = get_site_option('AD_Integration_syncback_global_pwd'); // Bulk Import (7) $this->_bulkimport_enabled = (bool)get_site_option('AD_Integration_bulkimport_enabled'); $this->_bulkimport_authcode = get_site_option('AD_Integration_bulkimport_authcode'); $this->_bulkimport_new_authcode = (bool)get_site_option('AD_Integration_bulkimport_new_authcode'); $this->_bulkimport_security_groups = get_site_option('AD_Integration_bulkimport_security_groups'); $this->_bulkimport_user = get_site_option('AD_Integration_bulkimport_user'); $this->_bulkimport_pwd = get_site_option('AD_Integration_bulkimport_pwd'); $this->_disable_users = (bool)get_site_option('AD_Integration_disable_users'); } else { $this->_log(ADI_LOG_INFO,'loading options ...'); // Server (5) $this->_domain_controllers = get_option('AD_Integration_domain_controllers'); $this->_port = get_option('AD_Integration_port'); $this->_use_tls = get_option('AD_Integration_use_tls'); $this->_network_timeout = (int)get_option('AD_Integration_network_timeout'); $this->_base_dn = get_option('AD_Integration_base_dn'); // User (13) $this->_account_suffix = get_option('AD_Integration_account_suffix'); $this->_append_suffix_to_new_users = get_option('AD_Integration_append_suffix_to_new_users'); $this->_auto_create_user = (bool)get_option('AD_Integration_auto_create_user'); $this->_auto_update_user = (bool)get_option('AD_Integration_auto_update_user'); $this->_auto_update_description = (bool)get_option('AD_Integration_auto_update_description'); $this->_default_email_domain = get_option('AD_Integration_default_email_domain'); $this->_duplicate_email_prevention = get_option('AD_Integration_duplicate_email_prevention'); $this->_prevent_email_change = (bool)get_option('AD_Integration_prevent_email_change'); $this->_display_name = get_option('AD_Integration_display_name'); $this->_show_user_status = (bool)get_option('AD_Integration_show_user_status'); $this->_enable_password_change = get_option('AD_Integration_enable_password_change'); $this->_no_random_password = (bool)get_option('AD_Integration_no_random_password'); $this->_auto_update_password = (bool)get_option('AD_Integration_auto_update_password'); // Authorization (3) $this->_authorize_by_group = (bool)get_option('AD_Integration_authorize_by_group'); $this->_authorization_group = get_option('AD_Integration_authorization_group'); $this->_role_equivalent_groups = get_option('AD_Integration_role_equivalent_groups'); // Security (6) $this->_fallback_to_local_password = get_option('AD_Integration_fallback_to_local_password'); $this->_enable_lost_password_recovery = (bool)get_option('AD_Integration_enable_lost_password_recovery'); $this->_max_login_attempts = (int)get_option('AD_Integration_max_login_attempts'); $this->_block_time = (int)get_option('AD_Integration_block_time'); $this->_user_notification = (bool)get_option('AD_Integration_user_notification'); $this->_admin_notification = (bool)get_option('AD_Integration_admin_notification'); $this->_admin_email = get_option('AD_Integration_admin_email'); // User Meta (8) $this->_additional_user_attributes = get_option('AD_Integration_additional_user_attributes'); $this->_usermeta_empty_overwrite = (bool)get_option('AD_Integration_usermeta_empty_overwrite'); $this->_show_attributes = (bool)get_option('AD_Integration_show_attributes'); $this->_attributes_to_show = get_option('AD_Integration_attributes_to_show'); $this->_syncback = (bool)get_option('AD_Integration_syncback'); $this->_syncback_use_global_user = (bool)get_option('AD_Integration_syncback_use_global_user'); $this->_syncback_global_user = get_option('AD_Integration_syncback_global_user'); $this->_syncback_global_pwd = get_option('AD_Integration_syncback_global_pwd'); // Bulk Import (7) $this->_bulkimport_enabled = (bool)get_option('AD_Integration_bulkimport_enabled'); $this->_bulkimport_authcode = get_option('AD_Integration_bulkimport_authcode'); $this->_bulkimport_new_authcode = (bool)get_option('AD_Integration_bulkimport_new_authcode'); $this->_bulkimport_security_groups = get_option('AD_Integration_bulkimport_security_groups'); $this->_bulkimport_user = get_option('AD_Integration_bulkimport_user'); $this->_bulkimport_pwd = get_option('AD_Integration_bulkimport_pwd'); $this->_disable_users = (bool)get_option('AD_Integration_disable_users'); } } /** * Get array of descriptions for default AD attributes * This function is needed for i18n. * @return array */ protected function _get_attribute_descriptions() { $descriptions = array(); // General $descriptions['cn'] = __('Common Name','ad-integration'); $descriptions['givenname'] = __('First name','ad-integration'); $descriptions['initials'] = __('Initials','ad-integration'); $descriptions['sn'] = __('Last name','ad-integration'); $descriptions['displayname'] = __('Display name','ad-integration'); $descriptions['description'] = __('Description','ad-integration'); $descriptions['physicaldeliveryofficename'] = __('Office','ad-integration'); $descriptions['telephonenumber'] = __('Telephone number','ad-integration'); $descriptions['mail'] = __('E-mail','ad-integration'); $descriptions['wwwhomepage'] = __('Web Page','ad-integration'); // Account $descriptions['samaccountname'] = __('User logon name','ad-integration'); // Address $descriptions['streetaddress'] = __('Street','ad-integration'); $descriptions['postofficebox'] = __('P.O. Box','ad-integration'); $descriptions['l'] = __('City','ad-integration'); $descriptions['st'] = __('State','ad-integration'); $descriptions['postalcode'] = __('ZIP/Postal cide','ad-integration'); $descriptions['c'] = __('Country abbreviation','ad-integration'); $descriptions['co'] = __('Country','ad-integration'); $descriptions['countrycode'] = __('Country code (number)','ad-integration'); // Telephones $descriptions['homephone'] = __('Home','ad-integration'); $descriptions['otherhomephone'] = __('Home (other)','ad-integration'); $descriptions['pager'] = __('Pager','ad-integration'); $descriptions['otherpager'] = __('Pager (other)','ad-integration'); $descriptions['mobile'] = __('Mobile','ad-integration'); $descriptions['othermobile'] = __('Mobile (Other)','ad-integration'); $descriptions['facsimiletelephonenumber'] = __('Fax','ad-integration'); $descriptions['otherfacsimiletelephonenumber'] = __('Fax (other)','ad-integration'); $descriptions['ipphone'] = __('IP Phone','ad-integration'); $descriptions['otheripphone'] = __('IP Phone (other)','ad-integration'); $descriptions['info'] = __('Notes','ad-integration'); // Organization $descriptions['title'] = __('Title','ad-integration'); $descriptions['department'] = __('Department','ad-integration'); $descriptions['company'] = __('Company','ad-integration'); $descriptions['manager'] = __('Manager','ad-integration'); $descriptions['directreports'] = __('Direct reports','ad-integration'); return $descriptions; } /** * Get array of descriptions for default AD attributes * plus additional user defined attributes * * @return array descriptions in associative array * @deprecated */ protected function _get_all_attribute_descriptions() { // get descriptions for default AD attributes first $descriptions = $this->_get_attribute_descriptions(); // and now for additional AD attributes to show if (trim($this->_attributes_to_show) != '') { $lines = explode("\n", $this->_attributes_to_show); foreach ($lines AS $line) { $parts = explode(":", $line, 2); // limit is important here $attribute = trim($parts[0]); $description = ''; if ($attribute != '') { if (isset($parts[1])) { // remove possible sync flag $part = trim($parts[1]); if (substr($part,-2) == ':*') { $description = substr($part,0,-2); } else { $description = $part; } } if ($description == '') { if (isset($descriptions[$attribute])) { $description = $descriptions[$attribute]; } else { $description = $attribute; } } $descriptions[$attribute] = $description; } } } return $descriptions; } /** * Get list list of attributes to load from AD (default + additional) * * @return array all attributes to load */ protected function _get_user_attributes() { // default attributes $attributes = $this->_default_user_attributes; // additional attributes if (trim($this->_additional_user_attributes) != '') { $lines = explode("\n", str_replace("\r",'',$this->_additional_user_attributes)); foreach ($lines AS $line) { $parts = explode(":",$line); if ($parts[0] != '') { if (!in_array($parts[0], $attributes)) { $attributes[] = $parts[0]; } } } } return $attributes; } /** * Get associative array of attributes, types, metakeys, descriptions, sync flag and show flag for AD attributes * * @return array */ protected function _get_attributes_array() { $attributes = array(); // default attributes // type is always string, meta key is set to ADI_ and description is loaded $descriptions = $this->_get_attribute_descriptions(); // default descriptions foreach($this->_default_user_attributes AS $attribute) { $attributes[$attribute]['type'] = 'string'; $attributes[$attribute]['metakey'] = $this->_usermeta_prefix.$attribute; if (isset($descriptions[$attribute])) { $attributes[$attribute]['description'] = $descriptions[$attribute]; } else { $attributes[$attribute]['description'] = $attribute; } $attributes[$attribute]['sync'] = false; $attributes[$attribute]['show'] = false; } // additional attributes // type and metakey if (trim($this->_additional_user_attributes) != '') { $lines = explode("\n", str_replace("\r",'',$this->_additional_user_attributes)); foreach ($lines AS $line) { $parts = explode(":",$line); if (isset($parts[0]) && (trim($parts[0]) != '')) { $attribute = trim($parts[0]); // type if (!isset($parts[1])) { $parts[1] = 'string'; } else { $parts[1] = strtolower(trim($parts[1])); } if (!in_array($parts[1], array('string','list','integer','bool','time','timestamp','octet','cn'))) { $parts[1] = 'string'; } $attributes[$attribute]['type'] = $parts[1]; // meta key if (!isset($parts[2])) { $parts[2] = $this->_usermeta_prefix.$attribute; } else { $parts[2] = trim($parts[2]); } $attributes[$attribute]['metakey'] = $parts[2]; // description if (isset($descriptions[$attribute])) { $attributes[$attribute]['description'] = $descriptions[$attribute]; } else { $attributes[$attribute]['description'] = $attribute; } $attributes[$attribute]['sync'] = false; $attributes[$attribute]['show'] = false; } } } // and now for additional AD attributes to show if (trim($this->_attributes_to_show) != '') { $lines = explode("\n", $this->_attributes_to_show); foreach ($lines AS $line) { $parts = explode(":", $line, 2); // limit is important here $attribute = trim($parts[0]); $description = ''; $sync = false; if ($attribute != '') { if (isset($parts[1])) { // remove possible sync flag $part = trim($parts[1]); if (substr($part,-2) == ':*') { $description = substr($part,0,-2); $sync = true; } else { $description = $part; } } if ($description == '') { if (isset($descriptions[$attribute])) { $description = $descriptions[$attribute]; } else { $description = $attribute; } } $attributes[$attribute]['description'] = $description; $attributes[$attribute]['sync'] = $sync; $attributes[$attribute]['show'] = true; } } } return $attributes; } /** * Returns formatted value according to attribute type * * @param string $type (string, integer, bool, time, timestamp, octet, cn) * @param mixed $value * @return mixed formatted value */ protected function _format_attribute_value($type, $value) { switch ($type) { case 'string': return $value; case 'integer': return (int)$value; case 'bool': return (bool)$value; case 'time': // ASN.1 GeneralizedTime $timestamp = mktime(substr($value,8,2),substr($value,10,2),substr(12,2),substr($value,4,2),substr($value,6,2),substr($value,0,4)); if (substr($value, -1) == 'Z') { $offset = get_option('gmt_offset',0) * 3600; } else { $offset = 0; } return date_i18n(get_option('date_format','Y-m-d').' / '.get_option('time_format','H:i:s'), $timestamp + $offset, true); case 'timestamp': $timestamp = ($value / 10000000) - 11644473600 + get_option('gmt_offset',0) * 3600; return date_i18n(get_option('date_format','Y-m-d').' / '.get_option('time_format','H:i:s'), $timestamp, true); case 'octet': return base64_encode($value); case 'cn': // Get CN from string $pos = stripos($value,'cn='); if ($pos !== false) { $start = $pos+3; $foundEnd = false; $valueLength = strlen($value); for ($x = $start; $x < $valueLength; $x++) { if ($value[$x] == ',') { if ($value[$x-1] !== '\\') { break; } } } return stripslashes(substr($value, $start, $x - $start)); } else { return ''; } } return $value; } /** * Returns true if the user is an ADI User * * @param integer $user_id */ protected function _is_adi_user($user_id) { return (get_user_meta($user_id, 'adi_samaccountname', true) != ''); } /** * Saves the options to the sitewide options store. This is only needed for multisite. * * @param $arrPost the POST-Array with the new options * @return unknown_type */ protected function _save_multisite_options($arrPost) { if ( is_multisite() ) { // Server if ($arrPost['option_page'] == 'ADI-server-settings') { if (!isset($arrPost['AD_Integration_use_tls'])) { $arrPost['AD_Integration_use_tls'] = 0; } update_site_option('AD_Integration_domain_controllers', $arrPost['AD_Integration_domain_controllers']); update_site_option('AD_Integration_base_dn', $arrPost['AD_Integration_base_dn']); update_site_option('AD_Integration_port', $arrPost['AD_Integration_port']); update_site_option('AD_Integration_use_tls', $arrPost['AD_Integration_use_tls']); update_site_option('AD_Integration_network_timeout', (int)$arrPost['AD_Integration_network_timeout']); } // User if ($arrPost['option_page'] == 'ADI-user-settings') { if (!isset($arrPost['AD_Integration_append_suffix_to_new_users'])) { $arrPost['AD_Integration_append_suffix_to_new_users'] = 0; } if (!isset($arrPost['AD_Integration_auto_create_user'])) { $arrPost['AD_Integration_auto_create_user'] = 0; } if (!isset($arrPost['AD_Integration_auto_update_user'])) { $arrPost['AD_Integration_auto_update_user'] = 0; } if (!isset($arrPost['AD_Integration_auto_update_description'])) { $arrPost['AD_Integration_auto_update_description'] = 0; } if (!isset($arrPost['AD_Integration_prevent_email_change'])) { $arrPost['AD_Integration_prevent_email_change'] = 0; } if (!isset($arrPost['AD_Integration_show_user_status'])) { $arrPost['AD_Integration_show_user_status'] = 0; } if (!isset($arrPost['AD_Integration_enable_password_change'])) { $arrPost['AD_Integration_enable_password_change'] = 0; } if (!isset($arrPost['AD_Integration_no_random_password'])) { $arrPost['AD_Integration_no_random_password'] = 0; } if (!isset($arrPost['AD_Integration_auto_update_password'])) { $arrPost['AD_Integration_auto_update_password'] = 0; } update_site_option('AD_Integration_account_suffix', $arrPost['AD_Integration_account_suffix']); update_site_option('AD_Integration_append_suffix_to_new_users', (bool)$arrPost['AD_Integration_append_suffix_to_new_users']); update_site_option('AD_Integration_auto_create_user', (bool)$arrPost['AD_Integration_auto_create_user']); update_site_option('AD_Integration_auto_update_user', (bool)$arrPost['AD_Integration_auto_update_user']); update_site_option('AD_Integration_auto_update_description', (bool)$arrPost['AD_Integration_auto_update_description']); update_site_option('AD_Integration_default_email_domain', $arrPost['AD_Integration_default_email_domain']); update_site_option('AD_Integration_duplicate_email_prevention', (bool)$arrPost['AD_Integration_duplicate_email_prevention']); update_site_option('AD_Integration_prevent_email_change', (bool)$arrPost['AD_Integration_prevent_email_change']); update_site_option('AD_Integration_display_name', $arrPost['AD_Integration_display_name']); update_site_option('AD_Integration_show_user_status', (bool)$arrPost['AD_Integration_show_user_status']); update_site_option('AD_Integration_enable_password_change', (bool)$arrPost['AD_Integration_enable_password_change']); update_site_option('AD_Integration_no_random_password', (bool)$arrPost['AD_Integration_no_random_password']); update_site_option('AD_Integration_auto_update_password', (bool)$arrPost['AD_Integration_auto_update_password']); } // Authorization if ($arrPost['option_page'] == 'ADI-auth-settings') { if (!isset( $arrPost['AD_Integration_authorize_by_group'] )) { $arrPost['AD_Integration_authorize_by_group'] = 0; } update_site_option('AD_Integration_authorize_by_group', (bool)$arrPost['AD_Integration_authorize_by_group']); update_site_option('AD_Integration_authorization_group', $arrPost['AD_Integration_authorization_group']); update_site_option('AD_Integration_role_equivalent_groups', $arrPost['AD_Integration_role_equivalent_groups']); } // Security if ($arrPost['option_page'] == 'ADI-security-settings') { if (!isset($arrPost['AD_Integration_fallback_to_local_password'])) { $arrPost['AD_Integration_fallback_to_local_password'] = 0; } if (!isset($arrPost['AD_Integration_enable_lost_password_recovery'])) { $arrPost['AD_Integration_enable_lost_password_recovery'] = 0; } if (!isset($arrPost['AD_Integration_user_notification'])) { $arrPost['AD_Integration_user_notification'] = 0; } if (!isset($arrPost['AD_Integration_admin_notification'])) { $arrPost['AD_Integration_admin_notification'] = 0; } update_site_option('AD_Integration_fallback_to_local_password', $arrPost['AD_Integration_fallback_to_local_password']); update_site_option('AD_Integration_enable_lost_password_recovery', $arrPost['AD_Integration_enable_lost_password_recovery']); update_site_option('AD_Integration_max_login_attempts', $arrPost['AD_Integration_max_login_attempts']); update_site_option('AD_Integration_block_time', $arrPost['AD_Integration_block_time']); update_site_option('AD_Integration_user_notification', $arrPost['AD_Integration_user_notification']); update_site_option('AD_Integration_admin_notification', $arrPost['AD_Integration_admin_notification']); update_site_option('AD_Integration_admin_email', $arrPost['AD_Integration_admin_email']); } // User Meta if ($arrPost['option_page'] == 'ADI-usermeta-settings') { if (!isset($arrPost['AD_Integration_usermeta_empty_overwrite'])) { $arrPost['AD_Integration_usermeta_empty_overwrite'] = 0; } if (!isset($arrPost['AD_Integration_show_attributes'])) { $arrPost['AD_Integration_show_attributes'] = 0; } if (!isset($arrPost['AD_Integration_syncback'])) { $arrPost['AD_Integration_syncback'] = 0; } if (!isset($arrPost['AD_Integration_syncback_use_global_user'])) { $arrPost['AD_Integration_syncback_use_global_user'] = 0; } update_site_option('AD_Integration_additional_user_attributes', $arrPost['AD_Integration_additional_user_attributes']); update_site_option('AD_Integration_usermeta_empty_overwrite', $arrPost['AD_Integration_usermeta_empty_overwrite']); update_site_option('AD_Integration_show_attributes', $arrPost['AD_Integration_show_attributes']); update_site_option('AD_Integration_attributes_to_show', $arrPost['AD_Integration_attributes_to_show']); update_site_option('AD_Integration_syncback', (bool)$arrPost['AD_Integration_syncback']); update_site_option('AD_Integration_syncback_use_global_user', (bool)$arrPost['AD_Integration_syncback_use_global_user']); update_site_option('AD_Integration_syncback_global_user', $arrPost['AD_Integration_syncback_global_user']); if (!empty($arrPost['AD_Integration_syncback_global_pwd'])) update_site_option('AD_Integration_syncback_global_pwd', $arrPost['AD_Integration_syncback_global_pwd']); } // Bulk Import if ($arrPost['option_page'] == 'ADI-bulkimport-settings') { if (!isset($arrPost['AD_Integration_bulkimport_enabled'])) { $arrPost['AD_Integration_bulkimport_enabled'] = 0; } if (!isset($arrPost['AD_Integration_disable_users'])) { $arrPost['AD_Integration_disable_users'] = 0; } if (!isset($arrPost['AD_Integration_bulkimport_new_authcode'])) { $arrPost['AD_Integration_bulkimport_new_authcode'] = 0; } update_site_option('AD_Integration_bulkimport_enabled', $arrPost['AD_Integration_bulkimport_enabled']); update_site_option('AD_Integration_bulkimport_new_authcode', $arrPost['AD_Integration_bulkimport_new_authcode']); update_site_option('AD_Integration_bulkimport_security_groups', $arrPost['AD_Integration_bulkimport_security_groups']); update_site_option('AD_Integration_bulkimport_user', $arrPost['AD_Integration_bulkimport_user']); if (!empty($arrPost['AD_Integration_bulkimport_pwd'])) update_site_option('AD_Integration_bulkimport_pwd', $arrPost['AD_Integration_bulkimport_pwd']); update_site_option('AD_Integration_disable_users', $arrPost['AD_Integration_disable_users']); } // let's load the new values $this->_load_options(); } } /** * Determine the display_name to be stored in WP database. * * @param $username the username used to login * @param $userinfo the array with data returned from AD * @return string display_name */ protected function _get_display_name_from_AD($username, $userinfo) { $display_name = ''; if (($this->_display_name == '') OR ($this->_display_name == 'sAMAccountName')) { return $username; } if ($this->_display_name == 'givenname sn') { if (isset($userinfo['givenname'][0]) && isset($userinfo['sn'][0])) { $display_name = $userinfo['givenname'][0].' '.$userinfo['sn'][0]; } } else { if (isset($userinfo[$this->_display_name][0])) { $display_name = $userinfo[$this->_display_name][0]; } } if ($display_name == '') { return $username; } else { return $display_name; } } /** * Stores the username and the current time in the db. * * @param $username * @return query result */ protected function _store_failed_login($username) { global $wpdb; $this->_log(ADI_LOG_WARN,'storing failed login for user "'.$username.'"'); $table_name = $wpdb->base_prefix . ADIntegrationPlugin::TABLE_NAME; $sql = $wpdb->prepare("INSERT INTO $table_name (user_login, failed_login_time) VALUES (%s, %d)", $username, time()); $result = $wpdb->query($sql); } /** * Determines the number of failed login attempts of specific user within a specific time from now to the past. * * @param $username * @param $seconds number of seconds * @return number of failed login attempts */ protected function _get_failed_logins_within_block_time($username) { global $wpdb; if (empty($username)) { return 0; } $table_name = $wpdb->base_prefix . ADIntegrationPlugin::TABLE_NAME; $time = time() - (int)$this->_block_time; $sql = $wpdb->prepare("SELECT count(*) AS count from $table_name WHERE user_login = %s AND failed_login_time >= %d", $username, $time); return $wpdb->get_var($sql); } /** * Deletes entries from store where the time of failed logins is more than the specified block time ago. * Deletes also all entries of a user, if its username is given . * * @param $username * @return query result */ protected function _cleanup_failed_logins($username = NULL) { global $wpdb; $this->_log(ADI_LOG_NOTICE,'cleaning up failed logins for user "'.$username.'"'); $table_name = $wpdb->base_prefix . ADIntegrationPlugin::TABLE_NAME; $time = time() - $this->_block_time; if ($username != NULL) { $sql = $wpdb->prepare("DELETE FROM $table_name WHERE failed_login_time < %d OR user_login = %s", $time, $username); } else { $sql = $wpdb->prepare("DELETE FROM $table_name WHERE failed_login_time < %d", $time); } $results = $wpdb->query($sql); } /** * Get the rest of the time an account is blocked. * * @param $username * @return int seconds the account is blocked, or 0 */ protected function _get_rest_of_blocking_time($username) { global $wpdb; $table_name = $wpdb->base_prefix . ADIntegrationPlugin::TABLE_NAME; $sql = $wpdb->prepare("SELECT max(failed_login_time) FROM $table_name WHERE user_login = %s", $username); $max_time = $wpdb->get_var($sql); if ($max_time == NULL ) { return 0; } return ($max_time + $this->_block_time) - time(); } /** * Generate a random password. * * @param int $length Length of the password * @return password as string */ protected function _get_password($length = 10) { return substr(md5(uniqid(microtime())), 0, $length); } /** * Create a new WordPress account for the specified username. * * @param string $username * @param array $userinfo * @param string $display_name * @param string $role * @param string $password * @return integer user_id */ protected function _create_user($username, $userinfo, $display_name, $role = '', $password = '', $bulkimport = false) { global $wp_version; $info = $this->_create_info_array($userinfo); // get UPN suffix $parts = explode('@',$info['userprincipalname']); if (isset($parts[1])) { $account_suffix = '@'.$parts[1]; } else { $account_suffix = ''; } if (isset($info['mail'])) { $email = $info['mail']; } else { $email = ''; } if ( $info['mail'] == '' ) { if (trim($this->_default_email_domain) != '') { $email = $username . '@' . $this->_default_email_domain; } else { if (strpos($username, '@') !== false) { $email = $username; } } } // append account suffix to new users? if ($this->_append_suffix_to_new_users) { $username .= $account_suffix; } $this->_log(ADI_LOG_NOTICE,"Creating user '$username' with following data:\n". "- email : ".$email."\n". "- first name : ".$info['givenname']."\n". "- last name : ".$info['sn']."\n". "- display name : $display_name\n". "- account suffix: $account_suffix\n". "- role : $role"); // set local password if needed or on Bulk Import if (!$this->_no_random_password || ($bulkimport === true)) { $password = $this->_get_password(); $this->_log(ADI_LOG_DEBUG,'Setting random password.'); } else { $this->_log(ADI_LOG_DEBUG,'Setting local password to the used for this login.'); } if (version_compare($wp_version, '3.1', '<')) { require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php'); } if ($this->_duplicate_email_prevention == ADI_DUPLICATE_EMAIL_ADDRESS_ALLOW) { if (!defined('WP_IMPORTING')) { define('WP_IMPORTING',true); // This is a dirty hack. See wp-includes/registration.php } } if ($this->_duplicate_email_prevention == ADI_DUPLICATE_EMAIL_ADDRESS_CREATE) { $new_email = $this->_create_non_duplicate_email($email); if ($new_email !== $email) { $this->_log(ADI_LOG_NOTICE, "Duplicate email address prevention: Email changed from $email to $new_email."); } $email = $new_email; } // Here we go! $return = wp_create_user($username, $password, $email); // log errors if (is_wp_error($return)) { $this->_log(ADI_LOG_ERROR, $return->get_error_message()); } $user_id = username_exists($username); $this->_log(ADI_LOG_NOTICE,'- user_id : '.$user_id); if ( !$user_id ) { // do not die on bulk import if (!$bulkimport) { $this->_log(ADI_LOG_FATAL,'Error creating user.'); die("Error creating user!"); } else { $this->_log(ADI_LOG_ERROR,'Error creating user.'); return false; } } else { update_user_meta($user_id, 'first_name', $info['givenname']); update_user_meta($user_id, 'last_name', $info['sn']); if ($this->_auto_update_description) { update_user_meta($user_id, 'description', $info['description']); } // set display_name if ($display_name != '') { $return = wp_update_user(array('ID' => $user_id, 'display_name' => $display_name)); } // set role if ( $role != '' ) { $roles = new WP_Roles(); if ($roles->is_role($role)) { // Updates role only if role exists (Issue #0051) wp_update_user(array('ID' => $user_id, 'role' => $role)); } else { $this->_log(ADI_LOG_WARN, 'Role "' . $role . '" currently does not exist in WordPress. Role of "' . $username . '" is not set.'); } } // Important for SyncBack: store account suffix in user meta update_user_meta($user_id, 'ad_integration_account_suffix', $account_suffix); // Update User Meta if ($this->_write_usermeta === true) { $attributes = $this->_get_attributes_array(); // load attribute informations: type, metakey, description foreach($info AS $attribute => $value) { // conversion/formatting $type = $attributes[$attribute]['type']; $metakey = $attributes[$attribute]['metakey']; $value = $this->_format_attribute_value($type, $value); if ((trim($value) != '') || ($this->_usermeta_empty_overwrite == true)) { $this->_log(ADI_LOG_DEBUG,"$attribute = $value / type = $type / meta key = $metakey"); // store it update_user_meta($user_id, $metakey, $value); } else { $this->_log(ADI_LOG_DEBUG,"$attribute is empty. Local value of meta key $metakey left unchanged."); } } } } return $user_id; } /** * Updates a specific Wordpress user account * * @param string $username * @param array $userinfo * @param string $display_name * @param string $role * @param string $password * @return integer user_id */ protected function _update_user($username, $userinfo, $display_name='', $role = '', $password = '', $bulkimport = false) { global $wp_version; /* Since WP 4.3 we have to disable email on password and email change */ add_filter('send_password_change_email', '__return_false'); add_filter('send_email_change_email', '__return_false'); $info = $this->_create_info_array($userinfo); // get UPN suffix $parts = explode('@',$info['userprincipalname']); if (isset($parts[1])) { $account_suffix = '@'.$parts[1]; } else { $account_suffix = ''; } if (isset($info['mail'])) { $email = $info['mail']; } else { $email = ''; } if ( $email == '' ) { if (trim($this->_default_email_domain) != '') { $email = $username . '@' . $this->_default_email_domain; } else { if (strpos($username, '@') !== false) { $email = $username; } } } if ($this->_append_suffix_to_new_users) { $username .= $account_suffix; } $this->_log(ADI_LOG_NOTICE,'Updating user "'.$username."\" with following data:\n". "- email : $email\n". "- first name : ".$info['givenname']."\n". "- last name : ".$info['sn']."\n". "- display name : $display_name\n". "- account suffix: $account_suffix\n". "- role : $role"); if (version_compare($wp_version, '3.1', '<')) { require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php'); } $user_id = username_exists($username); if ($user_id === false) { return false; } $this->_log(ADI_LOG_NOTICE,'- user_id : '.$user_id); if ( !$user_id ) { $this->_log(ADI_LOG_FATAL,'Error updating user.'); die('Error updating user!'); } else { update_user_meta($user_id, 'first_name', $info['givenname']); update_user_meta($user_id, 'last_name', $info['sn']); if ($this->_auto_update_description) { update_user_meta($user_id, 'description', $info['description']); } // set display_name if ($display_name != '') { wp_update_user(array('ID' => $user_id, 'display_name' => $display_name)); } // set role if ( $role != '' ) { $roles = new WP_Roles(); if ($roles->is_role($role)) { // Updates role only if role exists wp_update_user(array('ID' => $user_id, 'role' => $role)); } else { $this->_log(ADI_LOG_WARN, 'Role "' . $role . '" currently does not exist in WordPress. Role of "' . $username . '" is not set.'); } } // set email if not empty if ( $email != '' ) { // if we allow duplicate email addresses just set it if ($this->_duplicate_email_prevention == ADI_DUPLICATE_EMAIL_ADDRESS_ALLOW) { $return = wp_update_user(array('ID' => $user_id, 'user_email' => $email)); } else { // duplicate email addresses disallowed // if we don't have a conflict, just set it if (!email_exists($email)) { $return = wp_update_user(array('ID' => $user_id, 'user_email' => $email)); } else { // we have a conflict, so only update when the "create" option is set if ($this->_duplicate_email_prevention == ADI_DUPLICATE_EMAIL_ADDRESS_CREATE) { $userdata = get_userdata($user_id); // only update if the email is not already set if ($userdata->user_email == '') { $new_email = $this->_create_non_duplicate_email($email); $this->_log(ADI_LOG_NOTICE, "Duplicate email address prevention: Email changed from $email to $new_email."); $return = wp_update_user(array('ID' => $user_id, 'user_email' => $new_email)); } else { $this->_log(ADI_LOG_NOTICE, "Duplicate email address prevention: Existing email " . $userdata->user_email . " left unchanged."); } } } } } } // Update password if needed (NOT on Bulk Import) if (($this->_auto_update_password === true) && ($bulkimport === false)) { $this->_log(ADI_LOG_NOTICE,'Setting local password to the one used for this login.'); @wp_update_user(array('ID' => $user_id, 'user_pass' => $password)); // can lead to notices so we use @ } // Important for SyncBack: store account suffix in user meta update_user_meta($user_id, 'ad_integration_account_suffix', $account_suffix); // Update User Meta if ($this->_write_usermeta === true) { $attributes = $this->_get_attributes_array(); // load attribute informations: type, metakey, description foreach($info AS $attribute => $value) { // conversion/formatting $type = $attributes[$attribute]['type']; $metakey = $attributes[$attribute]['metakey']; $value = $this->_format_attribute_value($type, $value); if ((trim($value) != '') || ($this->_usermeta_empty_overwrite == true)) { $this->_log(ADI_LOG_DEBUG,"$attribute = $value / type = $type / meta key = $metakey"); // store it update_user_meta($user_id, $metakey, $value); } else { $this->_log(ADI_LOG_DEBUG,"$attribute is empty. Local value of meta key $metakey left unchanged."); } } } // log errors if (isset($return)) { if (is_wp_error($return)) { $this->_log(ADI_LOG_ERROR, $return->get_error_message()); } } return $user_id; } /** * Disable a user by setting adi_user_disabled to true in usermeta and changing the email * * @param int $user_id User ID * @param string $reason Reason for disabling the user */ protected function _disable_user($user_id, $reason) { $userdata = get_userdata($user_id); if (strpos($userdata->user_email,'DISABLED-USER-') !== 0) { $new_email = 'DISABLED-USER-' . $userdata->user_email; } else { $new_email = $userdata->user_email; } update_user_meta($user_id, 'adi_user_disabled', true); // set disabled flag update_user_meta($user_id, 'adi_user_disabled_reason', $reason); // store reason // Do not overwrite previously stored email if (get_user_meta($user_id, 'adi_user_disabled_email', true) == '') { update_user_meta($user_id, 'adi_user_disabled_email', $userdata->user_email); // store email in meta } wp_update_user( array ('ID' => $user_id, 'user_email' => $new_email) ) ; // change email of user // DIRTY: if we come from profile page, we have to change to POST data. if (isset($_POST['email'])) { $_POST['email'] = $new_email; } } /** * Enables a user by setting adi_user_disabled to false in usermeta and restoring the email * * @param int $user_id User ID */ protected function _enable_user($user_id) { update_user_meta($user_id, 'adi_user_disabled', false); // remove disabled flag update_user_meta($user_id, 'adi_user_disabled_reason', ''); // remove reaon $email = get_user_meta($user_id, 'adi_user_disabled_email', true); // fetch stored email from meta // Only restore email if it has been previously stored if ($email != '') { wp_update_user( array ('ID' => $user_id, 'user_email' => $email) ) ; // restore email } delete_user_meta($user_id, 'adi_user_disabled_email'); // delete stored email from meta // DIRTY: if we come from profile page, we have to change the POST data. if (isset($_POST['email'])) { $_POST['email'] = $email; } } /** * Returns the given email address or a newly created so no 2 users * can have the same email address. * * @param $email original email address * @return unique email address */ protected function _create_non_duplicate_email($email) { if (!email_exists($email)) { return $email; } // Ok, lets create a new email address that does not already exists in the database $arrEmailParts = split('@',$email); $counter = 1; $ok = false; while ($ok !== true) { $email = $arrEmailParts[0].$counter.'@'.$arrEmailParts[1]; $ok = !email_exists($email); $counter++; } return $email; } /** * Build an array with the values for AD attributes * * @param array $userinfo * @return array */ protected function _create_info_array($userinfo) { $info = array(); foreach($this->_all_user_attributes AS $attribute) { $attribute = strtolower($attribute); if (isset($userinfo[$attribute])) { if (isset($userinfo[$attribute]['count'])) { unset($userinfo[$attribute]['count']); $info[$attribute] = implode("\n", $userinfo[$attribute]); } else { $info[$attribute] = $userinfo[$attribute]; } } else { $info[$attribute] = ''; } } return $info; } /** * Checks if the user is member of the group(s) allowed to login * * @param $username * @return boolean */ protected function _check_authorization_by_group($username) { // Debugging: show all groups the user is a member of if (defined('WP_DEBUG')) { $this->_log(ADI_LOG_DEBUG,"USER GROUPS:".print_r($this->_adldap->user_groups($username),true)); } if ($this->_authorize_by_group) { $authorization_groups = explode(';', $this->_authorization_group); foreach ($authorization_groups as $authorization_group) { if ($this->_adldap->user_ingroup($username, $authorization_group, true)) { $this->_log(ADI_LOG_NOTICE,'Authorized by membership of group "'.$authorization_group.'"'); return true; } } $this->_log(ADI_LOG_WARN,'Authorization by group failed. User is not authorized.'); return false; } else { return true; } } /** * Get the first matching role from the list of role equivalent groups the user belongs to. * * @param $ad_username * @return string matching role */ protected function _get_user_role_equiv($ad_username) { $role_equiv_groups = explode(';', $this->_role_equivalent_groups); $user_role = ''; foreach ($role_equiv_groups as $whatever => $role_group) { $role_group = explode('=', $role_group); if ( count($role_group) != 2 ) { continue; } $ad_group = $role_group[0]; $corresponding_role = $role_group[1]; if ( $this->_adldap->user_ingroup($ad_username, $ad_group, true ) ) { $user_role = $corresponding_role; break; } } return $user_role; } /** * Generate a new Auth Code of 20 characters and store it */ protected function _generate_authcode() { $length = 20; $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; $code = ''; for ($x = 0; $x < $length; $x++) { $code .= $chars[mt_rand(0, strlen($chars)-1)]; } $this->_bulkimport_authcode = $code; // Save authcode if ( is_multisite() ) { update_site_option('AD_Integration_bulkimport_authcode',$code); } else { update_option('AD_Integration_bulkimport_authcode',$code); } $this->_log(ADI_LOG_NOTICE,'New Auth Code for Bulk Import generated.'); } /** * Send an email to the user who's account is blocked * * @param $username string * @return unknown_type */ protected function _notify_user($username) { // if auto creation is enabled look for the user in AD if ($this->_auto_create_user) { $userinfo = $this->_adldap->user_info($username, array("sn", "givenname", "mail")); if ($userinfo) { $userinfo = $userinfo[0]; $email = $userinfo['mail'][0]; $first_name = $userinfo['givenname'][0]; $last_name = $userinfo['sn'][0]; } else { return false; } } else { // auto creation is disabled, so look for the user in local database if (version_compare($wp_version, '3.1', '<')) { require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php'); } $user_id = username_exists($username); if ($user_id) { $user_info = get_userdata($user_id); $last_name = $user_info->last_name; $first_name = $user_info->first_name; $email = $user_info->user_email; } else { return false; } } // do we have a correct email address? if (is_email($email)) { // Load up the localization file if we're using WordPress in a different language // Place it in this plugin's folder and name it "ad-integration-[value in wp-config].mo" load_plugin_textdomain( 'ad-integration', false, dirname( plugin_basename( __FILE__ ) ) ); $blog_url = get_bloginfo('url'); $blog_name = get_bloginfo('name'); $blog_domain = preg_replace ('/^(http:\/\/)(.+)\/.*$/i','$2', $blog_url); $subject = '['.$blog_name.'] '.__('Account blocked','ad-integration'); $body = sprintf(__('Someone tried to login to %s (%s) with your username (%s) - but in vain. For security reasons your account is now blocked for %d seconds.','ad-integration'), $blog_name, $blog_url, $username, $this->_block_time); $body .= "\n\r"; $body .= __('THIS IS A SYSTEM GENERATED E-MAIL, PLEASE DO NOT RESPOND TO THE E-MAIL ADDRESS SPECIFIED ABOVE.','ad-integration'); $header = 'From: "WordPress" \r\n"; return wp_mail($email, $subject, $body, $header); } else { return false; } } /** * Notify administrator(s) by e-mail if an account is blocked * * @param $username username of the blocked account * @return boolean false if no e-mail is sent, true on success */ protected function _notify_admin($username) { $arrEmail = array(); // list of recipients if ($this->_admin_notification) { $email = $this->_admin_email; // Should we use Blog-Administrator's e-mail if (trim($email) == '') { // Is this an e-mail address? if (is_email($email)) { $arrEmail[0] = trim(get_bloginfo('admin_email ')); } } else { // Using own list of notification recipients $arrEmail = explode(";",$email); // remove wrong e-mail addresses from array for ($x=0; $x < count($arrEmail); $x++) { $arrEmail[$x] = trim($arrEmail[$x]); // remove possible whitespaces if (!is_email($arrEmail[$x])) { unset($arrEmail[$x]); } } } // Do we have valid e-mail addresses? if (count($arrEmail) > 0) { if ($this->_auto_create_user) { // auto creation is enabled, so look for the user in AD $userinfo = $this->_adldap->user_info($username, array("sn", "givenname", "mail")); if ($userinfo) { $userinfo = $userinfo[0]; $first_name = $userinfo['givenname'][0]; $last_name = $userinfo['sn'][0]; } else { return false; } } else { // auto creation is disabled, so look for the user in local database if (version_compare($wp_version, '3.1', '<')) { require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php'); } $user_id = username_exists($username); if ($user_id) { $user_info = get_userdata($user_id); $last_name = $user_info->last_name; $first_name = $user_info->first_name; } else { return false; } } // Load up the localization file if we're using WordPress in a different language // Place it in this plugin's folder and name it "ad-integration-[value in wp-config].mo" load_plugin_textdomain( 'ad-integration', false, dirname( plugin_basename( __FILE__ ) ) ); $blog_url = get_bloginfo('url'); $blog_name = get_bloginfo('name'); $blog_domain = preg_replace ('/^(http:\/\/)(.+)\/.*$/i','$2', $blog_url); $subject = '['.$blog_name.'] '.__('Account blocked','ad-integration'); $body = sprintf(__('Someone tried to login to %s (%s) with the username "%s" (%s %s) - but in vain. For security reasons this account is now blocked for %d seconds.','ad-integration'), $blog_name, $blog_url, $username, $first_name, $last_name, $this->_block_time); $body .= "\n\r"; $body .= sprintf(__('The login attempt was made from IP-Address: %s','ad-integration'), $_SERVER['REMOTE_ADDR']); $body .= "\n\r"; $body .= __('THIS IS A SYSTEM GENERATED E-MAIL, PLEASE DO NOT RESPOND TO THE E-MAIL ADDRESS SPECIFIED ABOVE.','ad-integration'); $header = 'From: "WordPress" \r\n"; // send e-mails $blnSuccess = true; foreach($arrEmail AS $email) { $blnSuccess = ($blnSuccess AND wp_mail($email, $subject, $body, $header)); } return $blnSuccess; } else { return false; } } else { return false; } return true; } /** * Encrypt strings with AES/Rijndael 128 if mcrypt is available and base64 encode it. * * @param string $text data to encrypt */ protected function _encrypt($text) { if (function_exists('mcrypt_encrypt')) { $iv = md5('Active-Directory-Integration'); // not nice $key = substr(AUTH_SALT,0, mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)); $encrypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB, $iv); } else { $this->_log(ADI_LOG_WARN,'Encrypting: mcrypt not installed.'); $encrypted_text = $text; } return base64_encode($encrypted_text); } /** * base64_decode and decrypt strings with AES/Rijndael 128 if mcrypt is available. * * @param string $encrypted_text data to decrypt */ protected function _decrypt($encrypted_text) { $encrypted_text = base64_decode($encrypted_text); if (function_exists('mcrypt_decrypt')) { $iv = md5('Active-Directory-Integration'); $key = substr(AUTH_SALT,0, mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)); $text = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted_text, MCRYPT_MODE_ECB, $iv), "\0"); } else { $text = $encrypted_text; } return $text; } /** * Output debug informations * * @param integer level * @param string $notice */ protected function _log($level = 0, $info = '') { if ($level <= $this->_loglevel) { echo '[' .$level . '] '.$info."\n\r"; } if (WP_DEBUG) { if ($fh = @fopen($this->_logfile,'a+')) { fwrite($fh,'[' .$level . '] '.$info."\n"); fclose($fh); } } } /** * Show a blocking page for blocked accounts. * * @param $username */ protected function _display_blocking_page($username) { $seconds = $this->_get_rest_of_blocking_time($username); // Load up the localization file if we're using WordPress in a different language // Place it in this plugin's folder and name it "ad-integration-[value in wp-config].mo" load_plugin_textdomain( 'ad-integration', false, dirname( plugin_basename( __FILE__ ) ) ); ?> > <?php bloginfo('name'); ?> › <?php echo $title; ?>

.