1
0
mirror of https://github.com/beestat/app.git synced 2025-07-09 03:04:07 -04:00

Fixed ecobee tokens not always deleting when they are revoked.

This commit is contained in:
Jon Ziebell 2021-03-01 22:30:31 -05:00
parent 01656e2cff
commit 159670e08c
8 changed files with 77 additions and 69 deletions

View File

@ -14,9 +14,10 @@ namespace cora;
* @author Jon Ziebell * @author Jon Ziebell
*/ */
final class exception extends \Exception { final class exception extends \Exception {
public function __construct($message, $code, $reportable = true, $extra = null) { public function __construct($message, $code, $reportable = true, $extra_info = null, $rollback = true) {
$this->reportable = $reportable; $this->reportable = $reportable;
$this->extra = $extra; $this->extra_info = $extra_info;
$this->rollback = $rollback;
return parent::__construct($message, $code, null); return parent::__construct($message, $code, null);
} }
@ -25,6 +26,10 @@ final class exception extends \Exception {
} }
public function getExtraInfo() { public function getExtraInfo() {
return $this->extra; return $this->extra_info;
}
public function getRollback() {
return $this->rollback;
} }
} }

View File

@ -100,11 +100,11 @@ final class request {
private $current_working_directory; private $current_working_directory;
/** /**
* A list of create calls queued up to run at the end of the request. * A list of database calls to make at the very end of the script.
* *
* @var array * @var array
*/ */
private $queued_creates = []; private $queued_database_actions = [];
/** /**
* Save the request variables for use later on. If unset, they are defaulted * Save the request variables for use later on. If unset, they are defaulted
@ -439,6 +439,7 @@ final class request {
$this->set_error_response( $this->set_error_response(
$error_message, $error_message,
$error_code, $error_code,
true,
true true
); );
@ -468,7 +469,8 @@ final class request {
$this->set_error_response( $this->set_error_response(
$e->getMessage(), $e->getMessage(),
$e->getCode(), $e->getCode(),
(method_exists($e, 'getReportable') === true ? $e->getReportable() : true) (method_exists($e, 'getReportable') === true ? $e->getReportable() : true),
(method_exists($e, 'getRollback') === true ? $e->getRollback() : true)
); );
try { try {
@ -489,23 +491,26 @@ final class request {
* *
* There are a few places that call this function to set an error response, * There are a few places that call this function to set an error response,
* so this can't just be done in the exception handler alone. If an error * so this can't just be done in the exception handler alone. If an error
* occurs, rollback the current transaction. * occurs, rollback the current transaction unless specified otherwise.
* *
* @param string $error_message The error message. * @param string $error_message The error message.
* @param mixed $error_code The supplied error code. * @param mixed $error_code The supplied error code.
* @param array $reportable Whether or not the error is reportable. * @param array $reportable Whether or not the error is reportable.
* @param array $rollback Whether or not the error should cause a rollback.
*/ */
public function set_error_response($error_message, $error_code, $reportable) { public function set_error_response($error_message, $error_code, $reportable, $rollback) {
$setting = setting::get_instance(); $setting = setting::get_instance();
$session = session::get_instance(); $session = session::get_instance();
// I guess if this fails then things are really bad, but let's at least // I guess if this fails then things are really bad, but let's at least
// protect against additional exceptions if the database connection or // protect against additional exceptions if the database connection or
// similar fails. // similar fails.
if($rollback === true) {
try { try {
$database = database::get_instance(); $database = database::get_instance();
$database->rollback_transaction(); $database->rollback_transaction();
} catch(\Exception $e) {} } catch(\Exception $e) {}
}
$this->response = [ $this->response = [
'success' => false, 'success' => false,
@ -595,8 +600,29 @@ final class request {
chdir($this->current_working_directory); chdir($this->current_working_directory);
// Run any queued creates. // Run any queued creates.
foreach($this->queued_creates as $queued_create) { foreach($this->queued_database_actions as $queued_database_action) {
$database->create($queued_create['resource'], $queued_create['attributes'], 'id'); switch($queued_database_action['method']) {
case 'create':
$database->create(
$queued_database_action['resource'],
$queued_database_action['attributes'],
'id'
);
break;
case 'update':
$database->update(
$queued_database_action['resource'],
$queued_database_action['attributes'],
'id'
);
break;
case 'delete':
$database->delete(
$queued_database_action['resource'],
$queued_database_action['attributes']
);
break;
}
} }
// If I didn't catch an error/exception with my handlers, look here...this // If I didn't catch an error/exception with my handlers, look here...this
@ -611,6 +637,7 @@ final class request {
$this->set_error_response( $this->set_error_response(
$error['message'], $error['message'],
$error['type'], $error['type'],
true,
true true
); );
@ -666,7 +693,8 @@ final class request {
$this->set_error_response( $this->set_error_response(
$e->getMessage(), $e->getMessage(),
$e->getCode(), $e->getCode(),
(method_exists($e, 'getReportable') === true ? $e->getReportable() : true) (method_exists($e, 'getReportable') === true ? $e->getReportable() : true),
(method_exists($e, 'getRollback') === true ? $e->getRollback() : true)
); );
try { try {
@ -684,12 +712,15 @@ final class request {
* be used for logging things as these will not be affected by transaction * be used for logging things as these will not be affected by transaction
* rollbacks in the event of an exception. * rollbacks in the event of an exception.
* *
* @param string $resource * @param string $resource The database table to act on.
* @param array $attributes * @param string $method create|update|delete
* @param array $attributes The attributes of the create or update. If
* delete just the ID to delete.
*/ */
public function queue_create($resource, $attributes) { public function queue_database_action($resource, $method, $attributes) {
$this->queued_creates[] = [ $this->queued_database_actions[] = [
'resource' => $resource, 'resource' => $resource,
'method' => $method,
'attributes' => $attributes 'attributes' => $attributes
]; ];
} }

View File

@ -164,7 +164,7 @@ class ecobee extends external_api {
[] []
); );
if(count($ecobee_tokens) !== 1) { if(count($ecobee_tokens) !== 1) {
$this->api('user', 'log_out', ['all' => true]); $this->api('user', 'log_out');
throw new cora\exception('No ecobee access for this user.', 10501, false); throw new cora\exception('No ecobee access for this user.', 10501, false);
} }
$ecobee_token = $ecobee_tokens[0]; $ecobee_token = $ecobee_tokens[0];
@ -227,8 +227,7 @@ class ecobee extends external_api {
$this->log_mysql($curl_response, true); $this->log_mysql($curl_response, true);
} }
$this->api('ecobee_token', 'delete', $ecobee_token['ecobee_token_id']); $this->api('ecobee_token', 'delete', $ecobee_token['ecobee_token_id']);
$this->api('user', 'log_out', ['all' => true]); throw new cora\exception('Ecobee access was revoked by user.', 10500, false, null, false);
throw new cora\exception('Ecobee access was revoked by user.', 10500, false);
} }
else if (isset($response['status']) === true && $response['status']['code'] !== 0) { else if (isset($response['status']) === true && $response['status']['code'] !== 0) {
// Any other error // Any other error
@ -240,10 +239,16 @@ class ecobee extends external_api {
else if (isset($response['error']) === true) { else if (isset($response['error']) === true) {
// Authorization errors are a bit different // Authorization errors are a bit different
// https://www.ecobee.com/home/developer/api/documentation/v1/auth/auth-req-resp.shtml // https://www.ecobee.com/home/developer/api/documentation/v1/auth/auth-req-resp.shtml
if($response['error'] === 'invalid_grant') {
$ecobee_token = $this->api('ecobee_token', 'read')[0];
$this->api('ecobee_token', 'delete', $ecobee_token['ecobee_token_id']);
}
if($this::$log_mysql !== 'all') { if($this::$log_mysql !== 'all') {
$this->log_mysql($curl_response, true); $this->log_mysql($curl_response, true);
} }
throw new cora\exception(isset($response['error_description']) === true ? $response['error_description'] : $response['error'], 10505); throw new cora\exception(isset($response['error_description']) === true ? $response['error_description'] : $response['error'], 10505, true, null, false);
} }
else { else {
return $response; return $response;

View File

@ -97,9 +97,8 @@ class ecobee_token extends cora\crud {
isset($response['refresh_token']) === false isset($response['refresh_token']) === false
) { ) {
$this->delete($ecobee_token['ecobee_token_id']); $this->delete($ecobee_token['ecobee_token_id']);
$this->api('user', 'log_out', ['all' => true]);
$database->release_lock($lock_name); $database->release_lock($lock_name);
throw new cora\exception('Could not refresh ecobee token; ecobee returned no token.', 10002); throw new cora\exception('Could not refresh ecobee token; ecobee returned no token.', 10002, true, null, false);
} }
$ecobee_token = $database->update( $ecobee_token = $database->update(
@ -118,19 +117,16 @@ class ecobee_token extends cora\crud {
} }
/** /**
* Delete an ecobee token. If this happens immediately log out all of this * Delete an ecobee token and log the user out. Make sure to delete the
* user's currently logged in sessions. * token prior to logging out so the right permissions are present.
* *
* @param int $id * @param int $id
* *
* @return int * @return int
*/ */
public function delete($id) { public function delete($id) {
$database = cora\database::get_transactionless_instance(); $return = parent::delete($id);
$this->api('user', 'log_out');
// Need to delete the token before logging out or else the delete fails.
$return = $database->delete($this->resource, $id);
return $return; return $return;
} }

View File

@ -16,7 +16,7 @@ class external_api_log extends cora\crud {
*/ */
public function create($attributes) { public function create($attributes) {
$attributes['user_id'] = $this->session->get_user_id(); $attributes['user_id'] = $this->session->get_user_id();
$this->request->queue_create($this->resource, $attributes); $this->request->queue_database_action($this->resource, 'create', $attributes);
} }
} }

View File

@ -105,7 +105,7 @@ class patreon_token extends cora\crud {
) { ) {
$this->delete($patreon_token['patreon_token_id']); $this->delete($patreon_token['patreon_token_id']);
$database->release_lock($lock_name); $database->release_lock($lock_name);
throw new cora\exception('Could not refresh Patreon token; Patreon returned no token.', 10102); throw new cora\exception('Could not refresh Patreon token; Patreon returned no token.', 10102, true, null, false);
} }
$database->update( $database->update(
@ -122,16 +122,15 @@ class patreon_token extends cora\crud {
} }
/** /**
* Delete an patreon token. * Delete a Patreon 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 * @param int $id
* *
* @return int * @return int
*/ */
public function delete($id) { public function delete($id) {
$database = cora\database::get_transactionless_instance(); return parent::delete($id);
$return = $database->delete($this->resource, $id);
return $return;
} }
} }

View File

@ -131,38 +131,14 @@ class user extends cora\crud {
/** /**
* Logs out the currently logged in user. * Logs out the currently logged in user.
*
* @return bool True if it was successfully invalidated. Could return false
* for a non-existant session key or if it was already logged out. In the
* case of multiple sessions, return true if all open sessions were
* successfully deleted, false if not.
*/ */
public function log_out($all) { public function log_out() {
if($this->setting->is_demo() === true) { if($this->setting->is_demo() === true) {
return; return;
} }
if($all === true) {
// Sometimes I need to log out and then throw an exception. Using the
// transactionless instance makes sure that actually works.
$database = cora\database::get_transactionless_instance();
$sessions = $database->read(
'cora\session',
[
'user_id' => $this->session->get_user_id(),
'api_user_id' => null
]
);
$success = true;
foreach($sessions as $session) {
$success &= $database->delete('cora\session', $session['session_id']);
}
return $success;
}
else {
return $this->session->delete(); return $this->session->delete();
} }
}
/** /**
* Set a setting on a user. This utilizes a lock because all settings are * Set a setting on a user. This utilizes a lock because all settings are

View File

@ -192,11 +192,7 @@ beestat.component.header.prototype.decorate_ = function(parent) {
.set_callback(function() { .set_callback(function() {
window.location.href = window.location.href.replace('app.', ''); window.location.href = window.location.href.replace('app.', '');
}) })
.add_call( .add_call('user', 'log_out')
'user',
'log_out',
{'all': false}
)
.send(); .send();
})); }));
}; };