Auth.php 7.39 KB
<?php

/*
 * TODO:
 * Test registration/activations system
 * Test injection - none was done
 * Possibly create hook for login page
 */

namespace Tz\WordPress\Tools\Auth;

use Tz\WordPress\Tools;
use Tz, Tz\Common;
use Exception, LogicException, InvalidArgumentException, BadMethodCallException;
use WP_User;

// These are all WordPress hooks, I put them here for easy reference
const ACTION_LOGIN    = 'wp_login';
const ACTION_LOGOUT   = 'wp_logout';
const ACTION_REGISTER = 'user_register';
const ACTION_ACTIVATE = 'wpmu_activate_user';

const OPTION_NAME = 'tz_auth'; // Database lookup key (`wp_options`.`option_name`)

    call_user_func(function() {
        Vars::$options = new Tools\WP_Option(OPTION_NAME);

        if (is_admin()) {
            require_once(__DIR__ . DIRECTORY_SEPARATOR . 'Settings.php');
        }

        if (is_array(Vars::$options['third_party'])) {
            foreach (Vars::$options['third_party'] as $tp => $on) {
                if ($on) {
                    require_once(__DIR__ . DIRECTORY_SEPARATOR . $tp . DIRECTORY_SEPARATOR . $tp . '.php');
                }
            }
        }

        Tz\import('Tz', 'trunk');
    });

/**
 * Attempts to login the user
 * @param {String} $username
 * @param {String} $password
 * @param {Boolean} $remember
 * @returns WP_User instance
 * @throws LogicException If headers have already been passed
 * @throws InvalidArgumentException If the authentication is invalid
 */
function login($username, $password, $remember = true) {
    if (headers_sent()) {
        throw new LogicException('Unable to login because headers have been sent');
    }

    $auth = _signon(Array(
        'user_login'    => esc_sql($username)
      , 'user_password' => esc_sql($password)
      , 'remember'      => $remember
    ));

    if (get_class($auth) == 'WP_User') {
        // This is done to ensure the auth'd user is logged in; cookie is not yet readable, redirect needed
        _set_current_user($auth->ID);

        return $auth;
    }

    throw new InvalidArgumentException('Invalid username/password');
        //$auth->get_error_message()); this would be nice except it links to a wp-page
}

/**
 * NOTE: Exerciese EXTREME caution!!! This automatically logs a user user without password verification!!!
 * Intended use is for third party authentication
 * @param {String} $username Username of the person to login as
 * @param {Boolean} $remember Longer session
 * @throws
 * @returns {WP_User} of the newly authenticated user
 */
function signin($username, $remember = true) {
    // What happens if someone is already signed on?  Throw exception?

    $user = get_user_by('login', $username);
    if (false === $user) {
        throw new Exception('Invalid username');
    }

    _set_auth_cookie($user->ID, $remember);
    _set_current_user($user->ID);

    do_action('wp_login', $username);
    return new WP_User($user->ID);
}

/**
 * Attempts to log the user out
 * @returns Boolean
 * @throws LogicException If HTTP headers have already been sent
 */
function logout() {
    if (headers_sent()) {
        throw new LogicException('Unable to logout because headers have been sent');
    }

    _logout();
    // I might need to do _set_current_user(0);

    return true;
}

/**
 * @param {Array} $user_data User data array, requires minimum (username, password, email)
 * @throws {InvalidArgumentException} If an invalid $registration_method is passed
 * @throw {BadMethodCallException} If any of the $user_data parameters are invalid
 * @returns {String} Unique key to activate the account
 * @uses wp-includes/registration.php
 * @global $wpdb
 * @see wpmu_signup_user
 */
function register($username, $email, $password, $meta = Array()) {
    require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php');

    $user_data = Array(
        'username' => $username
      , 'password' => $password
      , 'email'    => $email
    );
    $meta['password'] = $password;
//    array_filter($user_data, 'esc_sql');

    $valid = new Validation($user_data);
    if (count($valid->errors) > 0) {
        throw new BadMethodCallException(implode("\n", $valid->errors));
    }

    global $wpdb;

    $username = preg_replace( '/\s+/', '', sanitize_user($username, true));
    $email    = sanitize_email($email);
    $key      = substr(md5(time() . rand() . $email ), 0, 16);
    $meta     = serialize($meta);

    $wpdb->insert($wpdb->signups, Array(
        'domain'         => '',
        'path'           => '',
        'title'          => '',
        'user_login'     => $username,
        'user_email'     => $email,
        'registered'     => current_time('mysql', true),
        'activation_key' => $key,
        'meta'           => $meta
    ));

    // do_action('ACTION_REGISTER'); ???

    return $key;
}

/**
 * @param {String} $key Unique key to activate account
 * @global $wpdb
 * @see wpmu_activate_signup
 */
function activate($key) {
    global $wpdb, $current_blog;
    $signup = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->signups} WHERE activation_key = %s", $key));

    if (empty($signup)) {
        throw new Exception("{$key} is not a valid registration key");
    }
    if ($signup->active) {
        throw new Exception('Account has already been activated');
    }

    // Do I need to do another username_exists() call?
    // Can 2 users put the same username in the signup table at the same time?

    // Do I need to re-sanatize this?
    $meta = unserialize($signup->meta);
    $id = _create_user($signup->user_login, $meta['password'], $signup->user_email);
    unset($meta['password']);

    if (!$id) {
        throw new Exception('Unable to create user');
    }

    $wpdb->update($wpdb->signups, Array('active' => 1, 'activated' => current_time('mysql', true)), Array('activation_key' => $key));
//    $user_site = get_site_option('dashboard_blog', $current_site->blog_id);
    add_user_to_blog($current_blog->blog_id, $id, get_site_option('default_user_role', 'subscriber'));

    // If use these, fix variables, they're wrong
    //add_new_user_to_blog( $id, $user_email, $meta );
    //do_action(ACTION_ACTIVATE, $id, $password, $meta);

    return (int)$id;
}

class Validation extends Common\Validation {
    /**
     * @rule Not blank
     * @rule Valid WordPress username
     * @returns Boolean
     */
    protected function username($val) {
        if (empty($val)) {
            throw new Exception('Username is blank');
        }

        require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php');
        if (!validate_username($val)) {
            throw new Exception('Username must be at least 4 characters, letters and numbers only');
        }

        if (username_exists($_POST['reg_username'])) {
            throw new Exception('Username already exists');
        }
    }

    /**
     * @rule Not blank
     * @returns Boolean 
     */
    protected function password($val) {
        if (empty($val)) {
            throw new Exception('Password can not be blank');
        }
    }

    /**
     * @rule Valid email address (*@*.*)
     * @returns Boolean
     */
    protected function email($val) {
        if (!(boolean)filter_var($val, FILTER_VALIDATE_EMAIL)) {
            throw new Exception('Invalid email address');
        }

        if (false !== email_exists($val)) {
            throw new Exception('Email address already registered');
        }
    }
}

class Vars {
    /**
     * WordPress option for this module
     * @type WP_Option
     */
    public static $options;
}
?>