Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUGFIX SingleSignOn does not function properly in all cases #403

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion _config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Name: foxystripeconfig
SilverStripe\Security\Member:
extensions:
- Dynamic\FoxyStripe\ORM\CustomerExtension
password_logging_enabled: false

PageController:
extensions:
Expand All @@ -16,4 +17,4 @@ SilverStripe\Forms\FormField:
SilverStripe\Core\Injector\Injector:
TimeZoneData:
properties:
format: '%Region/%Name'
format: '%Region/%Name'
34 changes: 18 additions & 16 deletions src/Controller/FoxyStripeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ class FoxyStripeController extends \PageController
/**
* @var array
*/
private static $allowed_actions = array(
private static $allowed_actions = [
'index',
'sso',
);
];

/**
* @return string
Expand Down Expand Up @@ -98,6 +98,7 @@ public function parseOrder($transactions, $order)
$this->parseOrderInfo($transactions, $order);
$this->parseOrderCustomer($transactions, $order);
$this->parseOrderDetails($transactions, $order);
$this->extend('updateParseOrder', $transactions, $order);
}

/**
Expand All @@ -124,15 +125,15 @@ public function parseOrderInfo($orders, $transaction)
* @param Order $transaction
* @throws \SilverStripe\ORM\ValidationException
*/
public function parseOrderCustomer($orders, $transaction)
public function parseOrderCustomer(&$orders, &$transaction)
{
foreach ($orders->transactions->transaction as $order) {
if (!isset($order->customer_email) || $order->is_anonymous != 0) {
continue;
}

// if Customer is existing member, associate with current order
if (Member::get()->filter('Email', $order->customer_email)->First()) {
if (Member::get()->filter('Email', $order->customer_email)->first()) {
$customer = Member::get()->filter('Email', $order->customer_email)->First();
/* todo: make sure local password is updated if changed on FoxyCart
$this->updatePasswordFromData($customer, $order);
Expand All @@ -144,8 +145,8 @@ public function parseOrderCustomer($orders, $transaction)
$customer->FirstName = (string)$order->customer_first_name;
$customer->Surname = (string)$order->customer_last_name;
$customer->Email = (string)$order->customer_email;
$this->updatePasswordFromData($customer, $order);
}
$this->updatePasswordFromData($customer, $order);
$customer->write();
// set Order MemberID
$transaction->MemberID = $customer->ID;
Expand All @@ -155,20 +156,21 @@ public function parseOrderCustomer($orders, $transaction)
/**
* Updates a customer's password. Sets password encryption to 'none' to avoid encryting it again.
*
* @param Member $customer
* @param $customer
* @param $order
*/
public function updatePasswordFromData($customer, $order)
public function updatePasswordFromData(&$customer, &$order)
{
$password_encryption_algorithm = Security::config()->get('password_encryption_algorithm');
Security::config()->update('password_encryption_algorithm', 'none');

$customer->PasswordEncryption = 'none';
$customer->Password = (string) $order->customer_password;
$customer->Password = (string)$order->customer_password;
$customer->write();

$customer->PasswordEncryption = $this->getEncryption((string) $order->customer_password_hash_type);
$customer->Salt = (string) $order->customer_password_salt;
$customer->PasswordEncryption = 'sha1_v2.4';
$customer->Salt = (string)$order->customer_password_salt;
$customer->write();

Security::config()->update('password_encryption_algorithm', $password_encryption_algorithm);
}
Expand Down Expand Up @@ -259,7 +261,7 @@ public function getProductPage($product)
}

return ProductPage::get()
->filter('ID', (int) $productOptions->product_option_value)
->filter('ID', (int)$productOptions->product_option_value)
->First();
}
}
Expand All @@ -277,10 +279,10 @@ public function modifyOrderDetailPrice($OrderProduct, $OrderDetail, $product)
$OrderDetail->ProductID = $OrderProduct->ID;

foreach ($this->getTransactionOptions($product) as $option) {
$OptionItem = OptionItem::get()->filter(array(
$OptionItem = OptionItem::get()->filter([
'ProductID' => (string)$OrderProduct->ID,
'Title' => (string)$option->product_option_value
))->First();
'Title' => (string)$option->product_option_value,
])->First();

if (!$OptionItem) {
continue;
Expand Down Expand Up @@ -323,8 +325,8 @@ public function sso()
$link = FoxyCart::getFoxyCartStoreName() . '.foxycart.com';
}

$redirect_complete = 'https://'.$link.'/checkout?fc_auth_token='.$auth_token.'&fcsid='.$fcsid.
'&fc_customer_id='.$Member->Customer_ID.'&timestamp='.$timestampNew;
$redirect_complete = 'https://' . $link . '/checkout?fc_auth_token=' . $auth_token . '&fcsid=' . $fcsid .
'&fc_customer_id=' . $Member->Customer_ID . '&timestamp=' . $timestampNew;

$this->redirect($redirect_complete);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Model/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public function parseOrderInfo($response)
$this->ReceiptURL = (string) $transaction->receipt_url;
$this->OrderStatus = (string) $transaction->status;

$this->extend('handleOrderInfo', $order, $response);
$this->extend('handleOrderInfo', $this, $response);
}
}

Expand Down Expand Up @@ -270,7 +270,7 @@ public function parseOrderCustomer($response)
// set Order MemberID
$this->MemberID = $customer->ID;

$this->extend('handleOrderCustomer', $order, $response, $customer);
$this->extend('handleOrderCustomer', $this, $response, $customer);
}
}
}
Expand Down Expand Up @@ -327,7 +327,7 @@ public function parseOrderDetails($response)
$OrderDetail->Price = (float) $detail->product_price + (float) $priceModifier;

// extend OrderDetail parsing, allowing for recording custom fields from FoxyCart
$this->extend('handleOrderItem', $order, $response, $OrderDetail);
$this->extend('handleOrderItem', $this, $response, $OrderDetail);

// write
$OrderDetail->write();
Expand Down
77 changes: 77 additions & 0 deletions src/Security/PasswordEncryptor_BCrypt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Dynamic\FoxyStripe\Security;

use SilverStripe\Security\PasswordEncryptor;
use SilverStripe\Security\PasswordEncryptor_EncryptionFailed;

/**
* Class PasswordEncryptor_BCrypt
* @package Dynamic\FoxyStripe\Security
*/
class PasswordEncryptor_BCrypt extends PasswordEncryptor
{
/**
* Cost of encryption.
* Higher costs will increase security, but also increase server load.
* If you are using basic auth, you may need to decrease this as encryption
* will be run on every request.
* The two digit cost parameter is the base-2 logarithm of the iteration
* count for the underlying Blowfish-based hashing algorithmeter and must
* be in range 04-31, values outside this range will cause crypt() to fail.
*/
protected static $cost = 10;

/**
* Sets the cost of the blowfish algorithm.
* See {@link PasswordEncryptor_Blowfish::$cost}
* Cost is set as an integer but
* Ensure that set values are from 4-31
*
* @param int $cost range 4-31
*/
public static function set_cost($cost)
{
self::$cost = max(min(31, $cost), 4);
}

/**
* Gets the cost that is set for the PASSWORD_BCRYPT algorithm
*
* @return int
*/
public static function get_cost()
{
return self::$cost;
}

/**
* @param String $password
* @param null $salt
* @param null $member
* @return bool|String
* @throws PasswordEncryptor_EncryptionFailed
*/
public function encrypt($password, $salt = null, $member = null)
{
$encryptedPassword = password_hash($password, PASSWORD_BCRYPT, ['cost' => static::get_cost()]);

if (strpos($encryptedPassword, '$2y$') === false) {
throw new PasswordEncryptor_EncryptionFailed('BCrypt password encryption failed.');
}

return $encryptedPassword;
}

/**
* @param string $hash
* @param string $password
* @param null $salt
* @param null $member
* @return bool
*/
public function check($hash, $password, $salt = null, $member = null)
{
return password_verify($password, $hash);
}
}