/** * @route crew/permissions/{string}/context/{id} * * @param string $cabin * @param string $contextId */ public function editContext(string $cabin, string $contextId) { $contextId = (int) $contextId; if (!\in_array($cabin, $this->getCabinNamespaces())) { \Airship\redirect($this->airship_cabin_prefix . '/crew/permissions'); } $context = $this->perms->getContext($contextId, $cabin); if (empty($context)) { \Airship\redirect($this->airship_cabin_prefix . '/crew/permissions' . $cabin); } // Handle post data $post = $this->post(new SaveContextFilter()); if (!empty($post)) { if ($this->perms->saveContext($cabin, $contextId, $post)) { \Airship\redirect($this->airship_cabin_prefix . '/crew/permissions/' . $cabin . '/context/' . $contextId, ['msg' => 'saved']); } } // Okay, $actions = $this->perms->getActionNames($cabin); $groupPerms = $this->perms->buildGroupTree($cabin, $contextId, $actions); $userPerms = $this->perms->buildUserList($cabin, $contextId, $actions); $users = []; foreach ($userPerms as $userid => $userPerm) { $userid = (int) $userid; $users[$userid] = $this->users->getUserAccount($userid, true); unset($users[$userid]['password']); } if (!empty($_GET['msg'])) { if ($_GET['msg'] === 'saved') { $this->storeLensVar('message', \__('Your changes have been saved.')); } } $this->lens('perms/context', ['actions' => $actions, 'cabin' => $cabin, 'context' => $context, 'permissions' => $groupPerms, 'userperms' => $userPerms, 'users' => $users]); }
/** * Edit a user's information * * @route crew/users/edit/{id} * @param string $userId */ public function editUser(string $userId = '') { $userId = (int) $userId; $user = $this->account->getUserAccount($userId, true); $post = $this->post(new EditUserFilter()); if ($post) { if ($this->account->editUser($userId, $post)) { \Airship\redirect($this->airship_cabin_prefix . '/crew/users'); } } $this->lens('crew/user_edit', ['active_link' => 'bridge-link-admin-crew-users', 'user' => $user, 'groups' => $this->account->getGroupTree()]); }
/** * Handle user authentication * * @param array $post */ protected function processLogin(array $post = []) { $state = State::instance(); if (empty($post['username']) || empty($post['passphrase'])) { $this->lens('login', ['post_response' => ['message' => \__('Please fill out the form entirely'), 'status' => 'error']]); } $airBrake = Gears::get('AirBrake'); if (IDE_HACKS) { $airBrake = new AirBrake(); } if ($airBrake->failFast($post['username'], $_SERVER['REMOTE_ADDR'])) { $this->lens('login', ['post_response' => ['message' => \__('You are doing that too fast. Please wait a few seconds and try again.'), 'status' => 'error']]); } elseif (!$airBrake->getFastExit()) { $delay = $airBrake->getDelay($post['username'], $_SERVER['REMOTE_ADDR']); if ($delay > 0) { \usleep($delay * 1000); } } try { $userID = $this->airship_auth->login($post['username'], new HiddenString($post['passphrase'])); } catch (InvalidMessage $e) { $this->log('InvalidMessage Exception on Login; probable cause: password column was corrupted', LogLevel::CRITICAL, ['exception' => \Airship\throwableToArray($e)]); $this->lens('login', ['post_response' => ['message' => \__('Incorrect username or passphrase. Please try again.'), 'status' => 'error']]); } if (!empty($userID)) { $userID = (int) $userID; $user = $this->acct->getUserAccount($userID); if ($user['enable_2factor']) { if (empty($post['two_factor'])) { $post['two_factor'] = ''; } $gauth = $this->twoFactorPreamble($userID); $checked = $gauth->validateCode($post['two_factor'], \time()); if (!$checked) { $fails = $airBrake->getFailedLoginAttempts($post['username'], $_SERVER['REMOTE_ADDR']) + 1; // Instead of the password, seal a timestamped and // signed message saying the password was correct. // We use a signature with a key local to this Airship // so attackers can't just spam a string constant to // make the person decrypting these strings freak out // and assume the password was compromised. // // False positives are bad. This gives the sysadmin a // surefire way to reliably verify that a log entry is // due to two-factor authentication failing. $message = '**Note: The password was correct; ' . ' invalid 2FA token was provided.** ' . (new \DateTime('now'))->format(\AIRSHIP_DATE_FORMAT); $signed = Base64UrlSafe::encode(Asymmetric::sign($message, $state->keyring['notary.online_signing_key'], true)); $airBrake->registerLoginFailure($post['username'], $_SERVER['REMOTE_ADDR'], $fails, new HiddenString($signed . $message)); $this->lens('login', ['post_response' => ['message' => \__('Incorrect username or passphrase. Please try again.'), 'status' => 'error']]); } } if ($user['session_canary']) { $_SESSION['session_canary'] = $user['session_canary']; } elseif ($this->config('password-reset.logout')) { $_SESSION['session_canary'] = $this->acct->createSessionCanary($userID); } // Regenerate session ID: Session::regenerate(true); $_SESSION['userid'] = (int) $userID; if (!empty($post['remember'])) { $autoPilot = Gears::getName('AutoPilot'); if (IDE_HACKS) { $autoPilot = new AutoPilot(); } $httpsOnly = (bool) $autoPilot::isHTTPSConnection(); Cookie::setcookie('airship_token', Symmetric::encrypt($this->airship_auth->createAuthToken($userID), $state->keyring['cookie.encrypt_key']), \time() + ($state->universal['long-term-auth-expire'] ?? self::DEFAULT_LONGTERMAUTH_EXPIRE), '/', $state->universal['session_config']['cookie_domain'] ?? '', $httpsOnly ?? false, true); } \Airship\redirect($this->airship_cabin_prefix); } else { $fails = $airBrake->getFailedLoginAttempts($post['username'], $_SERVER['REMOTE_ADDR']) + 1; // If the server is setup (with an EncryptionPublicKey) and the // number of failures is above the log threshold, this will // encrypt the password guess with the public key so that only // the person in possession of the secret key can decrypt it. $airBrake->registerLoginFailure($post['username'], $_SERVER['REMOTE_ADDR'], $fails, new HiddenString($post['passphrase'])); $this->lens('login', ['post_response' => ['message' => \__('Incorrect username or passphrase. Please try again.'), 'status' => 'error']]); } }