public static function link_to($args, $attributes) { $text = isset($args[1]) ? $args[1] : $args[0]; $app_key = \Hook\Application\Context::getKey(); $public_url = public_url($args[0]) . '?X-App-Id=' . $app_key->app_id . '&X-App-Key=' . $app_key->key; return array('<a href="' . $public_url . '"' . html_attributes($attributes) . '>' . $text . '</a>', 'raw'); }
public static function getInstance() { $app_key = Context::getKey(); if (!static::$instance && $app_key) { static::$instance = new static($app_key->app->secret, $app_key->app_id); } return static::$instance; }
/** * url * @static * @param string $segments segments * @param array $options options * @return Database\CollectionDelegator */ public static function url($segments, $options = array()) { $request = Router::getInstance()->request; $options['X-App-Id'] = Context::getKey()->app_id; $options['X-App-Key'] = Context::getKey()->key; $segments .= '?' . http_build_query($options); return $request->getUrl() . $request->getScriptName() . '/' . $segments; }
public function notify() { if (!(Context::getKey()->isServer() && Request::header('X-Scheduled-Task'))) { throw new ForbiddenException("Need a 'device' key to perform this action."); } $notifier = new PushNotification\Notifier(); $messages = Model\App::collection('push_messages')->where('status', Model\PushMessage::STATUS_QUEUE); return $notifier->push_messages($messages); }
public function before() { $is_commandline = true; //(Request::header('User-Agent') == 'hook-cli'); $key = Context::getKey(); $allowed = $is_commandline && ($key && $key->isCommandline()); if (!static::isRootOperation() && !$allowed) { throw new UnauthorizedException("Your IP Address is not allowed to perform this operation."); } }
public function beforeSave() { if (!$this->getAttribute('_id')) { if (!Context::getKey()->isServer()) { throw new ForbiddenException("Need a 'server' key to perform this action."); } if (!$this->getAttribute('message')) { throw new InternalException("Can't create PushMessage: 'message' is required."); } $this->setAttribute('status', self::STATUS_QUEUE); $this->setAttribute('devices', 0); $this->setAttribute('failure', 0); } parent::beforeSave(); }
public function call() { $app = $this->app; $app_key = Context::getKey(); // // TODO: need a way to enable/disable logs for production use // // Log all queries $dispatcher = \Hook\Model\Collection::getEventDispatcher(); $dispatcher->listen('illuminate.query', function ($query, $bindings, $time, $name) use(&$app) { $data = compact('bindings', 'time', 'name'); // Format binding data for sql insertion foreach ($bindings as $i => $binding) { if ($binding instanceof \DateTime) { $bindings[$i] = $binding->format('\'Y-m-d H:i:s\''); } else { if (is_string($binding)) { $bindings[$i] = "'{$binding}'"; } } } // Insert bindings into query $query = str_replace(array('%', '?'), array('%%', '%s'), $query); $query = vsprintf($query, $bindings); \Logger::debug($query); }); if (!$app->request->isOptions() && $app_key) { // set application log writer for this app $log_file = storage_dir() . 'logs.txt'; $app->log->setWriter(new LogWriter($log_file)); // disable log if storage directory doesn't exists. // maybe we're on a readonly filesystem $app->log->setEnabled(is_writable($log_file)); if (strpos($app->request->getPath(), "/apps/") === false) { $app->log->info($app->request->getIp() . ' - [' . date('d-m-Y H:i:s') . '] ' . $app->request->getMethod() . ' ' . $app->request->getResourceUri()); $app->log->info('Params: ' . json_encode($app->request->params())); } } return $this->next->call(); }
public static function form() { $args = func_get_args(); $options = array_pop($args); // use empty string as default action if (!isset($options['hash']['action'])) { $options['hash']['action'] = ""; } // use GET method as default if (!isset($options['hash']['method'])) { $options['hash']['method'] = 'get'; } // evaluate action url $app_key = \Hook\Application\Context::getKey(); $action_url = parse_url($options['hash']['action']); if (!isset($action_url['query'])) { $action_url['query'] = 'X-App-Id=' . $app_key->app_id . '&X-App-Key=' . $app_key->key; } $options['hash']['action'] = unparse_url($action_url); $html = '<form' . html_attributes($options['hash']) . '>' . "\n" . $options['fn']() . '</form>'; return $html; }
public static function current() { return Context::getKey(); }
/** * compile * Compile module code * @param array options * @return mixed */ public function compile($options = array()) { $app = Slim\Slim::getInstance(); $extension = '.' . pathinfo($this->name, PATHINFO_EXTENSION); $name = basename($this->name, $extension); if ($this->type == static::TYPE_OBSERVER || $this->type == static::TYPE_CHANNEL || $this->type == static::TYPE_ROUTE) { // // Expose handy aliases for modules // $aliases = ''; $aliases .= 'use Hook\\Application\\Context;'; $aliases .= 'use Hook\\Model\\Module;'; $aliases .= 'use Hook\\Model\\File;'; $aliases .= 'use Hook\\Model\\Auth;'; $aliases .= 'use Hook\\Model\\AuthToken;'; $aliases .= 'use Hook\\Model\\Collection;'; $aliases .= 'use Hook\\Cache\\Cache;'; $aliases .= 'use Hook\\Logger\\Logger;'; if ($this->type == self::TYPE_OBSERVER || $this->type == self::TYPE_CHANNEL) { // Prevent name conflict by using unique class names for custom modules $klass = 'CustomModule' . uniqid(); eval($aliases . preg_replace('/class ([^\\ {]+)/', 'class ' . $klass, $this->code, 1)); if (class_exists($klass)) { // Return module instance for registering on model. return new $klass(); } else { throw new Exceptions\MethodFailureException("Module '{$name}.php' must define a class."); } } elseif ($this->type == self::TYPE_ROUTE) { try { eval($aliases . $this->code); } catch (\Exception $e) { $message = $this->name . ': ' . $e->getMessage(); $app->log->info($message); $app->response->headers->set('X-Error-' . uniqid(), $message); file_put_contents('php://stderr', $message); } } } elseif ($this->type == static::TYPE_TEMPLATE) { $app->view->setTemplateString($this->code); // Expose app_key to compiled templates. // Mainly used to generate server-side routes when // `Hook\View\Helper::link_to` isn't capable to handle it // // TODO: remove this after implementing issue #131 // (https://github.com/doubleleft/hook/issues/131) $options['app_key'] = \Hook\Application\Context::getKey(); return $app->view->render($this->name, $options); } }
public function auth($strategy = null, $callback = null) { $query_params = $this->getQueryParams(); if (isset($_POST['opauth'])) { $opauth = unserialize(base64_decode($_POST['opauth'])); if (isset($opauth['error'])) { // throw new UnauthorizedException($opauth['error']['code']); return $this->relay_frame_close(); } $opauth_data = $opauth['auth']; $identity = AuthIdentity::firstOrNew(array('provider' => strtolower($opauth_data['provider']), 'uid' => $opauth_data['uid'])); if (!$identity->auth_id || $identity->auth == NULL) { // cleanup nested infos before registering it foreach ($opauth_data['info'] as $key => $value) { if (is_array($value)) { unset($opauth_data['info'][$key]); } } // register new auth if (isset($opauth_data['info']['email'])) { $auth = Auth::current() ?: Auth::firstOrNew(array('email' => $opauth_data['info']['email'])); } else { // creating auth entry without email $auth = Auth::current() ?: new Auth(); } // If is a new user, fill and save with auth data if (!$auth->_id) { $auth->fill($opauth_data['info']); } // set visible provider_id on auth row. // such as 'facebook_id', 'google_id', etc. $auth->setTrustedAction(true); $auth->setAttribute($identity->provider . '_id', $identity->uid); $auth->save(); $identity->auth_id = $auth->_id; $identity->save(); } else { $auth = $identity->auth; } $data = $auth->dataWithToken(); // output oauth credentials on authentication request if (isset($opauth_data['credentials'])) { $data['credentials'] = $opauth_data['credentials']; } if (Context::getKey()->isBrowser()) { $js_origin = "window.opener.location.protocol + '//' + window.opener.location.hostname + (window.opener.location.port ? ':' + window.opener.location.port: '')"; // Use mozilla/winchan to allow trusted cross-browser postMessages $winchanjs = 'WinChan=function(){var RELAY_FRAME_NAME="__winchan_relay_frame";var CLOSE_CMD="die";function addListener(w,event,cb){if(w.attachEvent)w.attachEvent("on"+event,cb);else if(w.addEventListener)w.addEventListener(event,cb,false)}function removeListener(w,event,cb){if(w.detachEvent)w.detachEvent("on"+event,cb);else if(w.removeEventListener)w.removeEventListener(event,cb,false)}function isInternetExplorer(){var rv=-1;var ua=navigator.userAgent;if(navigator.appName==="Microsoft Internet Explorer"){var re=new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");if(re.exec(ua)!=null)rv=parseFloat(RegExp.$1)}else if(ua.indexOf("Trident")>-1){var re=new RegExp("rv:([0-9]{2,2}[.0-9]{0,})");if(re.exec(ua)!==null){rv=parseFloat(RegExp.$1)}}return rv>=8}function isFennec(){try{var userAgent=navigator.userAgent;return userAgent.indexOf("Fennec/")!=-1||userAgent.indexOf("Firefox/")!=-1&&userAgent.indexOf("Android")!=-1}catch(e){}return false}function isSupported(){return window.JSON&&window.JSON.stringify&&window.JSON.parse&&window.postMessage}function extractOrigin(url){if(!/^https?:\\/\\//.test(url))url=window.location.href;var a=document.createElement("a");a.href=url;return a.protocol+"//"+a.host}function findRelay(){var loc=window.location;var frames=window.opener.frames;for(var i=frames.length-1;i>=0;i--){try{if(frames[i].location.protocol===window.location.protocol&&frames[i].location.host===window.location.host&&frames[i].name===RELAY_FRAME_NAME){return frames[i]}}catch(e){}}return}var isIE=isInternetExplorer();if(isSupported()){return{open:function(opts,cb){if(!cb)throw"missing required callback argument";var err;if(!opts.url)err="missing required \'url\' parameter";if(!opts.relay_url)err="missing required \'relay_url\' parameter";if(err)setTimeout(function(){cb(err)},0);if(!opts.window_name)opts.window_name=null;if(!opts.window_features||isFennec())opts.window_features=undefined;var iframe;var origin=extractOrigin(opts.url);if(origin!==extractOrigin(opts.relay_url)){return setTimeout(function(){cb("invalid arguments: origin of url and relay_url must match")},0)}var messageTarget;if(isIE){iframe=document.createElement("iframe");iframe.setAttribute("src",opts.relay_url);iframe.style.display="none";iframe.setAttribute("name",RELAY_FRAME_NAME);document.body.appendChild(iframe);messageTarget=iframe.contentWindow}var w=window.open(opts.url,opts.window_name,opts.window_features);if(!messageTarget)messageTarget=w;var closeInterval=setInterval(function(){if(w&&w.closed){cleanup();if(cb){cb("unknown closed window");cb=null}}},500);var req=JSON.stringify({a:"request",d:opts.params});function cleanup(){if(iframe)document.body.removeChild(iframe);iframe=undefined;if(closeInterval)closeInterval=clearInterval(closeInterval);removeListener(window,"message",onMessage);removeListener(window,"unload",cleanup);if(w){try{w.close()}catch(securityViolation){messageTarget.postMessage(CLOSE_CMD,origin)}}w=messageTarget=undefined}addListener(window,"unload",cleanup);function onMessage(e){if(e.origin!==origin){return}try{var d=JSON.parse(e.data);if(d.a==="ready")messageTarget.postMessage(req,origin);else if(d.a==="error"){cleanup();if(cb){cb(d.d);cb=null}}else if(d.a==="response"){cleanup();if(cb){cb(null,d.d);cb=null}}}catch(err){}}addListener(window,"message",onMessage);return{close:cleanup,focus:function(){if(w){try{w.focus()}catch(e){}}}}},onOpen:function(cb){var o="*";var msgTarget=isIE?findRelay():window.opener;if(!msgTarget)throw"cant find relay frame";function doPost(msg){msg=JSON.stringify(msg);if(isIE)msgTarget.doPost(msg,o);else msgTarget.postMessage(msg,o)}function onMessage(e){var d;try{d=JSON.parse(e.data)}catch(err){}if(!d||d.a!=="request")return;removeListener(window,"message",onMessage);o=e.origin;if(cb){setTimeout(function(){cb(o,d.d,function(r){cb=undefined;doPost({a:"response",d:r})})},0)}}function onDie(e){if(e.data===CLOSE_CMD){try{window.close()}catch(o_O){}}}addListener(isIE?msgTarget:window,"message",onMessage);addListener(isIE?msgTarget:window,"message",onDie);try{doPost({a:"ready"})}catch(e){addListener(msgTarget,"load",function(e){doPost({a:"ready"})})}var onUnload=function(){try{removeListener(isIE?msgTarget:window,"message",onDie)}catch(ohWell){}if(cb)doPost({a:"error",d:"client closed window"});cb=undefined;try{window.close()}catch(e){}};addListener(window,"unload",onUnload);return{detach:function(){removeListener(window,"unload",onUnload)}}}}}else{return{open:function(url,winopts,arg,cb){setTimeout(function(){cb("unsupported browser")},0)},onOpen:function(cb){setTimeout(function(){cb("unsupported browser")},0)}}}}();'; return "<!DOCTYPE html>\n <html>\n <head>\n <meta http-equiv='X-UA-Compatible' content='chrome=1' />\n </head>\n <body>\n <script type='text/javascript'>\n {$winchanjs}\n WinChan.onOpen(function(origin, args, cb) {\n cb(" . to_json($data) . ");\n });\n </script>\n </body>\n </html>"; } else { return $data; } } ob_start(); $opauth = new Opauth(array('path' => substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], 'oauth/') + 6), 'callback_url' => '{path}callback' . $query_params, 'callback_transport' => 'post', 'Strategy' => Config::get('oauth'), 'security_salt' => Context::getKey()->app->secret), false); $this->fixOauthStrategiesCallback($opauth, $query_params); $opauth->run(); $response = ob_get_contents(); ob_end_clean(); return $response; }