/** * Add status messages to the "Store administration" page. * * This hook is used to add items to the store status table on the main store * administration screen. Each item gets a row in the table that consists of a * status icon, title, and description. These items should be used to give * special instructions, notifications, or indicators for components of the cart * enabled by the modules. At a glance, a store owner should be able to look here * and see if a critical component of your module is not functioning properly. * * For example, if the catalog module is installed and it cannot find the catalog * taxonomy vocabulary, it will show an error message here to alert the store * administrator. * * @return * An array of tore status items which are arrays with the following keys: * - "status": "ok", "warning", or "error" depending on the message. * - "title" The title of the status message or module that defines it. * - "desc": The description; can be any message, including links to pages and * forms that deal with the issue being reported. */ function hook_store_status() { if ($key = uc_credit_encryption_key()) { $statuses[] = array('status' => 'ok', 'title' => t('Credit card encryption'), 'desc' => t('Credit card data in the database is currently being encrypted.')); } return $statuses; }
/** * Overrides FieldPluginBase::render(). */ public function render(ResultRow $values) { // Initialize the encryption key and class. $key = uc_credit_encryption_key(); $crypt = new Encryption(); $data = unserialize($values->{$this->field_alias}); if (isset($data['cc_data'])) { $cc_data = $crypt->decrypt($key, $data['cc_data']); if (strpos($cc_data, ':') === FALSE) { $cc_data = base64_decode($cc_data); } $cc_data = unserialize($cc_data); if (isset($cc_data[$this->definition['cc field']])) { return $cc_data[$this->definition['cc field']]; } } }
/** * Tests security settings configuration. */ public function testSecuritySettings() { // TODO: Still need tests with existing key file // where key file is not readable or doesn't contain a valid key // Create key directory, make it readable and writeable. \Drupal::service('file_system')->mkdir('sites/default/files/testkey', 0755); // Try to submit settings form without a key file path. // Save current variable, reset to its value when first installed. $config = \Drupal::configFactory()->getEditable('uc_credit.settings'); $temp_variable = $config->get('encryption_path'); $config->set('encryption_path', '')->save(); $this->drupalGet('admin/store'); $this->assertText('You must review your credit card security settings and enable encryption before you can accept credit card payments.'); $this->drupalGet('admin/store/config/payment/credit'); $this->assertText(t('Credit card security settings must be configured in the security settings tab.')); $this->drupalPostForm('admin/store/config/payment/credit', array(), t('Save configuration')); $this->assertFieldByName('uc_credit_encryption_path', t('Not configured.'), 'Key file has not yet been configured.'); // Restore variable setting. $config->set('encryption_path', $temp_variable)->save(); // Try to submit settings form with an empty key file path. $this->drupalPostForm('admin/store/config/payment/credit', array('uc_credit_encryption_path' => ''), t('Save configuration')); $this->assertText('Key path must be specified in security settings tab.'); // Specify non-existent directory $this->drupalPostForm('admin/store/config/payment/credit', array('uc_credit_encryption_path' => 'sites/default/ljkh/asdfasfaaaaa'), t('Save configuration')); $this->assertText('You have specified a non-existent directory.'); // Next, specify existing directory that's write protected. // Use /dev, as that should never be accessible. $this->drupalPostForm('admin/store/config/payment/credit', array('uc_credit_encryption_path' => '/dev'), t('Save configuration')); $this->assertText('Cannot write to directory, please verify the directory permissions.'); // Next, specify writeable directory, but with excess whitespace // and trailing / $this->drupalPostForm('admin/store/config/payment/credit', array('uc_credit_encryption_path' => ' sites/default/files/testkey/ '), t('Save configuration')); // See that the directory has been properly re-written to remove // whitespace and trailing / $this->assertFieldByName('uc_credit_encryption_path', 'sites/default/files/testkey', 'Key file path has been set.'); $this->assertText('Credit card encryption key file generated.'); // Check that warning about needing key file goes away. $this->assertNoText(t('Credit card security settings must be configured in the security settings tab.')); // Remove key file. \Drupal::service('file_system')->unlink('sites/default/files/testkey/' . UC_CREDIT_KEYFILE_NAME); // Finally, specify good directory $this->drupalPostForm('admin/store/config/payment/credit', array('uc_credit_encryption_path' => 'sites/default/files/testkey'), t('Save configuration')); $this->assertText('Credit card encryption key file generated.'); // Test contents - must contain 32-character hexadecimal string. $this->assertTrue(file_exists('sites/default/files/simpletest.keys/' . UC_CREDIT_KEYFILE_NAME), 'Key has been generated and stored.'); $this->assertTrue(preg_match("([0-9a-fA-F]{32})", uc_credit_encryption_key()), 'Valid key detected in key file.'); // Cleanup keys directory after test. \Drupal::service('file_system')->unlink('sites/default/files/testkey/' . UC_CREDIT_KEYFILE_NAME); \Drupal::service('file_system')->rmdir('sites/default/files/testkey'); }
/** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { // Check that the encryption key directory has been specified, that it // exists, and that it is readable. // Trim trailing whitespace and any trailing / or \ from the key path name. $key_path = rtrim(trim($form_state->getValue('uc_credit_encryption_path')), '/\\'); // Test to see if a path was entered. if (empty($key_path)) { $form_state->setErrorByName('uc_credit_encryption_path', $this->t('Key path must be specified in security settings tab.')); } // Construct complete key file path. $key_file = $key_path . '/' . UC_CREDIT_KEYFILE_NAME; // Shortcut - test to see if we already have a usable key file. if (file_exists($key_file)) { if (is_readable($key_file)) { // Test contents - must contain 32-character hexadecimal string. $key = uc_credit_encryption_key(); if ($key) { if (!preg_match("([0-9a-fA-F]{32})", $key)) { $form_state->setErrorByName('uc_credit_encryption_path', $this->t('Key file already exists in directory, but it contains an invalid key.')); } else { // Key file exists and is valid, save result of trim() back into // $form_state and proceed to submit handler. $form_state->setValue('uc_credit_encryption_path', $key_path); return; } } } else { $form_state->setErrorByName('uc_credit_encryption_path', $this->t('Key file already exists in directory, but is not readable. Please verify the file permissions.')); } } // Check if directory exists and is writeable. if (is_dir($key_path)) { // The entered directory is valid and in need of a key file. // Flag this condition for the submit handler. $form_state->setValue('update_cc_encrypt_dir', TRUE); // Can we open for writing? $file = @fopen($key_path . '/encrypt.test', 'w'); if ($file === FALSE) { $form_state->setErrorByName('uc_credit_encryption_path', $this->t('Cannot write to directory, please verify the directory permissions.')); $form_state->setValue('update_cc_encrypt_dir', FALSE); } else { // Can we actually write? if (@fwrite($file, '0123456789') === FALSE) { $form_state->setErrorByName('uc_credit_encryption_path', $this->t('Cannot write to directory, please verify the directory permissions.')); $form_state->setValue('update_cc_encrypt_dir', FALSE); fclose($file); } else { // Can we read now? fclose($file); $file = @fopen($key_path . '/encrypt.test', 'r'); if ($file === FALSE) { $form_state->setErrorByName('uc_credit_encryption_path', $this->t('Cannot read from directory, please verify the directory permissions.')); $form_state->setValue('update_cc_encrypt_dir', FALSE); } else { fclose($file); } } unlink($key_path . '/encrypt.test'); } } else { // Directory doesn't exist. $form_state->setErrorByName('uc_credit_encryption_path', $this->t('You have specified a non-existent directory.')); } // If validation succeeds, save result of trim() back into $form_state. $form_state->setValue('uc_credit_encryption_path', $key_path); }
/** * {@inheritdoc} */ public function cartProcess(OrderInterface $order, array $form, FormStateInterface $form_state) { if (!$form_state->hasValue(['panes', 'payment', 'details', 'cc_number'])) { return; } $fields = $this->getEnabledFields(); // Fetch the CC details from the $_POST directly. $cc_data = $form_state->getValue(['panes', 'payment', 'details']); $cc_data['cc_number'] = str_replace(' ', '', $cc_data['cc_number']); // Recover cached CC data in form state, if it exists. if ($form_state->hasValue(['panes', 'payment', 'details', 'payment_details_data'])) { $cache = uc_credit_cache('save', $form_state->getValue(['panes', 'payment', 'details', 'payment_details_data'])); } // Account for partial CC numbers when masked by the system. if (substr($cc_data['cc_number'], 0, strlen(t('(Last4)'))) == $this->t('(Last4)')) { // Recover the number from the encrypted data in the form if truncated. if (isset($cache['cc_number'])) { $cc_data['cc_number'] = $cache['cc_number']; } else { $cc_data['cc_number'] = ''; } } // Account for masked CVV numbers. if (!empty($cc_data['cc_cvv']) && $cc_data['cc_cvv'] == str_repeat('-', strlen($cc_data['cc_cvv']))) { // Recover the number from the encrypted data in $_POST if truncated. if (isset($cache['cc_cvv'])) { $cc_data['cc_cvv'] = $cache['cc_cvv']; } else { $cc_data['cc_cvv'] = ''; } } // Go ahead and put the CC data in the payment details array. $order->payment_details = $cc_data; // Default our value for validation. $return = TRUE; // Make sure an owner value was entered. if (!empty($fields['owner']) && empty($cc_data['cc_owner'])) { $form_state->setErrorByName('panes][payment][details][cc_owner', $this->t('Enter the owner name as it appears on the card.')); $return = FALSE; } // Validate the credit card number. if (!_uc_credit_valid_card_number($cc_data['cc_number'])) { $form_state->setErrorByName('panes][payment][details][cc_number', $this->t('You have entered an invalid credit card number.')); $return = FALSE; } // Validate the start date (if entered). if (!empty($fields['start']) && !_uc_credit_valid_card_start($cc_data['cc_start_month'], $cc_data['cc_start_year'])) { $form_state->setErrorByName('panes][payment][details][cc_start_month', $this->t('The start date you entered is invalid.')); $form_state->setErrorByName('panes][payment][details][cc_start_year'); $return = FALSE; } // Validate the card expiration date. if (!_uc_credit_valid_card_expiration($cc_data['cc_exp_month'], $cc_data['cc_exp_year'])) { $form_state->setErrorByName('panes][payment][details][cc_exp_month', $this->t('The credit card you entered has expired.')); $form_state->setErrorByName('panes][payment][details][cc_exp_year'); $return = FALSE; } // Validate the issue number (if entered). With issue numbers, '01' is // different from '1', but is_numeric() is still appropriate. if (!empty($fields['issue']) && !_uc_credit_valid_card_issue($cc_data['cc_issue'])) { $form_state->setErrorByName('panes][payment][details][cc_issue', $this->t('The issue number you entered is invalid.')); $return = FALSE; } // Validate the CVV number if enabled. if (!empty($fields['cvv']) && !_uc_credit_valid_cvv($cc_data['cc_cvv'])) { $form_state->setErrorByName('panes][payment][details][cc_cvv', $this->t('You have entered an invalid CVV number.')); $return = FALSE; } // Validate the bank name if enabled. if (!empty($fields['bank']) && empty($cc_data['cc_bank'])) { $form_state->setErrorByName('panes][payment][details][cc_bank', $this->t('You must enter the issuing bank for that card.')); $return = FALSE; } // Initialize the encryption key and class. $key = uc_credit_encryption_key(); $crypt = \Drupal::service('uc_store.encryption'); // Store the encrypted details in the session for the next pageload. // We are using base64_encode() because the encrypt function works with a // limited set of characters, not supporting the full Unicode character // set or even extended ASCII characters that may be present. // base64_encode() converts everything to a subset of ASCII, ensuring that // the encryption algorithm does not mangle names. $session = \Drupal::service('session'); $session->set('sescrd', $crypt->encrypt($key, base64_encode(serialize($order->payment_details)))); // Log any errors to the watchdog. uc_store_encryption_errors($crypt, 'uc_credit'); // If we're going to the review screen, set a variable that lets us know // we're paying by CC. if ($return) { $session->set('cc_pay', TRUE); } return $return; }