<commit_before><?php
// This file is part of the auth_neon plugin for Moodle - http://moodle.org/
//
// This software is Copyright(c) 2016 by <NAME> - https://github.com/gharley/neon-auth-moodle
// License - MIT https://opensource.org/licenses/MIT MIT

/**
 * Login with Neon CRM password.
 *
 * @package auth_none
 * @author <NAME>
 * @license   https://opensource.org/licenses/MIT MIT
 *
 * Inspired by the lenauth plugin by <NAME> ( @tigusigalpa )
 */

defined('MOODLE_INTERNAL') || die();

require_once($CFG->libdir . '/authlib.php');
require_once('neon.php');

/**
 * Plugin for neon authentication.
 */
class auth_plugin_neon extends auth_plugin_base{
  private $usingSandbox = true;

  /**
   * Neon webservice settings
   *
   * request_token_url (set in constructor) endpoint for requesting auth token
   * grant_type always 'authorization_code'
   * request_api_url API endpoint for retrieving user data
   */
  protected $_settings = array(
      'grant_type' => 'authorization_code',
      'request_api_url' => 'https://api.neoncrm.com/neonws/services/api',
  );

  protected $_config;
  protected $_last_user_number = 0;

  private static $_instance;

  function auth_plugin_neon(){
    global $DB;

    $this->_settings['request_token_url'] = $this->usingSandbox ? 'https://trial.z2systems.com/np/oauth/token' : 'https://z2systems.com/np/oauth/token';

    $this->authtype = 'neon';
    $this->_config = get_config('auth/neon');
    $this->roleauth = 'auth_neon';
    $this->errorlogtag = '[AUTH neon]';
  }

  public static function getInstance(){
    if( !isset(self::$_instance) && !(self::$_instance instanceof auth_plugin_neon) ){
      self::$_instance = new self();
    }

    return self::$_instance;
  }

  private function showDataAndDie($data, $die = false){
    echo '<pre>';
    print_r($data);
    echo '</pre>';

    if( isset($die) && $die == true ) die();
  }

  /**
   * Returns true if the username and password work or don't exist and false
   * if the user exists and the password is wrong.
   *
   * @param string $username The username
   * @param string $password The password
   * @return bool Authentication success or failure.
   */
  function user_login($username, $password){
    global $CFG, $DB;

    $user = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id));

    if( !empty($user) && $user->auth == 'neon' ){
      if( !empty(optional_param('code', '', PARAM_TEXT)) ) return true;
    }

    return false;
  }

  /**
   * Use this function to gather and save additional data about the user logging in.
   * The default implementation checks Neon Memberships and maps them to Moodle enrolments
   * but feel free to completely rewrite this to do whatever you nedd for you implementation.
   *
   * @param Neon $neon - The already logged in Neon API object for querying Neon data
   * @return null
   */
  protected function _get_additional_data(Neon $neon, $access_token){
    global $DB;

    $queryparams = array(
        'method' => 'membership/listMembershipHistory',
        'parameters' => array(
            'accountId' =>  $access_token,
        )
    );

    $result = $neon->go($queryparams);
//        $this->showDataAndDie($result, true);
  }

  protected function _get_query_data(array $array){
    $query_array = array();

    foreach( $array as $key => $value ){
      $query_array[] = urlencode($key) . '=' . urlencode($value);
    }

    return join('&', $query_array);
  }

  /**
   * This is where the OAuth request is made, if necessary
   */
  function loginpage_hook(){
    global $SESSION, $CFG, $DB;

    $access_token = false;
    $send_oauth_request = false;
    $authorizationcode = optional_param('oauthcode', '', PARAM_TEXT);

    if( !empty($authorizationcode) ){
      if( !isset($_COOKIE[$this->authtype . '_access_token']) ) $send_oauth_request = true;

      // require cURL from Moodle core
      require_once($CFG->libdir . '/filelib.php');

      $curl = new curl();
      $curl->resetHeader();
      $curl->setHeader('Content-Type: application/x-www-form-urlencoded');

      if( $send_oauth_request ){
        $params = array();
        $params['client_id'] = $this->_config->auth_neon_client_id;
        $params['client_secret'] = $this->_config->auth_neon_client_secret;
        $params['grant_type'] = $this->_settings['grant_type'];
        $params['code'] = $authorizationcode;
        $params['redirect_uri'] = $CFG->wwwroot . '/auth/neon/redirect.php?auth_service=' . $this->authtype;

        $curl_tokens_values = $curl->post(
            $this->_settings['request_token_url'],
            $this->_get_query_data($params)
        );
      }

      // check for token response
      if( !empty($curl_tokens_values) || !$send_oauth_request ){
        $token_values = array();

        // parse token values
        if( $send_oauth_request || !isset($_COOKIE[$this->authtype . '_access_token']) ){
          $token_values = json_decode($curl_tokens_values, true);
          $access_token = $token_values['access_token'];

          if( !empty($access_token) ){
            setcookie($this->authtype . '_access_token', $access_token, time() + 86400, '/'); // 86400 = 24 hours
          }else{
            //check native errors if exists
            if( isset($token_values['error']) ){
              switch( $token_values['error'] ){
                case 'invalid_client':
                  throw new moodle_exception('Neon CRM invalid OAuth settings. Check your Private Key and Secret Key', 'auth_neon');
                default:
                  throw new moodle_exception('Neon CRM Unknown Error with code: ' . $token_values['error']);
              }
            }else{
              throw new moodle_exception('Can not get access for "access_token" or/and "expires" params after request', 'auth_neon');
            }
          }
        }else{
          if( isset($_COOKIE[$this->authtype . '_access_token']) ){
            $access_token = $_COOKIE[$this->authtype . '_access_token'];
          }else{
            throw new moodle_exception('Someting wrong, maybe expires', 'auth_neon');
          }
        }
      }

      if( !empty($access_token) ){
        $neon = new Neon();
        $keys = array(
            'orgId' => $this->_config->auth_neon_org_id,
            'apiKey' => $this->_config->auth_neon_api_key
        );

        $neon->login($keys);

        $queryparams = array(
            'method' => 'account/retrieveIndividualAccount',
            'parameters' => array(
                'accountId' =>  $access_token,
            )
        );

        $isOrg = false;
        $result = $neon->go($queryparams);

        // If query fails try getting org data
        if( $result['operationResult'] != 'SUCCESS' ){
          $queryparams['method'] = 'account/retrieveOrganizationAccount';
          $isOrg = true;
          $result = $neon->go($queryparams);
        }

        if( $result['operationResult'] == 'SUCCESS' ){
          $neon_account = $isOrg ? $result['organizationAccount'] : $result['individualAccount'];

          $username = $neon_account['login']['username'];
          $contact = $neon_account['primaryContact'];

          $contactId = $contact['contactId'];
          $user_email = $contact['email1'];
          $first_name = $contact['firstName'];
          $last_name = $contact['lastName'];
        }else{
          throw new moodle_exception('Unable to log in as individual or organization', 'auth_neon');
        }

        if( !empty($username) ){
          $moodle_user = $DB->get_record('user', array('username' => $username, auth => $this->authtype, 'deleted' => 0, 'mnethostid' => $CFG->mnet_localhost_id));
        }else{
          @setcookie($this->authtype . '_access_token', null, time() - 3600);
          throw new moodle_exception('Login failed', 'auth_neon');
        }

        if( empty($moodle_user) ){
          //check for username exists in DB
          $user_neon_check = $DB->get_record('user', array('username' => $username));

          if( !empty($user_neon_check) ){
            throw new moodle_exception('Username exists with a different auth type', 'auth_neon');
          }

          // create user HERE
          $moodle_user = create_user_record($username, '', 'neon');
        }

        // Gather any additional data for later use
        $this->_get_additional_data($neon, $access_token);

        // complete Authenticate user
        authenticate_user_login($username, null);

        // fill $newuser object with response data from webservices
        $newuser = new stdClass();
        if( !empty($user_email) ){
          $newuser->email = $user_email;
        }

        if( !empty($first_name) ){
          $newuser->firstname = $first_name;
        }

        if( !empty($last_name) ){
          $newuser->lastname = $last_name;
        }

        if( !empty($this->_config->auth_neon_default_country) ){
          $newuser->country = $this->_config->auth_neon_default_country;
        }

        if( $moodle_user ){
          if( $moodle_user->suspended == 1 ){
            throw new moodle_exception('auth_neon_user_suspended', 'auth_neon');
          }

          if( !empty($newuser) ){<commit_msg>refactor neon queries<commit_after><?php
// This file is part of the auth_neon plugin for Moodle - http://moodle.org/
//
// This software is Copyright(c) 2016 by <NAME> - https://github.com/gharley/neon-auth-moodle
// License - MIT https://opensource.org/licenses/MIT MIT

/**
 * Login with Neon CRM password.
 *
 * @package auth_none
 * @author <NAME>
 * @license   https://opensource.org/licenses/MIT MIT
 *
 * Inspired by the lenauth plugin by <NAME> ( @tigusigalpa )
 */

defined('MOODLE_INTERNAL') || die();

require_once($CFG->libdir . '/authlib.php');
require_once('neon.php');

/**
 * Plugin for neon authentication.
 */
class auth_plugin_neon extends auth_plugin_base{
  private $usingSandbox = true;

  /**
   * Neon webservice settings
   *
   * request_token_url (set in constructor) endpoint for requesting auth token
   * grant_type always 'authorization_code'
   * request_api_url API endpoint for retrieving user data
   */
  protected $_settings = array(
      'grant_type' => 'authorization_code',
      'request_api_url' => 'https://api.neoncrm.com/neonws/services/api',
  );

  protected $_config;
  protected $_last_user_number = 0;

  private static $_instance;

  function auth_plugin_neon(){
    global $DB;

    $this->_settings['request_token_url'] = $this->usingSandbox ? 'https://trial.z2systems.com/np/oauth/token' : 'https://z2systems.com/np/oauth/token';

    $this->authtype = 'neon';
    $this->_config = get_config('auth/neon');
    $this->roleauth = 'auth_neon';
    $this->errorlogtag = '[AUTH neon]';
  }

  public static function getInstance(){
    if( !isset(self::$_instance) && !(self::$_instance instanceof auth_plugin_neon) ){
      self::$_instance = new self();
    }

    return self::$_instance;
  }

  private function showDataAndDie($data, $die = false){
    echo '<pre>';
    print_r($data);
    echo '</pre>';

    if( isset($die) && $die == true ) die();
  }

  /**
   * Returns true if the username and password work or don't exist and false
   * if the user exists and the password is wrong.
   *
   * @param string $username The username
   * @param string $password The password
   * @return bool Authentication success or failure.
   */
  function user_login($username, $password){
    global $CFG, $DB;

    $user = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id));

    if( !empty($user) && $user->auth == 'neon' ){
      if( !empty(optional_param('code', '', PARAM_TEXT)) ) return true;
    }

    return false;
  }

  /**
   * Use this function to gather and save additional data about the user logging in.
   * The default implementation checks Neon Memberships and maps them to Moodle enrolments
   * but feel free to completely rewrite this to do whatever you nedd for you implementation.
   *
   * @param Neon $neon - The already logged in Neon API object for querying Neon data
   * @return null
   */
  protected function _get_additional_data(Neon $neon, $access_token){
    global $DB;

    $queryparams = array(
        'method' => 'membership/listMembershipHistory',
        'parameters' => array(
            'accountId' =>  $access_token,
        )
    );

    $result = $neon->go($queryparams);
//    $this->showDataAndDie($result, true);
  }

  protected function _get_query_data(array $array){
    $query_array = array();

    foreach( $array as $key => $value ){
      $query_array[] = urlencode($key) . '=' . urlencode($value);
    }

    return join('&', $query_array);
  }

  function get_userinfo($username) {
    $neon = new Neon();
    $keys = array(
        'orgId' => $this->_config->auth_neon_org_id,
        'apiKey' => $this->_config->auth_neon_api_key
    );
    $user_info = array();

    $neon->login($keys);

    $search = array(
        'method' => 'account/listAccounts',
        'criteria' => array(
            array('Account Login Name', 'EQUAL', $username)
        ),
        'columns' => array(
          'standardFields' => array('Account Type', 'Email 1', 'First Name', 'Last Name', 'City')
        )
    );

    $response = $neon->search($search);
    if( $response['operationResult'] == 'SUCCESS' ){
      $searchResults = $response['searchResults'][0];

      $user_info['is_org'] = $searchResults['Account Type'] == 'Organization' ? 1 : 0;
      $user_info['email'] = $searchResults['Email 1'];
      $user_info['firstname'] = $searchResults['First Name'];
      $user_info['lastname'] = $searchResults['Last Name'];
      $user_info['city'] = $searchResults['City'];
    }

    return $user_info;
  }

    /**
   * This is where the OAuth request is made, if necessary
   */
  function loginpage_hook(){
    global $SESSION, $CFG, $DB;

    $access_token = false;
    $send_oauth_request = false;
    $authorizationcode = optional_param('oauthcode', '', PARAM_TEXT);

    if( !empty($authorizationcode) ){
      if( !isset($_COOKIE[$this->authtype . '_access_token']) ) $send_oauth_request = true;

      // require cURL from Moodle core
      require_once($CFG->libdir . '/filelib.php');

      $curl = new curl();
      $curl->resetHeader();
      $curl->setHeader('Content-Type: application/x-www-form-urlencoded');

      if( $send_oauth_request ){
        $params = array();
        $params['client_id'] = $this->_config->auth_neon_client_id;
        $params['client_secret'] = $this->_config->auth_neon_client_secret;
        $params['grant_type'] = $this->_settings['grant_type'];
        $params['code'] = $authorizationcode;
        $params['redirect_uri'] = $CFG->wwwroot . '/auth/neon/redirect.php?auth_service=' . $this->authtype;

        $curl_tokens_values = $curl->post(
            $this->_settings['request_token_url'],
            $this->_get_query_data($params)
        );
      }

      // check for token response
      if( !empty($curl_tokens_values) || !$send_oauth_request ){
        $token_values = array();

        // parse token values
        if( $send_oauth_request || !isset($_COOKIE[$this->authtype . '_access_token']) ){
          $token_values = json_decode($curl_tokens_values, true);
          $access_token = $token_values['access_token'];

          if( !empty($access_token) ){
            setcookie($this->authtype . '_access_token', $access_token, time() + 86400, '/'); // 86400 = 24 hours
          }else{
            //check native errors if exists
            if( isset($token_values['error']) ){
              switch( $token_values['error'] ){
                case 'invalid_client':
                  throw new moodle_exception('Neon CRM invalid OAuth settings. Check your Private Key and Secret Key', 'auth_neon');
                default:
                  throw new moodle_exception('Neon CRM Unknown Error with code: ' . $token_values['error']);
              }
            }else{
              throw new moodle_exception('Can not get access for "access_token" or/and "expires" params after request', 'auth_neon');
            }
          }
        }else{
          if( isset($_COOKIE[$this->authtype . '_access_token']) ){
            $access_token = $_COOKIE[$this->authtype . '_access_token'];
          }else{
            throw new moodle_exception('Someting wrong, maybe expires', 'auth_neon');
          }
        }
      }

      if( !empty($access_token) ){
        $neon = new Neon();
        $keys = array(
            'orgId' => $this->_config->auth_neon_org_id,
            'apiKey' => $this->_config->auth_neon_api_key
        );

        $neon->login($keys);

        $search = array(
            'method' => 'account/listAccounts',
            'criteria' => array(
                array('Account ID', 'EQUAL', $access_token)
            ),
            'columns' => array(
                'standardFields' => array('Account Login Name')
            )
        );

        $searchResult = $neon->search($search);
        if( $searchResult['operationResult'] == 'SUCCESS' ){
          $username = $searchResult['searchResults'][0]['<NAME>'];

          $user_info = $this->get_userinfo($username);
        }else{
          throw new moodle_exception('Unable to log in as individual or organization', 'auth_neon');
        }

        if( !empty($username) ){
          $moodle_user = $DB->get_record('user', array('username' => $username, auth => $this->authtype, 'deleted' => 0, 'mnethostid' => $CFG->mnet_localhost_id));
        }else{
          @setcookie($this->authtype . '_access_token', null, time() - 3600);
          throw new moodle_exception('Login failed', 'auth_neon');
        }

        if( empty($moodle_user) ){
          //check for username exists in DB
          $user_neon_check = $DB->get_record('user', array('username' => $username));

          if( !empty($user_neon_check) ){
            throw new moodle_exception('Username exists with a different auth type', 'auth_neon');
          }

          // create user HERE
          $moodle_user = create_user_record($username, '', 'neon');
        }

        // Gather any additional data for later use
        $this->_get_additional_data($neon, $access_token);

        // complete Authenticate user
        authenticate_user_login($username, null);

        // fill $newuser object with response data from webservices
        $newuser = new stdClass();
        if( !empty($user_info['email']) ){
          $newuser->email = $user_info['email'];
        }

        if( !empty($user_info['firstName']) ){
          $newuser->firstname = $user_info['firstName'];
        }

        if( !empty($user_info['lastName']) ){
          $newuser->lastname = $user_info['lastName'];
        }

        if( !empty($user_info['city']) ){
          $newuser->city = $user_info['city'];
        }

        if( !empty($this->_config->auth_neon_default_country) ){
          $newuser->country = $this->_config->auth_neon_default_country;
        }

        if( $moodle_user ){
          if( $moodle_user->suspended == 1 ){
            throw new moodle_exception('auth_neon_user_suspended', 'auth_neon');
          }

          if( !empty($newuser) ){
{'fraction_non_alphanumeric': 0.14791731588241405, 'fraction_numerical': 0.002960423808039888, 'mean_word_length': 2.6599505797376923, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 168, 'https://': 12, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1802175', 'n_tokens_mistral': 6018, 'n_tokens_neox': 5795, 'n_words': 1315}