[Cs-webapplibs-commits] SF.net SVN: cs-webapplibs:[112] trunk/0.3
Status: Beta
Brought to you by:
crazedsanity
From: <cra...@us...> - 2009-08-19 19:01:31
|
Revision: 112 http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=112&view=rev Author: crazedsanity Date: 2009-08-19 19:01:21 +0000 (Wed, 19 Aug 2009) Log Message: ----------- Can authenticate against a given token. /cs_authToken.class.php: * create_token(): -- ARG CHANGE: NEW ARG: #4 ($lifetime=null) -- ARG CHANGE: NEW ARG: #5 ($maxUses=null) -- allow setting of duration ($lifetime) or max_uses ($maxUses) when creating the token. -- update the $insertData array with 'duration' or 'max_uses' data if appropriate, based on args #4 and #5 * update_token_uses() [NEW]: -- updates the number of times a given token has been used. * destroy_token() [NEW]: -- deletes a specific token. * authenticate_token() [NEW]: -- authenticates the given data against a token. -- this handles updating uses & destroying tokens that have been used their maximum number of times: this means that mass token expiration can be done based purely on creation + duration. * get_token_data() [NEW]: -- returns data about the given token. /setup/authtoken_schema.pgsql.sql: * cswal_auth_token_table: -- changed "use_limit" to "max_uses" for sanity. /tests/testOfAuthToken.php: * additional tests for authentication. Modified Paths: -------------- trunk/0.3/cs_authToken.class.php trunk/0.3/setup/authtoken_schema.pgsql.sql trunk/0.3/tests/testOfAuthToken.php Modified: trunk/0.3/cs_authToken.class.php =================================================================== --- trunk/0.3/cs_authToken.class.php 2009-08-19 17:44:04 UTC (rev 111) +++ trunk/0.3/cs_authToken.class.php 2009-08-19 19:01:21 UTC (rev 112) @@ -72,15 +72,19 @@ //========================================================================= - public function create_token($uid, $checksum, $stringToHash) { + public function create_token($uid, $checksum, $stringToHash, $lifetime=null, $maxUses=null) { - $this->db->beginTrans(); - $insertData = array( 'uid' => $uid, 'checksum' => $checksum, 'token' => '____INCOMPLETE____' ); + if(!is_null($lifetime) && strlen($lifetime)) { + $insertData['duration'] = $lifetime; + } + if(!is_null($maxUses) && is_numeric($maxUses) && $maxUses > 0) { + $insertData['max_uses'] = $maxUses; + } try { $sql = "INSERT INTO cswal_auth_token_table ". $this->gfObj->string_from_array($insertData, 'insert', null, 'sql'); @@ -105,5 +109,128 @@ }//end create_token() //========================================================================= + + + //========================================================================= + protected function update_token_uses($tokenId) { + + try { + $sql = "UPDATE ". $this->table ." SET total_uses= total_uses+1 " . + "WHERE auth_token_id=". $tokenId; + $updateRes = $this->db->run_update($sql); + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to update usage count::: ". $e->getMessage()); + } + return($updateRes); + }//end update_token_uses() + //========================================================================= + + + + //========================================================================= + protected function destroy_token($tokenId) { + try { + $sql = "DELETE FROM ". $this->table ." WHERE auth_token_id=". $tokenId; + $deleteRes = $this->db->run_update($sql); + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to "); + } + + return($deleteRes); + }//end destroy_token() + //========================================================================= + + + + //========================================================================= + /** + * Determine if a token is authentic: the id is used to make the search as + * fast as possible, while the hash & checksum are given to compare against. + * Failure results in FALSE, while success returns the contact_id for the + * given token. + * + * NOTE: the calling program can leave it to this method to say if the + * token is authentic, or use a checksum which can in turn be used to get + * a specific contact_id; when they authenticate, the return of this + * method must then match the contact_id retrieved from the checksum... + * + * EXAMPLE: + * $tokenUid = cs_authToken::authenticate_token($tokenId, $hash, $checksum); + * $realUid = userClass::get_uid_from_email($checksum); + * if($tokenUid == $realUid) { + * //token is truly authentic + * } + */ + public function authenticate_token($tokenId, $checksum, $hash) { + + $authTokenRes = null; + + if(is_numeric($tokenId) && strlen($checksum) && strlen($hash) == 32) { + $sql = "SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId + ." AND (creation + duration)::date >= CURRENT_DATE"; + + try { + $data = $this->db->run_query($sql, 'auth_token_id'); + + if(count($data) == 1 && isset($data[$tokenId]) && is_array($data[$tokenId])) { + $data = $data[$tokenId]; + + if($data['token'] == $hash && $data['checksum'] == $checksum) { + + $methodCall = 'update_token_uses'; + if(is_numeric($data['max_uses'])) { + $authTokenRes = null; + if($data['max_uses'] == $data['total_uses']) { + //reached max uses already... (maybe this should throw an exception?) + $methodCall = 'destroy_token'; + } + elseif($data['total_uses'] < $data['max_uses']) { + $authTokenRes = $data['uid']; + if(($data['total_uses'] +1) == $data['max_uses']) { + //this is the last use: just destroy it. + $methodCall = 'destroy_token'; + } + } + else { + throw new exception(__METHOD__ .": token (". $tokenId .") used more than max allowed uses [total=". $data['total_uses'] .", max=". $data['max_uses'] ."]"); + } + } + else { + $authTokenRes = $data['uid']; + } + $this->$methodCall($tokenId); + } + } + elseif($data === false) { + $authTokenRes = null; + } + else { + throw new exception(__METHOD__ .": invalid data returned:: ". $this->gfObj->debug_var_dump($data,0)); + } + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to authenticate token::: ". $e->getMessage()); + } + } + + return($authTokenRes); + }//end authenticate_token() + //========================================================================= + + + //========================================================================= + protected function get_token_data($tokenId) { + try { + $data = $this->db->run_query("SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId); + } + catch(exception $e) { + throw new exception(__METHOD__ .": failed to retrieve tokenId (". $tokenId .")::: ". $e->getMessage()); + } + return($data); + }//end get_token_data(); + //========================================================================= + } ?> Modified: trunk/0.3/setup/authtoken_schema.pgsql.sql =================================================================== --- trunk/0.3/setup/authtoken_schema.pgsql.sql 2009-08-19 17:44:04 UTC (rev 111) +++ trunk/0.3/setup/authtoken_schema.pgsql.sql 2009-08-19 19:01:21 UTC (rev 112) @@ -13,7 +13,7 @@ uid integer NOT NULL DEFAULT 0, checksum text NOT NULL, token text NOT NULL, - use_limit integer DEFAULT NULL, + max_uses integer DEFAULT NULL, total_uses integer NOT NULL DEFAULT 0, creation date NOT NULL DEFAULT NOW(), duration interval NOT NULL DEFAULT '1 day'::interval Modified: trunk/0.3/tests/testOfAuthToken.php =================================================================== --- trunk/0.3/tests/testOfAuthToken.php 2009-08-19 17:44:04 UTC (rev 111) +++ trunk/0.3/tests/testOfAuthToken.php 2009-08-19 19:01:21 UTC (rev 112) @@ -69,19 +69,62 @@ //-------------------------------------------------------------------------- function test_basics() { $db = $this->create_db(); - $tok = new cs_authToken($db); + $tok = new authTokenTester($db); //Generic test to ensure we get the appropriate data back. - $tokenData = $tok->create_token(0, 'test', 'abc123'); + $tokenData = $tok->create_token(1, 'test', 'abc123'); $this->assertTrue(is_array($tokenData)); $this->assertTrue((count($tokenData) == 2)); $this->assertTrue(isset($tokenData['id'])); $this->assertTrue(isset($tokenData['hash'])); $this->assertTrue(($tokenData['id'] > 0)); $this->assertTrue((strlen($tokenData['hash']) == 32)); + + $this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1); + + + //now create a token with a maximum lifetime... + { + //Generic test to ensure we get the appropriate data back. + $tokenData = $tok->create_token(1, 'test', 'abc123', '2 years'); + $this->assertTrue(is_array($tokenData)); + $this->assertTrue((count($tokenData) == 2)); + $this->assertTrue(isset($tokenData['id'])); + $this->assertTrue(isset($tokenData['hash'])); + $this->assertTrue(($tokenData['id'] > 0)); + $this->assertTrue((strlen($tokenData['hash']) == 32)); + + $this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1); + } + + //create a token with only 1 available use and try to authenticate it twice. + { + //Generic test to ensure we get the appropriate data back. + $tokenData = $tok->create_token(1, 'test', 'abc123', null, 1); + $this->assertTrue(is_array($tokenData)); + $this->assertTrue((count($tokenData) == 2)); + $this->assertTrue(isset($tokenData['id'])); + $this->assertTrue(isset($tokenData['hash'])); + $this->assertTrue(($tokenData['id'] > 0)); + $this->assertTrue((strlen($tokenData['hash']) == 32)); + + if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) { + $this->gfObj->debug_print($tok->tokenData($tokenData['id']),1); + } + if(!$this->assertTrue(($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']) === null), "Able to authenticate twice on a token with only 1 use")) { + $this->gfObj->debug_print($tok->tokenData($tokenData['id'])); + } + } }//end test_basics() //-------------------------------------------------------------------------- } +class authTokenTester extends cs_authToken { + public $isTest=true; + + public function tokenData($id) { + return($this->get_token_data($id)); + } +} ?> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |