diff --git a/README.md b/README.md index e0f6dc36..514e7b6d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Installation ```json { "require": { - "parse/php-sdk" : "1.0.*" + "parse/php-sdk" : "1.1.*" } } ``` diff --git a/src/Parse/ParseClient.php b/src/Parse/ParseClient.php index d38d36f4..d85742a1 100755 --- a/src/Parse/ParseClient.php +++ b/src/Parse/ParseClient.php @@ -44,11 +44,16 @@ final class ParseClient */ private static $storage; + /** + * @var - Boolean for enabling revocable sessions. + */ + private static $forceRevocableSession = false; + /** * Constant for version string to include with requests. * @ignore */ - const VERSION_STRING = 'php1.0.6'; + const VERSION_STRING = 'php1.1.0'; /** * Parse\Client::initialize, must be called before using Parse features. @@ -56,7 +61,7 @@ final class ParseClient * @param string $app_id Parse Application ID * @param string $rest_key Parse REST API Key * @param string $master_key Parse Master Key - * @param string $enableCurlExceptions Enable or disable Parse curl exceptions + * @param boolean $enableCurlExceptions Enable or disable Parse curl exceptions * * @return null */ @@ -65,6 +70,7 @@ public static function initialize($app_id, $rest_key, $master_key, $enableCurlEx ParseUser::registerSubclass(); ParseRole::registerSubclass(); ParseInstallation::registerSubclass(); + ParseSession::registerSubclass(); self::$applicationId = $app_id; self::$restKey = $rest_key; self::$masterKey = $master_key; @@ -341,6 +347,9 @@ public static function _getRequestHeaders($sessionToken, $useMasterKey) } else { $headers[] = 'X-Parse-REST-API-Key: ' . self::$restKey; } + if (self::$forceRevocableSession) { + $headers[] = 'X-Parse-Revocable-Session: 1'; + } /** * Set an empty Expect header to stop the 100-continue behavior for post * data greater than 1024 bytes. @@ -384,4 +393,17 @@ public static function getLocalPushDateFormat($value) $date = date_format($value, $dateFormatString); return $date; } + + /** + * Allows an existing application to start using revocable sessions, without forcing + * all requests for the app to use them. After calling this method, login & signup requests + * will be returned a unique and revocable session token. + * + * @return null + */ + public static function enableRevocableSessions() + { + self::$forceRevocableSession = true; + } + } diff --git a/src/Parse/ParseObject.php b/src/Parse/ParseObject.php index 33e20d12..d701eeb9 100755 --- a/src/Parse/ParseObject.php +++ b/src/Parse/ParseObject.php @@ -532,7 +532,7 @@ public function _mergeAfterFetchWithSelectedKeys($result, $selectedKeys) private function mergeFromServer($data, $completeData = true) { $this->hasBeenFetched = ($this->hasBeenFetched || $completeData) ? true : false; - $this->mergeMagicFields($data); + $this->_mergeMagicFields($data); foreach ($data as $key => $value) { if ($key === '__type' && $value === 'className') { continue; @@ -567,7 +567,7 @@ private function mergeFromServer($data, $completeData = true) * * @return null */ - private function mergeMagicFields(&$data) + public function _mergeMagicFields(&$data) { if (isset($data['objectId'])) { $this->objectId = $data['objectId']; diff --git a/src/Parse/ParseSession.php b/src/Parse/ParseSession.php new file mode 100644 index 00000000..2c93f8c2 --- /dev/null +++ b/src/Parse/ParseSession.php @@ -0,0 +1,90 @@ + + */ +class ParseSession extends ParseObject +{ + + public static $parseClassName = "_Session"; + + private $_sessionToken = null; + + /** + * Returns the session token string. + * + * @return string + */ + public function getSessionToken() + { + return $this->_sessionToken; + } + + /** + * Retrieves the Session object for the currently logged in user. + * + * @param boolean $useMasterKey If the Master Key should be used to override security. + * + * @return ParseSession + */ + public static function getCurrentSession($useMasterKey = false) + { + $token = ParseUser::getCurrentUser()->getSessionToken(); + $response = ParseClient::_request('GET', '/1/sessions/me', $token, null, $useMasterKey); + $session = new ParseSession(); + $session->_mergeAfterFetch($response); + $session->handleSaveResult(); + return $session; + } + + + /** + * Determines whether the current session token is revocable. + * This method is useful for migrating an existing app to use + * revocable sessions. + * + * @return boolean + */ + public static function isCurrentSessionRevocable() + { + $user = ParseUser::getCurrentUser(); + if ($user) { + return self::_isRevocable($user->getSessionToken()); + } + } + + /** + * Determines whether a session token is revocable. + * + * @param string $token The session token to check + * + * @return boolean + */ + public static function _isRevocable($token) + { + return strpos($token, "r:") === 0; + } + + /** + * After a save, perform Session object specific logic. + * + * @return null + */ + private function handleSaveResult() + { + if (isset($this->serverData['sessionToken'])) { + $this->_sessionToken = $this->serverData['sessionToken']; + unset($this->serverData['sessionToken']); + } + $this->rebuildEstimatedData(); + } + +} \ No newline at end of file diff --git a/src/Parse/ParseUser.php b/src/Parse/ParseUser.php index f357a927..f2f4d72c 100644 --- a/src/Parse/ParseUser.php +++ b/src/Parse/ParseUser.php @@ -162,13 +162,20 @@ public static function become($sessionToken) /** * Log out the current user. This will clear the storage and future calls - * to current will return null + * to current will return null. + * This will make a network request to /1/logout to invalidate the session * * @return null */ public static function logOut() { - if (ParseUser::getCurrentUser()) { + $user = ParseUser::getCurrentUser(); + if ($user) { + try { + ParseClient::_request('POST', '/1/logout', $user->getSessionToken()); + } catch (ParseException $ex) { + // If this fails, we're going to ignore it. + } static::$currentUser = null; } ParseClient::getStorage()->remove('user'); diff --git a/tests/IncrementTest.php b/tests/IncrementTest.php index 2e35f8db..5f5650db 100644 --- a/tests/IncrementTest.php +++ b/tests/IncrementTest.php @@ -211,7 +211,7 @@ public function testIncrementEmptyFieldAndTypeConflict() $obj->save(); $objAgain->increment('randomkey'); $this->setExpectedException('Parse\ParseException', - "can't increment a field that isn't a number" + "invalid type for key" ); $objAgain->save(); } diff --git a/tests/ParseCloudTest.php b/tests/ParseCloudTest.php index c8749acc..04dc573e 100644 --- a/tests/ParseCloudTest.php +++ b/tests/ParseCloudTest.php @@ -31,13 +31,6 @@ public function testFunctionsWithGeoPointParamsDoNotThrow() ParseCloud::run('unknown_function', $params); } - public function testExplicitFunctionFailure() - { - $params = array('key1' => 'value1'); - $this->setExpectedException('Parse\ParseException','bad stuff happened'); - ParseCloud::run('bar', $params); - } - public function testUnknownFunctionFailure() { $params = array('key1' => 'value1'); @@ -45,49 +38,4 @@ public function testUnknownFunctionFailure() ParseCloud::run('unknown_function', $params); } - public function testFunctions() - { - $params = array( - 'key1' => 'value1', - 'key2' => array(1,2,3) - ); - $response = ParseCloud::run('foo', $params); - $obj = $response['object']; - $this->assertTrue($obj instanceof ParseObject); - $this->assertEquals('Foo', $obj->className); - $this->assertEquals(2, $obj->get('x')); - $relation = $obj->get('relation'); - $this->assertTrue($relation instanceof ParseObject); - $this->assertEquals('Bar', $relation->className); - $this->assertEquals(3, $relation->get('x')); - $obj = $response['array'][0]; - $this->assertTrue($obj instanceof ParseObject); - $this->assertEquals('Bar', $obj->className); - $this->assertEquals(2, $obj->get('x')); - - $response = ParseCloud::run('foo', array('key1' => 'value1')); - $this->assertEquals(2, $response['a']); - - try { - $response = ParseCloud::run('bar', array('key1' => 'value1')); - $this->fail('Should have thrown an exception.'); - } catch(Parse\ParseException $ex) { - // A parse exception should occur. - } - - $response = ParseCloud::run('bar', array('key2' => 'value1')); - $this->assertEquals('Foo', $response); - - $obj = ParseObject::create('SomeClass'); - $obj->set('name', 'Zanzibar'); - $obj->save(); - - $params = array('key2' => 'value1', 'key1' => $obj); - try { - $response = ParseCloud::run('foo', $params); - $this->fail('Should have thrown an exception.'); - } catch (\Exception $ex) { - // An exception should occur. - } - } } \ No newline at end of file diff --git a/tests/ParseObjectTest.php b/tests/ParseObjectTest.php index 89921ad6..2262d5a0 100644 --- a/tests/ParseObjectTest.php +++ b/tests/ParseObjectTest.php @@ -271,7 +271,7 @@ public function testCanSetBoolean() public function testInvalidClassName() { $obj = ParseObject::create('Foo^bar'); - $this->setExpectedException('Parse\ParseException', 'Bad Request'); + $this->setExpectedException('Parse\ParseException', 'bad characters in classname'); $obj->save(); } diff --git a/tests/ParseSessionTest.php b/tests/ParseSessionTest.php new file mode 100644 index 00000000..42db1846 --- /dev/null +++ b/tests/ParseSessionTest.php @@ -0,0 +1,61 @@ +setUsername("username"); + $user->setPassword("password"); + $user->signUp(); + $session = ParseSession::getCurrentSession(); + $this->assertEquals($user->getSessionToken(), $session->getSessionToken()); + $this->assertTrue($session->isCurrentSessionRevocable()); + + ParseUser::logOut(); + + ParseUser::logIn("username", "password"); + $session = ParseSession::getCurrentSession(); + $this->assertEquals(ParseUser::getCurrentUser()->getSessionToken(), $session->getSessionToken()); + $this->assertTrue($session->isCurrentSessionRevocable()); + + $sessionToken = $session->getSessionToken(); + + ParseUser::logOut(); + + $this->setExpectedException('Parse\ParseException', 'invalid session token'); + ParseUser::become($sessionToken); + } + +} \ No newline at end of file