Add api key to user, add status update support, add other things i dont remember at 2am...

This commit is contained in:
osaajani 2020-01-08 02:14:38 +01:00
parent 193dd00c1e
commit fb6abb4d91
14 changed files with 323 additions and 8 deletions

View File

@ -36,6 +36,11 @@
* Does the implemented service support flash smss
*/
public static function meta_support_flash() : bool;
/**
* Does the implemented service support status change
*/
public static function meta_support_status_change() : bool;
/**
@ -61,4 +66,11 @@
* @return array : Array of the sms reads
*/
public function read () : array;
/**
* Method called on reception of a status update notification for a SMS
* @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms ('unknown', 'delivered', 'failed')]
*/
public static function status_change_callback ();
}

View File

@ -36,6 +36,11 @@
* Does the implemented service support flash smss
*/
public static function meta_support_flash() : bool { return false ; }
/**
* Does the implemented service support status change
*/
public static function meta_support_status_change() : bool { return true; }
/**
@ -82,4 +87,14 @@
{
return [];
}
/**
* Method called on reception of a status update notification for a SMS
* @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms ('unknown', 'delivered', 'failed')]
*/
public static function status_change_callback ()
{
return false;
}
}

View File

@ -36,6 +36,11 @@
* Does the implemented service support flash smss
*/
public static function meta_support_flash() : bool { return true ; }
/**
* Does the implemented service support status change
*/
public static function meta_support_status_change() : bool { return true; }
/**
@ -118,4 +123,42 @@
return $return;
}
/**
* Method called on reception of a status update notification for a SMS
* @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms ('unknown', 'delivered', 'failed')]
*/
public static function status_change_callback ()
{
$uid = $_GET['uid'] ?? false;
$status = $_GET['status'] ?? false;
if (!$uid || !$status)
{
return false;
}
$return = [
'uid' => $uid,
'status' => 'unknown',
];
switch ($status)
{
case 'delivered' :
$return['status'] = 'delivered';
break;
case 'failed' :
$return['status'] = 'failed';
break;
default :
$return['status'] = 'unknown';
break;
}
return $return;
}
}

View File

@ -100,6 +100,22 @@ namespace controllers\internals;
return (bool) $this->get_model()->update_for_user($id_user, $id_sended, $sended);
}
/**
* Update a sended status for a sended
* @param int $id_sended : Sended id
* @param string $status : Status of a the sms (unknown, delivered, failed)
* @return bool : false on error, true on success
*/
public function update_status (int $id_sended, string $status) : bool
{
$sended = [
'status' => $status,
];
return (bool) $this->get_model()->update($id_sended, $sended);
}
/**
@ -124,6 +140,18 @@ namespace controllers\internals;
{
return $this->get_model()->gets_by_destination_and_user($id_user, $origin);
}
/**
* Return sended for an uid and an adapter
* @param string $uid : Uid of the sended
* @param string $adapter : Adapter used to send the message
* @return array
*/
public function get_by_uid_and_adapter(string $uid, string $adapter)
{
return $this->get_model()->get_by_uid_and_adapter($uid, $adapter);
}
/**

View File

@ -117,6 +117,28 @@ namespace controllers\internals;
{
return (bool) $this->model_user->update_email($id, $email);
}
/**
* Update user api key.
*
* @param string $id : user id
* @param ?string $api_key : new api key
*
* @return mixed : false on error, else new api key;
*/
public function update_api_key($id, ?string $api_key = null)
{
$api_key = $api_key ?? $this->generate_random_api_key();
$success = $this->model_user->update($id, ['api_key' => $api_key]);
if (!$success)
{
return false;
}
return $api_key;
}
/**
* Get a user by his email address
@ -128,6 +150,18 @@ namespace controllers\internals;
{
return $this->model_user->get_by_email($email);
}
/**
* Get a user by his api_key address
* @param string $api_key : User api key
*
* @return mixed boolean | array : false if cannot find user for this api key, the user else
*/
public function get_by_api_key(string $api_key)
{
return $this->model_user->get_by_api_key($api_key);
}
/**
* Return users by transfer status.
@ -168,16 +202,18 @@ namespace controllers\internals;
* @param mixed $password
* @param mixed $admin
* @param mixed $transfer
* @param ?string $api_key : The api key of the user, if null generate randomly
*
* @return mixed bool|int : false on error, id of the new user else
*/
public function create($email, $password, $admin, $transfer = false)
public function create($email, $password, $admin, $transfer = false, ?string $api_key = null)
{
$user = [
'email' => $email,
'password' => password_hash($password, PASSWORD_DEFAULT),
'admin' => $admin,
'transfer' => $transfer,
'api_key' => $api_key ?? $this->generate_random_api_key(),
];
$new_user_id = $this->model_user->insert($user);
@ -197,4 +233,14 @@ namespace controllers\internals;
return $new_user_id;
}
/**
* Generate a random api key
* @return string : The api key
*/
public function generate_random_api_key () : string
{
return bin2hex(random_bytes(16));
}
}

View File

@ -155,6 +155,37 @@ namespace controllers\publics;
return $this->redirect(\descartes\Router::url('Account', 'show'));
}
/**
* Update user api key.
*
* @param $csrf : Le jeton CSRF
*/
public function update_api_key($csrf)
{
if (!$this->verify_csrf($csrf))
{
\FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
return $this->redirect(\descartes\Router::url('Account', 'show'));
}
$new_api_key = $this->internal_user->update_api_key($_SESSION['user']['id']);
if (!$new_api_key)
{
\FlashMessage\FlashMessage::push('danger', 'Impossible de mettre à jour.');
return $this->redirect(\descartes\Router::url('Account', 'show'));
}
$_SESSION['user']['api_key'] = $new_api_key;
\FlashMessage\FlashMessage::push('success', 'Votre ancienne clef API a été désactivée et une nouvelle clef générée.');
return $this->redirect(\descartes\Router::url('Account', 'show'));
}
/**
* Delete a user.

View File

@ -0,0 +1,87 @@
<?php
/*
* This file is part of RaspiSMS.
*
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
*
* This source file is subject to the GPL-3.0 license that is bundled
* with this source code in the file LICENSE.
*/
namespace controllers\publics;
/**
* Controller of callback pages, like sms status update notification
*/
class Callback extends \descartes\Controller
{
private $internal_user;
private $internal_sended;
private $internal_adapter;
public function __construct()
{
$bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
$this->internal_user = new \controllers\internals\User($bdd);
$this->internal_sended = new \controllers\internals\Sended($bdd);
$this->internal_adapter = new \controllers\internals\Adapter();
}
/**
* Function call on a sended sms status change notification reception
* @param string $adapter_name : Name of the adapter to use
* @return false : We must always return false, and we respect a random usleep before returning anything
* in order to prevent bruteforce api key guessing and time guessing
*/
public function update_sended_status (string $adapter_name)
{
//Wait between 0.5 and 1.03s in order to counter time guessing bruteforce attack against api key
usleep(mt_rand(5,10) / 10 * 1000000 + mt_rand(0, 30000));
//Search for an adapter
$find_adapter = false;
$adapters = $this->internal_adapter->list_adapters();
foreach ($adapters as $adapter)
{
if (mb_strtolower($adapter['meta_name']) === mb_strtolower($adapter_name))
{
$find_adapter = $adapter;
}
}
if (false === $find_adapter)
{
return false;
}
//Instanciate adapter, check if status change is supported and if so call status change callback
$adapter_classname = $find_adapter['meta_classname'];
if (!$find_adapter['meta_support_status_change'])
{
return false;
}
$callback_return = $adapter_classname::status_change_callback();
var_dump($callback_return);
if (!$callback_return)
{
return false;
}
$sended = $this->internal_sended->get_by_uid_and_adapter($callback_return['uid'], $adapter_classname);
if (!$sended)
{
return false;
}
$this->internal_sended->update_status($sended['id'], $callback_return['status']);
return false;
}
}

View File

@ -91,6 +91,8 @@ class Phone extends AbstractDaemon
continue;
}
$internal_sended = new \controllers\internals\Sended($this->bdd);
//Update last message time
$this->last_message_at = microtime(true);
@ -116,7 +118,6 @@ class Phone extends AbstractDaemon
$this->logger->info('Successfully send message : ' . json_encode($message));
$internal_sended = new \controllers\internals\Sended($this->bdd);
$internal_sended->create($at, $message['text'], $message['origin'], $message['destination'], $sended_sms_uid, $this->phone['adapter'], $message['flash']);
}
}
@ -154,8 +155,8 @@ class Phone extends AbstractDaemon
$internal_received->create($sms['at'], $sms['text'], $sms['origin'], $sms['destination'], 'unread', $is_command);
}
}
/**
* Process a sms to find if its a command and so execute it
* @param array $sms : The sms
@ -226,7 +227,8 @@ class Phone extends AbstractDaemon
$this->webhook_queue = msg_get_queue(QUEUE_ID_WEBHOOK);
//Instanciate adapter
$this->adapter = new \adapters\TestAdapter($this->phone['number'], $this->phone['adapter_datas']);
$adapter_class = $this->phone['adapter'];
$this->adapter = new $adapter_class($this->phone['number'], $this->phone['adapter_datas']);
$this->logger->info("Starting Phone daemon with pid " . getmypid());
}

View File

@ -1 +0,0 @@

View File

@ -241,7 +241,18 @@ namespace models;
return $this->_run_query($query, $params);
}
/**
* Return sended for an uid and an adapter
* @param string $uid : Uid of the sended
* @param string $adapter : Adapter used to send the message
* @return array
*/
public function get_by_uid_and_adapter(string $uid, string $adapter)
{
return $this->_select_one('sended', ['uid' => $uid, 'adapter' => $adapter]);
}
/**
* Get number of sended SMS for every date since a date for a specific user

View File

@ -158,6 +158,19 @@ namespace models;
{
return $this->_update($this->get_table_name(), $entry, ['id_user' => $id_user, 'id' => $id]);
}
/**
* Update a entry by his id
* @param int $id : Entry id
* @param array $datas : datas to update
*
* @return int : number of modified rows
*/
public function update(int $id, array $entry)
{
return $this->_update($this->get_table_name(), $entry, ['id' => $id]);
}
/**

View File

@ -25,6 +25,18 @@ namespace models;
{
return $this->_select_one('user', ['email' => $email]);
}
/**
* Get a user by his api_key address
* @param string $api_key : User api key
*
* @return mixed boolean | array : false if cannot find user for this api key, the user else
*/
public function get_by_api_key(string $api_key)
{
return $this->_select_one('user', ['api_key' => $api_key]);
}
/**

View File

@ -18,6 +18,7 @@
'update_password' => '/account/update_password/{csrf}/',
'update_transfer' => '/account/update_transfer/{csrf}/',
'update_email' => '/account/update_email/{csrf}/',
'update_api_key' => '/account/update_api_key/{csrf}/',
'delete' => '/account/delete/{csrf}/',
'logout' => '/logout/',
],
@ -178,6 +179,10 @@
'edit' => '/webhook/edit/',
'update' => '/webhook/update/{csrf}/',
],
'Callback' => [
'update_sended_status' => '/callback/status/{adapter_name}/',
],
);
define('ROUTES', $routes);

View File

@ -42,11 +42,12 @@
<div class="panel-body">
<strong>Adresse e-mail :</strong> <?php $this->s($_SESSION['user']['email']); ?><br/>
<strong>Niveau administrateur :</strong> <?php echo $_SESSION['user']['admin'] ? 'Oui' : 'Non'; ?><br/>
<strong>Clef API :</strong> <?php echo $_SESSION['user']['api_key']; ?><br/>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><i class="fa fa-key fa-fw"></i> Modifier mot de passe</h4>
<h4 class="panel-title"><i class="fa fa-lock fa-fw"></i> Modifier mot de passe</h4>
</div>
<div class="panel-body">
<form action="<?php echo \descartes\Router::url('Account', 'update_password', ['csrf' => $_SESSION['csrf']]); ?>" method="POST">
@ -95,6 +96,16 @@
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><i class="fa fa-key fa-fw"></i> Modifier clef API</h4>
</div>
<div class="panel-body">
<div class="text-center">
<a class="btn btn-success" href="<?= \descartes\Router::url('Account', 'update_api_key', ['csrf' => $_SESSION['csrf']]); ?>"><i class="fa fa-refresh"></i> Générer une nouvelle clef API</a>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><i class="fa fa-share fa-fw"></i> Transfert des SMS par e-mail</h4>