mirror of
				https://github.com/beestat/app.git
				synced 2025-10-30 17:52:25 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			216 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  * Tokens for authorizing access to ecobee accounts.
 | |
|  *
 | |
|  * @author Jon Ziebell
 | |
|  */
 | |
| class ecobee_token extends cora\crud {
 | |
| 
 | |
|   /**
 | |
|    * Create an ecobee token. Does the normal CRUD create and also extracts the
 | |
|    * ecobee_account_id from the token and attaches it to the user. A user only
 | |
|    * has one ecobee_token row so this really only runs once per user.
 | |
|    *
 | |
|    * @param array $attributes
 | |
|    *
 | |
|    * @return int
 | |
|    */
 | |
|   public function create($attributes) {
 | |
|     $this->api(
 | |
|       'user',
 | |
|       'update',
 | |
|       [
 | |
|         'attributes' => [
 | |
|           'user_id' => $this->session->get_user_id(),
 | |
|           'ecobee_account_id' => $this->get_ecobee_account_id($attributes)
 | |
|         ]
 | |
|       ]
 | |
|     );
 | |
| 
 | |
|     return parent::create($attributes);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * This should be called when connecting a new user. Get the access/refresh
 | |
|    * tokens, then attach them to a brand new anonymous user.
 | |
|    *
 | |
|    * @param string $code The code from ecobee used to obtain the
 | |
|    * access/refresh tokens.
 | |
|    *
 | |
|    * @return array The access/refresh tokens.
 | |
|    */
 | |
|   public function obtain($code) {
 | |
|     // Obtain the access and refresh tokens from the authorization code.
 | |
|     $response = $this->api(
 | |
|       'ecobee',
 | |
|       'ecobee_api',
 | |
|       [
 | |
|         'method' => 'POST',
 | |
|         'endpoint' => 'token',
 | |
|         'arguments' => [
 | |
|           'grant_type' => 'authorization_code',
 | |
|           'code' => $code,
 | |
|           'redirect_uri' => $this->setting->get('ecobee_redirect_uri')
 | |
|         ]
 | |
|       ]
 | |
|     );
 | |
| 
 | |
|     // Make sure we got the expected result.
 | |
|     if (
 | |
|       isset($response['access_token']) === false ||
 | |
|       isset($response['refresh_token']) === false
 | |
|     ) {
 | |
|       throw new cora\exception('Could not get first ecobee token.', 10000);
 | |
|     }
 | |
| 
 | |
|     return [
 | |
|       'access_token' => $response['access_token'],
 | |
|       'refresh_token' => $response['refresh_token'],
 | |
|       'ecobee_account_id' => $this->get_ecobee_account_id($response),
 | |
|       'timestamp' => date('Y-m-d H:i:s'),
 | |
|       'deleted' => 0
 | |
|     ];
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Get an ecobee_account_id from the ecobee JWT. Check a bunch of stuff to
 | |
|    * make sure it's valid.
 | |
|    *
 | |
|    * @param ecobee_token $ecobee_token The ecobee_token.
 | |
|    *
 | |
|    * @return string The ecobee_account_id.
 | |
|    */
 | |
|   public function get_ecobee_account_id($ecobee_token) {
 | |
|     $parts = explode('.', $ecobee_token['access_token']);
 | |
|     if(count($parts) !== 3) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     $payload = $parts[1];
 | |
|     $payload = str_replace(['_', '-'], ['/', '+'], $payload);
 | |
| 
 | |
|     $json = base64_decode($payload);
 | |
| 
 | |
|     if($json === false) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     $object = json_decode($json, true);
 | |
|     if($object === null) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     if(isset($object['sub']) === false) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     $sub_parts = explode('|', $object['sub']);
 | |
|     if(count($sub_parts) !== 2) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     $ecobee_account_id = $sub_parts[1];
 | |
| 
 | |
|     if(strlen($ecobee_account_id) !== 36) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     return $ecobee_account_id;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Get some new tokens. A database lock is obtained prior to getting a token
 | |
|    * so that no other API call can attempt to get a token at the same time.
 | |
|    * This way if two API calls fire off to ecobee at the same time, then
 | |
|    * return at the same time, then call token->refresh() at the same time,
 | |
|    * only one can run and actually refresh at a time. If the transactionless
 | |
|    * one runs after that's fine as it will look up the token prior to
 | |
|    * refreshing.
 | |
|    *
 | |
|    * @return array The new token.
 | |
|    */
 | |
|   public function refresh() {
 | |
|     $database = cora\database::get_transactionless_instance();
 | |
| 
 | |
|     $lock_name = 'ecobee_token->refresh(' . $this->session->get_user_id() . ')';
 | |
|     $database->get_lock($lock_name, 3);
 | |
| 
 | |
|     $ecobee_tokens = $database->read(
 | |
|       'ecobee_token',
 | |
|       [
 | |
|         'user_id' => $this->session->get_user_id(),
 | |
|         'deleted' => false
 | |
|       ]
 | |
|     );
 | |
| 
 | |
|     if(count($ecobee_tokens) === 0) {
 | |
|       throw new cora\exception('Could not refresh ecobee token; no token found.', 10001);
 | |
|     }
 | |
|     $ecobee_token = $ecobee_tokens[0];
 | |
| 
 | |
|     $response = $this->api(
 | |
|       'ecobee',
 | |
|       'ecobee_api',
 | |
|       [
 | |
|         'method' => 'POST',
 | |
|         'endpoint' => 'token',
 | |
|         'arguments' => [
 | |
|           'ecobee_type' => 'jwt',
 | |
|           'grant_type' => 'refresh_token',
 | |
|           'refresh_token' => $ecobee_token['refresh_token']
 | |
|         ]
 | |
|       ]
 | |
|     );
 | |
| 
 | |
|     if (
 | |
|       isset($response['access_token']) === false ||
 | |
|       isset($response['refresh_token']) === false
 | |
|     ) {
 | |
|       $this->delete($ecobee_token['ecobee_token_id']);
 | |
|       $database->release_lock($lock_name);
 | |
|       throw new cora\exception('Could not refresh ecobee token; ecobee returned no token.', 10002, true, null, false);
 | |
|     }
 | |
| 
 | |
|     $ecobee_token = $database->update(
 | |
|       'ecobee_token',
 | |
|       [
 | |
|         'ecobee_token_id' => $ecobee_token['ecobee_token_id'],
 | |
|         'access_token' => $response['access_token'],
 | |
|         'refresh_token' => $response['refresh_token'],
 | |
|         'timestamp' => date('Y-m-d H:i:s')
 | |
|       ]
 | |
|     );
 | |
| 
 | |
|     $this->api(
 | |
|       'user',
 | |
|       'update',
 | |
|       [
 | |
|         'attributes' => [
 | |
|           'user_id' => $this->session->get_user_id(),
 | |
|           'ecobee_account_id' => $this->get_ecobee_account_id($ecobee_token)
 | |
|         ]
 | |
|       ]
 | |
|     );
 | |
| 
 | |
|     $database->release_lock($lock_name);
 | |
| 
 | |
|     return $ecobee_token;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Delete an ecobee token and log the user out. Make sure to delete the
 | |
|    * token prior to logging out so the right permissions are present.
 | |
|    *
 | |
|    * @param int $id
 | |
|    *
 | |
|    * @return int
 | |
|    */
 | |
|   public function delete($id) {
 | |
|     $return = parent::delete($id);
 | |
|     $this->api('user', 'log_out');
 | |
|     return $return;
 | |
|   }
 | |
| 
 | |
| }
 |