diff --git a/docker-compose.yaml b/docker-compose.yaml index 04166aa..c75a446 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,13 +7,16 @@ services: volumes: - ./src:/var/www/html/ - ./vendor:/var/www/vendor/ - redis: + master: image: redis - -# (EXTENDED-HA) -# master: - -# slave: - -# sentinel: - + slave: + image: redis + command: redis-server --slaveof master 6379 + sentinel: + image: redis + command: + - redis-sentinel + - /dev/null + - --sentinel monitor chatapp master 6379 2 + - --sentinel down-after-milliseconds chatapp 5000 + - --sentinel config-epoch chatapp 0 diff --git a/src/init.php b/src/init.php index 8dcd708..0d7586d 100644 --- a/src/init.php +++ b/src/init.php @@ -1,36 +1,28 @@ 'sentinel', 'service' => 'chatapp'] +); -$redis = new Predis\Client([ - 'scheme' => 'tcp', - 'host' => 'redis', - 'port' => 6379, -]); - -function loadCurentUserId($authSecret) { - return 1; // EXTENDED TASK: delete this line to complete the extended task - +function verify_auth_secret($authSecret) { global $redis; // empty auth secret means the user is logged out - if ($authSecret == '') { - return null; - } - - // use the auth secret to get the user ID - // $userId = _____________ (EXTENDED TASK) - if ($userId) { - // cross check that this auth secret is also stored in the user hash - // $userAuthSecret = _____________ (EXTENDED TASK) - if ($userAuthSecret != $authSecret) { - return null; + if ($authSecret) { + if ($username = $redis->get($authSecret)) { + return $username; } - return $userId; + // invalid secret - wait few seconds to defense against brutal force attack + sleep(3); } - - // no user ID was found by the auth secret + // no user was found by the auth secret return null; } diff --git a/src/login.php b/src/login.php index e319f30..a736764 100644 --- a/src/login.php +++ b/src/login.php @@ -1,53 +1,41 @@ hexists('users', $username); -if ($userId) { - // user ID exists => continue with the login flow - // $realPassword = __________________ (EXTENDED TASK) - if ($password === $realPassword) { - doLogin($userId); +if ($isRegistered) { + // user exists => continue with the login flow + $hash = $redis->hget('users', $username); + if (password_verify($password, $hash)) { + doLogin($username); } else { http_response_code(401); echo 'This account already exists and entered password is incorrect!'; exit; } } else { - // user ID does not exist => continue with the register flow - // obtain new user ID - // $userId = _________________ (EXTENDED TASK) - // store this user account into a hash - // ________________________ (EXTENDED TASK) - // store the user ID into a hash - this is needed to lookup user IDs by usernames - // ________________________ (EXTENDED TASK) + // user does not exist => continue with the register flow + $hash = password_hash($password, PASSWORD_DEFAULT); // always store only a hash of the password - USE secure methods + $redis->hset('users', $username, $hash); // login the user - doLogin($userId); + doLogin($username); } -function doLogin($userId) { +function doLogin($username) { global $redis; // calculate random user secret - $rand = rand(0, PHP_INT_MAX) . $userId; - $authSecret = hash('sha256', $rand); - - // delete the old auth secret (in case it exists) - // ________________________ (EXTENDED TASK) - - // update the auth secret stored in the user hash - // ________________________ (EXTENDED TASK) + $authSecret = bin2hex(random_bytes(16)); // use cryptographically safe functions (rand() is too predictable) - // store the user ID into a hash - this is needed to lookup user IDs by user secrets - // ________________________ (EXTENDED TASK) + // save session with expiration + $redis->setex($authSecret, SESSION_TTL, $username); - setcookie("auth", $authSecret, time() + 3600 * 24 * 365); + setcookie("auth", $authSecret, time() + SESSION_TTL, '', '', false, true); // make sure that session ID is not readable by JS } diff --git a/src/sendMessage.php b/src/sendMessage.php index 9254db1..44fd151 100644 --- a/src/sendMessage.php +++ b/src/sendMessage.php @@ -1,22 +1,26 @@ time(), + 'text' => $_POST['text'], + 'username' => $username, +]; -// get the ID of the message -// $messageId = _______________ (BASIC TASK) +// this is much more convenient for list of messages, store whole message in the +// simple list - don't pollute Redis DB +$redis->lpush('messages', json_encode($message)); -// insert the message into its own hash -// _______________ (BASIC TASK) - -// push the message into the list of message IDs -// _______________ (BASIC TASK) +// this will make sure that list is capped on 10 messages (no DB overflow) +$redis->ltrim('messages', 0, MESSAGE_CAP - 1); diff --git a/src/showMessages.php b/src/showMessages.php index 338f694..1d8fce5 100644 --- a/src/showMessages.php +++ b/src/showMessages.php @@ -1,24 +1,22 @@