Commit 818fd24a authored by Sumesh S's avatar Sumesh S

Base module

parents
.DS_Store
._*
*~
*.kpf
*.swp
*.swo
vendor/
composer.lock
phpunit.xml
README
======
The zendesk module allows you to integrate your drupal site with the zendesk
customer support system (http://www.zendesk.com)
Zendesk allows external authentication. This means that your Drupal site can serve
as authentication service. This way customers have a seamless experience without
having to log in in both the drupal site and the zendesk support system.
Installation is easy:
- install the module
- configure your zendesk account (go to 'Settings - Security - Single Sign-On')
* Enable the "JSON Web Token" strategy.
* insert the remote authentication url: http://yoursite.com/services/zendesk
* optionally you can insert your logout url: http://yoursite.com/logout
- go to admin/config/people/zendesk and fill in the url of your zendesk support page (e.g. http://yourdomain.zendesk.com) together with the secret key
Local Development
=================
Zendesk remote authentication does not require your site to be public.
Zendesk PHP library
===================
This module depends on the Zendesk PHP library. For ease of use, this library is included with the module in the lib directory.
For more information on this library, please visit: http://code.google.com/p/zendesk-php-lib
=================
Originally developed for 6.x by twom <http://drupal.org/user/25564>
Ported to Drupal 7.x by markwk <http://drupal.org/user/1094790>
zendesk_api_token: 'Your API Token'
zendesk_api_mail: ''
zendesk_subdomain: ''
zendesk_url':
zendesk_jwt_shared_secret: 'Your secret key'
zendesk_api_sync_users': ''
zendesk_no_permission_page': ''
<?php
/**
* @file
* Contains \Drupal\zendesk\Form\ZendeskAdminForm.
*/
namespace Drupal\zendesk\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
/**
* Configure zendesk settings for this site.
*/
class ZendeskAdminForm extends ConfigFormBase {
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'zendesk_admin_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('zendesk.settings');
$form['zendesk']['zendesk_api'] = array(
'#type' => 'fieldset',
'#title' => 'API configuration',
);
$form['zendesk']['zendesk_api']['zendesk_url'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Zendesk URL'),
'#description' => 'The url of your zendesk support page (e.g. http://yourdomain.zendesk.com).',
'#default_value' => $config->get('zendesk_url'),
);
$form['zendesk']['zendesk_api']['zendesk_subdomain'] = array(
'#type' => 'textfield',
'#title' => t('Zendesk subdomain'),
'#default_value' => $config->get('zendesk_subdomain'),
'#description' => t('The subdomain of your zendesk page: if your zendesk is http://subdomain.zendesk.com, then you have to fil in "subdomain".'),
);
$form['zendesk']['zendesk_api']['zendesk_api_token'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#description' => 'The zendesk API token.',
'#default_value' => $config->get('zendesk_api_token'),
'#suffix' => t('Use the http://www.yourdomain.com/agent/#/admin/channels page in your zendesk configuration page. (Go to Account -> Channels'),
);
$form['zendesk']['zendesk_api']['zendesk_api_mail'] = array(
'#type' => 'textfield',
'#title' => t('Mail address of the API user'),
'#default_value' => $config->get('zendesk_api_mail'),
'#description' => t('This is typically the mail address of the zendesk admin account'),
);
// Role-based visibility settings.
foreach (user_roles() as $machine_name => $role_object) {
$role_options[$machine_name] = $role_object->id();
}
$form['zendesk']['zendesk_permissions'] = array(
'#type' => 'fieldset',
'#description' => t('Restrict access to zendesk based on user roles. These rules will apply for both user synchronization as remote authentication.'),
);
$form['zendesk']['zendesk_permissions']['zendesk_roles'] = array(
'#type' => 'checkboxes',
'#title' => t('Authenticate only for specific roles'),
'#default_value' => $config->get('zendesk_roles'),
'#options' => $role_options,
'#description' => t('Select which roles may be authenticated for zendesk. If you select no roles, all authenticated drupal users will be authenticated for Zendesk.'),
);
$form['zendesk']['zendesk_permissions']['zendesk_no_permission_page'] = array(
'#type' => 'textfield',
'#title' => t('No permission page'),
'#default_value' => $config->get('zendesk_no_permission_page'),
'#description' => t('To what pages do you want to redirect user that have no permission to access Zendesk.'),
);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}c
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('zendesk.settings')
->set('zendesk_url', $form_state->getValue('zendesk_url'))
->set('zendesk_subdomain', $form_state->getValue('zendesk_subdomain'))
->set('zendesk_api_token', $form_state->getValue('zendesk_api_token'))
->set('zendesk_api_mail', $form_state->getValue('zendesk_api_mail'))
->set('zendesk_roles', $form_state->getValue('zendesk_roles'))
->set('zendesk_no_permission_page', $form_state->getValue('zendesk_no_permission_page'))
->save();
parent::submitForm($form, $form_state);
}
}
name: Zendesk
description: "Main module that handle Zendesk's API"
core: 8.x
package: Zendesk
configure: admin/config/system/zendesk
type: module
<?php
/**
* @file
* The Zendesk module helps you to interact with zendesk using Drupal.
*/
require 'vendor/autoload.php';
use Drupal\zendesk;
use Zendesk\API\Client as ZendeskAPI;
use Drupal\user\Entity;
/**
* Implements hook_permission().
*/
function zendesk_permission() {
return array(
'configure zendesk' => array(
'title' => t('Configure Zendesk'),
'description' => t('Configure Drupal settings to communicate with Zendesk.'),
),
);
}
/**
* Initialization of the zendesk library.
*/
function zendesk_initialize_library() {
$config = Drupal::config('zendesk.settings');
$api_key = $config->get('zendesk_api_token');
$user = $config->get('zendesk_api_mail');
$subdomain = $config->get('zendesk_subdomain');
$client = new ZendeskAPI($subdomain, $user);
$client->setAuth('token', $api_key);
return $client;
}
/**
* Check if the user may be be authenticated or synced with zendesk.
*/
function zendesk_user_has_access($account) {
$zendesk_roles = \Drupal::config('zendesk.settings')->get('zendesk_roles');
if (!array_sum($zendesk_roles)) {
// No roles are set, give access.
return TRUE;
}
else {
$keys = array_keys($account->getRoles);
foreach ($keys as $key) {
if ($zendesk_roles[$key] > 0) {
return TRUE;
}
}
}
return FALSE;
}
zendesk_admin:
path: 'admin/config/system/zendesk'
defaults:
_form: '\Drupal\zendesk\Form\ZendeskAdminForm'
requirements:
_permission: 'configure zendesk'
<?php
/**
* @file
* Contains \Drupal\zendesk\Controller\ZendeskSSOController.
*/
namespace Drupal\zendesk_sso\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Controller\ControllerBase;
/**
* Controller routines for zendesk routes.
*/
class ZendeskSSOController extends ControllerBase {
/**
* Zendesk Manager Service.
*
* @var \Drupal\zendesk\ZendeskManager
*/
protected $zendeskManager;
/**
* Injects ZendeskManager Service.
*/
public static function create(ContainerInterface $container) {
return new static($container->get('zendesk.manager'));
}
/**
* Remote authentication script.
*
* @see http://developer.zendesk.com/documentation/sso
* @see https://support.zendesk.com/entries/23675367
*/
public function sso() {
// Check if anonymous, if so redirect to login with destination the path where
// he comes from.
$account = \Drupal::currentUser();
if ($account->id()) {
// Check if user role is allowed to be authenticated.
if (zendesk_user_has_access($account)) {
$account = \Drupal::currentUser();
$token = array(
'jti' => sha1($account->id() . REQUEST_TIME . rand()),
'iat' => REQUEST_TIME,
'name' => $account->getUsername(),
'email' => $account->getEmail(),
'external_id' => $account->id(),
);
$key = \Drupal::config('zendesk.settings')
->get('zendesk_jwt_shared_secret');
$jwt = zendesk_jwt_encode($token, $key);
// Redirect
$url = \Drupal::config('zendesk.settings')->get('zendesk_url') . '/access/jwt';
return new RedirectResponse(url($url, array('query' => array('jwt' => $jwt))));
}
else {
return new RedirectResponse(url(Drupal::config('zendesk.settings')
->get('zendesk_no_permission_page')));
}
}
else {
return new RedirectResponse(url('user', array('query' => array('destination' => 'services/zendesk'))));
}
}
}
name: Zendesk SSO
description: "Enable the Single Sing-on with Zendesk."
core: 8.x
package: Zendesk
configure: admin/config/system/zendesk
type: module
dependencies:
- zendesk
<?php
/**
* @file
* The Zendesk module helps you to interact with zendesk using Drupal.
*/
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\zendesk;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_FORM_ID_alter().
*/
function zendesk_sso_form_zendesk_admin_form_alter(array &$form, FormStateInterface $form_state) {
$config = \Drupal::config('zendesk.settings');
$form['zendesk']['zendesk_sso'] = array(
'#type' => 'fieldset',
'#title' => 'SSO configuration',
);
$form['zendesk']['zendesk_sso']['zendesk_jwt_shared_secret'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#description' => 'The zendesk SSO secret key.',
'#default_value' => $config->get('zendesk_jwt_shared_secret'),
'#suffix' => t('Use the http://www.yourdomain.com/agent/#/admin/security page in your zendesk configuration page. (Go to Account -> Security)'),
);
$form['zendesk']['zendesk_sso']['zendesk_api_sync_users'] = array(
'#type' => 'checkbox',
'#title' => t('Synchronize the Drupal users with Zendesk'),
'#default_value' => $config->get('zendesk_api_sync_users'),
'#description' => t('When accounts are created in your drupal site, updated and delete, push these changes to Zendesk.'),
);
// We need tu save those values. So we add our own custom submit handler.
$form['#submit'][] = 'zendesk_admin_zendesk_sso_submit';
}
/**
* Submit callback.
*
* @see zendesk_sso_form_zendesk_admin_form_alter().
*
* @param array $form
* @param FormStateInterface $form_state
*/
function zendesk_admin_zendesk_sso_submit(array &$form, FormStateInterface $form_state) {
\Drupal::config('zendesk.settings')
->set('zendesk_jwt_shared_secret', $form_state->getValue('zendesk_jwt_shared_secret'))
->set('zendesk_api_sync_users', $form_state->getValue('zendesk_api_sync_users'))
->save();
}
/**
* Converts and signs a PHP object or array into a JWT string.
*
* Taken from PEAR::JWT.
*
* @param $payload
* PHP object or array.
* @param $key
* The secret key.
*
* @return
* A signed JWT.
*/
function zendesk_jwt_encode($payload, $key) {
$header = array(
'typ' => 'JWT',
'alg' => 'HS256'
);
$segments = array();
$segments[] = zendesk_urlsafeb64_encode(json_encode($header));
$segments[] = zendesk_urlsafeb64_encode(json_encode($payload));
$signing_input = implode('.', $segments);
$signature = hash_hmac('sha256', $signing_input, $key, TRUE);
$segments[] = zendesk_urlsafeb64_encode($signature);
return implode('.', $segments);
}
/**
* Encodes the given data with urlsafe base64.
*
* A base64 encoded string is made urlsafe by replacing '+' with '-',
* '/' with '_', and removing '='.
*
* Taken from PEAR::JWT.
*
* @param $data
* The data to encode.
*
* @return
* The encoded string.
*/
function zendesk_urlsafeb64_encode($data) {
$b64 = base64_encode($data);
return str_replace(array('+', '/', '\r', '\n', '='), array('-', '_'), $b64);
}
zendesk_sso:
path: 'services/zendesk'
defaults:
_controller: '\Drupal\zendesk_sso\Controller\ZendeskSSOController::sso'
requirements:
_permission: 'access content'
name: Zendesk users
description: "Sync users with Zendesk."
core: 8.x
package: Zendesk
configure: admin/config/system/zendesk
type: module
dependencies:
- zendesk
- zendesk_sso
<?php
/**
* @file
* Install, update and uninstall functions for the Zendesk module.
*/
/**
* Implements hook_schema().
*/
function zendesk_users_schema() {
$schema['zendesk_users'] = array(
'description' => "Links the drupal user ids with the zendesk user id's",
'fields' => array(
'uid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => 0,
'default' => 0,
'description' => 'Drupal user id',
),
'zid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => 0,
'default' => 0,
'description' => 'Zendesk user id',
),
),
'primary_key' => array('uid', 'zid'),
'indexes' => array(
'uid' => array('uid'),
),
);
return $schema;
}
<?php
/**
* @file
* The Zendesk module helps you to interact with zendesk using Drupal.
*/
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\zendesk;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Serialization\Json;
/**
* Implements hook_form_FORM_ID_alter().
*/
function zendesk_users_form_zendesk_admin_form_alter(array &$form, FormStateInterface $form_state) {
$config = \Drupal::config('zendesk.settings');
$form['zendesk']['zendesk_users'] = array(
'#type' => 'fieldset',
'#title' => 'Users configuration',
);
$form['zendesk']['zendesk_users']['zendesk_authed_user'] = array(
'#type' => 'checkbox',
'#title' => t('Create user as verified users'),
'#default_value' => $config->get('zendesk_authed_user'),
'#description' => t('Check this is you want to create user as already verified users.'),
);
// We need tu save those values. So we add our own custom submit handler.
$form['#submit'][] = 'zendesk_admin_zendesk_users_submit';
}
/**
* Submit callback.
*
* @see zendesk_sso_form_zendesk_admin_form_alter().
*
* @param array $form
* @param FormStateInterface $form_state
*/
function zendesk_admin_zendesk_users_submit(array &$form, FormStateInterface $form_state) {
\Drupal::config('zendesk.settings')
->set('zendesk_authed_user', $form_state->getValue('zendesk_authed_user'))
->save();
}
/**
* Implements hook_ENTITY_TYPE_insert() for user entities.
*/
function zendesk_user_insert(AccountInterface $account) {
if (Drupal::config('zendesk.settings')->get('zendesk_api_sync_users') && zendesk_user_has_access($account)) {
zendesk_users_create_user($account);
}
}
/**
* Implements hook_ENTITY_TYPE_update() for user entities.
*/
function zendesk_users_user_update(AccountInterface $account) {
if (Drupal::config('zendesk.settings')->get('zendesk_api_sync_users') && zendesk_user_has_access($account)) {
if ($user_id = _zendesk_users_get_user($account->id())) {
$data = array(
'id' => $user_id,
'name' => $account->getUsername(),
'email' => $account->getEmail(),
'role' => 'end-user',
);
// Invoke a alter call to allow other modules to pass data to ZenDesk.
\Drupal::moduleHandler()
->alter(array('zendesk_user', 'zendesk_user_update'), $data, $account);
// Make the call
$client = zendesk_initialize_library();
$client->users()->update($data);
}
}
}
/**
* Implements hook_ENTITY_TYPE_delete() for user entities.
*/
function zendesk_users_user_delete(AccountInterface $account) {
if (Drupal::config('zendesk.settings')->get('zendesk_api_sync_users')) {
if ($user_id = _zendesk_users_get_user($account->id())) {
$data = array(
'id' => $user_id,
'name' => $account->getUsername(),
'email' => $account->getEmail(),
'suspended' => TRUE,
);
// Alter call to omitted since we are destroying the User object.
// Make the call
$client = zendesk_initialize_library();
$client->users()->delete($data);
}
}
}
/**
* Create a user on Zendesk's side.
*
* @param object $account
* The Drupal user object that we want to create on zendesk.
*
* @return mixed
* The zendesk's user id if succeeded, FALSE otherwise.
*/
function zendesk_users_create_user(AccountInterface $account) {
$client = zendesk_initialize_library();
$data = array(
'name' => $account->getUsername(),
'email' => $account->getEmail(),
'role' => 'end-user',
);
if (\Drupal::config('zendesk.settings')->get('zendesk_authed_user')) {
$data['user']['verified'] = TRUE;
}
// Invoke a alter call to allow other modules to pass data to ZenDesk.
\Drupal::moduleHandler()
->alter(array('zendesk_user', 'zendesk_user_update'), $data, $account);
// Make the call
$result = $client->users()->create($data);
if (!empty($result->error)) {
// Try to handle special case where a user can be on zendesk's side, but
// not recorded on our table.
if ($result->error == 'RecordInvalid' && $result->details->email[0]->description == 'Email: ' . $account->getEmail() . ' is already being used by another user') {
return zendesk_users_sync_user_back($account);
}
\Drupal::logger('zendesk_users')->error($result->description . ': ' . print_r($result->details, 1));
return FALSE;
}
else {
db_insert('zendesk_users')
->fields(array(
'uid' => $account->id(),
'zid' => $result->user->id,
))
->execute();
return $result->user->id;
}
}
/**
* Try to sync user from zendesk on drupal side.
*
* If a user already exist on zendesk's side but not our the drupal side, add
* it in our database.
*
* @param AccountInterface $account
* An object containing the user account.
*
* @return mixed
* Zendesk's user ID if succeeded, FALSE otherwise.
*/
function zendesk_users_sync_user_back(AccountInterface $account) {
// Look for the user.
$client = zendesk_initialize_library();
$result = $client->users()->search($account->getEmail());
if (isset($result->users[0]->id)) {
db_insert('zendesk_users')
->fields(array(
'uid' => $account->id(),
'zid' => $result->users[0]->id,
))
->execute();
return $result->users[0]->id;
}
else {
return FALSE;
}
}
/**
* Helper function to retrieve zendesk id of the user.
*/
function _zendesk_users_get_user($uid) {
$result = db_select('zendesk_users', 'zu')
->fields('zu', array('uid', 'zid'))
->condition('zu.uid', $uid, '=')
->execute();
if ($result->rowCount() <> 0) {
foreach ($result as $user) {
return $user->zid;
}
}
else {
return FALSE;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment