Your IP : 216.73.217.154


Current Path : /home/ereika83/public_html/wp-content/plugins/airstory/includes/
Upload File :
Current File : /home/ereika83/public_html/wp-content/plugins/airstory/includes/credentials.php

<?php
/**
 * Credentials storage for Airstory API keys.
 *
 * At the time of this writing, Airstory exposes bearer tokens directly via the "My Account" panel
 * when logged into the app. Since it would be silly to simply store these tokens — despite the API
 * being largely read-only — in the database, this approach leverages the OpenSSL library.
 *
 * @link http://php.net/manual/en/intro.openssl.php
 *
 * @package Airstory
 */

namespace Airstory\Credentials;

use Airstory\Settings as Settings;
use Exception;
use InvalidArgumentException;
use WP_Error;

/**
 * Get the cipher algorithm used in this environment.
 *
 * Will compare a list of preferred ciphers against the ciphers available in this environment, then
 * store the cipher used in the database. This avoids users having to reconnect to Airstory after
 * system updates (for instance, a new version of PHP that includes a more preferred cipher).
 *
 * For a full list of available options, @see openssl_get_cipher_methods().
 *
 * @throws InvalidArgumentException If no preferred algorithm is found.
 *
 * @return string The cipher algorithm to use on this site.
 */
function get_cipher_algorithm() {
	$cached = get_site_option( '_airstory_cipher_algorithm' );

	if ( $cached ) {
		return $cached;
	}

	// Of the preferred ciphers, which ones are available?
	$preferred = array(
		'AES-256-CTR', // Must be first in the list, as this used to be the *only* option.
		'AES-256-CFB',
		'AES-128-CFB',
		'aes-256-ctr', // Need lowercase as well since some servers only give lowercase.
		'aes-256-cfb',
		'aes-128-cfb',
	);
	$available = array_intersect( $preferred, openssl_get_cipher_methods() );

	if ( empty( $available ) ) {
		throw new InvalidArgumentException(
			__( 'None of the preferred cipher algorithms are available on this server.', 'airstory' )
		);
	}

	// Get the first value of the filtered array — that's our top choice.
	$algorithm = array_shift( $available );

	// Cache the result.
	add_site_option( '_airstory_cipher_algorithm', $algorithm );

	return $algorithm;
}

/**
 * Generate an initialization vector (IV) for encrypting tokens.
 *
 * In PHP 7.0+, this will be done with random_bytes(), which is the preferred method and will be
 * available on better hosts. For those users who are running on older servers, however, we'll fall
 * back to the now-deprecated mcrypt_create_iv().
 *
 * @see random_bytes()
 *
 * @return string A 16-byte initialization vector, for use with openssl_encrypt().
 */
function get_iv() {
	// phpcs:disable PHPCompatibility.PHP.NewFunctions.random_bytesFound, PHPCompatibility.PHP.RemovedExtensions.mcryptDeprecatedRemoved, PHPCompatibility.PHP.DeprecatedFunctions.mcrypt_create_ivDeprecatedRemoved
	$bytes = function_exists( 'random_bytes' ) ? random_bytes( 8 ) : mcrypt_create_iv( 8 );
	// phpcs:enable PHPCompatibility.PHP.NewFunctions.random_bytesFound, PHPCompatibility.PHP.RemovedExtensions.mcryptDeprecatedRemoved, PHPCompatibility.PHP.DeprecatedFunctions.mcrypt_create_ivDeprecatedRemoved

	return bin2hex( $bytes ); // Will produce an IV 16 characters long.
}

/**
 * Encrypt and store the Airstory token for a given user.
 *
 * @throws Exception When OpenSSL fails to encrypt a token.
 *
 * @param int    $user_id The user ID.
 * @param string $token   The token to store for the user.
 * @return string|WP_Error The encrypted version of the token, which has been stored. If the token
 *                         could not be encrypted, a WP_Error object will be returned instead.
 */
function set_token( $user_id, $token ) {
	try {
		$iv        = get_iv();
		$encrypted = openssl_encrypt( $token, get_cipher_algorithm(), AUTH_KEY, null, $iv );

		if ( false === $encrypted ) {
			throw new Exception( __( 'Encrypted token was empty', 'airstory' ) );
		}
	} catch ( Exception $e ) {
		return new WP_Error(
			'airstory-encryption',
			__( 'Unable to encrypt Airstory token', 'airstory' ),
			$e->getMessage()
		);
	}

	// Store the encrypted values and the IV.
	Settings\set_user_data(
		$user_id, 'user_token', array(
			'token' => $encrypted,
			'iv'    => $iv,
		)
	);

	return $encrypted;
}

/**
 * Retrieve the unencrypted Airstory token for the current user.
 *
 * @throws Exception When OpenSSL fails to decrypt a token.
 *
 * @param  int $user_id The ID of the user to retrieve the token for.
 * @return string|WP_Error Either the unencrypted Airstory token for the current user, an empty
 *                         string if no token exists, or a WP_Error if we're unable to decrypt.
 */
function get_token( $user_id ) {

	// Verify the user actually exists.
	if ( ! get_user_by( 'ID', $user_id ) ) {
		return '';
	}

	$encrypted = Settings\get_user_data( $user_id, 'user_token', false );

	// Return early if either meta value is empty.
	if ( ! isset( $encrypted['token'], $encrypted['iv'] ) ) {
		return '';
	}

	try {
		$token = openssl_decrypt( $encrypted['token'], get_cipher_algorithm(), AUTH_KEY, null, $encrypted['iv'] );

		if ( false === $token ) {
			throw new Exception();
		}
	} catch ( Exception $e ) {
		return new WP_Error(
			'airstory-decryption',
			__( 'Unable to decrypt Airstory token', 'airstory' ),
			$e->getMessage()
		);
	}

	// Extra sanitization on the now-unencrypted value.
	return sanitize_text_field( $token );
}

/**
 * Clear a user's token.
 *
 * @param int $user_id The user ID to clear token-related user meta for.
 * @return bool Were the relevant user meta entries deleted?
 */
function clear_token( $user_id ) {
	return Settings\set_user_data( $user_id, 'user_token', null );
}