Don't like ads? PRO users don't see any ads ;-)
Guest

code sample

By: sas05 on Jun 17th, 2012  |  syntax: PHP  |  size: 27.82 KB  |  hits: 42  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2. namespace PBackend\Service;
  3.  
  4. use PBackend\DB\Mappings\Doctrine\MongoDB\Document;
  5. use PBackend\Model;
  6. use PBackend\Service;
  7. use PBackend\Api;
  8. use PBackend\Util;
  9. use PBackend\Model\Api\Oauth\Token;
  10. use PBackend\DB\Mappings\Doctrine\MongoDB\Document\Api\Oauth\Consumer;
  11.  
  12. class Account extends Service
  13. {
  14.  
  15.         /**
  16.          * Set Service ACL.
  17.          *
  18.          * @param \Zend_Acl $acl
  19.          * @return \Zend_Acl
  20.          */
  21.         public static function provideMethodsAcl(\Zend_Acl $acl) {
  22.  
  23.                 self::_aclAllowMethod($acl, 'guest', 'requestRegistration');
  24.                 self::_aclAllowMethod($acl, 'guest', 'confirmRegistration');
  25.                 self::_aclAllowMethod($acl, 'user', 'get');
  26.                 self::_aclAllowMethod($acl, 'user', 'update');
  27.                 self::_aclAllowMethod($acl, 'user', 'requestPrimaryEmailUpdate');
  28.                 self::_aclAllowMethod($acl, 'user', 'confirmPrimaryEmailUpdate');
  29.                 self::_aclAllowMethod($acl, 'guest', 'validatePassword');
  30.                 self::_aclAllowMethod($acl, 'guest', 'requestPasswordReset');
  31.                 self::_aclAllowMethod($acl, 'guest', 'confirmPasswordReset');
  32.  
  33.                 return $acl;
  34.         }
  35.  
  36.     /**
  37.      * Register new user account.
  38.      *
  39.      * API Method:
  40.      *  account.requestRegistration(confirmationURL, email, password, firstName, lastName)
  41.      *
  42.      * Authorization:
  43.      *  guest
  44.      *
  45.      * Description:
  46.      *  Register new user account.
  47.      *  This is a first part of the classic registration process:
  48.      *   1. User submits his account details
  49.      *   2. User receives an email with confirmation link
  50.      *   3. User clicks the link in order to finish the process.
  51.      *
  52.      *  Confirmation token is generated from user's email address and system's secret key. It is then appended to "confirmationURL" and is sent in the email.
  53.      *  For example if confirmationURL is http://www.example.com/user/confirmRegistration,the following URL will be sent to the user - http://www.example.com/user/confirmRegistration?confirmationToken=b20ab007190ccb069019935a9ef72188
  54.      *  @see \PBackend\Service\User\Account#confirmRegistration()
  55.      *
  56.      * Arguments:
  57.      *  @param string $confirmationURL (required) URL link which user must access in order to complete registration process. Confirmation token will be appended to this URL which will be later sent in the email message.
  58.      *  @param string $email (required) Valid email address.
  59.      *  @param string $password (required) Password, at least 6 characters, any character is accepted.
  60.      *  @param string $firstName (required) First name, limited to 32 characters.
  61.      *  @param string $lastName (required) Last name, limited to 32 characters.
  62.      *
  63.      * Response:
  64.      *  This method has no specific response if completes without error.
  65.      *
  66.      * Error codes (in addition to standard API errors):
  67.      *  @throws \PBackend\Service\Exception if API error occurres
  68.      *  201: Confirmation URL is empty or malformed.
  69.      *  202: Email is empty or malformed.
  70.      *  203: Password is too short, should be at least 6 characters.
  71.      *  204: First name is empty or too long, should be at most 32 characters.
  72.      *  205: Last name is empty or too long, should be at most 32 characters.
  73.      *  206: User already registered.
  74.      *
  75.      *  @todo If user has not confirmed registration yet, just resend the email
  76.      */
  77.     public static function requestRegistration($confirmationURL, $email, $password, $firstName, $lastName) {
  78.         $config = self::getConfig();
  79.  
  80.         try {
  81.             $url = \Zend_Uri::factory($confirmationURL);
  82.             $salt = Util\Security::getSalt();
  83.             /* @var Zend_Uri_Http $url */
  84.             $url->setQuery(array_merge($url->getQueryAsArray(), array('confirmationToken' => md5($email . $salt))));
  85.             $url = $url->getUri();
  86.         } catch (\Zend_Uri_Exception $e) {
  87.             throw new Service\Exception('Confirmation URL is empty or malformed.', 201, $e);
  88.         }
  89.  
  90.         try {
  91.             $user = new Model\User();
  92.             $user->requestRegistration($email, $password, $firstName, $lastName);
  93.         } catch (Model\Exception $e) {
  94.             throw new Service\Exception($e->getMessage(), 200 + $e->getCode(), $e);
  95.         }
  96.  
  97.         $mailOptions = array(
  98.             'to' => $email,
  99.             'toName' => $user->getFullName(),
  100.         );
  101.         $mailVariables = array(
  102.             'NAME' => $user->getFullName(),
  103.             'LINK' => $url,
  104.             'SERVICE' => $config['service']['site']['name'],
  105.             'HOME' => $config['service']['site']['home']
  106.         );
  107.  
  108.         Util\Mail::send('registration/confirmation', $mailOptions, $mailVariables);
  109.     }
  110.  
  111.     /**
  112.      * Finalize account registration.
  113.      *
  114.      * API Method:
  115.      *  account.confirmRegistration(consumerKey, confirmationToken, email)
  116.      *
  117.      * Authorization:
  118.      *  guest
  119.      *
  120.      * Description:
  121.      *  Finalize account registration. This is the second part of registration process @see PBackend\Service\User#requestRegistration()
  122.      *  After this step user is considered fully registered in the system and receives a general "Welcome" email.
  123.      *  At the end of the process API user will receive user's unique identifier of newly registered application user and credentials to authenticate against API and access its methods as registered user.
  124.      *
  125.      * Arguments:
  126.      *  @param string $consumerKey (required) Key of a consumer requesting this method.
  127.      *  @param string $confirmationToken (required) Confirmation token, previously generated by "user.account.requestRegistration" method.
  128.      *  @param string $email (required) Valid email address.
  129.      *
  130.      * Response:
  131.      *  @return array [
  132.      *      string 'userId' Unique user identifier.
  133.      *      string 'accessToken' Access token, used for authentication with API.
  134.      *      string 'accessTokenSecret' Access token secret, used for signing authenticated API requests.
  135.      *      ]
  136.      *
  137.      * Error codes (in addition to standard API errors):
  138.      *  @throws \PBackend\Service\Exception if API error occurres
  139.      *  201: Consumer not found.
  140.      *  202: Confirmation token is invalid.
  141.      *  203: Email is empty or malformed.
  142.      *  204: User already confirmed.
  143.      *  205: User not found.
  144.      *
  145.      */
  146.     public static function confirmRegistration($consumerKey, $confirmationToken, $email)
  147.     {
  148.         $config = self::getConfig();
  149.  
  150.         $salt = Util\Security::getSalt();
  151.        
  152.         $consumer = Consumer::findOneByKey($consumerKey);
  153.         if (is_null($consumer)) {
  154.             throw new Service\Exception('Consumer not found.', 201);
  155.         }
  156.  
  157.         try {
  158.             Model\User::validateEmail($email);
  159.         } catch (Model\Exception $exception) {
  160.             if($exception->getCode() == 1) { // Email is empty or malformed.
  161.                 throw new Service\Exception($exception->getMessage(), 203);
  162.             } else {
  163.                 throw $exception;
  164.             }
  165.         }
  166.  
  167.         if (md5($email . $salt) != $confirmationToken) {
  168.             throw new Service\Exception('Confirmation token is invalid.', 202);
  169.         }
  170.  
  171.         /* @var \PBackend\Model\User $user */
  172.         $user = Model\User::findOneByEmail($email);
  173.         if (!$user) {
  174.             throw new Service\Exception('User not found.', 205);
  175.         }
  176.  
  177.         try {
  178.             $user->confirmRegistration($email);
  179.         } catch (Model\Exception $exception) {
  180.             switch($exception->getCode()) {
  181.             case 1: // Email is empty or malformed.
  182.                 $code = 203;
  183.                 break;
  184.             case 3: // User already confirmed.
  185.                 $code = 204;
  186.                 break;
  187.             default:
  188.                 throw new \PBackend\Exception('Unknown exception code');
  189.             }
  190.             throw new Service\Exception($exception->getMessage(), $code, $exception);
  191.         }
  192.  
  193.         $accessToken = new Token\Access();
  194.         $accessToken->user = $user;
  195.         $accessToken->consumer = $consumer;
  196.  
  197.         $mailOptions = array(
  198.             'to' => $user->email,
  199.             'toName' => $user->getFullName(),
  200.         );
  201.         $mailVariables = array(
  202.             'NAME' => $user->getFullName(),
  203.             'SERVICE' => $config['service']['site']['name'],
  204.             'HOME' => $config['service']['site']['home']
  205.         );
  206.  
  207.         Util\Mail::send('registration/congratulation', $mailOptions, $mailVariables);
  208.  
  209.         return array(
  210.             'userId' => $user->id,
  211.             'accessToken' => $accessToken->token,
  212.             'accessTokenSecret' => $accessToken->secret
  213.         );
  214.     }
  215.  
  216.     /**
  217.      * Get information about currently authenticated user.
  218.      *
  219.      * API Method:
  220.      *  account.get()
  221.      *
  222.      * Authorization:
  223.      *  user
  224.      *
  225.      * Description:
  226.      *  Get information about currently authenticated user.
  227.      *
  228.      * Arguments:
  229.      *  This method has no specific arguments.
  230.      * Response:
  231.      *  @return array [
  232.      *      string 'userId' Identifier.
  233.      *      int 'createdAt' Unix timestamp, date when the user was created in the system.
  234.      *      int 'updatedAt' Unix timestamp, date when user's information was updated in the system.
  235.      *      string 'email' Email address.
  236.      *      string 'firstName' First name.
  237.      *      string 'lastName' Last name.
  238.      *      string 'role' User's role.
  239.      *      int 'availableInvitations' Count of invitations available to the user.
  240.      *      ]
  241.      *
  242.      * Error codes (in addition to standard API errors):
  243.      *  This method has no additional error codes.
  244.      *
  245.      */
  246.     public static function get()
  247.     {
  248.         $api = self::getApi();
  249.  
  250.         $user = $api->getIdentity();
  251.  
  252.         if(is_null($user->createdAt)){
  253.             $createdAt = null;
  254.         }else{
  255.             $createdAt = $user->createdAt->format('U');
  256.         }
  257.  
  258.         if(is_null($user->updatedAt)){
  259.             $updatedAt = null;
  260.         }else{
  261.             $updatedAt = $user->updatedAt->format('U');
  262.         }
  263.  
  264.         return array(
  265.             'userId' => $user->id,
  266.             'createdAt' => $createdAt,
  267.             'updatedAt' => $updatedAt,
  268.             'email' => $user->email,
  269.             'firstName' => $user->firstName,
  270.             'lastName' => $user->lastName,
  271.             'role' => $user->role,
  272.             'availableInvitations' => $user->availableInvitations,
  273.         );
  274.     }
  275.  
  276.     /**
  277.      * Update user account details.
  278.      *
  279.      * API Method:
  280.      *  account.udpate(password, firstName, lastName)
  281.      *
  282.      * Authorization:
  283.      *  user
  284.      *
  285.      * Description:
  286.      *  Update user account details.
  287.      *  In addition, methods "account.requestPrimaryEmailUpdate" and "account.confirmPrimaryEmailUpdate" can be used to change user's primary email address.
  288.      *
  289.      * Arguments:
  290.      *  @param string $password (required) Password, at least 6 characters, any character is accepted.
  291.      *  @param string $firstName (required) First name, limited to 32 characters.
  292.      *  @param string $lastName (required) Last name, limited to 32 characters.
  293.      *
  294.      * Response:
  295.      *  This method has no specific response if completes without error.
  296.      *
  297.      * Error codes (in addition to standard API errors):
  298.      *  @throws \PBackend\Service\Exception if API error occurres
  299.      *  201: Password is too short, should be at least 6 characters.
  300.      *  202: First name is empty or too long, should be at most 32 characters.
  301.      *  203: Last name is empty or too long, should be at most 32 characters.
  302.      */
  303.     public static function update($password, $firstName, $lastName)
  304.     {
  305.         $api = self::getApi();
  306.  
  307.         $user = $api->getIdentity();
  308.         try {
  309.             $user->update($password, $firstName, $lastName);
  310.         } catch (Model\Exception $exception) {
  311.             throw new Service\Exception($exception->getMessage(), 200 + $exception->getCode(), $exception);
  312.         }
  313.     }
  314.  
  315.     /**
  316.      * Request primary email address update.
  317.      *
  318.      * API Method:
  319.      *  account.requestEmailUpdate(confirmationURL, email)
  320.      *
  321.      * Authorization:
  322.      *  user
  323.      *
  324.      * Description:
  325.      *  This is a first step of a three-step process allowing authenticated users to change their email address.
  326.      *   1. User requests primary email change
  327.      *   2. User receives an email with confirmation link
  328.      *   3. User clicks the link in order to finish the process
  329.      *  Confirmation token is appended to "confirmationURL", for example if confirmationURL is http://www.example.com/user/account/confirmPrimaryEmailUpdate, the following URL will be sent to the user:  http://www.example.com/user/account/confirmPrimaryEmailUpdate?confirmationToken=b20ab007190ccb069019935a9ef72188
  330.      *
  331.      * Developer notes:
  332.      *  Confirmation token is generated from requested email address and system's secret key.
  333.      *
  334.      * Arguments:
  335.      *  @param string $confirmationURL (required) URL link which user must access in order to complete email update process. Confirmation token will be appended to this URL which will be later sent in the email message.
  336.      *  @param string $email (required) Valid, not-registered email address.
  337.      *
  338.      * Response:
  339.      *  This method has no specific response if completes without error.
  340.      *
  341.      * Error codes (in addition to standard API errors):
  342.      *  @throws \PBackend\Service\Exception if API error occurres
  343.      *  201: Confirmation URL is empty or malformed.
  344.      *  202: Email is empty or malformed.
  345.      *  203: Email is already in use.
  346.      */
  347.     public static function requestPrimaryEmailUpdate($confirmationURL, $email)
  348.     {
  349.         $config     = self::getConfig();
  350.         $api        = self::getApi();
  351.  
  352.         $user   = $api->getIdentity();
  353.  
  354.         try {
  355.             $url = \Zend_Uri::factory($confirmationURL);
  356.             $salt = Util\Security::getSalt();
  357.             /* @var Zend_Uri_Http $url */
  358.             $url->setQuery(array_merge($url->getQueryAsArray(), array('confirmationToken' => md5($email . $salt))));
  359.             $url = $url->getUri();
  360.         } catch (\Zend_Uri_Exception $e) {
  361.             throw new Service\Exception('Confirmation URL is empty or malformed.', 201, $e);
  362.         }
  363.  
  364.         try {
  365.             Model\User::validateEmail($email);
  366.         } catch (Model\Exception $exception) {
  367.             if($exception->getCode() == 1) { // Email is empty or malformed.
  368.                 throw new Service\Exception($exception->getMessage(), 202);
  369.             } else {
  370.                 throw $exception;
  371.             }
  372.         }
  373.  
  374.         $userDocument = Document\User::findOneByEmail($email);
  375.         if (!is_null($userDocument)) {
  376.             throw new Service\Exception('Email is already in use.', 203);
  377.         }
  378.  
  379.         $mailOptions = array(
  380.             'to' => $email,
  381.             'toName' => $user->getFullName(),
  382.         );
  383.         $mailVariables = array(
  384.             'NAME' => $user->getFullName(),
  385.             'LINK' => $url,
  386.             'SERVICE' => $config['service']['site']['name'],
  387.             'HOME' => $config['service']['site']['home']
  388.         );
  389.  
  390.         Util\Mail::send('update-email/requestPrimaryEmailUpdate', $mailOptions, $mailVariables);
  391.     }
  392.  
  393.     /**
  394.      * Confirm primary email address update.
  395.      *
  396.      * API Method:
  397.      *  account.confirmPrimaryEmailUpdate(email, confirmationToken)
  398.      *
  399.      * Authorization:
  400.      *  user
  401.      *
  402.      * Description:
  403.      *  This is the second part of the email update process (see "account.requestPrimaryEmailUpdate"). If confirmation token is valid, user's email address is changed to the reqeusted one. At the end of the process the user will recieve a general "Email update success" email to his new address.
  404.      *
  405.      * Developer notes:
  406.      *  Validate confirmation token by matching it with hash of requested email address and system's secret key.
  407.      *
  408.      * Arguments:
  409.      *  @param string $email (required) Valid, not-registered email address.
  410.      *  @param string $confirmationToken (required) Confirmation token, previously generated by "account.requestPrimaryEmailUpdate" method.
  411.      *
  412.      * Response:
  413.      *  This method has no specific response if completes without error.
  414.      *
  415.      * Error codes (in addition to standard API errors):
  416.      *  @throws \PBackend\Service\Exception if API error occurres
  417.      *  201: Email is empty or malformed.
  418.      *  202: Email is already in use.
  419.      *  203: Confirmation token is invalid.
  420.      */
  421.     public static function confirmPrimaryEmailUpdate($email, $confirmationToken) {
  422.         $config = self::getConfig();
  423.         $log = self::getLog();
  424.  
  425.         $api = self::getApi();
  426.         $user= $api->getIdentity();
  427.        
  428.         try {
  429.             Model\User::validateEmail($email);
  430.         } catch (Model\Exception $exception) {
  431.             if($exception->getCode() == 1) { // Email is empty or malformed.
  432.                 throw new Service\Exception($exception->getMessage(), 201);
  433.             } else {
  434.                 throw $exception;
  435.             }
  436.         }
  437.  
  438.         $userDocument = Document\User::findOneByEmail($email);
  439.         if (!is_null($userDocument)) {
  440.             throw new Service\Exception('Email is already in use.', 203);
  441.         }
  442.  
  443.         $salt = Util\Security::getSalt();
  444.         if (md5($email . $salt) != $confirmationToken) {
  445.             throw new Service\Exception('Confirmation token is invalid.', 203);
  446.         }
  447.  
  448.         try {
  449.             $user->updateEmail($email);
  450.         } catch (Model\Exception $e) {
  451.             throw new Service\Exception($e->getMessage(), 200 + $e->getCode(), $e);
  452.         }
  453.  
  454.         $mailOptions = array(
  455.             'to' => $email,
  456.             'toName' => $user->getFullName(),
  457.         );
  458.         $mailVariables = array(
  459.             'NAME' => $user->getFullName(),
  460.             'SERVICE' => $config['service']['site']['name'],
  461.             'HOME' => $config['service']['site']['home']
  462.         );
  463.  
  464.         Util\Mail::send('update-email/confirmPrimaryEmailUpdate', $mailOptions, $mailVariables);
  465.     }
  466.  
  467.     /**
  468.      * Match password and email address.
  469.      *
  470.      * API Method:
  471.      *  account.validatePassword(email, password)
  472.      *
  473.      * Authorization:
  474.      *  guest
  475.      *
  476.      * Description:
  477.      *  This method allows API clients to perform log-in operation by matching user's email and password.
  478.      *
  479.      * Developer notes:
  480.      *  Make sure to use User Model's utility for password hashing.
  481.      *
  482.      * Arguments:
  483.      *  @param string $email (required) Valid, registered email address.
  484.      *  @param string $password (required) Password, at least 6 characters, any character is accepted.
  485.      *
  486.      * Response:
  487.      *  This method has no specific response if completes without error.
  488.      *
  489.      * Error codes (in addition to standard API errors):
  490.      *  @throws \PBackend\Service\Exception if API error occurres
  491.      *  201: Email is empty or malformed.
  492.      *  202: Password is too short, should be at least 6 characters.
  493.      *  203: Invalid credentials.
  494.      */
  495.     public static function validatePassword($email, $password)
  496.     {
  497.  
  498.         try {
  499.             Model\User::validateEmail($email);
  500.         } catch (Model\Exception $exception) {
  501.             if($exception->getCode() == 1) { // Email is empty or malformed.
  502.                 throw new Service\Exception($exception->getMessage(), 201);
  503.             } else {
  504.                 throw $exception;
  505.             }
  506.         }
  507.  
  508.         try {
  509.             Model\User::validatePassword($password);
  510.         } catch (Model\Exception $exception) {
  511.             if($exception->getCode() == 1) { // Password is too short, should be at least 6 characters.
  512.                 throw new Service\Exception($exception->getMessage(), 202);
  513.             } else {
  514.                 throw $exception;
  515.             }  
  516.         }
  517.  
  518.         $user = Model\User::findOneByEmail($email);
  519.         if (!$user || $user->password != $user->hashPassword($password)) {
  520.             throw new Service\Exception('Invalid credentials.', 203);
  521.         }
  522.     }
  523.  
  524.     /**
  525.      * Request password reset.
  526.      *
  527.      * API Method:
  528.      *  account.requestPasswordReset(confirmationURL, email)
  529.      *
  530.      * Authorization:
  531.      *  guest
  532.      *
  533.      * Description:
  534.      *  The reset password process consists of two steps:
  535.      *   1. Guest User requests password reset by calling "account.requestPasswordReset" method, the system sends a confirmation token to email address that the user provides.
  536.      *   2. Guest User confirms password reset by calling "account.confirmPasswordReset" method, the system changes the password to one provided by the user.
  537.      *  For example if confirmationURL is http://www.example.com/user/confirmResetPassword, the following URL will be sent to the user: http://www.example.com/user/confirmResetPassword?confirmationToken=b20ab007190ccb069019935a9ef72188&confirmationTokenTime=1322402163.0289
  538.      *  Note: "confirmationToken" and "confirmationTokenTime" parameters will be overwritten in the original confirmationURL.
  539.      *
  540.      * Developer notes:
  541.      *  - Confirmation token is a hash of email, current time and system security salt.
  542.      *  - "confirmationToken" and "confirmationTokenTime" parameters are added to the original confirmationURL.
  543.      *  - Time should include milliseconds, use "microtime(true)" PHP function.
  544.      *
  545.      * Arguments:
  546.      *  @param string $confirmationURL (required) URL link which user must access in order to complete password reset process. Confirmation token and current time will be appended to this URL which will be later sent in the email message.
  547.      *  @param string $email (required) Valid, registered email address.
  548.      *
  549.      * Response:
  550.      *  This method has no specific response if completes without error.
  551.      *
  552.      * Error codes (in addition to standard API errors):
  553.      *  @throws \PBackend\Service\Exception if API error occurres
  554.      *  201: Confirmation URL is empty or malformed.
  555.      *  202: Email is empty or malformed.
  556.      *  203: Email not found.
  557.      */
  558.     public static function requestPasswordReset($confirmationURL, $email)
  559.     {
  560.         $config = self::getConfig();
  561.  
  562.         try {
  563.             $url = \Zend_Uri::factory($confirmationURL);
  564.             $salt = Util\Security::getSalt();
  565.             $currentTime = microtime(true);
  566.             /* @var Zend_Uri_Http $url */
  567.             $url->setQuery(array_merge($url->getQueryAsArray(), array('confirmationToken' => md5($email . $currentTime . $salt), 'confirmationTokenTime' => $currentTime )));
  568.             $url = $url->getUri();
  569.         } catch (\Zend_Uri_Exception $e) {
  570.             throw new Service\Exception('Confirmation URL is empty or malformed.', 201, $e);
  571.         }
  572.  
  573.         try {
  574.             Model\User::validateEmail($email);
  575.         } catch (Model\Exception $exception) {
  576.             if($exception->getCode() == 1) { // Email is empty or malformed.
  577.                 throw new Service\Exception($exception->getMessage(), 202);
  578.             } else {
  579.                 throw $exception;
  580.             }
  581.         }
  582.  
  583.         $user = Model\User::findOneByEmail($email);
  584.         if (!$user) {
  585.             throw new Service\Exception('Email not found.', 203);
  586.         }
  587.  
  588.         $mailOptions = array(
  589.                         'to' => $email,
  590.                 'toName' => $user->getFullName(),
  591.                 );
  592.  
  593.         $mailVariables = array(
  594.                         'NAME' => $user->getFullName(),
  595.                 'LINK' => $url,
  596.                 'SERVICE' => $config['service']['site']['name'],
  597.                 'HOME' => $config['service']['site']['home']
  598.                 );
  599.  
  600.         Util\Mail::send('request-password/requestPasswordReset', $mailOptions, $mailVariables);
  601.     }
  602.  
  603.     /**
  604.      * Confirm password reset.
  605.      *
  606.      * API Method:
  607.      *  account.confirmPasswordReset(email, password, confirmationToken, confirmationTokenTime)
  608.      *
  609.      * Authorization:
  610.      *  guest
  611.      *
  612.      * Description:
  613.      *  This is the second step of the password reset process (see "account.requestPasswordReset" method). When Guest User received email and clicked password reset link, API client calls "user.account.confirmPasswordReset" in order to complete the process. If confirmationToken and confirmationTokenDate are valid, password of user with given email is changed and user will receive notification email.
  614.      *  Note: confrimationTokenTime should not be greater than current time and should not differ from current time for less than 2 seconds or more than 48 hours.
  615.      *
  616.      * Developer notes:
  617.      *  - Confirmation token is a hash of email, current time and system security salt.
  618.      *  - Confrimation token Time should be a valid result of PHP function "microtime(true)", should be not greater than current time and should not differ from current time for less than 2 seconds or more than 48 hours.
  619.      *
  620.      * Arguments:
  621.      *  @param string $email (required) Valid, registered email address.
  622.      *  @param string $password (required) Password, at least 6 characters, any character is accepted.
  623.      *  @param string $confirmationToken (required) Confirmation token, previously generated by "account.requestPasswordReset" method.
  624.      *  @param float $confirmationTokenTime (required) Confirmation token time, previously generated by "account.requestPasswordReset" method.
  625.      *
  626.      * Response:
  627.      *  This method has no specific response if completes without error.
  628.      *
  629.      * Error codes (in addition to standard API errors):
  630.      *  @throws \PBackend\Service\Exception if API error occurres
  631.      *  201: Email is empty or malformed.
  632.      *  202: Email not found.
  633.      *  203: Password is too short, should be at least 6 characters.
  634.      *  204: Confirmation token is invalid.
  635.      *  205: Confirmation token time is invalid.
  636.      *  
  637.      */
  638.     public static function confirmPasswordReset($email, $password, $confirmationToken, $confirmationTokenTime)
  639.     {
  640.         $config = self::getConfig();
  641.  
  642.         try {
  643.             Model\User::validateEmail($email);
  644.         } catch (Model\Exception $exception) {
  645.             if($exception->getCode() == 1) { // Email is empty or malformed.
  646.                 throw new Service\Exception($exception->getMessage(), 201);
  647.             } else {
  648.                 throw $exception;
  649.             }
  650.         }
  651.  
  652.         $user = Model\User::findOneByEmail($email);
  653.         if (!$user) {
  654.             throw new Service\Exception('Email not found.', 202);
  655.         }
  656.  
  657.         try {
  658.             Model\User::validatePassword($password);
  659.         } catch (Model\Exception $exception) {
  660.             if($exception->getCode() == 1) { // Password is too short, should be at least 6 characters.
  661.                 throw new Service\Exception($exception->getMessage(), 203);
  662.             } else {
  663.                 throw $exception;
  664.             }
  665.         }
  666.  
  667.         $salt = Util\Security::getSalt();
  668.         $currentTime = microtime(true);
  669.  
  670.         if (md5($email . $confirmationTokenTime . $salt) != $confirmationToken) {
  671.             throw new Service\Exception('Confirmation token is invalid.', 204);
  672.         }
  673.  
  674.         /*
  675.          * Difference Between CurrentTime and ConfirmationTokenTime
  676.          */
  677.         $timeDifference = $currentTime - $confirmationTokenTime;
  678.         if ($confirmationTokenTime > $currentTime || $timeDifference > 48 * 60 * 60 || $timeDifference < 2) {
  679.             throw new Service\Exception('Confirmation token time is invalid.', 205);
  680.         }
  681.  
  682.         $user->updatePassword($password);
  683.  
  684.         $mailOptions = array(
  685.                         'to' => $email,
  686.                 'toName' => $user->getFullName(),
  687.                 );
  688.  
  689.         $mailVariables = array(
  690.                         'NAME' => $user->getFullName(),
  691.                 'SERVICE' => $config['service']['site']['name'],
  692.                 'HOME' => $config['service']['site']['home']
  693.                 );
  694.  
  695.         Util\Mail::send('request-password/confirmPasswordReset', $mailOptions, $mailVariables);
  696.     }
  697. }