diff --git a/.gitignore b/.gitignore index b62cb81d..4b27fc43 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ nbproject composer.lock composer.phar /vendor/* +phpunit.xml \ No newline at end of file diff --git a/application/configs/default/auth.php b/application/configs/default/auth.php index eecc4782..19d2ddcb 100644 --- a/application/configs/default/auth.php +++ b/application/configs/default/auth.php @@ -10,17 +10,5 @@ "encryptFunction" => function ($password, $salt) { return md5(md5($password) . $salt); } - ), - "facebook" => array( - "appId" => "%%appId%%", - "secret" => "%%secret%%", - ), - "twitter" => array( - "consumer_key" => "%%consumerKey%%", - "consumer_secret" => "%%consumerSecret%%" - ), - "google" => array( - 'client_id' => "%%client_id%%", - 'client_secret' => '%%client_secret%%' ) ); diff --git a/application/configs/default/hybridauth.php b/application/configs/default/hybridauth.php new file mode 100644 index 00000000..4653c3f2 --- /dev/null +++ b/application/configs/default/hybridauth.php @@ -0,0 +1,49 @@ + "%%domain%%/auth/endpoint", + + "providers" => array( + // google + "Google" => array( // 'id' is your google client id + "enabled" => true, + "wrapper" => array( "path" => "Providers/Google.php", "class" => "Hybrid_Providers_Google" ), + "keys" => array("id" => "%%client_id%%", + "secret" => "%%client_secret%%"), + "scope" => "https://www.googleapis.com/auth/userinfo.profile ". // optional + "https://www.googleapis.com/auth/userinfo.email" , // optional + "access_type" => "offline", // optional + "approval_prompt" => "force", // optional + ), + + // facebook + "Facebook" => array( // 'id' is your facebook application id + "enabled" => true, + "wrapper" => array( "path" => "Providers/Facebook.php", "class" => "Hybrid_Providers_Facebook" ), + "keys" => array("id" => "%%appId%%", "secret" => "%%secret%%"), + "scope" => "email, user_about_me, user_birthday, user_hometown, publish_actions", // optional + ), + + // twitter + "Twitter" => array( // 'key' is your twitter application consumer key + "enabled" => true, + "wrapper" => array( "path" => "Providers/Twitter.php", "class" => "Hybrid_Providers_Twitter" ), + "keys" => array("key" => "%%consumerKey%%", "secret" => "%%consumerSecret%%") + ) + ), + + "debug_mode" => false, + + // to enable logging, set 'debug_mode' to true, then provide here a path of a writable file + "debug_file" =>'' + ); diff --git a/application/library/Google/Client.php b/application/library/Google/Client.php deleted file mode 100644 index 0e36d9b5..00000000 --- a/application/library/Google/Client.php +++ /dev/null @@ -1,111 +0,0 @@ -domain, $config); - } - - /** - * @param $callbackUrl - * @return \Guzzle\Http\Url|string - */ - public function getAuthUrl($callbackUrl) - { - $request = $this->get( - $this->requestCode, - null, - [ - 'query' => [ - 'redirect_uri' => $callbackUrl, - 'response_type' => 'code', - 'client_id' => $this->getConfig('client_id'), - 'scope' => 'https://www.googleapis.com/auth/userinfo.email' . - ' https://www.googleapis.com/auth/userinfo.profile' - ] - ] - ); - - return $request->getUrl(); - } - - /** - * @param $code - * @param $callbackUrl - */ - public function getOauthAccessToken($code, $callbackUrl) - { - if (!isset($code)) { - throw new BadResponseException('Authorization failed'); - } - - $request = $this->post( - $this->requestAccessToken, - null, - [ - 'client_id' => $this->getConfig('client_id'), - 'client_secret' => $this->getConfig('client_secret'), - 'redirect_uri' => $callbackUrl, - 'grant_type' => 'authorization_code', - 'code' => $code - ] - ); - - $response = $request->send(); - $result = json_decode($response->getBody()->__toString(), true); - if (isset($result['access_token'])) { - $this->accessToken = $result['access_token']; - } else { - throw new BadResponseException('Authorization failed'); - } - } - - /** - * @return array - */ - public function getUserInfo() - { - if (!isset($this->accessToken)) { - throw new BadResponseException('Access token is missed'); - } - - $request = $this->get( - 'https://www.googleapis.com/oauth2/v1/userinfo', - null, - [ - 'query' => [ - 'access_token' => $this->accessToken - ] - ] - ); - - $response = $request->send(); - $result = json_decode($response->getBody()->__toString(), true); - if (isset($result['id'])) { - return $result; - } else { - throw new BadResponseException('Invalid access token'); - } - - } -} diff --git a/application/library/Twitter/Client.php b/application/library/Twitter/Client.php deleted file mode 100644 index a9d0ef0b..00000000 --- a/application/library/Twitter/Client.php +++ /dev/null @@ -1,127 +0,0 @@ -domain); - - /** - * Create Object. - * - * @param array $config - * @return OauthPlugin - */ - $this->oauthPlugin = new OauthPlugin($config); - $this->addSubscriber($this->oauthPlugin); - } - - /** - * Get request token from API - * - * $callbackUrl = Router::getFullUrl('twitter', 'callback'); - * $oauthRequestToken = $twitterAuth->getOauthRequestToken($callbackUrl); * - * - * @param string $callbackUrl - * @throws \Exception - * @return array - */ - public function getOauthRequestToken($callbackUrl) - { - $request = $this->get( - $this->requestToken, - null, - array( - 'query' => array( - 'oauth_callback' => $callbackUrl - ) - ) - ); - - - $response = $request->send(); - parse_str($response->getBody(), $result); - - if (!$result || !isset($result['oauth_token']) || empty($result['oauth_token'])) { - throw new \Exception("System can't retrieve token. Please check configuration of Twitter authorization"); - } - - return $result['oauth_token']; - } - - /** - * getOauthAccessToken - * - * $oauthToken = Request::getParam('oauth_token'); - * $oauthVerifier = Request::getParam('oauth_verifier'); - * $oauthTokenSecret = Session::get('oauthTokenSecret'); - * $oauthRequestToken = $twitterAuth->getOauthAccessToken($oauthToken, $oauthVerifier, $oauthTokenSecret); - * - * @param string $oauthToken - * @param string $oauthVerifier - * @param string $oauthTokenSecret - * @throws \Exception - * @return array - */ - public function getOauthAccessToken($oauthToken, $oauthVerifier, $oauthTokenSecret) - { - $request = $this->get( - $this->accessToken, - null, - array( - 'query' => array( - 'oauth_token' => $oauthToken, - 'oauth_verifier' => $oauthVerifier, - 'oauth_consumer_key' => $oauthTokenSecret - ) - ) - ); - - $response = $request->send(); - parse_str($response->getBody(), $result); - if (!$result || !isset($result['user_id']) || empty($result['user_id'])) { - throw new \Exception('User data is not received'); - } - - return $result; - } -} diff --git a/application/models/Auth/AuthInterface.php b/application/models/Auth/AuthInterface.php new file mode 100644 index 00000000..5c57cfd9 --- /dev/null +++ b/application/models/Auth/AuthInterface.php @@ -0,0 +1,44 @@ + + */ +namespace Application\Auth; + +use Bluz\Application\Exception\ApplicationException; +use Bluz\Proxy\Config; +use Bluz\Proxy\Messages; +use Application\Auth; +use Application\Users; + +/** + * Class AuthProvider + * @package Application\Auth + */ +class AuthProvider implements AuthInterface +{ + /** @var \Bluz\Http\Response */ + protected $response; + + /** @var \Application\Users\Row $identity */ + protected $identity; + + /** @var \Hybrid_Auth $hybridauth */ + protected $hybridauth; + + /** @var \Hybrid_Provider_Adapter $authAdapter */ + protected $authAdapter; + + /** + * the same name as was mentioned in hybridauth config section providers + * @var string + */ + protected $providerName; + + public function __construct($providerName) + { + if (!in_array(ucfirst($providerName), $this->getAvailableProviders())) { + throw new ApplicationException(sprintf('Provider % is not defined + in configuration file', ucfirst($providerName))); + } + $this->providerName = ucfirst($providerName); + } + + + /** + * @return \Hybrid_Auth + */ + public function getHybridauth() + { + if (!$this->hybridauth) { + $this->hybridauth = new \Hybrid_Auth($this->getOptions()); + } + + return $this->hybridauth; + } + + public function setHybridauth($hybridauth) + { + $this->hybridauth = $hybridauth; + } + + + /** + * @param \Bluz\Http\Response $response + */ + public function setResponse($response) + { + $this->response = $response; + } + + /** + * @return \Bluz\Http\Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * @param \Application\Users\Row $identity + */ + public function setIdentity($identity) + { + $this->identity = $identity; + } + + /** + * @return \Application\Users\Row $user + */ + public function getIdentity() + { + return $this->identity; + } + + /** + * @return string + */ + public function getProviderName() + { + return $this->providerName; + } + + /** + * @param string $providerName + */ + public function setProviderName($providerName) + { + $this->providerName = $providerName; + } + + /** + * @return \Hybrid_Provider_Adapter + * @throws \Exception + */ + public function getAuthAdapter() + { + if (!$this->authAdapter) { + /** @var \Hybrid_Provider_Adapter $authProvider */ + $this->authAdapter = $this->getHybridauth()->authenticate($this->providerName); + + if (!$this->authAdapter->isUserConnected()) { + throw new \Exception('Cannot connect to current provider !'); + } + } + + return $this->authAdapter; + } + + /** + * @param \Hybrid_Provider_Adapter $authAdapter + */ + public function setAuthAdapter($authAdapter) + { + $this->authAdapter = $authAdapter; + } + + + /** + * @param \Hybrid_User_Profile $data + * @param \Application\Users\Row $user + * @return void + */ + public function registration($data, $user) + { + $row = new Auth\Row(); + $row->userId = $user->id; + $row->provider = strtolower($this->providerName); + + $row->foreignKey = $data->identifier; + $row->token = $this->authAdapter->getAccessToken()['access_token']; + $row->tokenSecret = ($this->authAdapter->getAccessToken()['access_token_secret']) ? : ''; + $row->tokenType = Auth\Table::TYPE_ACCESS; + $row->save(); + + Messages::addNotice(sprintf('Your account was linked to %s successfully !', $this->providerName)); + $this->response->redirectTo('users', 'profile', ['id' => $user->id]); + } + + + /** + * @return void + */ + public function authProcess() + { + $this->authAdapter = $this->getAuthAdapter(); + $profile = $this->getProfile(); + + /** + * @var Auth\Table $authTable + */ + $authTable = Auth\Table::getInstance(); + $auth = $authTable->getAuthRow(strtolower($this->providerName), $profile->identifier); + + + if ($this->identity) { + if ($auth) { + Messages::addNotice(sprintf('You have already linked to %s', $this->providerName)); + $this->response->redirectTo('users', 'profile', ['id' => $this->identity->id]); + } else { + $user = Users\Table::findRow($this->identity->id); + $this->registration($profile, $user); + } + } + + if ($auth) { + $this->alreadyRegisteredLogic($auth); + } else { + Messages::addError(sprintf('First you need to be linked to %s', $this->providerName)); + $this->response->redirectTo('users', 'signin'); + } + } + + /** + * @return array + * @throws \Application\Exception + */ + public function getOptions() + { + return Config::getData('hybridauth'); + } + + /** + * @return array + */ + public function getAvailableProviders() + { + return array_keys(Config::getData('hybridauth')['providers']); + } + + /** + * @param $auth + * @return mixed + */ + public function alreadyRegisteredLogic($auth) + { + $user = Users\Table::findRow($auth->userId); + + if ($user->status != Users\Table::STATUS_ACTIVE) { + Messages::addError('User is not active'); + } + + $user->login(); + $this->response->redirectTo('index', 'index'); + } + + /** + * @return \Hybrid_User_Profile + */ + public function getProfile() + { + return $this->authAdapter->getUserProfile(); + } +} diff --git a/application/models/Facebook/BaseFacebook.php b/application/models/Facebook/BaseFacebook.php deleted file mode 100644 index c0f48f4b..00000000 --- a/application/models/Facebook/BaseFacebook.php +++ /dev/null @@ -1,1484 +0,0 @@ - - */ -abstract class BaseFacebook -{ - /** - * Version. - */ - const VERSION = '3.2.2'; - - /** - * Signed Request Algorithm. - */ - const SIGNED_REQUEST_ALGORITHM = 'HMAC-SHA256'; - - /** - * Default options for curl. - */ - public static $CURL_OPTS = array( - CURLOPT_CONNECTTIMEOUT => 10, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 60, - CURLOPT_USERAGENT => 'facebook-php-3.2', - ); - - /** - * List of query parameters that get automatically dropped when rebuilding - * the current URL. - */ - protected static $DROP_QUERY_PARAMS = array( - 'code', - 'state', - 'signed_request', - ); - - /** - * Maps aliases to Facebook domains. - */ - public static $DOMAIN_MAP = array( - 'api' => 'https://api.facebook.com/', - 'api_video' => 'https://api-video.facebook.com/', - 'api_read' => 'https://api-read.facebook.com/', - 'graph' => 'https://graph.facebook.com/', - 'graph_video' => 'https://graph-video.facebook.com/', - 'www' => 'https://www.facebook.com/', - ); - - /** - * The Application ID. - * - * @var string - */ - protected $appId; - - /** - * The Application App Secret. - * - * @var string - */ - protected $appSecret; - - /** - * The ID of the Facebook user, or 0 if the user is logged out. - * - * @var integer - */ - protected $user; - - /** - * The data from the signed_request token. - * - * @var array - */ - protected $signedRequest; - - /** - * A CSRF state variable to assist in the defense against CSRF attacks. - */ - protected $state; - - /** - * The OAuth access token received in exchange for a valid authorization - * code. null means the access token has yet to be determined. - * - * @var string - */ - protected $accessToken = null; - - /** - * Indicates if the CURL based @ syntax for file uploads is enabled. - * - * @var boolean - */ - protected $fileUploadSupport = false; - - /** - * Indicates if we trust HTTP_X_FORWARDED_* headers. - * - * @var boolean - */ - protected $trustForwarded = false; - - /** - * Initialize a Facebook Application. - * - * The configuration: - * - appId: the application ID - * - secret: the application secret - * - fileUpload: (optional) boolean indicating if file uploads are enabled - * - * @param array $config The application configuration - */ - public function __construct($config) - { - $this->setAppId($config['appId']); - $this->setAppSecret($config['secret']); - if (isset($config['fileUpload'])) { - $this->setFileUploadSupport($config['fileUpload']); - } - if (isset($config['trustForwarded']) && $config['trustForwarded']) { - $this->trustForwarded = true; - } - $state = $this->getPersistentData('state'); - if (!empty($state)) { - $this->state = $state; - } - } - - /** - * Set the Application ID. - * - * @param string $appId The Application ID - * @return BaseFacebook - */ - public function setAppId($appId) - { - $this->appId = $appId; - return $this; - } - - /** - * Get the Application ID. - * - * @return string the Application ID - */ - public function getAppId() - { - return $this->appId; - } - - /** - * Set the App Secret. - * - * @param string $apiSecret The App Secret - * @return BaseFacebook - * @deprecated - */ - public function setApiSecret($apiSecret) - { - $this->setAppSecret($apiSecret); - return $this; - } - - /** - * Set the App Secret. - * - * @param string $appSecret The App Secret - * @return BaseFacebook - */ - public function setAppSecret($appSecret) - { - $this->appSecret = $appSecret; - return $this; - } - - /** - * Get the App Secret. - * - * @return string the App Secret - * @deprecated - */ - public function getApiSecret() - { - return $this->getAppSecret(); - } - - /** - * Get the App Secret. - * - * @return string the App Secret - */ - public function getAppSecret() - { - return $this->appSecret; - } - - /** - * Set the file upload support status. - * - * @param boolean $fileUploadSupport The file upload support status. - * @return BaseFacebook - */ - public function setFileUploadSupport($fileUploadSupport) - { - $this->fileUploadSupport = $fileUploadSupport; - return $this; - } - - /** - * Get the file upload support status. - * - * @return boolean true if and only if the server supports file upload. - */ - public function getFileUploadSupport() - { - return $this->fileUploadSupport; - } - - /** - * DEPRECATED! Please use getFileUploadSupport instead. - * - * Get the file upload support status. - * - * @return boolean true if and only if the server supports file upload. - */ - public function useFileUploadSupport() - { - return $this->getFileUploadSupport(); - } - - /** - * Sets the access token for api calls. Use this if you get - * your access token by other means and just want the SDK - * to use it. - * - * @param string $access_token an access token. - * @return BaseFacebook - */ - public function setAccessToken($access_token) - { - $this->accessToken = $access_token; - return $this; - } - - /** - * Extend an access token, while removing the short-lived token that might - * have been generated via client-side flow. Thanks to http://bit.ly/b0Pt0H - * for the workaround. - * - * @return bool - */ - public function setExtendedAccessToken() - { - try { - // need to circumvent json_decode by calling _oauthRequest - // directly, since response isn't JSON format. - $access_token_response = $this->oauthRequest( - $this->getUrl('graph', '/oauth/access_token'), - $params = array( - 'client_id' => $this->getAppId(), - 'client_secret' => $this->getAppSecret(), - 'grant_type' => 'fb_exchange_token', - 'fb_exchange_token' => $this->getAccessToken(), - ) - ); - } catch (FacebookApiException $e) { - // most likely that user very recently revoked authorization. - // In any event, we don't have an access token, so say so. - return false; - } - - if (empty($access_token_response)) { - return false; - } - - $response_params = array(); - parse_str($access_token_response, $response_params); - - if (!isset($response_params['access_token'])) { - return false; - } - - $this->destroySession(); - - $this->setPersistentData( - 'access_token', - $response_params['access_token'] - ); - return true; - } - - /** - * Determines the access token that should be used for API calls. - * The first time this is called, $this->accessToken is set equal - * to either a valid user access token, or it's set to the application - * access token if a valid user access token wasn't available. Subsequent - * calls return whatever the first call returned. - * - * @return string The access token - */ - public function getAccessToken() - { - if ($this->accessToken !== null) { - // we've done this already and cached it. Just return. - return $this->accessToken; - } - - // first establish access token to be the application - // access token, in case we navigate to the /oauth/access_token - // endpoint, where SOME access token is required. - $this->setAccessToken($this->getApplicationAccessToken()); - $user_access_token = $this->getUserAccessToken(); - if ($user_access_token) { - $this->setAccessToken($user_access_token); - } - - return $this->accessToken; - } - - /** - * Determines and returns the user access token, first using - * the signed request if present, and then falling back on - * the authorization code if present. The intent is to - * return a valid user access token, or false if one is determined - * to not be available. - * - * @return string A valid user access token, or false if one - * could not be determined. - */ - protected function getUserAccessToken() - { - // first, consider a signed request if it's supplied. - // if there is a signed request, then it alone determines - // the access token. - $signed_request = $this->getSignedRequest(); - if ($signed_request) { - // apps.facebook.com hands the access_token in the signed_request - if (array_key_exists('oauth_token', $signed_request)) { - $access_token = $signed_request['oauth_token']; - $this->setPersistentData('access_token', $access_token); - return $access_token; - } - - // the JS SDK puts a code in with the redirect_uri of '' - if (array_key_exists('code', $signed_request)) { - $code = $signed_request['code']; - if ($code && $code == $this->getPersistentData('code')) { - // short-circuit if the code we have is the same as the one presented - return $this->getPersistentData('access_token'); - } - - $access_token = $this->getAccessTokenFromCode($code, ''); - if ($access_token) { - $this->setPersistentData('code', $code); - $this->setPersistentData('access_token', $access_token); - return $access_token; - } - } - - // signed request states there's no access token, so anything - // stored should be cleared. - $this->clearAllPersistentData(); - return false; // respect the signed request's data, even - // if there's an authorization code or something else - } - - $code = $this->getCode(); - if ($code && $code != $this->getPersistentData('code')) { - $access_token = $this->getAccessTokenFromCode($code); - if ($access_token) { - $this->setPersistentData('code', $code); - $this->setPersistentData('access_token', $access_token); - return $access_token; - } - - // code was bogus, so everything based on it should be invalidated. - $this->clearAllPersistentData(); - return false; - } - - // as a fallback, just return whatever is in the persistent - // store, knowing nothing explicit (signed request, authorization - // code, etc.) was present to shadow it (or we saw a code in $_REQUEST, - // but it's the same as what's in the persistent store) - return $this->getPersistentData('access_token'); - } - - /** - * Retrieve the signed request, either from a request parameter or, - * if not present, from a cookie. - * - * @return array the signed request, if available, or null otherwise. - */ - public function getSignedRequest() - { - if (!$this->signedRequest) { - if (!empty($_REQUEST['signed_request'])) { - $this->signedRequest = $this->parseSignedRequest( - $_REQUEST['signed_request'] - ); - } else { - if (!empty($_COOKIE[$this->getSignedRequestCookieName()])) { - $this->signedRequest = $this->parseSignedRequest( - $_COOKIE[$this->getSignedRequestCookieName()] - ); - } - } - } - return $this->signedRequest; - } - - /** - * Get the UID of the connected user, or 0 - * if the Facebook user is not connected. - * - * @return integer the UID if available. - */ - public function getUser() - { - if ($this->user !== null) { - // we've already determined this and cached the value. - return $this->user; - } - - return $this->user = $this->getUserFromAvailableData(); - } - - /** - * Determines the connected user by first examining any signed - * requests, then considering an authorization code, and then - * falling back to any persistent store storing the user. - * - * @return integer The id of the connected Facebook user, - * or 0 if no such user exists. - */ - protected function getUserFromAvailableData() - { - // if a signed request is supplied, then it solely determines - // who the user is. - $signed_request = $this->getSignedRequest(); - if ($signed_request) { - if (array_key_exists('user_id', $signed_request)) { - $user = $signed_request['user_id']; - - if ($user != $this->getPersistentData('user_id')) { - $this->clearAllPersistentData(); - } - - $this->setPersistentData('user_id', $signed_request['user_id']); - return $user; - } - - // if the signed request didn't present a user id, then invalidate - // all entries in any persistent store. - $this->clearAllPersistentData(); - return 0; - } - - $user = $this->getPersistentData('user_id', $default = 0); - $persisted_access_token = $this->getPersistentData('access_token'); - - // use access_token to fetch user id if we have a user access_token, or if - // the cached access token has changed. - $access_token = $this->getAccessToken(); - if ($access_token && - $access_token != $this->getApplicationAccessToken() && - !($user && $persisted_access_token == $access_token) - ) { - $user = $this->getUserFromAccessToken(); - if ($user) { - $this->setPersistentData('user_id', $user); - } else { - $this->clearAllPersistentData(); - } - } - - return $user; - } - - /** - * Get a Login URL for use with redirects. By default, full page redirect is - * assumed. If you are using the generated URL with a window.open() call in - * JavaScript, you can pass in display=popup as part of the $params. - * - * The parameters: - * - redirect_uri: the url to go to after a successful login - * - scope: comma separated list of requested extended perms - * - * @param array $params Provide custom parameters - * @return string The URL for the login flow - */ - public function getLoginUrl($params = array()) - { - $this->establishCSRFTokenState(); - $currentUrl = $this->getCurrentUrl(); - - // if 'scope' is passed as an array, convert to comma separated list - $scopeParams = isset($params['scope']) ? $params['scope'] : null; - if ($scopeParams && is_array($scopeParams)) { - $params['scope'] = implode(',', $scopeParams); - } - - return $this->getUrl( - 'www', - 'dialog/oauth', - array_merge( - array( - 'client_id' => $this->getAppId(), - 'redirect_uri' => $currentUrl, // possibly overwritten - 'state' => $this->state - ), - $params - ) - ); - } - - /** - * Get a Logout URL suitable for use with redirects. - * - * The parameters: - * - next: the url to go to after a successful logout - * - * @param array $params Provide custom parameters - * @return string The URL for the logout flow - */ - public function getLogoutUrl($params = array()) - { - return $this->getUrl( - 'www', - 'logout.php', - array_merge( - array( - 'next' => $this->getCurrentUrl(), - 'access_token' => $this->getUserAccessToken(), - ), - $params - ) - ); - } - - /** - * Get a login status URL to fetch the status from Facebook. - * - * The parameters: - * - ok_session: the URL to go to if a session is found - * - no_session: the URL to go to if the user is not connected - * - no_user: the URL to go to if the user is not signed into facebook - * - * @param array $params Provide custom parameters - * @return string The URL for the logout flow - */ - public function getLoginStatusUrl($params = array()) - { - return $this->getUrl( - 'www', - 'extern/login_status.php', - array_merge( - array( - 'api_key' => $this->getAppId(), - 'no_session' => $this->getCurrentUrl(), - 'no_user' => $this->getCurrentUrl(), - 'ok_session' => $this->getCurrentUrl(), - 'session_version' => 3, - ), - $params - ) - ); - } - - /** - * Make an API call. - * - * @return mixed The decoded response - */ - public function api() - { - $args = func_get_args(); - if (is_array($args[0])) { - return $this->restServer($args[0]); - } else { - return call_user_func_array(array($this, 'graph'), $args); - } - } - - /** - * Constructs and returns the name of the cookie that - * potentially houses the signed request for the app user. - * The cookie is not set by the BaseFacebook class, but - * it may be set by the JavaScript SDK. - * - * @return string the name of the cookie that would house - * the signed request value. - */ - protected function getSignedRequestCookieName() - { - return 'fbsr_' . $this->getAppId(); - } - - /** - * Constructs and returns the name of the coookie that potentially contain - * metadata. The cookie is not set by the BaseFacebook class, but it may be - * set by the JavaScript SDK. - * - * @return string the name of the cookie that would house metadata. - */ - protected function getMetadataCookieName() - { - return 'fbm_' . $this->getAppId(); - } - - /** - * Get the authorization code from the query parameters, if it exists, - * and otherwise return false to signal no authorization code was - * discoverable. - * - * @return mixed The authorization code, or false if the authorization - * code could not be determined. - */ - protected function getCode() - { - if (isset($_REQUEST['code'])) { - if ($this->state !== null && - isset($_REQUEST['state']) && - $this->state === $_REQUEST['state'] - ) { - // CSRF state has done its job, so clear it - $this->state = null; - $this->clearPersistentData('state'); - return $_REQUEST['code']; - } else { - self::errorLog('CSRF state token does not match one provided.'); - return false; - } - } - - return false; - } - - /** - * Retrieves the UID with the understanding that - * $this->accessToken has already been set and is - * seemingly legitimate. It relies on Facebook's Graph API - * to retrieve user information and then extract - * the user ID. - * - * @return integer Returns the UID of the Facebook user, or 0 - * if the Facebook user could not be determined. - */ - protected function getUserFromAccessToken() - { - try { - $user_info = $this->api('/me'); - return $user_info['id']; - } catch (FacebookApiException $e) { - return 0; - } - } - - /** - * Returns the access token that should be used for logged out - * users when no authorization code is available. - * - * @return string The application access token, useful for gathering - * public information about users and applications. - */ - protected function getApplicationAccessToken() - { - return $this->appId . '|' . $this->appSecret; - } - - /** - * Lays down a CSRF state token for this process. - * - * @return void - */ - protected function establishCSRFTokenState() - { - if ($this->state === null) { - $this->state = md5(uniqid(mt_rand(), true)); - $this->setPersistentData('state', $this->state); - } - } - - /** - * Retrieves an access token for the given authorization code - * (previously generated from www.facebook.com on behalf of - * a specific user). The authorization code is sent to graph.facebook.com - * and a legitimate access token is generated provided the access token - * and the user for which it was generated all match, and the user is - * either logged in to Facebook or has granted an offline access permission. - * - * @param string $code An authorization code. - * @param string $redirect_uri - * @return mixed An access token exchanged for the authorization code, or - * false if an access token could not be generated. - */ - protected function getAccessTokenFromCode($code, $redirect_uri = null) - { - if (empty($code)) { - return false; - } - - if ($redirect_uri === null) { - $redirect_uri = $this->getCurrentUrl(); - } - - try { - // need to circumvent json_decode by calling _oauthRequest - // directly, since response isn't JSON format. - $access_token_response = - $this->oauthRequest( - $this->getUrl('graph', '/oauth/access_token'), - $params = array( - 'client_id' => $this->getAppId(), - 'client_secret' => $this->getAppSecret(), - 'redirect_uri' => $redirect_uri, - 'code' => $code - ) - ); - } catch (FacebookApiException $e) { - // most likely that user very recently revoked authorization. - // In any event, we don't have an access token, so say so. - return false; - } - - if (empty($access_token_response)) { - return false; - } - - $response_params = array(); - parse_str($access_token_response, $response_params); - if (!isset($response_params['access_token'])) { - return false; - } - - return $response_params['access_token']; - } - - /** - * Invoke the old restserver.php endpoint. - * - * @param array $params Method call object - * - * @return mixed The decoded response object - * @throws FacebookApiException - */ - protected function restServer($params) - { - // generic application level parameters - $params['api_key'] = $this->getAppId(); - $params['format'] = 'json-strings'; - - $result = json_decode( - $this->oauthRequest( - $this->getApiUrl($params['method']), - $params - ), - true - ); - - // results are returned, errors are thrown - if (is_array($result) && isset($result['error_code'])) { - $this->throwAPIException($result); - // @codeCoverageIgnoreStart - } - // @codeCoverageIgnoreEnd - - $method = strtolower($params['method']); - if ($method === 'auth.expiresession' || - $method === 'auth.revokeauthorization' - ) { - $this->destroySession(); - } - - return $result; - } - - /** - * Return true if this is video post. - * - * @param string $path The path - * @param string $method The http method (default 'GET') - * - * @return boolean true if this is video post - */ - protected function isVideoPost($path, $method = 'GET') - { - if ($method == 'POST' && preg_match("/^(\/)(.+)(\/)(videos)$/", $path)) { - return true; - } - return false; - } - - /** - * Invoke the Graph API. - * - * @param string $path The path (required) - * @param string $method The http method (default 'GET') - * @param array $params The query/post data - * - * @return mixed The decoded response object - * @throws FacebookApiException - */ - protected function graph($path, $method = 'GET', $params = array()) - { - if (is_array($method) && empty($params)) { - $params = $method; - $method = 'GET'; - } - $params['method'] = $method; // method override as we always do a POST - - if ($this->isVideoPost($path, $method)) { - $domainKey = 'graph_video'; - } else { - $domainKey = 'graph'; - } - - $result = json_decode( - $this->oauthRequest( - $this->getUrl($domainKey, $path), - $params - ), - true - ); - - // results are returned, errors are thrown - if (is_array($result) && isset($result['error'])) { - $this->throwAPIException($result); - // @codeCoverageIgnoreStart - } - // @codeCoverageIgnoreEnd - - return $result; - } - - /** - * Make a OAuth Request. - * - * @param string $url The path (required) - * @param array $params The query/post data - * - * @return string The decoded response object - * @throws FacebookApiException - */ - protected function oauthRequest($url, $params) - { - if (!isset($params['access_token'])) { - $params['access_token'] = $this->getAccessToken(); - } - - if (isset($params['access_token'])) { - $params['appsecret_proof'] = $this->getAppSecretProof($params['access_token']); - } - - // json_encode all params values that are not strings - foreach ($params as $key => $value) { - if (!is_string($value)) { - $params[$key] = json_encode($value); - } - } - - return $this->makeRequest($url, $params); - } - - /** - * Generate a proof of App Secret - * This is required for all API calls originating from a server - * It is a sha256 hash of the access_token made using the app secret - * - * @param string $access_token The access_token to be hashed (required) - * - * @return string The sha256 hash of the access_token - */ - protected function getAppSecretProof($access_token) - { - return hash_hmac('sha256', $access_token, $this->getAppSecret()); - } - - /** - * Makes an HTTP request. This method can be overridden by subclasses if - * developers want to do fancier things or use something other than curl to - * make the request. - * - * @param string $url The URL to make the request to - * @param array $params The parameters to use for the POST body - * @param resource $ch Initialized curl handle - * - * @throws FacebookApiException - * @return string The response text - */ - protected function makeRequest($url, $params, $ch = null) - { - if (!$ch) { - $ch = curl_init(); - } - - $opts = self::$CURL_OPTS; - if ($this->getFileUploadSupport()) { - $opts[CURLOPT_POSTFIELDS] = $params; - } else { - $opts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&'); - } - $opts[CURLOPT_URL] = $url; - - // disable the 'Expect: 100-continue' behaviour. This causes CURL to wait - // for 2 seconds if the server does not support this header. - if (isset($opts[CURLOPT_HTTPHEADER])) { - $existing_headers = $opts[CURLOPT_HTTPHEADER]; - $existing_headers[] = 'Expect:'; - $opts[CURLOPT_HTTPHEADER] = $existing_headers; - } else { - $opts[CURLOPT_HTTPHEADER] = array('Expect:'); - } - - curl_setopt_array($ch, $opts); - $result = curl_exec($ch); - - if (curl_errno($ch) == 60) { -// CURLE_SSL_CACERT - self::errorLog( - 'Invalid or no certificate authority found, ' . - 'using bundled information' - ); - curl_setopt( - $ch, - CURLOPT_CAINFO, - dirname(__FILE__) . '/fb_ca_chain_bundle.crt' - ); - $result = curl_exec($ch); - } - - // With dual stacked DNS responses, it's possible for a server to - // have IPv6 enabled but not have IPv6 connectivity. If this is - // the case, curl will try IPv4 first and if that fails, then it will - // fall back to IPv6 and the error EHOSTUNREACH is returned by the - // operating system. - if ($result === false && empty($opts[CURLOPT_IPRESOLVE])) { - $matches = array(); - $regex = '/Failed to connect to ([^:].*): Network is unreachable/'; - if (preg_match($regex, curl_error($ch), $matches)) { - if (strlen(@inet_pton($matches[1])) === 16) { - self::errorLog( - 'Invalid IPv6 configuration on server, ' . - 'Please disable or get native IPv6 on your server.' - ); - self::$CURL_OPTS[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; - curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - $result = curl_exec($ch); - } - } - } - - if ($result === false) { - $e = new FacebookApiException( - array( - 'error_code' => curl_errno($ch), - 'error' => array( - 'message' => curl_error($ch), - 'type' => 'CurlException', - ), - ) - ); - curl_close($ch); - /** @var $e FacebookApiException */ - throw $e; - } - curl_close($ch); - return $result; - } - - /** - * Parses a signed_request and validates the signature. - * - * @param string $signed_request A signed token - * @return array The payload inside it or null if the sig is wrong - */ - protected function parseSignedRequest($signed_request) - { - list($encoded_sig, $payload) = explode('.', $signed_request, 2); - - // decode the data - $sig = self::base64UrlDecode($encoded_sig); - $data = json_decode(self::base64UrlDecode($payload), true); - - if (strtoupper($data['algorithm']) !== self::SIGNED_REQUEST_ALGORITHM) { - self::errorLog( - 'Unknown algorithm. Expected ' . self::SIGNED_REQUEST_ALGORITHM - ); - return null; - } - - // check sig - $expected_sig = hash_hmac( - 'sha256', - $payload, - $this->getAppSecret(), - $raw = true - ); - if ($sig !== $expected_sig) { - self::errorLog('Bad Signed JSON signature!'); - return null; - } - - return $data; - } - - /** - * Makes a signed_request blob using the given data. - * - * @param array $data The data array. - * @return string The signed request. - */ - protected function makeSignedRequest($data) - { - if (!is_array($data)) { - throw new \InvalidArgumentException( - 'makeSignedRequest expects an array. Got: ' . print_r($data, true) - ); - } - $data['algorithm'] = self::SIGNED_REQUEST_ALGORITHM; - $data['issued_at'] = time(); - $json = json_encode($data); - $b64 = self::base64UrlEncode($json); - $raw_sig = hash_hmac('sha256', $b64, $this->getAppSecret(), $raw = true); - $sig = self::base64UrlEncode($raw_sig); - return $sig . '.' . $b64; - } - - /** - * Build the URL for api given parameters. - * - * @param string $method String the method name. - * @return string The URL for the given parameters - */ - protected function getApiUrl($method) - { - static $READ_ONLY_CALLS = - array( - 'admin.getallocation' => 1, - 'admin.getappproperties' => 1, - 'admin.getbannedusers' => 1, - 'admin.getlivestreamvialink' => 1, - 'admin.getmetrics' => 1, - 'admin.getrestrictioninfo' => 1, - 'application.getpublicinfo' => 1, - 'auth.getapppublickey' => 1, - 'auth.getsession' => 1, - 'auth.getsignedpublicsessiondata' => 1, - 'comments.get' => 1, - 'connect.getunconnectedfriendscount' => 1, - 'dashboard.getactivity' => 1, - 'dashboard.getcount' => 1, - 'dashboard.getglobalnews' => 1, - 'dashboard.getnews' => 1, - 'dashboard.multigetcount' => 1, - 'dashboard.multigetnews' => 1, - 'data.getcookies' => 1, - 'events.get' => 1, - 'events.getmembers' => 1, - 'fbml.getcustomtags' => 1, - 'feed.getappfriendstories' => 1, - 'feed.getregisteredtemplatebundlebyid' => 1, - 'feed.getregisteredtemplatebundles' => 1, - 'fql.multiquery' => 1, - 'fql.query' => 1, - 'friends.arefriends' => 1, - 'friends.get' => 1, - 'friends.getappusers' => 1, - 'friends.getlists' => 1, - 'friends.getmutualfriends' => 1, - 'gifts.get' => 1, - 'groups.get' => 1, - 'groups.getmembers' => 1, - 'intl.gettranslations' => 1, - 'links.get' => 1, - 'notes.get' => 1, - 'notifications.get' => 1, - 'pages.getinfo' => 1, - 'pages.isadmin' => 1, - 'pages.isappadded' => 1, - 'pages.isfan' => 1, - 'permissions.checkavailableapiaccess' => 1, - 'permissions.checkgrantedapiaccess' => 1, - 'photos.get' => 1, - 'photos.getalbums' => 1, - 'photos.gettags' => 1, - 'profile.getinfo' => 1, - 'profile.getinfooptions' => 1, - 'stream.get' => 1, - 'stream.getcomments' => 1, - 'stream.getfilters' => 1, - 'users.getinfo' => 1, - 'users.getloggedinuser' => 1, - 'users.getstandardinfo' => 1, - 'users.hasapppermission' => 1, - 'users.isappuser' => 1, - 'users.isverified' => 1, - 'video.getuploadlimits' => 1 - ); - $name = 'api'; - if (isset($READ_ONLY_CALLS[strtolower($method)])) { - $name = 'api_read'; - } elseif (strtolower($method) == 'video.upload') { - $name = 'api_video'; - } - return self::getUrl($name, 'restserver.php'); - } - - /** - * Build the URL for given domain alias, path and parameters. - * - * @param string $name string The name of the domain - * @param $path string Optional path (without a leading slash) - * @param $params array Optional query parameters - * - * @return string The URL for the given parameters - */ - protected function getUrl($name, $path = '', $params = array()) - { - $url = self::$DOMAIN_MAP[$name]; - if ($path) { - if ($path[0] === '/') { - $path = substr($path, 1); - } - $url .= $path; - } - if ($params) { - $url .= '?' . http_build_query($params, null, '&'); - } - - return $url; - } - - protected function getHttpHost() - { - if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { - return $_SERVER['HTTP_X_FORWARDED_HOST']; - } - return $_SERVER['HTTP_HOST']; - } - - protected function getHttpProtocol() - { - if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { - if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { - return 'https'; - } - return 'http'; - } - /*apache + variants specific way of checking for https*/ - if (isset($_SERVER['HTTPS']) && - ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1) - ) { - return 'https'; - } - /*nginx way of checking for https*/ - if (isset($_SERVER['SERVER_PORT']) && - ($_SERVER['SERVER_PORT'] === '443') - ) { - return 'https'; - } - return 'http'; - } - - /** - * Get the base domain used for the cookie. - */ - protected function getBaseDomain() - { - // The base domain is stored in the metadata cookie if not we fallback - // to the current hostname - $metadata = $this->getMetadataCookie(); - if (array_key_exists('base_domain', $metadata) && - !empty($metadata['base_domain']) - ) { - return trim($metadata['base_domain'], '.'); - } - return $this->getHttpHost(); - } - - /** - * Returns the Current URL, stripping it of known FB parameters that should - * not persist. - * - * @return string The current URL - */ - protected function getCurrentUrl() - { - $protocol = $this->getHttpProtocol() . '://'; - $host = $this->getHttpHost(); - $currentUrl = $protocol . $host . $_SERVER['REQUEST_URI']; - $parts = parse_url($currentUrl); - - $query = ''; - if (!empty($parts['query'])) { - // drop known fb params - $params = explode('&', $parts['query']); - $retained_params = array(); - foreach ($params as $param) { - if ($this->shouldRetainParam($param)) { - $retained_params[] = $param; - } - } - - if (!empty($retained_params)) { - $query = '?' . implode($retained_params, '&'); - } - } - - // use port if non default - $port = - isset($parts['port']) && - (($protocol === 'http://' && $parts['port'] !== 80) || - ($protocol === 'https://' && $parts['port'] !== 443)) - ? ':' . $parts['port'] : ''; - - // rebuild - return $protocol . $parts['host'] . $port . $parts['path'] . $query; - } - - /** - * Returns true if and only if the key or key/value pair should - * be retained as part of the query string. This amounts to - * a brute-force search of the very small list of Facebook-specific - * params that should be stripped out. - * - * @param string $param A key or key/value pair within a URL's query (e.g. - * 'foo=a', 'foo=', or 'foo'. - * - * @return boolean - */ - protected function shouldRetainParam($param) - { - foreach (self::$DROP_QUERY_PARAMS as $drop_query_param) { - if (strpos($param, $drop_query_param . '=') === 0) { - return false; - } - } - - return true; - } - - /** - * Analyzes the supplied result to see if it was thrown - * because the access token is no longer valid. If that is - * the case, then we destroy the session. - * - * @param $result array A record storing the error message returned - * by a failed API call. - * @throws FacebookApiException - */ - protected function throwAPIException($result) - { - $e = new FacebookApiException($result); - - switch ($e->getType()) { - // OAuth 2.0 Draft 00 style - case 'OAuthException': - // OAuth 2.0 Draft 10 style - case 'invalid_token': - // REST server errors are just Exceptions - case 'Exception': - $message = $e->getMessage(); - if ((strpos($message, 'Error validating access token') !== false) || - (strpos($message, 'Invalid OAuth access token') !== false) || - (strpos($message, 'An active access token must be used') !== false) - ) { - $this->destroySession(); - } - break; - } - - /** @var $e FacebookApiException */ - throw $e; - } - - - /** - * Prints to the error log if you aren't in command line mode. - * - * @param string $msg Log message - */ - protected static function errorLog($msg) - { - // disable error log if we are running in a CLI environment - // @codeCoverageIgnoreStart - if (php_sapi_name() != 'cli') { - error_log($msg); - } - // uncomment this if you want to see the errors on the page - // print 'error_log: '.$msg."\n"; - // @codeCoverageIgnoreEnd - } - - /** - * Base64 encoding that doesn't need to be urlencode()ed. - * Exactly the same as base64_encode except it uses - * - instead of + - * _ instead of / - * No padded = - * - * @param string $input base64UrlEncoded string - * @return string - */ - protected static function base64UrlDecode($input) - { - return base64_decode(strtr($input, '-_', '+/')); - } - - /** - * Base64 encoding that doesn't need to be urlencode()ed. - * Exactly the same as base64_encode except it uses - * - instead of + - * _ instead of / - * - * @param string $input string - * @return string base64Url encoded string - */ - protected static function base64UrlEncode($input) - { - $str = strtr(base64_encode($input), '+/', '-_'); - $str = str_replace('=', '', $str); - return $str; - } - - /** - * Destroy the current session - */ - public function destroySession() - { - $this->accessToken = null; - $this->signedRequest = null; - $this->user = null; - $this->clearAllPersistentData(); - - // Javascript sets a cookie that will be used in getSignedRequest that we - // need to clear if we can - $cookie_name = $this->getSignedRequestCookieName(); - if (array_key_exists($cookie_name, $_COOKIE)) { - unset($_COOKIE[$cookie_name]); - if (!headers_sent()) { - $base_domain = $this->getBaseDomain(); - setcookie($cookie_name, '', 1, '/', '.' . $base_domain); - } else { - // @codeCoverageIgnoreStart - self::errorLog( - 'There exists a cookie that we wanted to clear that we couldn\'t ' . - 'clear because headers was already sent. Make sure to do the first ' . - 'API call before outputing anything.' - ); - // @codeCoverageIgnoreEnd - } - } - } - - /** - * Parses the metadata cookie that our Javascript API set - * - * @return array mapping key to value - */ - protected function getMetadataCookie() - { - $cookie_name = $this->getMetadataCookieName(); - if (!array_key_exists($cookie_name, $_COOKIE)) { - return array(); - } - - // The cookie value can be wrapped in "-characters so remove them - $cookie_value = trim($_COOKIE[$cookie_name], '"'); - - if (empty($cookie_value)) { - return array(); - } - - $parts = explode('&', $cookie_value); - $metadata = array(); - foreach ($parts as $part) { - $pair = explode('=', $part, 2); - if (!empty($pair[0])) { - $metadata[urldecode($pair[0])] = - (count($pair) > 1) ? urldecode($pair[1]) : ''; - } - } - - return $metadata; - } - - protected static function isAllowedDomain($big, $small) - { - if ($big === $small) { - return true; - } - return self::endsWith($big, '.' . $small); - } - - /** - * @param string $big - * @param string $small - * @return bool - */ - protected static function endsWith($big, $small) - { - $len = strlen($small); - if ($len === 0) { - return true; - } - return substr($big, -$len) === $small; - } - - /** - * Each of the following four methods should be overridden in - * a concrete subclass, as they are in the provided Facebook class. - * The Facebook class uses PHP sessions to provide a primitive - * persistent store, but another subclass--one that you implement-- - * might use a database, memcache, or an in-memory cache. - * - * @see Facebook - */ - - /** - * Stores the given ($key, $value) pair, so that future calls to - * getPersistentData($key) return $value. This call may be in another request. - * - * @param string $key - * @param mixed $value - * - * @return void - */ - abstract protected function setPersistentData($key, $value); - - /** - * Get the data for $key, persisted by BaseFacebook::setPersistentData() - * - * @param string $key The key of the data to retrieve - * @param boolean $default The default value to return if $key is not found - * - * @return mixed - */ - abstract protected function getPersistentData($key, $default = false); - - /** - * Clear the data with $key from the persistent storage - * - * @param string $key - * @return void - */ - abstract protected function clearPersistentData($key); - - /** - * Clear all data from the persistent storage - * - * @return void - */ - abstract protected function clearAllPersistentData(); -} diff --git a/application/models/Facebook/Facebook.php b/application/models/Facebook/Facebook.php deleted file mode 100644 index bcbae52f..00000000 --- a/application/models/Facebook/Facebook.php +++ /dev/null @@ -1,174 +0,0 @@ -initSharedSession(); - } - } - - protected static $kSupportedKeys = - array('state', 'code', 'access_token', 'user_id'); - - protected function initSharedSession() - { - $cookie_name = $this->getSharedSessionCookieName(); - if (isset($_COOKIE[$cookie_name])) { - $data = $this->parseSignedRequest($_COOKIE[$cookie_name]); - if ($data && !empty($data['domain']) && - self::isAllowedDomain($this->getHttpHost(), $data['domain']) - ) { - // good case - $this->sharedSessionID = $data['id']; - return; - } - // ignoring potentially unreachable data - } - // evil/corrupt/missing case - $base_domain = $this->getBaseDomain(); - $this->sharedSessionID = md5(uniqid(mt_rand(), true)); - $cookie_value = $this->makeSignedRequest( - array( - 'domain' => $base_domain, - 'id' => $this->sharedSessionID, - ) - ); - $_COOKIE[$cookie_name] = $cookie_value; - if (!headers_sent()) { - $expire = time() + self::FBSS_COOKIE_EXPIRE; - setcookie($cookie_name, $cookie_value, $expire, '/', '.' . $base_domain); - } else { - // @codeCoverageIgnoreStart - self::errorLog( - 'Shared session ID cookie could not be set! You must ensure you ' . - 'create the Facebook instance before headers have been sent. This ' . - 'will cause authentication issues after the first request.' - ); - // @codeCoverageIgnoreEnd - } - } - - /** - * Provides the implementations of the inherited abstract - * methods. The implementation uses PHP sessions to maintain - * a store for authorization codes, user ids, CSRF states, and - * access tokens. - */ - protected function setPersistentData($key, $value) - { - if (!in_array($key, self::$kSupportedKeys)) { - self::errorLog('Unsupported key passed to setPersistentData.'); - return; - } - - $session_var_name = $this->constructSessionVariableName($key); - $_SESSION[$session_var_name] = $value; - } - - protected function getPersistentData($key, $default = false) - { - if (!in_array($key, self::$kSupportedKeys)) { - self::errorLog('Unsupported key passed to getPersistentData.'); - return $default; - } - - $session_var_name = $this->constructSessionVariableName($key); - return isset($_SESSION[$session_var_name]) ? - $_SESSION[$session_var_name] : $default; - } - - protected function clearPersistentData($key) - { - if (!in_array($key, self::$kSupportedKeys)) { - self::errorLog('Unsupported key passed to clearPersistentData.'); - return; - } - - $session_var_name = $this->constructSessionVariableName($key); - unset($_SESSION[$session_var_name]); - } - - protected function clearAllPersistentData() - { - foreach (self::$kSupportedKeys as $key) { - $this->clearPersistentData($key); - } - if ($this->sharedSessionID) { - $this->deleteSharedSessionCookie(); - } - } - - protected function deleteSharedSessionCookie() - { - $cookie_name = $this->getSharedSessionCookieName(); - unset($_COOKIE[$cookie_name]); - $base_domain = $this->getBaseDomain(); - setcookie($cookie_name, '', 1, '/', '.' . $base_domain); - } - - protected function getSharedSessionCookieName() - { - return self::FBSS_COOKIE_NAME . '_' . $this->getAppId(); - } - - /** - * @param string $key - * @return string - */ - protected function constructSessionVariableName($key) - { - $parts = array('fb', $this->getAppId(), $key); - if ($this->sharedSessionID) { - array_unshift($parts, $this->sharedSessionID); - } - return implode('_', $parts); - } -} diff --git a/application/models/Facebook/FacebookApiException.php b/application/models/Facebook/FacebookApiException.php deleted file mode 100644 index bf7fa253..00000000 --- a/application/models/Facebook/FacebookApiException.php +++ /dev/null @@ -1,96 +0,0 @@ - - */ -class FacebookApiException extends \Exception -{ - /** - * The result from the API server that represents the exception information. - */ - protected $result; - - /** - * Make a new API Exception with the given result. - * - * @param array $result The result from the API server - */ - public function __construct($result) - { - $this->result = $result; - - $code = isset($result['error_code']) ? $result['error_code'] : 0; - - if (isset($result['error_description'])) { - // OAuth 2.0 Draft 10 style - $msg = $result['error_description']; - } else { - if (isset($result['error']) && is_array($result['error'])) { - // OAuth 2.0 Draft 00 style - $msg = $result['error']['message']; - } else { - if (isset($result['error_msg'])) { - // Rest server style - $msg = $result['error_msg']; - } else { - $msg = 'Unknown Error. Check getResult()'; - } - } - } - - parent::__construct($msg, $code); - } - - /** - * Return the associated result object returned by the API server. - * - * @return array The result from the API server - */ - public function getResult() - { - return $this->result; - } - - /** - * Returns the associated type for the error. This will default to - * 'Exception' when a type is not available. - * - * @return string - */ - public function getType() - { - if (isset($this->result['error'])) { - $error = $this->result['error']; - if (is_string($error)) { - // OAuth 2.0 Draft 10 style - return $error; - } else { - if (is_array($error)) { - // OAuth 2.0 Draft 00 style - if (isset($error['type'])) { - return $error['type']; - } - } - } - } - - return 'Exception'; - } - - /** - * To make debugging easier. - * - * @return string The string representation of the error - */ - public function __toString() - { - $str = $this->getType() . ': '; - if ($this->code != 0) { - $str .= $this->code . ': '; - } - return $str . $this->message; - } -} diff --git a/application/models/Users/Crud.php b/application/models/Users/Crud.php index 2b13d591..de6afcbd 100644 --- a/application/models/Users/Crud.php +++ b/application/models/Users/Crud.php @@ -63,44 +63,6 @@ public function createOne($data) // create auth Auth\Table::getInstance()->generateEquals($row, $password); - //check if user tried to sign in via oauth before - if ($twitter = Session::get('twitter')) { - Session::delete('twitter'); - - $twitterRow = new Auth\Row(); - $twitterRow->userId = $userId; - $twitterRow->provider = Auth\Table::PROVIDER_TWITTER; - $twitterRow->foreignKey = $twitter['user_id']; - $twitterRow->token = $twitter['oauth_token']; - $twitterRow->tokenSecret = $twitter['oauth_token_secret']; - $twitterRow->tokenType = Auth\Table::TYPE_ACCESS; - $twitterRow->save(); - } - if ($facebook = Session::get('facebook')) { - Session::delete('facebook'); - - $facebookRow = new Auth\Row(); - $facebookRow->userId = $userId; - $facebookRow->provider = Auth\Table::PROVIDER_FACEBOOK; - $facebookRow->foreignKey = $facebook['id']; - $facebookRow->token = 0; - $facebookRow->tokenSecret = 0; - $facebookRow->tokenType = Auth\Table::TYPE_ACCESS; - $facebookRow->save(); - } - if ($google = Session::get('google')) { - Session::delete('google'); - - $googleRow = new Auth\Row(); - $googleRow->userId = $userId; - $googleRow->provider = Auth\Table::PROVIDER_GOOGLE; - $googleRow->foreignKey = $google['id']; - $googleRow->token = $google['access_token']; - $googleRow->tokenSecret = 0; - $googleRow->tokenType = Auth\Table::TYPE_ACCESS; - $googleRow->save(); - } - // create activation token // valid for 5 days $actionRow = UsersActions\Table::getInstance()->generate($userId, UsersActions\Table::ACTION_ACTIVATION, 5); diff --git a/application/modules/auth/controllers/auth.php b/application/modules/auth/controllers/auth.php new file mode 100644 index 00000000..987c2f54 --- /dev/null +++ b/application/modules/auth/controllers/auth.php @@ -0,0 +1,40 @@ +setResponse($this); + $auth->setIdentity($this->user()); + $auth->authProcess(); + } catch (Exception $e) { + Messages::addError($e->getMessage()); + } + + return + function () { + return false; + }; + + }; diff --git a/application/modules/auth/controllers/endpoint.php b/application/modules/auth/controllers/endpoint.php new file mode 100644 index 00000000..f7c2bb70 --- /dev/null +++ b/application/modules/auth/controllers/endpoint.php @@ -0,0 +1,20 @@ + - * @created 22.05.2013 13:12 - */ - -/** - * @namespace - */ -namespace Application; - -use Application\Auth; -use Application\Facebook; -use Application\Users; -use Bluz\Proxy\Config; -use Bluz\Proxy\Messages; -use Bluz\Proxy\Request; -use Bluz\Proxy\Session; - -return -/** - * @throws Exception - * @return void - */ -function () { - /** - * @var Bootstrap $this - */ - $options = Config::getData('auth', 'facebook'); - if (!$options || !isset($options['appId'], $options['secret']) - || empty($options['appId']) || empty($options['secret'])) { - throw new Exception('Facebook authorization is not configured'); - } - - // redirect sign in user to index page and init session - if ($this->user()) { - $this->redirectTo('index', 'index'); - } - - $facebook = new Facebook\Facebook( - array( - 'appId' => $options['appId'], - 'secret' => $options['secret'], - ) - ); - /** - * Should be return id of user, if he allows application. - * In false returned 0. - */ - - $facebook->destroySession(); - $user = $facebook->getUser(); - - if ($user) { - // Proceed knowing you have a logged in user who's authenticated. - $profile = $facebook->api('/me'); - - /** - * @var Auth\Table $authTable - */ - $authTable = Auth\Table::getInstance(); - $row = $authTable->getAuthRow(Auth\Table::PROVIDER_FACEBOOK, $profile['id']); - - if ($row) { - // if user has been registered - $user = Users\Table::findRow($row->userId); - - if ($user->status != Users\Table::STATUS_ACTIVE) { - Messages::addError('User is not active'); - $this->redirectTo('index', 'index'); - } - - $user->login(); - } else { - // sign up user - - if (!$user = $this->user()) { - /** - * Write facebook response to session - */ - Session::set('facebook', $profile); - Messages::addNotice('To finish your registration fill the form'); - $this->redirectTo('users', 'signup'); - } else { - $row = new Auth\Row(); - $row->userId = $user->id; - $row->provider = Auth\Table::PROVIDER_FACEBOOK; - $row->foreignKey = $profile['id']; - $row->token = $facebook->getAccessToken(); - $row->tokenSecret = 0; - $row->tokenType = Auth\Table::TYPE_ACCESS; - $row->save(); - } - } - } else { - /** - * If user doesn't allow application yet, redirect him to fb page for this. - * After this operation we will returned to this file. - * Is user declined app, we get param 'error' => 'access_denied' - */ - - // if user declined - if ('access_denied' == Request::getParam('error', null)) { - $this->redirectTo('users', 'signin'); - } - - $login_url = $facebook->getLoginUrl(array('scope' => 'email')); - $this->redirect($login_url); - } - - $this->redirectTo('index', 'index'); -}; diff --git a/application/modules/google/controllers/auth.php b/application/modules/google/controllers/auth.php deleted file mode 100644 index 6f0180e4..00000000 --- a/application/modules/google/controllers/auth.php +++ /dev/null @@ -1,20 +0,0 @@ -redirect($googleAuth->getAuthUrl($callbackUrl)); -}; diff --git a/application/modules/google/controllers/callback.php b/application/modules/google/controllers/callback.php deleted file mode 100644 index a91dca37..00000000 --- a/application/modules/google/controllers/callback.php +++ /dev/null @@ -1,81 +0,0 @@ -getOauthAccessToken($code, $callbackUrl); - $result = $googleAuth->getUserInfo(); - - $authTable = Auth\Table::getInstance(); - $row = $authTable->getAuthRow(Auth\Table::PROVIDER_GOOGLE, $result['id']); - - if ($row) { - $user = Users\Table::findRow($row->userId); - - /** - * Check the status of the user - */ - if ($user->status != Users\Table::STATUS_ACTIVE) { - Messages::addError('User is not active'); - $this->redirectTo('index', 'index'); - } - - /** - * Update tokens - */ - $row->token = $googleAuth->accessToken; - $row->save(); - - $user->login(); - } else { - if (!$user = $this->user()) { - /** - * Write twitter response to session - */ - $result['access_token'] = $googleAuth->accessToken; - Session::set('google', $result); - Messages::addNotice('To finish your registration fill the form'); - $this->redirectTo('users', 'signup'); - } else { - /** - * @var Auth\Row $row - * Users exists - save user info. - */ - $row = new Auth\Row(); - $row->userId = $user->id; - $row->provider = Auth\Table::PROVIDER_GOOGLE; - $row->foreignKey = $result['id']; - $row->token = $googleAuth->accessToken; - $row->tokenSecret = 0; - $row->tokenType = Auth\Table::TYPE_ACCESS; - $row->save(); - } - } - - Messages::addNotice('You are signed'); - $this->redirectTo('index', 'index'); - } catch (GuzzleException $e) { - Messages::addError($e->getMessage()); - $this->redirectTo('index', 'index'); - } -}; diff --git a/application/modules/index/controllers/index.php b/application/modules/index/controllers/index.php index d91a12cb..6554dbcd 100644 --- a/application/modules/index/controllers/index.php +++ b/application/modules/index/controllers/index.php @@ -6,17 +6,15 @@ * @created 06.07.11 18:39 * @return \Closure */ - /** * @namespace */ namespace Application; return -/** - * @var Bootstrap $this - * @return void - */ -function () { - -}; + /** + * @var Bootstrap $this + * @return void + */ + function () { + }; diff --git a/application/modules/twitter/controllers/auth.php b/application/modules/twitter/controllers/auth.php deleted file mode 100644 index cfa42712..00000000 --- a/application/modules/twitter/controllers/auth.php +++ /dev/null @@ -1,45 +0,0 @@ -getOauthRequestToken($callbackUrl); - $this->redirect('https://api.twitter.com/oauth/authenticate?oauth_token='.$token); - } catch (GuzzleException $e) { - Messages::addError($e->getMessage()); - } -}; diff --git a/application/modules/twitter/controllers/callback.php b/application/modules/twitter/controllers/callback.php deleted file mode 100644 index 4a56eaec..00000000 --- a/application/modules/twitter/controllers/callback.php +++ /dev/null @@ -1,130 +0,0 @@ - string - * 'oauth_token_secret' => string - * 'user_id' => string - * 'screen_name' => string - * ) - * Try to sign in - */ - try { - $result = $twitterAuth->getOauthAccessToken($oauthToken, $oauthVerifier, $oauthTokenSecret); - - /** - * @var Auth\Table $authTable - */ - $authTable = Auth\Table::getInstance(); - - /** - * Try to load previous information - * @var \Application\Auth\Row $row - */ - $row = $authTable->getAuthRow(Auth\Table::PROVIDER_TWITTER, $result['user_id']); - - if ($row) { - /** - * Try to sign in - * @var Users\Row $user - */ - $user = Users\Table::findRow($row->userId); - - /** - * Check the status of the user - */ - if ($user->status != Users\Table::STATUS_ACTIVE) { - Messages::addError('User is not active'); - $this->redirectTo('index', 'index'); - } - - /** - * Update tokens - */ - $row->token = $result['oauth_token']; - $row->tokenSecret = $result['oauth_token_secret']; - $row->tokenType = Auth\Table::TYPE_ACCESS; - $row->save(); - - /** - * sign in. - */ - $user->login(); - } else { - /** - * if user already signed - link new auth provider to account - * another - create new user - */ - if (!$user = $this->user()) { - /** - * Write twitter response to session - */ - Session::set('twitter', $result); - Messages::addNotice('To finish your registration fill the form'); - $this->redirectTo('users', 'signup'); - } else { - /** - * @var Auth\Row $row - * Users exists - save user info. - */ - $row = new Auth\Row(); - $row->userId = $user->id; - $row->provider = Auth\Table::PROVIDER_TWITTER; - $row->foreignKey = $result['user_id']; - $row->token = $result['oauth_token']; - $row->tokenSecret = $result['oauth_token_secret']; - $row->tokenType = Auth\Table::TYPE_ACCESS; - $row->save(); - } - } - - Messages::addNotice('You are signed'); - $this->redirectTo('index', 'index'); - } catch (GuzzleException $e) { - Messages::addError($e->getMessage()); - $this->redirectTo('index', 'index'); - } - - return false; -}; diff --git a/application/modules/users/controllers/profile.php b/application/modules/users/controllers/profile.php index c4cad2c7..0ae55ac9 100644 --- a/application/modules/users/controllers/profile.php +++ b/application/modules/users/controllers/profile.php @@ -8,6 +8,7 @@ namespace Application; use Application\Users; +use Bluz\Proxy\Config; use Bluz\Proxy\Layout; return diff --git a/application/modules/users/controllers/signout.php b/application/modules/users/controllers/signout.php index 3e05613c..355517e0 100644 --- a/application/modules/users/controllers/signout.php +++ b/application/modules/users/controllers/signout.php @@ -21,6 +21,7 @@ function () use ($view) { * @var Bootstrap $this * @var \Bluz\View\View $view */ + AppAuth\Table::getInstance()->removeCookieToken($this->user()->id); Auth::clearIdentity(); Messages::addNotice('You are signout'); diff --git a/application/modules/users/views/profile.phtml b/application/modules/users/views/profile.phtml index 68ad32e6..c0a24199 100644 --- a/application/modules/users/views/profile.phtml +++ b/application/modules/users/views/profile.phtml @@ -4,19 +4,47 @@ * @var \Bluz\View\View $this */ ?> + -
-
-
login?>
-
-
email?>
-
-
status?>
-
+ +
+
+
+ + avatar + +
+
+ + + + + + + + + + + + + + + +
email ?>
status ?>
+ + + +
+
+
+
diff --git a/application/modules/users/views/signin.phtml b/application/modules/users/views/signin.phtml index 65e2dfd4..70de4d73 100644 --- a/application/modules/users/views/signin.phtml +++ b/application/modules/users/views/signin.phtml @@ -34,13 +34,13 @@ diff --git a/composer.json b/composer.json index ec2a6a38..4dbe06da 100644 --- a/composer.json +++ b/composer.json @@ -1,49 +1,49 @@ { - "name": "bluzphp/skeleton", - "description": "Skeleton application for Bluz, a lightweight PHP framework", - "minimum-stability": "dev", - "type": "library", - "require": { - "php": ">=5.4", - "bluzphp/framework": "~0.9", - "phpmailer/phpmailer": "~5.2", - "guzzle/guzzle": "~3.8", - "zircote/swagger-php": "~0.9" + "name": "bluzphp/skeleton", + "description": "Skeleton application for Bluz, a lightweight PHP framework", + "minimum-stability": "dev", + "type": "library", + "require": { + "php": ">=5.4", + "bluzphp/framework": "~0.9", + "phpmailer/phpmailer": "~5.2", + "zircote/swagger-php": "~0.9", + "hybridauth/hybridauth": "dev-master" + }, + "require-dev": { + "phpunit/phpunit": "~4.4", + "satooshi/php-coveralls": "~0.6", + "squizlabs/php_codesniffer": "~2.0", + "zendframework/zend-dom": "~2.3" + }, + "config": { + "vendor-dir": "vendor" + }, + "autoload": { + "psr-0": { + "": "application/library" }, - "require-dev": { - "phpunit/phpunit": "~4.4", - "satooshi/php-coveralls": "~0.6", - "squizlabs/php_codesniffer": "~2.0", - "zendframework/zend-dom": "~2.3" + "psr-4": { + "Application\\": "application/models", + "Application\\Tests\\": "tests/library" }, - "config": { - "vendor-dir": "vendor" - }, - "autoload": { - "psr-0": { - "": "application/library" - }, - "psr-4": { - "Application\\": "application/models", - "Application\\Tests\\": "tests/library" - }, - "files": [ - "application/_functions.php", - "application/_loader.php" - ] - }, - "scripts": { - "post-install-cmd": "php bin/install.php" - }, - "authors": [ - { - "name": "Bluz Skeleton Contributors", - "homepage": "https://github.com/bluzphp/skeleton/graphs/contributors" - } - ], - "support": { - "issues": "https://github.com/bluzphp/skeleton/issues", - "wiki": "https://github.com/bluzphp/skeleton/wiki" - }, - "license": "MIT" + "files": [ + "application/_functions.php", + "application/_loader.php" + ] + }, + "scripts": { + "post-install-cmd": "php bin/install.php" + }, + "authors": [ + { + "name": "Bluz Skeleton Contributors", + "homepage": "https://github.com/bluzphp/skeleton/graphs/contributors" + } + ], + "support": { + "issues": "https://github.com/bluzphp/skeleton/issues", + "wiki": "https://github.com/bluzphp/skeleton/wiki" + }, + "license": "MIT" } diff --git a/public/.gitignore b/public/.gitignore new file mode 100644 index 00000000..3d0dbe44 --- /dev/null +++ b/public/.gitignore @@ -0,0 +1 @@ +tests/ \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 00dd1682..e63dbddc 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,14 +2,10 @@ // Environment error_reporting(E_ALL | E_STRICT); ini_set('display_errors', 1); - // Emulate session $_SESSION = array(); $_COOKIE[session_name()] = uniqid(); - require_once dirname(__DIR__) . '/vendor/autoload.php'; - $env = getenv('BLUZ_ENV') ?: 'testing'; - $app = \Application\Tests\BootstrapTest::getInstance(); -$app->init($env); +$app->init($env); \ No newline at end of file diff --git a/tests/library/TestCase.php b/tests/library/TestCase.php index 8a711ae7..1a10a51e 100644 --- a/tests/library/TestCase.php +++ b/tests/library/TestCase.php @@ -23,6 +23,7 @@ * @author Anton Shevchuk * @created 04.08.11 20:01 */ + class TestCase extends BluzTest { /** diff --git a/tests/models/Auth/AuthProviderTest.php b/tests/models/Auth/AuthProviderTest.php new file mode 100644 index 00000000..45b7a223 --- /dev/null +++ b/tests/models/Auth/AuthProviderTest.php @@ -0,0 +1,109 @@ +setArray( + [ + 'id' => 1, + 'login' => 'Donatello', + 'email' => 'donatello@turtles.org', + 'status' => 'pending' + ] + )->execute(); + + Db::insert('users')->setArray( + [ + 'id' => 2, + 'login' => 'Bill', + 'email' => 'bill@turtles.org', + 'status' => 'active' + ] + )->execute(); + + Auth::setIdentity(new \Application\Users\Row()); + } + + protected function tearDown() + { + Db::delete('users')->where('id IN (?)', [1, 2])->execute(); + Messages::popAll(); + } + + /** + * @expectedException \Exception + */ + + public function testProviderNotFound() + { + new AuthProvider('fake_data'); + } + + /** + * @expectedException \Bluz\Auth\AuthException + */ + public function testUserStatusNotActive() + { + $provider = new AuthProvider('Facebook'); + $authRow = new Row(); + $authRow->userId = 1; + $provider->alreadyRegisteredLogic($authRow); + } + + public function testUserStatusActive() + { + $provider = new AuthProvider('Facebook'); + $provider->setResponse($this->getApp()); + $authRow = new Row(); + $authRow->userId = 2; + try { + $provider->alreadyRegisteredLogic($authRow); + } catch (RedirectException $e) { + } + $this->assertNotNull(Auth::getIdentity()); + + } + + + /** + * @expectedException \Exception + */ + public function testFailureHybridProvider() + { + $provider = new AuthProvider('olo'); + $this->assertInstanceOf('\Hybrid_Provider_Adapter', $provider->authenticate('olo')); + } + + public function testOptions() + { + $provider = new AuthProvider('Facebook'); + $this->assertNotEmpty($provider->getOptions()); + } + + + public function testAvailableProviders() + { + $provider = new AuthProvider('Facebook'); + $this->assertContains("Facebook", $provider->getAvailableProviders()); + } +} diff --git a/tests/modules/auth/controllers/AuthTest.php b/tests/modules/auth/controllers/AuthTest.php new file mode 100644 index 00000000..3c831c9d --- /dev/null +++ b/tests/modules/auth/controllers/AuthTest.php @@ -0,0 +1,138 @@ +hybridAuthMock = $this->getMockBuilder('\Hybrid_Auth') + ->setMethods(['authenticate']) + ->disableOriginalConstructor() + ->getMock(); + + $this->authAdapterMock = $this->getMockBuilder('\Hybrid_Provider_Adapter') + ->setMethods(['getUserProfile']) + ->disableOriginalConstructor() + ->getMock(); + + Db::insert('users')->setArray( + [ + 'id' => 2, + 'login' => 'Bill', + 'email' => 'bill@turtles.org', + 'status' => 'active' + ] + )->execute(); + + Db::insert('auth')->setArray( + [ + 'provider' => 'facebook', + 'userId' => 2, + 'foreignKey' => 112233 + ] + )->execute(); + + Auth::setIdentity(new \Application\Users\Row()); + } + + protected function tearDown() + { + Db::delete('users')->where('id IN (?)', [2])->execute(); + Db::delete('auth')->where('userId IN (?)', [2])->execute(); + Messages::popAll(); + } + + public function testUserAlreadyLinkedTo() + { + + $identity = new \Application\Users\Row(); + $identity->id = 2; + + $userProfile = new \Hybrid_User_Profile(); + $userProfile->identifier = 112233; + + $this->authAdapterMock->method('getUserProfile') + ->willReturn($userProfile); + + $this->hybridAuthMock->method('authenticate') + ->willReturn(new \Hybrid_Provider_Adapter); + + $this->assertInstanceOf('\Hybrid_Auth', $this->hybridAuthMock); + + $provider = new AuthProvider('Facebook'); + $provider->setResponse($this->getApp()); + $provider->setIdentity($identity); + $provider->setHybridauth($this->hybridAuthMock); + $provider->setAuthAdapter($this->authAdapterMock); + try { + $provider->authProcess(); + } catch (RedirectException $red) { + } catch (\Exception $e) { + } + + $message = Messages::pop(); + $this->assertEquals("You have already linked to Facebook", $message->text); + + } + + public function testUserNotLinkedTo() + { + $userProfile = new \Hybrid_User_Profile(); + $userProfile->identifier = null; + + $this->hybridAuthMock->method('authenticate') + ->willReturn(new \Hybrid_Provider_Adapter); + + $this->authAdapterMock->method('getUserProfile') + ->willReturn($userProfile); + + $this->assertInstanceOf('\Hybrid_Auth', $this->hybridAuthMock); + + $provider = new AuthProvider('Facebook'); + $provider->setResponse($this->getApp()); + $provider->setHybridauth($this->hybridAuthMock); + $provider->setAuthAdapter($this->authAdapterMock); + try { + $provider->authProcess(); + } catch (RedirectException $red) { + } catch (\Exception $e) { + } + + $message = Messages::pop(); + $this->assertEquals("First you need to be linked to Facebook", $message->text); + + } +} diff --git a/tests/modules/facebook/controllers/AuthTest.php b/tests/modules/facebook/controllers/AuthTest.php deleted file mode 100644 index a51fd2d2..00000000 --- a/tests/modules/facebook/controllers/AuthTest.php +++ /dev/null @@ -1,47 +0,0 @@ -dispatchRouter('/facebook/auth/'); - */ - - // Remove the following lines when you implement this test. - // Need to refactoring Facebook library for avoid used global dependency - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * If user already signed - redirect to homepage - */ - public function testControllerPageWithUser() - { - $this->setupSuperUserIdentity(); - - $this->dispatchRouter('/facebook/auth/'); - $this->assertRedirect('index', 'index'); - } -}