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

Fixes API-E. Fixes API-K. Fixes API-J.

Cleaned up some logic related to how tokens and refreshing works. Lots of stuff was broken.
This commit is contained in:
Jon Ziebell 2020-01-20 20:36:28 -05:00
parent 5fb835737f
commit 279d97d72f
9 changed files with 116 additions and 75 deletions

View File

@ -271,6 +271,10 @@ final class cora {
$session = session::get_instance();
$session_is_valid = $session->touch($this->api_user['session_key']);
// Make sure the updated session timestamp doesn't get rolled back on
// exception.
$this->database->commit_transaction();
// Process each request.
foreach($this->api_calls as $api_call) {
// Store the currently running API call for tracking if an error occurs.
@ -291,7 +295,7 @@ final class cora {
// If the request requires a session, make sure it's valid.
if($call_type === 'private') {
if($session_is_valid === false) {
throw new \Exception('Session is expired.', 1004);
throw new exception('Session is expired.', 1004, false);
}
}
@ -638,7 +642,8 @@ final class cora {
$error_code,
$error_file,
$error_line,
debug_backtrace(false)
debug_backtrace(false),
true
);
die(); // Do not continue execution; shutdown handler will now run.
}
@ -655,7 +660,8 @@ final class cora {
$e->getCode(),
$e->getFile(),
$e->getLine(),
$e->getTrace()
$e->getTrace(),
(method_exists($e, 'getReportable') === true ? $e->getReportable() : true)
);
die(); // Do not continue execution; shutdown handler will now run.
}
@ -671,7 +677,7 @@ final class cora {
* @param int $error_line The line of the file the error happened on.
* @param array $error_trace The stack trace for the error.
*/
public function set_error_response($error_message, $error_code, $error_file, $error_line, $error_trace) {
public function set_error_response($error_message, $error_code, $error_file, $error_line, $error_trace, $reportable) {
// 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
// occurs, rollback the current transaction. Also only attempt to roll back
@ -706,43 +712,45 @@ final class cora {
// Send data to Sentry for error logging.
// https://docs.sentry.io/development/sdk-dev/event-payloads/
$data = [
'event_id' => str_replace('-', '', exec('uuidgen -r')),
'timestamp' => date('c'),
'logger' => 'cora',
'platform' => 'php',
'level' => 'error',
'tags' => [
'error_code' => $error_code
],
'extra' => [
'api_user_id' => $api_user_id,
'error_file' => $error_file,
'error_line' => $error_line,
'error_trace' => $error_trace
],
'exception' => [
'type' => 'Exception',
'value' => $error_message,
'handled' => false
],
'user' => [
'id' => $user_id,
'ip_address' => $_SERVER['REMOTE_ADDR']
]
];
if ($reportable === true) {
$data = [
'event_id' => str_replace('-', '', exec('uuidgen -r')),
'timestamp' => date('c'),
'logger' => 'cora',
'platform' => 'php',
'level' => 'error',
'tags' => [
'error_code' => $error_code
],
'extra' => [
'api_user_id' => $api_user_id,
'error_file' => $error_file,
'error_line' => $error_line,
'error_trace' => $error_trace
],
'exception' => [
'type' => 'Exception',
'value' => $error_message,
'handled' => false
],
'user' => [
'id' => $user_id,
'ip_address' => $_SERVER['REMOTE_ADDR']
]
];
exec(
'curl ' .
'-H "Content-Type: application/json" ' .
'-H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=' . $this->setting->get('sentry_key') . '" ' .
'--silent ' . // silent; keeps logs out of stderr
'--show-error ' . // override silent on failure
'--max-time 10 ' .
'--connect-timeout 5 ' .
'--data \'' . json_encode($data) . '\' ' .
'"https://sentry.io/api/' . $this->setting->get('sentry_project_id') . '/store/" > /dev/null &'
);
exec(
'curl ' .
'-H "Content-Type: application/json" ' .
'-H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=' . $this->setting->get('sentry_key') . '" ' .
'--silent ' . // silent; keeps logs out of stderr
'--show-error ' . // override silent on failure
'--max-time 10 ' .
'--connect-timeout 5 ' .
'--data \'' . json_encode($data) . '\' ' .
'"https://sentry.io/api/' . $this->setting->get('sentry_project_id') . '/store/" > /dev/null &'
);
}
}
/**
@ -779,7 +787,8 @@ final class cora {
$error['type'],
$error['file'],
$error['line'],
debug_backtrace(false)
debug_backtrace(false),
true
);
}
@ -824,7 +833,8 @@ final class cora {
$e->getCode(),
$e->getFile(),
$e->getLine(),
$e->getTrace()
$e->getTrace(),
(method_exists($e, 'getReportable') === true ? $e->getReportable() : true)
);
$this->set_default_headers();
$this->output_headers();

View File

@ -695,18 +695,21 @@ final class database extends \mysqli {
}
/**
* Actually delete a row from a table by the primary key.
* Set deleted = 1 on the database row.
*
* @param string $table The table to delete from.
* @param string $resource The table to delete from.
* @param int $id The value of the primary key to delete.
*
* @return int The number of rows affected by the delete (could be 0).
*/
public function delete($table, $id) {
$query = 'delete from ' . $this->escape_identifier($table) .
' where ' . $this->escape_identifier($table . '_id') . ' = ' .
$this->escape($id);
$this->query($query);
public function delete($resource, $id) {
$table = $this->get_table($resource);
$attributes = [];
$attributes[$table . '_id'] = $id;
$attributes['deleted'] = true;
$this->update($resource, $attributes);
return $this->affected_rows;
}

25
api/cora/exception.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace cora;
/**
* Custom exception class. Requires message, code, and replaces $previous with
* $reportable to indicate if the exception should be reported to a logging
* service or not.
*
* The class name was made lowercase to simplify autoincludes, but the
* interface was otherwise left alone because I still need to support catching
* regular exceptions.
*
* @author Jon Ziebell
*/
final class exception extends \Exception {
public function __construct($message, $code, $reportable = true) {
$this->reportable = $reportable;
return parent::__construct($message, $code, null);
}
public function getReportable() {
return $this->reportable;
}
}

View File

@ -225,13 +225,7 @@ final class session {
$sessions = $database->read('cora\session', ['session_key' => $session_key]);
if(count($sessions) === 1) {
$database->update(
'cora\session',
[
'session_id' => $sessions[0]['session_id'],
'deleted' => 1
]
);
$database->delete('cora\session', $sessions[0]['session_id']);
// Remove these if the current session got logged out.
if($session_key === $this->session_key) {
$this->session_key = null;

View File

@ -187,7 +187,8 @@ class ecobee extends external_api {
[]
);
if(count($ecobee_tokens) !== 1) {
throw new Exception('No token for this user');
$this->api('user', 'log_out', ['all' => true]);
throw new cora\exception('No ecobee access for this user.', 10501, false);
}
$ecobee_token = $ecobee_tokens[0];
}
@ -236,6 +237,15 @@ class ecobee extends external_api {
throw new Exception($response['status']['message']);
}
}
else if (isset($response['status']) === true && $response['status']['code'] === 16) {
// Token has been deauthorized by user. You must re-request authorization.
if($this::$log_mysql !== 'all') {
$this->log_mysql($curl_response);
}
$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);
}
else if (isset($response['status']) === true && $response['status']['code'] !== 0) {
// Any other error
if($this::$log_mysql !== 'all') {

View File

@ -68,7 +68,8 @@ class ecobee_token extends cora\crud {
$ecobee_tokens = $database->read(
'ecobee_token',
[
'user_id' => $this->session->get_user_id()
'user_id' => $this->session->get_user_id(),
'deleted' => false
]
);
if(count($ecobee_tokens) === 0) {
@ -94,6 +95,7 @@ class ecobee_token extends cora\crud {
isset($response['refresh_token']) === false
) {
$this->delete($ecobee_token['ecobee_token_id']);
$this->api('user', 'log_out', ['all' => true]);
$database->release_lock($lock_name);
throw new Exception('Could not refresh ecobee token; ecobee returned no token.', 10002);
}
@ -125,10 +127,7 @@ class ecobee_token extends cora\crud {
$database = cora\database::get_transactionless_instance();
// Need to delete the token before logging out or else the delete fails.
$return = $database->delete('ecobee_token', $id);
// Log out
$this->api('user', 'log_out', ['all' => true]);
$return = $database->delete($this->resource, $id);
return $return;
}

View File

@ -74,11 +74,11 @@ class patreon_token extends cora\crud {
$lock_name = 'patreon_token->refresh(' . $this->session->get_user_id() . ')';
$database->get_lock($lock_name, 3);
// $patreon_tokens = $this->read();
$patreon_tokens = $database->read(
'patreon_token',
[
'user_id' => $this->session->get_user_id()
'user_id' => $this->session->get_user_id(),
'deleted' => false
]
);
if(count($patreon_tokens) === 0) {
@ -130,7 +130,7 @@ class patreon_token extends cora\crud {
*/
public function delete($id) {
$database = cora\database::get_transactionless_instance();
$return = $database->delete('patreon_token', $id);
$return = $database->delete($this->resource, $id);
return $return;
}

View File

@ -144,7 +144,9 @@ class user extends cora\crud {
}
if($all === true) {
$database = cora\database::get_instance();
// 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',
[
@ -154,7 +156,7 @@ class user extends cora\crud {
);
$success = true;
foreach($sessions as $session) {
$success &= $this->session->delete($session['session_key']);
$success &= $database->delete('cora\session', $session['session_id']);
}
return $success;
}

View File

@ -46,10 +46,6 @@ beestat.api.prototype.send = function(opt_api_call) {
self.load_(this.responseText);
});
// var endpoint = (window.environment === 'live')
// ? 'https://api.beestat.io/'
// : 'http://' + window.environment + '.api.beestat.io/';
// this.xhr_.open('POST', endpoint + '?' + query_string);
this.xhr_.open('POST', 'api/?' + query_string);
this.xhr_.send();
} else {
@ -172,10 +168,12 @@ beestat.api.prototype.load_ = function(response_text) {
if (
response.data &&
(
response.data.error_code === 1004 || // Session is expired.
response.data.error_code === 10001 || // Could not get first token.
response.data.error_code === 10002 || // Could not refresh ecobee token; no token found.
response.data.error_code === 10003 // Could not refresh ecobee token; ecobee returned no token.
response.data.error_code === 1004 || // Session is expired.
response.data.error_code === 10000 || // Could not get first token.
response.data.error_code === 10001 || // Could not refresh ecobee token; no token found.
response.data.error_code === 10002 || // Could not refresh ecobee token; ecobee returned no token.
response.data.error_code === 10500 || // Ecobee access was revoked by user.
response.data.error_code === 10501 // No ecobee access for this user.
)
) {
window.location.href = '/';