public function adminHome($req, $res) { if (!$this->app['user']->isAdmin()) { return $res->redirect('/login?redir=' . urlencode($req->basePath() . $req->path())); } $metrics = []; $chartData = []; $chartGranularities = []; $chartLoadedIntervals = []; $start = -7; $end = 0; $dashboard = (array) $this->app['config']->get('statistics.dashboard'); foreach ($dashboard as $section => $metricClasses) { foreach ($metricClasses as $className) { $className = '\\app\\statistics\\metrics\\' . $className . 'Metric'; $metric = new $className($this->app); $k = $metric->key(); U::array_set($metrics, $section . '.' . $k, $metric->toArray()); if ($metric->hasChart()) { $chartData[$k] = $metric->values(); $gName = $metric::$granularityNames[$metric->granularity()]; $chartGranularities[$k] = $gName; $chartLoadedIntervals[$k] = 'last-7-' . $gName . 's'; } } } return new View('admin/index', ['metrics' => $metrics, 'chartNames' => array_keys($chartData), 'chartData' => $chartData, 'chartGranularities' => $chartGranularities, 'chartLoadedIntervals' => $chartLoadedIntervals, 'title' => 'Statistics']); }
public function __invoke(Request $req, Response $res, callable $next) { $config = $this->app['config']; if (!$config->get('sessions.enabled') || $req->isApi()) { return $next($req, $res); } $lifetime = $config->get('sessions.lifetime'); $hostname = $config->get('app.hostname'); ini_set('session.use_trans_sid', false); ini_set('session.use_only_cookies', true); ini_set('url_rewriter.tags', ''); ini_set('session.gc_maxlifetime', $lifetime); // set the session name $defaultSessionTitle = $config->get('app.title') . '-' . $hostname; $sessionTitle = $config->get('sessions.name', $defaultSessionTitle); $safeSessionTitle = str_replace(['.', ' ', "'", '"'], ['', '_', '', ''], $sessionTitle); session_name($safeSessionTitle); // set the session cookie parameters session_set_cookie_params($lifetime, '/', '.' . $hostname, $req->isSecure(), true); // register session_write_close as a shutdown function session_register_shutdown(); // install any custom session handlers $class = $config->get('sessions.driver'); if ($class) { $handler = new $class($this->app); $handler::registerHandler($handler); } session_start(); // fix the session cookie Utility::setCookieFixDomain(session_name(), session_id(), time() + $lifetime, '/', $hostname, $req->isSecure(), true); // make the newly started session in our request $req->setSession($_SESSION); return $next($req, $res); }
public function submitVolunteerApplication($req, $res) { $currentUser = $this->app['user']; // make sure the user is logged in if (!$currentUser->isLoggedIn()) { setcookie('redirect', '/volunteers/application', time() + 3600, '/'); return $res->redirect('/login'); } if (!$req->request('accept')) { $req->setParams(['accept_error' => true]); return $this->volunteerApplication($req, $res); } $input = $req->request(); $input['uid'] = $currentUser->id(); $input['birth_date'] = mktime(0, 0, 0, $input['month'] + 1, $input['day'], $input['year']); $input['first_time_volunteer'] = !U::array_value($input, 'volunteered_before'); $application = $currentUser->volunteerApplication(); if (!$application->exists()) { $application = new VolunteerApplication(); if ($application->create($input)) { return $res->redirect('/volunteers/application/thanks'); } } else { if ($application->set($input)) { return $res->redirect('/volunteers/application/thanks'); } } return $this->volunteerApplication($req, $res); }
public function send(array $message) { $result = []; $to = (array) array_value($message, 'to'); foreach ($to as $item) { $result[] = ['_id' => Utility::guid(false), 'email' => $item['email'], 'status' => 'sent']; } return $result; }
public function getFriendsCount() { $facebook = $this->app['facebook_service']; $facebook->setAccessTokenFromProfile($this); $friends = $facebook->api('me/friends', 'get'); if (is_array($friends)) { return count((array) U::array_value($friends, 'data')); } return -1; }
/** * The first step in the forgot password sequence. * * @param string $email email address * @param string $ip ip address making the request * @param string $userAgent user agent used to make the request * * @throws AuthException when the step cannot be completed. * * @return bool */ public function step1($email, $ip, $userAgent) { $email = trim(strtolower($email)); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new AuthException('Please enter a valid email address.'); } $userClass = $this->auth->getUserClass(); $user = $userClass::where('email', $email)->first(); if (!$user || $user->isTemporary()) { throw new AuthException('We could not find a match for that email address.'); } // can only issue a single active forgot token at a time $expiration = U::unixToDb(time() - UserLink::$forgotLinkTimeframe); $nExisting = UserLink::totalRecords(['user_id' => $user->id(), 'type' => UserLink::FORGOT_PASSWORD, 'created_at > "' . $expiration . '"']); if ($nExisting > 0) { return true; } // generate a reset password link $link = $this->buildLink($user->id(), $ip, $userAgent); // and send it return $user->sendEmail('forgot-password', ['ip' => $ip, 'forgot' => $link->link]); }
/** * Performs an API call on the facebook API (if available) or * returns a mock response. * * @param string $endpoint * @param string $method HTTP method * @param array $params optional params * * @return object */ public function api($endpoint, $method = null, $params = null) { $response = false; try { return $this->app['facebook']->api($endpoint, $method, $params); } catch (\FacebookApiException $e) { // access token has expired $result = $e->getResult(); $code = U::array_value($result, 'error.code'); if ($code == 190) { // clear the access token of the user's profile if ($this->profile) { $this->profile->grantAllPermissions(); $this->profile->set('access_token', ''); $this->profile->enforcePermissions(); } } else { $this->app['logger']->error($e); } return false; } }
public function send(array $message) { // build recipients $to = []; $bcc = []; $toIncoming = (array) array_value($message, 'to'); foreach ($toIncoming as $item) { $type = array_value($item, 'type'); if ($type == 'bcc') { $bcc[$item['email']] = $item['name']; } else { $to[$item['email']] = $item['name']; } } if (count($to) === 0) { return []; } $fromEmail = array_value($message, 'from_email'); $fromName = array_value($message, 'from_name'); $swiftMessage = Swift_Message::newInstance()->setFrom([$fromEmail => $fromName])->setTo($to)->setBcc($bcc)->setSubject($message['subject'])->setBody($message['html'], 'text/html'); if (isset($message['text'])) { $swiftMessage->addPart($message['text'], 'text/plain'); } if (isset($message['headers']) && is_array($message['headers'])) { $headers = $swiftMessage->getHeaders(); foreach ($message['headers'] as $k => $v) { $headers->addTextHeader($k, $v); } } $sent = $this->swift->send($swiftMessage); $result = []; foreach ($message['to'] as $item) { $result[] = ['_id' => Utility::guid(false), 'email' => $item['email'], 'status' => $sent ? 'sent' : 'rejected']; } return $result; }
public function value($start, $end) { return (int) $this->app['db']->select('COUNT(uid)')->from('Users')->where([['created_at', U::unixToDb($end), '<='], ['created_at', U::unixToDb($start), '>=']])->scalar(); }
/** * Generates the output of a report for a given type. * * @param string $type html|pdf|csv * @param bool $stream when true, streams the resulting file to the client (pdf, csv only) * @param Response $res when streaming, response object to use * * @return string|array|false */ public function output($type, $stream = false, Response $res = null) { // $this->organization->useTimezone(); $type = strtolower($type); if ($type == 'html') { $this->htmlOutput = true; // NOTE host name has the development port number stripped, // otherwise the css is not loaded $data = ['css' => 'file://' . INFUSE_PUBLIC_DIR . '/css/report.css', 'header' => $this->getHeader(), 'sections' => $this->getSections()]; $this->htmlOutput = false; $view = new View('report', $data); return $view->render(); } elseif ($type == 'pdf') { $html = $this->output('html'); // Run wkhtmltopdf $descriptorspec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; $process = proc_open(WKHTMLTOPDF_CMD, $descriptorspec, $pipes); // Send the HTML on stdin fwrite($pipes[0], $html); fclose($pipes[0]); // Read the outputs $pdf = stream_get_contents($pipes[1]); $errors = stream_get_contents($pipes[2]); // Close the process fclose($pipes[1]); $return_value = proc_close($process); // Handle errors if ($errors) { error_log($errors); } // Output the results if ($stream) { $res->setContentType('application/pdf')->setHeader('Cache-Control', 'public, must-revalidate, max-age=0')->setHeader('Pragma', 'public')->setHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT')->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT')->setHeader('Content-Length', strlen($pdf))->setHeader('Content-Disposition', 'attachment; filename="' . $this->baseFilename() . '.pdf";')->setBody($pdf); } else { return $pdf; } } elseif ($type == 'csv') { $output = []; $header = $this->getHeader(); foreach ($header as $key => $value) { $output[] = [$key, $value]; } $output[] = []; $sections = $this->getSections(); foreach ($sections as $section) { if (isset($section['title'])) { $output[] = [$section['title']]; } if (isset($section['keyvalue'])) { foreach ($section['keyvalue'] as $key => $value) { $output[] = [$key, $value]; } $output[] = []; } $entireTable = array_merge([(array) U::array_value($section, 'header')], (array) U::array_value($section, 'rows'), [(array) U::array_value($section, 'footer')]); foreach ($entireTable as $row) { $output[] = $row; } $output[] = []; } $csv = fopen('php://output', 'w'); ob_start(); foreach ($output as $row) { fputcsv($csv, $row); } fclose($csv); $output = ob_get_clean(); if ($stream) { $res->setContentType('text/csv')->setHeader('Cache-Control', 'public, must-revalidate, max-age=0')->setHeader('Pragma', 'public')->setHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT')->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT')->setHeader('Content-Length', strlen($output))->setHeader('Content-Disposition', 'attachment; filename="' . $this->baseFilename() . '".csv')->setBody($output); } else { return $output; } } return false; }
private function loginOrRegister($fbid, $user_profile, $req, $res) { $currentUser = $this->app['user']; $facebook = $this->app['facebook']; // get friend count $friendCount = 0; try { $friends = $facebook->api('me/friends'); $friendCount = count((array) U::array_value($friends, 'data')); } catch (\FacebookApiException $e) { $this->app['logger']->error($e); } // generate parameters to update profile $profileUpdateArray = ['id' => $fbid, 'access_token' => $facebook->getAccessToken(), 'friends_count' => $friendCount]; // fbid matches existing user? $user = User::findOne(['where' => ['facebook_id' => $fbid]]); if ($user) { // check if we are dealing with a temporary user if (!$user->isTemporary()) { if ($user->id() != $currentUser->id()) { if ($req->query('forceLogin') || !$currentUser->isLoggedIn()) { // log the user in $this->app['auth']->signInUser($user->id(), 'facebook'); } else { $logoutNextUrl = $this->app['base_url'] . 'facebook/connect?logout=t'; // inform the user that the facebook account they are trying to connect // belongs to someone else return new View('switchingAccounts/facebook', ['title' => 'Switch accounts?', 'otherUser' => $user, 'otherProfile' => $user->facebookProfile(), 'logoutUrl' => $facebook->getLogoutUrl(['next' => $logoutNextUrl])]); } } $profile = new FacebookProfile($fbid); // create or update the profile if ($profile->exists()) { $profile->set($profileUpdateArray); } else { $profile = new FacebookProfile(); $profile->create($profileUpdateArray); } // refresh profile from API $profile->refreshProfile($user_profile); return $this->finalRedirect($req, $res); } else { // show finish signup screen $req->setSessoin('fbid', $fbid); return $res->redirect('/signup/finish'); } } if ($currentUser->isLoggedIn()) { // add to current user's account $currentUser->set('facebook_id', $fbid); } else { // save this for later $req->setSession('fbid', $fbid); } $profile = new FacebookProfile($fbid); // create or update the profile if ($profile->exists()) { $profile->set($profileUpdateArray); } else { $profile = new FacebookProfile(); $profile->create($profileUpdateArray); } // refresh profile from API $profile->refreshProfile($user_profile); // get outta here if ($currentUser->isLoggedIn()) { $this->finalRedirect($req, $res); } else { $res->redirect('/signup/finish'); } }
private function getModelForAdmin($req, $res) { // lookup model class // index derived from /organizations/:username/admin/SECTION/.... $section = $req->paths(3); $modelClass = U::array_value(self::$sectionModels, $section); if (!$modelClass) { $res->setCode(404); return false; } // lookup org $org = $this->getOrgForAdmin($req, $res); if (!is_object($org)) { return false; } $model = new $modelClass($req->params('id')); if ($section == 'volunteers') { $model = new $modelClass([$req->params('id'), $org->id()]); } if (!$model->exists()) { $res->setCode(404); return false; } if (!$model->can('view', $this->app['user'])) { $res->setCode(401); return false; } return [$org, $model, $section]; }
/** * Increments the keys in an input array by some delta. * NOTE stats cannot be less than 0. * * @param array $source values to be incremented * @param array $delta values to be added * * @return array incremented source */ public static function increment(array $source, array $delta) { $return = []; foreach ($source as $k => $v) { $return[$k] = max(0, (int) $v + (int) U::array_value($delta, $k)); } return $return; }
/** * Upgrades the user from temporary to a fully registered account. * * @param array $data user data * * @throws InvalidArgumentException when trying to upgrade a non-temporary account. * * @return bool true if successful */ public function upgradeTemporaryAccount($data) { if (!$this->isTemporary()) { throw new InvalidArgumentException('Cannot upgrade a non-temporary account'); } $updateArray = array_replace($data, ['created_at' => U::unixToDb(time()), 'enabled' => true]); $success = false; $this->grantAllPermissions(); $this->_isUpgrade = true; if ($this->set($updateArray)) { // remove temporary and unverified links $app = $this->getApp(); $app['db']->delete('UserLinks')->where('user_id', $this->id())->where(function ($query) { return $query->where('type', UserLink::TEMPORARY)->orWhere('type', UserLink::VERIFY_EMAIL); })->execute(); // send the user a welcome message $this->sendEmail('welcome'); $success = true; } $this->_isUpgrade = false; $this->enforcePermissions(); return $success; }
/** * Maps the properties of the user profile from the API * to the properties in our model. * * @param array $user_profile user profile from API * * @return array */ protected function mapPropertiesFromApi(array $user_profile) { $info = []; foreach ($this->apiPropertyMapping() as $modelProperty => $apiProperty) { $info[$modelProperty] = U::array_value($user_profile, $apiProperty); } return $info; }
/** * Clears out expired user links. * * @return bool */ private function gcUserLinks() { return (bool) $this->app['db']->delete('UserLinks')->where('type', UserLink::FORGOT_PASSWORD)->where('created_at', U::unixToDb(time() - UserLink::$forgotLinkTimeframe), '<')->execute(); }
/** * Verifies the cookie against an incoming request. * * @param Request $req * @param AuthManager $auth * * @return bool */ public function verify(Request $req, AuthManager $auth) { if (!$this->isValid()) { return false; } // verify the user agent matches the one in the request if ($this->userAgent != $req->agent()) { return false; } // look up the user with a matching email address $userClass = $auth->getUserClass(); $user = $userClass::where('email', $this->email)->first(); if (!$user) { return false; } // hash series for matching with the db $seriesHash = $this->hash($this->series); // First, make sure all of the parameters match, except the token. // We match the token separately to detect if an older session is // being used, in which case we cowardly run away. $expiration = time() - $this->getExpires(); $db = $auth->getApp()['db']; $query = $db->select('token,two_factor_verified')->from('PersistentSessions')->where('email', $this->email)->where('created_at', U::unixToDb($expiration), '>')->where('series', $seriesHash); $persistentSession = $query->one(); if ($query->rowCount() !== 1) { return false; } // if there is a match, sign the user in $tokenHash = $this->hash($this->token); // Same series, but different token, meaning the user is trying // to use an older token. It's most likely an attack, so flush // all sessions. if (!hash_equals($persistentSession['token'], $tokenHash)) { $db->delete('PersistentSessions')->where('email', $this->email)->execute(); return false; } // remove the token once used $db->delete('PersistentSessions')->where('email', $this->email)->where('series', $seriesHash)->where('token', $tokenHash)->execute(); // mark the user as 2fa verified if ($persistentSession['two_factor_verified']) { $user->markTwoFactorVerified(); } return $user; }
/** * Fetches the models for a given controller. * * @param object $controller * * @return array */ private function models($controller) { $properties = $controller::$properties; $module = $this->name($controller); $models = []; foreach ((array) U::array_value($properties, 'models') as $model) { $modelClassName = '\\app\\' . $module . '\\models\\' . $model; $info = $modelClassName::metadata(); $models[$model] = array_replace($info, ['route_base' => '/' . $module . '/' . $info['plural_key']]); } return $models; }
public function preSetHook(&$data) { $organization = $this->relation('organization'); $currentUser = $this->app['user']; $currentRole = $organization->getRoleOfUser($currentUser); $isAdmin = $currentUser->isAdmin() || $currentRole == self::ROLE_ADMIN; // volunteers can only be promoted if current user is admin $maxLevel = $isAdmin ? self::ROLE_ADMIN : self::ROLE_AWAITING_APPROVAL; $role = U::array_value($data, 'role'); if ($role > $maxLevel) { $this->app['errors']->push(['error' => ERROR_NO_PERMISSION]); return false; } // email user if going from not approved to approved if ($role >= self::ROLE_VOLUNTEER && $this->role == self::ROLE_AWAITING_APPROVAL) { $data['approval_link'] = null; $this->needsApproveEmail = true; } return true; }
/** * Validates a password and hashes the value. * OPTIONAL password:10 sets the minimum length. * * @param mixed $value * @param array $parameters * * @return bool */ private function password(&$value, array $parameters) { $minimumPasswordLength = isset($parameters[0]) ? $parameters[0] : 8; if (strlen($value) < $minimumPasswordLength) { return false; } $value = Utility::encryptPassword($value, self::$config['salt']); return true; }
protected function preSetHook(&$data) { // make sure the place name is unique $name = U::array_value($data, 'name'); if (!empty($name) && $name != $this->name && self::totalRecords(['organization' => $this->organization, 'name' => $name]) > 0) { $errorStack = $this->app['errors']; $errorStack->push(['error' => ERROR_VOLUNTEER_PLACE_NAME_TAKEN, 'params' => ['place_name' => $name]]); return false; } // geocode if (isset($data['address'])) { $data['coordinates'] = $this->geocode($data['address']); } $this->justApproved = isset($data['verify_approved']) && $data['verify_approved'] && !$this->verify_approved; return true; }
protected function preCreateHook(&$data) { $org = new Organization(U::array_value($data, 'organization')); // check creator permission $requester = $this->app['user']; $role = $org->getRoleOfUser($requester); if ($role < Volunteer::ROLE_VOLUNTEER && !$requester->isAdmin()) { $this->app['errors']->push(['error' => ERROR_NO_PERMISSION]); return false; } // volunteers cannot approve own hours if ($role < Volunteer::ROLE_ADMIN && !$requester->isAdmin()) { $data['approved'] = false; } // validate number of hours $hours = $data['hours'] = floor($data['hours']); if ($hours <= 0 || $hours >= 13) { $this->app['errors']->push(['error' => 'invalid_num_volunteer_hours']); return false; } // convert day timestamp to beginning of day $data['timestamp'] = self::timestampToStartOfDay($data['timestamp']); // the timestamp on hours cannot be more than 1 day in the future if ($data['timestamp'] - 86400 > time()) { $this->app['errors']->push(['error' => 'invalid_hours_timestamp']); return false; } // approval link if (!U::array_value($data, 'approved')) { $data['approval_link'] = U::guid(false); } if (isset($data['tags'])) { self::$createTags = $data['tags']; if (!is_array(self::$createTags)) { self::$createTags = explode(' ', self::$createTags); } } return true; }
/** * Bundles up some useful properties of the statistic for convenience. * * @return array */ public function toArray() { $name = $this->name(); $value = $this->computeValue(); $delta = $this->computeDelta(); return ['name' => $name, 'key' => $this->key(), 'granularity' => $this->granularity(), 'prefix' => $this->prefix(), 'suffix' => $this->suffix(), 'hasChart' => $this->hasChart(), 'span' => $this->span(), 'value' => $value, 'abbreviated_value' => is_numeric($value) ? U::number_abbreviate(round($value, 2), 1) : $value, 'delta' => $delta, 'abbreviated_delta' => is_numeric($delta) ? U::number_abbreviate(round($delta, 2), 1) : $delta]; }