public function index() { $request = RequestWrapper::$request; $target = $request->get('player'); $target_id = $request->get('player_id'); if ($target_id) { $target_player_obj = Player::find($target_id); } else { $target_player_obj = Player::findByName($target); } if ($target_player_obj === null) { $template = 'no-player.tpl'; $viewed_name_for_title = null; $parts = array(); } else { $attack_error = 'You must become a ninja first.'; $clan = Clan::findByMember($target_player_obj); $combat_skills = null; $display_clan_options = false; $items = null; $same_clan = false; $self = false; $targeted_skills = null; $template = 'player.tpl'; $viewed_name_for_title = $target_player_obj->name(); $viewing_player_obj = Player::find(SessionFactory::getSession()->get('player_id')); $kills_today = query_item('SELECT sum(killpoints) FROM levelling_log WHERE _player_id = :player_id AND killsdate = CURRENT_DATE AND killpoints > 0', [':player_id' => $target_player_obj->id()]); $rank_spot = query_item('SELECT rank_id FROM rankings WHERE player_id = :player_id limit 1', [':player_id' => $target_player_obj->id()]); if ($viewing_player_obj !== null) { $viewers_clan = Clan::findByMember($viewing_player_obj); $self = $viewing_player_obj->id() === $target_player_obj->id(); $params = ['required_turns' => 0, 'ignores_stealth' => true]; $AttackLegal = new AttackLegal($viewing_player_obj, $target_player_obj, $params); $AttackLegal->check(false); $attack_error = $AttackLegal->getError(); if (!$attack_error && !$self) { // They're not dead or otherwise unattackable. // Pull the items and some necessary data about them. $inventory = new Inventory($viewing_player_obj); $items = $inventory->counts(); $skillDAO = new SkillDAO(); if (!$viewing_player_obj->isAdmin()) { $combat_skills = $skillDAO->getSkillsByTypeAndClass($viewing_player_obj->_class_id, 'combat', $viewing_player_obj->level); $targeted_skills = $skillDAO->getSkillsByTypeAndClass($viewing_player_obj->_class_id, 'targeted', $viewing_player_obj->level); } else { $combat_skills = $skillDAO->all('combat'); $targeted_skills = $skillDAO->all('targeted'); } } if ($clan && $viewers_clan) { $same_clan = $clan->id == $viewers_clan->id; $display_clan_options = !$self && $same_clan && $viewing_player_obj->isClanLeader(); } } $parts = ['viewing_player_obj' => $viewing_player_obj, 'target_player_obj' => $target_player_obj, 'combat_skills' => $combat_skills, 'targeted_skills' => $targeted_skills, 'self' => $self, 'rank_spot' => $rank_spot, 'kills_today' => $kills_today, 'status_list' => Player::getStatusList($target_player_obj->id()), 'clan' => $clan, 'items' => $items, 'account' => Account::findByChar($target_player_obj), 'same_clan' => $same_clan, 'display_clan_options' => $display_clan_options, 'attack_error' => $attack_error]; } $parts['authenticated'] = SessionFactory::getSession()->get('authenticated', false); $title = 'Ninja' . ($viewed_name_for_title ? ": {$viewed_name_for_title}" : ' Profile'); return new StreamedViewResponse($title, $template, $parts, ['quickstat' => 'player']); }
/** * Get the account object by id, or false * * @param int $id * @return Account|null */ public static function findById($id) { $account = new Account($id); if (!$account->getIdentity()) { return null; } else { return $account; } }
public function testFindPlayableFromInitialChar() { $pc = Player::find($this->char_id); $acc = Account::findByChar($pc); $pc2 = Player::findPlayable($acc->id()); $this->assertEquals($pc->id(), $pc2->id()); }
/** * Send a reset link to a given user. * * @return Response * @TODO: Authenticate the csrf, which must match, from the session. */ public function postEmail(Container $p_dependencies) { $request = RequestWrapper::$request; $error = null; $message = null; $account = null; $email = $request->get('email'); $ninja_name = $request->get('ninja_name'); if (!$email && !$ninja_name) { $error = 'You must specify either an email or a ninja name!'; } else { if ($email) { $account = Account::findByEmail($email); } if (!isset($account)) { $account = Account::findByNinjaName($ninja_name); } if ($account === null || !$account->id()) { $error = 'Sorry, unable to find a matching account!'; } else { // PWR created with default nonce $request = PasswordResetRequest::generate($account); if ($this->sendEmail($request->nonce, $account)) { $message = 'Your reset email was sent!'; } else { $error = 'Sorry, there was a problem sending to your account! Please contact support.'; } } } return new RedirectResponse('/password/?' . ($message ? 'message=' . rawurlencode($message) . '&' : '') . ($error ? 'error=' . rawurlencode($error) : '')); }
/** * Create a mock login, with real created account and character */ public function login() { SessionFactory::init(new MockArraySessionStorage()); $this->char = TestAccountCreateAndDestroy::char(); SessionFactory::getSession()->set('authenticated', true); $this->account = Account::findByChar($this->char); SessionFactory::getSession()->set('account_id', $this->account->id()); }
public static function canKill($clone1, $clone2) { // Input is transformed into if (!$clone1 instanceof Player) { if ($clone1 == Filter::toNonNegativeInt($clone1)) { $char1 = Player::find($clone1); } elseif (is_string($clone1)) { $char1 = Player::find($clone1); } } else { $char1 = $clone1; } if (!$clone2 instanceof Player) { if ($clone2 == Filter::toNonNegativeInt($clone2)) { $char2 = Player::find($clone2); } elseif (is_string($clone2)) { $char2 = Player::find($clone2); } } else { $char2 = $clone2; } // Reject invalid/nonexistent characters if ($char1 === null || $char2 === null) { return false; } // Reject same character if ($char1->id() == $char2->id()) { return false; } // Don't clone kill admins. if ($char1->isAdmin() || $char2->isAdmin()) { return false; } // Reject inactive characters if (!$char1->isActive() || !$char2->isActive()) { return false; } // TODO: Reject inoperative characters // TODO: You can't clone kill yourself.. $host = gethostname(); $server_ip = gethostbyname($host); $untouchable_ips = ['127.0.0.1', '173.203.99.229', $server_ip, '', null]; $account1 = Account::findByChar($char1); $account2 = Account::findByChar($char2); // Reject invalid custom ips if (in_array($account1->getLastIp(), $untouchable_ips) || in_array($account2->getLastIp(), $untouchable_ips)) { return false; } // If characters have the same joint account, and have been logged in recently... if ($account1->getLastIp() === $account2->getLastIp()) { // Activity was already tested above. return true; } return false; }
public function setUp() { SessionFactory::init(new MockArraySessionStorage()); $char_id = TestAccountCreateAndDestroy::char_id(); $char = Player::find($char_id); $account = Account::findByChar($char); $account_id = $account->id(); SessionFactory::getSession()->set('authenticated', true); SessionFactory::getSession()->set('player_id', $char_id); SessionFactory::getSession()->set('account_id', $account_id); }
public function testCantCloneKillSimilarCharactersEvenIfBothHaveIpOf127001() { $previous = $_SERVER['REMOTE_ADDR']; $_SERVER['REMOTE_ADDR'] = '127.0.0.11'; $char_id = TestAccountCreateAndDestroy::char_id(); $char_id_2 = TestAccountCreateAndDestroy::char_id_2(); $_SERVER['REMOTE_ADDR'] = $previous; $this->syncIps('127.0.0.1', $char_id, $char_id_2); // Must be 127.0.0.1 for this test. $p1 = Player::find($char_id); $p2 = Player::find($char_id_2); $account1 = Account::findByChar($p1); $account2 = Account::findByChar($p2); $this->assertEquals('127.0.0.1', $account1->getLastIp()); $this->assertEquals('127.0.0.1', $account2->getLastIp()); $this->assertFalse(CloneKill::canKill($char_id, $char_id_2)); }
/** * Display the main admin area * * Includes player viewing, account duplicates checking, npc balacing * * @return Response */ public function index() { $request = RequestWrapper::$request; if (!$this->self || !$this->self->isAdmin()) { return new RedirectResponse(WEB_ROOT); } $error = null; $char_infos = null; $char_inventory = null; $first_message = null; $first_char = null; $first_account = null; $first_description = null; $dupes = AdminViews::dupedIps(); $stats = AdminViews::highRollers(); $npcs = NpcFactory::allNonTrivialNpcs(); $trivial_npcs = NpcFactory::allTrivialNpcs(); $char_ids = preg_split("/[,\\s]+/", $request->get('view')); $char_name = trim($request->get('char_name')); if ($char_name) { // View a target non-self character $first_char = Player::findByName($char_name); if (null !== $first_char && null !== $first_char->id()) { $char_ids = [$first_char->id()]; } } if (is_array($char_ids)) { // Get a different first character if an array is specified $first_char = $first_char ? $first_char : Player::find(reset($char_ids)); if ($first_char) { assert($first_char instanceof Player); $first_account = Account::findByChar($first_char); $char_inventory = AdminViews::charInventory($first_char); $first_message = $first_char->messages; $first_description = $first_char->description; } // All the rest multi-character table view try { $char_infos = AdminViews::charInfos($char_ids); } catch (InvalidArgumentException $e) { $error = $e->getMessage(); } } $parts = ['error' => $error, 'stats' => $stats, 'first_char' => $first_char, 'first_description' => $first_description, 'first_message' => $first_message, 'first_account' => $first_account, 'char_infos' => $char_infos, 'dupes' => $dupes, 'char_inventory' => $char_inventory, 'char_name' => $char_name, 'npcs' => $npcs, 'trivial_npcs' => $trivial_npcs]; return new StreamedViewResponse('Admin Actions', 'ninjamaster.tpl', $parts); }
/** * Return true on matching ip characteristics. * * @todo cleanup the allowable IP addresses logic * @return boolean */ public function sameDomain(Player $target, Player $self) { // Get all the various ips that shouldn't be matches, and prevent them from being a problem. $server_addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : null; $host = gethostname(); $active_ip = gethostbyname($host); $allowable = array_merge(['127.0.0.1', $server_addr, $active_ip], Constants::$trusted_proxies); $self_account = Account::findByChar($self); $target_account = Account::findByChar($target); $self_ip = $self_account->getLastIp(); if (!$self_ip || in_array($self_ip, $allowable)) { return false; // Don't have to obtain the target's ip at all if these are the case! } else { return $self_ip === $target_account->getLastIp(); } }
public function testAccountCanHavePlayers() { $account = Account::findByNinjaName($this->test_ninja_name); $pcs = $account->getCharacters(); $this->assertNotEmpty($pcs); $this->assertInstanceOf(Player::class, reset($pcs)); }
/** * Generate a full password reset request for an account * * @param Account $account * @return PasswordResetRequest */ public static function generate(Account $account, $nonce = null) { $nonce = $nonce !== null ? $nonce : Crypto::nonce(); return self::create(['_account_id' => $account->id(), 'nonce' => $nonce]); }
/** * Convenience wrapper for the above, but confirms the account and returns the account id. */ public static function create_complete_test_account_and_return_id() { $player_mock = new Player(); $player_mock->player_id = TestAccountCreateAndDestroy::create_testing_account(true); $account = Account::findByChar($player_mock); return $account->id(); }
/** * Generate a full password reset request for an account * * @param Account $account * @return PasswordResetRequest */ public static function generate(Account $account, $nonce = null) { $nonce = $nonce !== null ? $nonce : nonce(); return PasswordResetRequest::create(['_account_id' => $account->id(), 'nonce' => $nonce]); }
public function testPerformingAResetInvalidatesUsedRequest() { $account_id = TestAccountCreateAndDestroy::account_id(); $account = Account::findById($account_id); PasswordResetRequest::generate($account, $this->nonce = '77warkwark', false); PasswordResetRequest::reset($account, 'new_pass34532'); $req = PasswordResetRequest::match($this->nonce); $this->assertEmpty($req); // Request shouldn't match because it should already be used. }
/** * Just do a check whether the input username and password is valid * * @return boolean */ public static function is_authentic($p_user, $p_pass) { $account = Account::findByLogin($p_user); return $account && $account->authenticate($p_pass); }
/** * Try to store a posted post, and redirect on successes or errors. * * @return RedirectResponse */ public function store() { $request = RequestWrapper::$request; try { $this->hasCreateRole($this->pc); $account = $this->pc ? Account::findByChar($this->pc) : null; $account_id = $account->id(); } catch (InvalidArgumentException $e) { $error = "Sorry, you must be logged in to try to save a news post."; return new RedirectResponse('/news/?error=' . rawurlencode($error)); } catch (ErrorException $e) { $error = "Sorry, you don't have permission to save a news post."; return new RedirectResponse('/news/?error=' . rawurlencode($error)); } // Handle POST $news_title = $request->get('news_title'); $news_content = $request->get('news_content'); $tag = $request->get('tag'); // Create new post if (!empty($news_content)) { try { // News Model $news = new News(); $news->createPost($news_title, $news_content, $account_id, $tag); return new RedirectResponse('/news/?create_successful=1'); } catch (InvalidArgumentException $e) { return new RedirectResponse('/news/?error=' . rawurlencode('Unable to create news post.')); } } else { return new RedirectResponse('/news/create/?error=' . rawurlencode('A News post must have a body.')); } }
public function testAccountHasAnId() { $account = new Account($this->testAccountId); $this->assertGreaterThan(0, $account->getId()); }
/** * Leveling up Function * * @return boolean */ public function levelUp() { $health_to_add = 100; $turns_to_give = 50; $ki_to_give = 50; $stat_value_to_add = 5; $karma_to_give = 1; if ($this->isAdmin()) { // If the character is an admin, do not auto-level return false; } else { // For normal characters, do auto-level // Have to be under the max level and have enough kills. $level_up_possible = $this->level + 1 <= MAX_PLAYER_LEVEL && $this->kills >= $this->killsRequiredForNextLevel(); if ($level_up_possible) { // Perform the level up actions $this->setHealth($this->health + $health_to_add); $this->setTurns($this->turns + $turns_to_give); $this->setKi($this->ki + $ki_to_give); // Must read from VO for these as accessors return modified values $this->setStamina($this->vo->stamina + $stat_value_to_add); $this->setStrength($this->vo->strength + $stat_value_to_add); $this->setSpeed($this->vo->speed + $stat_value_to_add); // no mutator for these yet $this->vo->kills = max(0, $this->kills - $this->killsRequiredForNextLevel()); $this->vo->karma = $this->karma + $karma_to_give; $this->vo->level = $this->level + 1; $this->save(); GameLog::recordLevelUp($this->id()); $account = Account::findByChar($this); $account->setKarmaTotal($account->getKarmaTotal() + $karma_to_give); $account->save(); // Send a level-up message, for those times when auto-levelling happens. Event::create($this->id(), $this->id(), "You levelled up! Your strength raised by {$stat_value_to_add}, speed by {$stat_value_to_add}, stamina by {$stat_value_to_add}, Karma by {$karma_to_give}, and your Ki raised {$ki_to_give}! You gained some health and turns, as well! You are now a level {$this->level} ninja! Go kill some stuff."); return true; } else { return false; } } }
function testThatAccountConfirmationProcessRejectsNinjaNamesOfTheWrongFormat() { // Same requirements as above, here we test that bad names are rejected. $bad_names = ['xz', 'bo', '69numfirst', 'underscorelast_', 'specialChar##', '@!#$#$^#$@#', 'double__underscore', 'double--dash']; foreach ($bad_names as $name) { $this->assertFalse(!(bool) Account::usernameIsValid($name)); } }
/** * Authenticate a set of credentials * * @return Array */ private function authenticate($dirty_login, $p_pass, $limit_login_attempts = true) { $filter_pattern = "/[^\\w\\d\\s_\\-\\.\\@\\:\\/]/"; $login = strtolower(preg_replace($filter_pattern, "", (string) $dirty_login)); $rate_limit = false; $pass = (string) $p_pass; $account = Account::findByLogin($login); if ($limit_login_attempts && $account) { $rate_limit = intval($account->login_failure_interval) <= 1; } if ($login != '' && $pass != '' && !$rate_limit) { // Pull the account data regardless of whether the password matches, // but create an int about whether it does match or not. $sql = "SELECT account_id, account_identity, uname, player_id, accounts.confirmed as confirmed,\n CASE WHEN phash = crypt(:pass, phash) THEN 1 ELSE 0 END AS authenticated,\n CASE WHEN accounts.operational THEN 1 ELSE 0 END AS operational\n FROM accounts\n JOIN account_players ON account_id = _account_id\n JOIN players ON player_id = _player_id\n WHERE (active_email = :login OR lower(uname) = :login)"; $result = query($sql, [':login' => $login, ':pass' => $pass]); if ($result->rowCount() < 1) { // Username does not exist return []; } else { if ($result->rowCount() > 1) { // Just for later reference, check for duplicate usernames via: //select array_accum(uname), count(*) from players group by lower(trim(uname)) having count(*) > 1; error_log('Case-insensitive duplicate username found: ' . $login); } return $result->fetch(); // account found, return results } } else { if ($account) { // Update the last login failure timestamp Account::updateLastLoginFailure($account); } return []; } }
/** * Update the time of last failed login. */ public static function updateLastLoginFailure(Account $account) { $update = "UPDATE accounts SET last_login_failure = now() WHERE account_id = :account_id"; return query($update, [':account_id' => [$account->id(), PDO::PARAM_INT]]); }
public function testLoginShouldFailOnBlanks() { $account = Account::findById(TestAccountCreateAndDestroy::account_id()); $this->assertInstanceOf(Account::class, $account); $account->confirmed = 0; $account->save(); $request = new Request([], ['user' => '', 'pass' => '']); // TestAccountCreateAndDestroy::$test_password RequestWrapper::inject($request); $controller = new LoginController(); $res = $controller->requestLogin($this->m_dependencies); $this->assertInstanceOf(RedirectResponse::class, $res); $this->assertTrue(stripos($res->getTargetUrl(), 'error') !== false); }
public function testSuccessfulSignupResultsInNoConfirmation() { $uname = 'KnownGood'; $email = '*****@*****.**'; // Due to the nature of hotmail, hotmail emails are listed // such that they will not be preconfirmed. This leaves an account needing confirmation. RequestWrapper::inject(new Request(['key' => 'password1', 'cpass' => 'password1', 'send_email' => $email, 'send_name' => $uname])); $controller = new SignupController(); $response = $controller->signup($this->m_dependencies); $account = Account::findByEmail($email); $player = Player::findByName($uname); $this->assertNotNull($player); $this->assertNotNull($account); $query_relationship = 'SELECT count(*) FROM account_players WHERE _account_id = :id1 AND _player_id = :id2'; $account_unconfirmed = null; if ($account && $player) { $relationship_count = query_item($query_relationship, [':id1' => $account->id(), ':id2' => $player->id()]); $account_unconfirmed = !$account->isConfirmed(); } else { $relationship_count = 0; } $delete_player = 'DELETE FROM players WHERE player_id = :id'; $delete_account = 'DELETE FROM accounts WHERE account_id = :id'; $delete_relationship = 'DELETE FROM account_players WHERE _account_id = :id1 OR _player_id = :id2'; query($delete_player, [':id' => $player->id()]); query($delete_account, [':id' => $account->id()]); query($delete_relationship, [':id1' => $account->id(), ':id2' => $player->id()]); $reflection = new \ReflectionProperty(get_class($response), 'data'); $reflection->setAccessible(true); $response_data = $reflection->getValue($response); $this->assertTrue($response_data['submit_successful'], 'Signup() returned error: ' . $response_data['error']); $this->assertEquals($relationship_count, 1); $this->assertTrue($account_unconfirmed); }
public function testPostEmailCanGetAnAccountUsingANinjaName() { $req = Request::create('/password/post_email/'); $req->setMethod('POST'); $char = TestAccountCreateAndDestroy::char(); $ninja_name = $char->name(); $req->request->set('ninja_name', $ninja_name); RequestWrapper::inject($req); $account = Account::findByNinjaName($ninja_name); $this->assertNotEmpty($account->id(), 'Unable to find id for newly created account.'); $controller = new PasswordController(); $controller->postEmail($this->m_dependencies); // Check for a matching request for the appropriate account. $pwrr = PasswordResetRequest::where('_account_id', '=', $account->id())->first(); $this->assertNotEmpty($pwrr, 'Fail: Unable to find a matching password reset request for account_id: [' . $this->account->id() . '].'); $this->nonce = $pwrr->nonce; }