From 498e00b7d27e09f48e4eab56a6ce12922dbcf4f6 Mon Sep 17 00:00:00 2001 From: Gergely POLONKAI Date: Sat, 6 Oct 2012 23:17:20 +0200 Subject: [PATCH] Created File* backends --- composer.lock | 6 +- json_sms.php | 22 +++-- src/SmsGateway/Auth/DatabaseAuth.php | 3 +- src/SmsGateway/Auth/FileAuth.php | 142 +++++++++++++++++++++++++++ src/SmsGateway/AuthInterface.php | 4 + src/SmsGateway/Logger/FileLogger.php | 130 ++++++++++++++++++++++++ src/SmsGateway/LoggerInterface.php | 4 +- src/SmsGateway/RpcServer.php | 67 ++++++++++--- src/SmsGateway/Sender/FileSender.php | 74 ++++++++++++++ src/SmsGateway/SenderInterface.php | 7 +- 10 files changed, 431 insertions(+), 28 deletions(-) create mode 100644 src/SmsGateway/Auth/FileAuth.php create mode 100644 src/SmsGateway/Logger/FileLogger.php create mode 100644 src/SmsGateway/Sender/FileSender.php diff --git a/composer.lock b/composer.lock index b957b06..1920401 100644 --- a/composer.lock +++ b/composer.lock @@ -22,7 +22,7 @@ "require-dev": { "symfony/finder": "2.1.*" }, - "time": "2012-09-01 01:02:36", + "time": "2012-09-01 07:02:36", "type": "library", "extra": { "branch-alias": { @@ -69,7 +69,7 @@ "require": { "php": ">=5.3.3" }, - "time": "2012-09-18 12:09:52", + "time": "2012-09-18 16:09:52", "type": "library", "extra": { "branch-alias": { @@ -128,7 +128,7 @@ "symfony/yaml": "2.1.*", "doctrine/common": ">=2.2,<2.4-dev" }, - "time": "2012-09-10 08:53:42", + "time": "2012-09-10 10:53:42", "type": "library", "extra": { "branch-alias": { diff --git a/json_sms.php b/json_sms.php index 6d31acc..739523d 100644 --- a/json_sms.php +++ b/json_sms.php @@ -10,9 +10,9 @@ use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\MethodNotAllowedException; -use SmsGateway\Sender\GnokiiSender; -use SmsGateway\Logger\DatabaseLogger; -use SmsGateway\Backend\DatabaseBackend; +use SmsGateway\Sender\FileSender; +use SmsGateway\Logger\FileLogger; +use SmsGateway\Auth\FileAuth; use SmsGateway\RpcServer; $request = Request::createFromGlobals(); @@ -56,7 +56,7 @@ if (!array_key_exists('method', $jsonData) || !array_key_exists('params', $jsonD $wantResponse = (!empty($jsonData['id'])); try { - $sender = new GnokiiSender(); + $sender = new FileSender('/var/www/html/smsgateway/app/cache/sms_spool'); } catch (Exception $e) { $response = new Response('Internal Server Error: Sender cannot be instantiated.', 500); $response->send(); @@ -64,22 +64,26 @@ try { } try { - $logger = new DatabaseLogger('pgsql:host=127.0.0.1;dbname=smsgateway', 'smsgateway', 'Imuiwai8'); -} catch (PDOException $e) { + $logger = new FileLogger('/var/www/html/smsgateway/app/logs/sms-message.log', '/var/www/html/smsgateway/app/logs/sms-audit.log'); +} catch (LogicException $e) { $response = new Response('Internal Server Error: Logger cannot be instantiated.', 500); $response->send(); exit; } try { - $backend = new DatabaseBackend(); + $auth = new FileAuth('/var/www/html/smsgateway/senders', '/var/www/html/smsgateway/app/cache/tokens'); } catch (Exception $e) { - $response = new Response('Internal Server Error: Backend cannot be instantiated.', 500); + $response = new Response('Internal Server Error: Authenticator cannot be instantiated.', 500); $response->send(); exit; } -$handler = new RpcServer($backend, $logger, $sender); +$auth->setLogger($logger); +$sender->setLogger($logger); + +$handler = new RpcServer($auth, $logger, $sender); + try { $result = $handler->handle($request, $jsonData); } catch (Exception $e) { diff --git a/src/SmsGateway/Auth/DatabaseAuth.php b/src/SmsGateway/Auth/DatabaseAuth.php index c03ec50..d445834 100644 --- a/src/SmsGateway/Auth/DatabaseAuth.php +++ b/src/SmsGateway/Auth/DatabaseAuth.php @@ -1,7 +1,8 @@ sendersFile = $sendersFile; + $this->tokenFile = $tokenFile; + } + + public function authenticate($username, $password, $ip, $sessionId) + { + $this->logger->auditLog(LoggerInterface::LOG_AUDIT_LOGIN, $username, "trying to authenticate"); + $lines = file($this->sendersFile); + foreach ($lines as $line) { + list($user, $cPassword) = explode(':', trim($line), 2); + if ($user == $username) { + if (crypt($password, $cPassword) == $cPassword) { + $this->logger->auditLog(LoggerInterface::LOG_AUDIT_LOGIN, $username, "authenticated successfully"); + + return $this->getToken($username, $ip, $sessionId); + } else { + $this->logger->auditLog(LoggerInterface::LOG_AUDIT_LOGIN, $username, "authentication failed: bad password"); + return false; + } + } + } + return false; + } + + public function getTokenUsername($token, $ip, $sessionId) + { + $lines = file($this->tokenFile); + foreach ($lines as $line) { + list($tokenUser, $tokenIp, $tokenSession, $tokenToken) = explode(':', trim($line), 4); + if (($tokenToken == $token) && ($tokenIp == $ip) && ($tokenSession == $sessionId)) { + return $tokenUser; + } + } + + return null; + } + + public function isTokenValid($token, $ip, $sessionId) + { + $this->logger->auditLog(LoggerInterface::LOG_AUDIT_LOGIN, null, 'Checking token validity'); + + $lines = file($this->tokenFile); + foreach ($lines as $line) { + list($tokenUser, $tokenIp, $tokenSession, $tokenToken) = explode(':', trim($line), 4); + if (($tokenToken == $token) && ($tokenIp == $ip) && ($tokenSession == $sessionId)) { + return true; + } + } + + return false; + } + + public function getToken($username, $ip, $sessionId) { + $this->logger->auditLog(LoggerInterface::LOG_AUDIT_LOGIN, $username, "Getting token"); + + $lines = file($this->tokenFile); + foreach ($lines as $line) { + list($tokenUser, $tokenIp, $tokenSession, $tokenToken) = explode(':', trim($line), 4); + if (($tokenUser == $username) && ($tokenIp == $ip) && ($tokenSession == $sessionId)) { + return $tokenToken; + } + } + + $token = str_replace(':', '', uniqid('', true)); + $fd = fopen($this->tokenFile, 'a'); + fwrite($fd, sprintf("%s:%s:%s:%s\n", $username, $ip, $sessionId, $token)); + fclose($fd); + + return $token; + } + + public function removeToken($token, $ip, $sessionId) { + $username = $this->getTokenUsername($token, $ip, $sessionId); + $this->logger->auditLog(LoggerInterface::LOG_AUDIT_LOGIN, $username, "Removing token"); + + $lines = file($this->tokenFile); + $fd = fopen($this->tokenFile, 'w'); + foreach ($lines as $line) { + list($tokenUser, $tokenIp, $tokenSession, $tokenToken) = explode(':', trim($line), 4); + if (($tokenToken != $token) || ($tokenIp != $ip) || ($tokenSession != $sessionId)) { + fwrite($fd, sprintf("%s:%s:%s:%s\n", $tokenUser, $tokenIp, $tokenSession, $tokenToken)); + } + } + fclose($fd); + + return false; + return true; + } + + public function getLogger() { + return $this->logger; + } + + public function setLogger(LoggerInterface $logger) { + $this->logger = $logger; + } +} diff --git a/src/SmsGateway/AuthInterface.php b/src/SmsGateway/AuthInterface.php index 44e860c..d50de0f 100644 --- a/src/SmsGateway/AuthInterface.php +++ b/src/SmsGateway/AuthInterface.php @@ -9,9 +9,13 @@ interface AuthInterface public function getLogger(); + public function authenticate($username, $password, $ip, $sessionId); + public function getToken($username, $ip, $sessionId); public function isTokenValid($token, $ip, $sessionId); public function removeToken($token, $ip, $sessionId); + + public function getTokenUsername($token, $ip, $sessionId); } diff --git a/src/SmsGateway/Logger/FileLogger.php b/src/SmsGateway/Logger/FileLogger.php new file mode 100644 index 0000000..1335e8d --- /dev/null +++ b/src/SmsGateway/Logger/FileLogger.php @@ -0,0 +1,130 @@ +messageLogHandle = fopen($messageLogFile, 'a')) === false) { + throw new \LogicException('Message log file could not be opened!'); + } + + if (($this->auditLogHandle = fopen($auditLogFile, 'a')) === false) { + throw new \LogicException('Audit log file could not be opened!'); + } + } + + public function __destruct() { + fclose($this->messageLogHandle); + fclose($this->auditLogHandle); + } + + private function orderPasswordLocations($a, $b) + { + if ($a[0] == $b[0]) { + return 0; + } elseif ($a[0] < $b[0]) { + return -1; + } + return 1; + } + + public function messageLog($username, $recipient, $message, array $passwordLocations) + { + usort($passwordLocations, array($this, 'orderPasswordLocations')); + + $encodedMessage = $message; + $mod = 0; + foreach ($passwordLocations as $loc) { + list($pos, $length) = $loc; + + $encodedMessage = substr_replace($encodedMessage, self::PASSWORD_MASK, $pos + $mod, $length); + $mod += (strlen(self::PASSWORD_MASK) - $length); + } + + $logMessage = "From $username +From: $username +To: $recipient + +$encodedMessage\n\n"; + + fwrite($this->messageLogHandle, $logMessage); + fflush($this->messageLogHandle); + return true; + } + + public function auditLog($type, $username, $message) + { + if ($username === null) { + $logMessage = "$message\n"; + } else { + $logMessage = "$username: $message\n"; + } + fwrite($this->auditLogHandle, $logMessage); + fflush($this->auditLogHandle); + return true; + } +} diff --git a/src/SmsGateway/LoggerInterface.php b/src/SmsGateway/LoggerInterface.php index 762cf71..0fd1aeb 100644 --- a/src/SmsGateway/LoggerInterface.php +++ b/src/SmsGateway/LoggerInterface.php @@ -3,7 +3,7 @@ namespace SmsGateway; interface LoggerInterface { - const SMSSENDER_AUDIT_LOGIN = 1; + const LOG_AUDIT_LOGIN = 1; /** * Log an audit event @@ -23,5 +23,5 @@ interface LoggerInterface * @param string $recipient The recipient of the message * @param string $message The message itself */ - public function messageLog($username, $recipient, $message); + public function messageLog($username, $recipient, $message, array $passwordLocations); } \ No newline at end of file diff --git a/src/SmsGateway/RpcServer.php b/src/SmsGateway/RpcServer.php index 7e5232d..148f63a 100644 --- a/src/SmsGateway/RpcServer.php +++ b/src/SmsGateway/RpcServer.php @@ -3,7 +3,7 @@ namespace SmsGateway; use Symfony\Component\HttpFoundation\Request; -use SmsGateway\BackendInterface; +use SmsGateway\AuthInterface; use SmsGateway\LoggerInterface; use SmsGateway\SenderInterface; @@ -12,9 +12,9 @@ class RpcServer /** * The user backend * - * @var SmsGateway\BackendInterface $backend + * @var SmsGateway\AuthInterface $backend */ - private $backend; + private $auth; /** * The logger @@ -30,23 +30,46 @@ class RpcServer */ private $sender; - public function __construct(BackendInterface $backend, LoggerInterface $logger, SenderInterface $sender) + public function __construct(AuthInterface $auth, LoggerInterface $logger, SenderInterface $sender) { + $this->auth = $auth; + $this->logger = $logger; + $this->sender = $sender; } - protected function login(array $params) + protected function login($username, $password) { - return true; + $token = $this->auth->authenticate($username, $password, $_SERVER['REMOTE_ADDR'], null); + + if ($token === false) { + throw new \Exception('Could not create token.'); + } + + return $token; } - protected function send(array $params) + protected function send($token, $recipient, $message, array $passwordLocations) { - return true; + if (!$this->auth->isTokenValid($token, $_SERVER['REMOTE_ADDR'], null)) { + throw new \Exception('Invalid token!'); + } + + $this->sender->send($this->auth->getTokenUsername($token, $_SERVER['REMOTE_ADDR'], null), $recipient, $message, $passwordLocations); + + // TODO: Send the message! + + return 'success'; } - protected function logout(array $params) + protected function logout($token) { - return true; + if (!$this->auth->isTokenValid($token, $_SERVER['REMOTE_ADDR'], null)) { + throw new \Exception('Invalid token!'); + } + + $this->auth->removeToken($token, $_SERVER['REMOTE_ADDR'], null); + + return 'success'; } public function handle(Request $request, array $jsonData) @@ -54,14 +77,34 @@ class RpcServer $params = $jsonData['params']; switch ($jsonData['method']) { case 'login': + if (count($params) != 2) { + throw new \InvalidArgumentException('Bad parameter count!'); + } + + return $this->login($params[0], $params[1]); + break; case 'send': + if (count($params) != 4) { + throw new \InvalidArgumentException('Bad parameter count!'); + } + if (!is_array($params[3])) { + throw new \InvalidArgumentException('Invalid 4th parameter!'); + } + + return $this->send($params[0], $params[1], $params[2], $params[3]); + break; case 'logout': + if (count($params) != 1) { + throw new \InvalidArgumentException('Bad parameter count!'); + } + + return $this->logout($params[0]); break; default: - throw new \Exception('Invalid request'); + throw new \BadMethodCallException('Unknown method ' . $jsonData['method']); + break; } - return 'ajaj'; } } \ No newline at end of file diff --git a/src/SmsGateway/Sender/FileSender.php b/src/SmsGateway/Sender/FileSender.php new file mode 100644 index 0000000..3c6f96a --- /dev/null +++ b/src/SmsGateway/Sender/FileSender.php @@ -0,0 +1,74 @@ +messageDir = $messageDir; + } + + public function setLogger(LoggerInterface $logger) { + if ($logger === null) { + throw new \InvalidArgumentException('A logger must be passed to the authenticator!'); + } + + $this->logger = $logger; + } + + public function getLogger() { + return $this->logger; + } + + public function send($username, $recipient, $message, $passwordLocations) + { + $rcptDir = $this->messageDir . '/' . $recipient; + + if (file_exists($rcptDir) && (!is_writable($rcptDir) || !is_dir($rcptDir))) { + throw new \RuntimeException('Message directory is not writable!'); + } + if (!file_exists($rcptDir)) { + mkdir($rcptDir); + } + + $messageFileName = date('YmdHis') . '-' . uniqid() . '.sms'; + $fd = fopen($rcptDir . '/' . $messageFileName, 'w'); + fwrite($fd, $message); + fclose($fd); + + $this->logger->messageLog($username, $recipient, $message, $passwordLocations); + + return true; + } +} diff --git a/src/SmsGateway/SenderInterface.php b/src/SmsGateway/SenderInterface.php index df4bf3f..f595a1e 100644 --- a/src/SmsGateway/SenderInterface.php +++ b/src/SmsGateway/SenderInterface.php @@ -3,6 +3,10 @@ namespace SmsGateway; interface SenderInterface { + public function setLogger(LoggerInterface $logger); + + public function getLogger(); + /** * * @param string $recipient @@ -10,5 +14,6 @@ interface SenderInterface * @return boolean true upon success. On error, throws exceptions. * @throws Exception Upon sending error. Gnokii output will be * stored in $e->message - public function send($recipient, $message); + */ + public function send($username, $recipient, $message, $passwordLocations); }