Browse Source

Refactor tokens.

mscherer 5 years ago
parent
commit
89b02e4325

+ 61 - 0
config/Migrations/20200430170235_MigrationToolsTokens.php

@@ -0,0 +1,61 @@
+<?php
+use Migrations\AbstractMigration;
+
+class MigrationToolsTokens extends AbstractMigration {
+
+	/**
+	 * Change Method.
+	 *
+	 * More information on this method is available here:
+	 * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
+	 *
+	 * @return void
+	 */
+	public function change() {
+		$this->table('tokens')
+			->addColumn('user_id', 'integer', [
+				'limit' => null,
+				'null' => true,
+			])
+			->addColumn('type', 'string', [
+				'comment' => 'e.g.:activate,reactivate',
+				'default' => null,
+				'limit' => 20,
+				'null' => false,
+			])
+			->addColumn('token', 'string', [
+				'default' => null,
+				'limit' => 60,
+				'null' => false,
+			])
+			->addColumn('content', 'string', [
+				'comment' => 'can transport some information',
+				'default' => null,
+				'limit' => 255,
+				'null' => true,
+			])
+			->addColumn('used', 'integer', [
+				'default' => 0,
+				'limit' => null,
+				'null' => false,
+			])
+			->addColumn('created', 'datetime', [
+				'default' => null,
+				'limit' => null,
+				'null' => true,
+			])
+			->addColumn('modified', 'datetime', [
+				'default' => null,
+				'limit' => null,
+				'null' => true,
+			])
+			->addIndex(
+				[
+					'user_id',
+				]
+			)
+			->addIndex(['token'], ['unique' => true])
+			->create();
+	}
+
+}

+ 57 - 0
docs/Model/Tokens.md

@@ -0,0 +1,57 @@
+# Tokens
+
+Easily easily manage (store, retrieve, validate) tokens.
+They are useful in the registration process of users,
+or if you want to send some double-opt-in confirmation emails, for example.
+
+The main methods of the model are
+* newKey(string $type, ?string $key = null, $uid = null, $content = null)
+* useKey(string $type, string $key, $uid = null)
+* spendKey(int $id)
+
+User and security relevant token usage should always be bound to the id of the user (user_id).
+Other operations can also omit this field.
+
+## Install
+```
+bin/cake migrations migrate -p Tools
+```
+If you need a different table schema, e.g. for user_id to be UUID, you can copy
+over the migration file and customize. In that case execute it then without the plugin option.
+
+## Usage for registration
+
+### Register action
+```php
+$this->loadModel('Tools.Tokens');
+$cCode = $this->Tokens->newKey('activate', null, $user->id);
+```
+
+### Activate action
+```php
+$this->loadModel('Tools.Tokens');
+$token = $this->Tokens->useKey('activate', $keyToCheck);
+
+if ($token && $token->used) {
+    $this->Flash->warning(__('alreadyActivatedMessage'));
+} elseif ($token) {
+    $uid = $token->user_id;
+    // Confirm activation and redirect to home
+}
+```
+
+## Other usage
+
+
+
+## Garbage Collect
+From Cronjob/CLI
+```php
+$this->loadModel('Tools.Tokens');
+$this->Tokens->garbageCollector();
+```
+
+## Upgrade Notes from 3.x
+If you come from 3.x:
+- The field `key` is now `token` to avoid SQL reserved keyword issue.
+- Typehints are now in place.

+ 3 - 0
docs/README.md

@@ -23,6 +23,9 @@ Auth
 Email
 * [Email](Mailer/Email.md) for sending Emails
 
+Tokens
+* [Tokens](Model/Tokens.md) for Token usage
+
 Controller:
 * [Controller](Controller/Controller.md)
 

+ 16 - 1
src/Model/Entity/Token.php

@@ -6,7 +6,8 @@ namespace Tools\Model\Entity;
  * @property int $id
  * @property int $user_id
  * @property string $type
- * @property string $key
+ * @property string $token
+ * @property-read string $key
  * @property string $content
  * @property int $used
  * @property \Cake\I18n\Time $created
@@ -14,4 +15,18 @@ namespace Tools\Model\Entity;
  * @property bool $unlimited
  */
 class Token extends Entity {
+
+	/**
+	 * Shim to allow ->key access for ->token.
+	 *
+	 * @deprecated Use token instead.
+	 *
+	 * @return string|null
+	 */
+	public function _getKey(): ?string {
+		trigger_error('Deprecated. Use ->token instead.', E_USER_DEPRECATED);
+
+		return $this->token;
+	}
+
 }

+ 26 - 44
src/Model/Table/TokensTable.php

@@ -3,7 +3,6 @@
 namespace Tools\Model\Table;
 
 use Cake\Utility\Hash;
-use Tools\Utility\Random;
 
 /**
  * A generic model to hold tokens
@@ -35,12 +34,12 @@ class TokensTable extends Table {
 	/**
 	 * @var int
 	 */
-	public $defaultLength = 22;
+	public $defaultLength = 30;
 
 	/**
 	 * @var int
 	 */
-	public $validity = MONTH;
+	public $validity = WEEK;
 
 	/**
 	 * @var array
@@ -52,15 +51,16 @@ class TokensTable extends Table {
 				'message' => 'valErrMandatoryField',
 			],
 		],
-		'key' => [
+		'token' => [
 			'notBlank' => [
 				'rule' => ['notBlank'],
 				'message' => 'valErrMandatoryField',
 				'last' => true,
 			],
 			'isUnique' => [
-				'rule' => ['isUnique'],
+				'rule' => ['validateUnique'],
 				'message' => 'valErrTokenExists',
+				'provider' => 'table',
 			],
 		],
 		'content' => [
@@ -79,22 +79,18 @@ class TokensTable extends Table {
 	 * Checks if this key is already used (should be unique in table)
 	 *
 	 * @param string $type Type: necessary
-	 * @param string|null $key Key: optional key, otherwise a key will be generated
+	 * @param string|null $token Key: optional key, otherwise a key will be generated
 	 * @param mixed|null $uid Uid: optional (if used, only this user can use this key)
 	 * @param string|array|null $content Content: up to 255 characters of content may be added (optional)
 	 *
 	 * @return string|null Key on success, null otherwise
 	 */
-	public function newKey($type, $key = null, $uid = null, $content = null): ?string {
-		if (!$type) {
-			return null;
-		}
-
-		if (!$key) {
-			$key = $this->generateKey($this->defaultLength);
+	public function newKey(string $type, ?string $token = null, $uid = null, $content = null): ?string {
+		if (!$token) {
+			$token = $this->generateKey($this->defaultLength);
 			$keyLength = $this->defaultLength;
 		} else {
-			$keyLength = mb_strlen($key);
+			$keyLength = mb_strlen($token);
 		}
 
 		if (is_array($content)) {
@@ -105,20 +101,20 @@ class TokensTable extends Table {
 			'type' => $type,
 			'user_id' => $uid,
 			'content' => (string)$content,
-			'key' => $key,
+			'token' => $token,
 		];
 
 		$entity = $this->newEntity($data);
 		$max = 99;
 		while (!$this->save($entity)) {
-			$entity['key'] = $this->generateKey($keyLength);
+			$entity->token = $this->generateKey($keyLength);
 			$max--;
 			if ($max === 0) {
 				return null;
 			}
 		}
 
-		return $entity['key'];
+		return $entity->token;
 	}
 
 	/**
@@ -126,15 +122,12 @@ class TokensTable extends Table {
 	 *
 	 * @param string $type : necessary
 	 * @param string $key : necessary
-	 * @param mixed|null $uid : needs to be provided if this key has a user_id stored
+	 * @param int|string|null $uid : needs to be provided if this key has a user_id stored
 	 * @param bool $treatUsedAsInvalid
 	 * @return \Tools\Model\Entity\Token|null Content - if successfully used or if already used (used=1), NULL otherwise.
 	 */
-	public function useKey($type, $key, $uid = null, $treatUsedAsInvalid = false) {
-		if (!$type || !$key) {
-			return null;
-		}
-		$options = ['conditions' => [$this->getAlias() . '.key' => $key, $this->getAlias() . '.type' => $type]];
+	public function useKey(string $type, string $key, $uid = null, $treatUsedAsInvalid = false) {
+		$options = ['conditions' => [$this->getAlias() . '.token' => $key, $this->getAlias() . '.type' => $type]];
 		if ($uid) {
 			$options['conditions'][$this->getAlias() . '.user_id'] = $uid;
 		}
@@ -143,12 +136,12 @@ class TokensTable extends Table {
 		if (!$tokenEntity) {
 			return null;
 		}
-		if ($uid && !empty($tokenEntity['user_id']) && $tokenEntity['user_id'] != $uid) {
+		if ($uid && !empty($tokenEntity->user_id) && $tokenEntity->user_id != $uid) {
 			// return $res; # more secure to fail here if user_id is not provided, but was submitted prev.
 			return null;
 		}
 		// already used?
-		if (!empty($tokenEntity['used'])) {
+		if (!empty($tokenEntity->used)) {
 			if ($treatUsedAsInvalid) {
 				return null;
 			}
@@ -156,11 +149,11 @@ class TokensTable extends Table {
 			return $tokenEntity;
 		}
 		// actually spend key (set to used)
-		if ($this->spendKey($tokenEntity['id'])) {
+		if ($this->spendKey($tokenEntity->id)) {
 			return $tokenEntity;
 		}
 		// no limit? we dont spend key then
-		if (!empty($tokenEntity['unlimited'])) {
+		if (!empty($tokenEntity->unlimited)) {
 			return $tokenEntity;
 		}
 		return null;
@@ -172,11 +165,7 @@ class TokensTable extends Table {
 	 * @param int $id Id of key to spend: necessary
 	 * @return bool Success
 	 */
-	public function spendKey($id) {
-		if (!$id) {
-			return false;
-		}
-
+	public function spendKey(int $id): bool {
 		//$expression = new \Cake\Database\Expression\QueryExpression(['used = used + 1', 'modified' => date(FORMAT_DB_DATETIME)]);
 		$result = $this->updateAll(
 			['used = used + 1', 'modified' => date(FORMAT_DB_DATETIME)],
@@ -194,7 +183,7 @@ class TokensTable extends Table {
 	 *
 	 * @return int Rows
 	 */
-	public function garbageCollector() {
+	public function garbageCollector(): int {
 		$conditions = [
 			$this->getAlias() . '.created <' => date(FORMAT_DB_DATETIME, time() - $this->validity),
 		];
@@ -227,24 +216,17 @@ class TokensTable extends Table {
 	 * @param int|null $length (defaults to defaultLength)
 	 * @return string Key
 	 */
-	public function generateKey($length = null) {
+	public function generateKey(int $length = null): string {
 		if (!$length) {
 			$length = $this->defaultLength;
 		}
 
-		if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
-			$function = 'random_bytes';
-		} elseif (extension_loaded('openssl')) {
-			$function = 'openssl_random_pseudo_bytes';
-		} else {
-			trigger_error('Not secure', E_USER_DEPRECATED);
-			return Random::pwd($length);
-		}
-
+		$function = 'random_bytes';
 		$value = bin2hex($function($length / 2));
 		if (strlen($value) !== $length) {
-			$value = str_pad($value, $length, '0');
+			$value = str_pad($value, $length, (string)random_int(0, 9));
 		}
+
 		return $value;
 	}
 

+ 10 - 104
src/Utility/Random.php

@@ -11,22 +11,14 @@ namespace Tools\Utility;
 class Random {
 
 	/**
+	 * Not for security relevant functionality - here use random_bytes()/random_bytes()
+	 *
 	 * @param int $min
 	 * @param int $max
 	 * @return int Random int value
 	 */
 	public static function int($min = 0, $max = 999) {
-		return mt_rand($min, $max);
-	}
-
-	/**
-	 * @param float $min
-	 * @param float $max
-	 * @return float Random float value
-	 */
-	public static function float($min = 0.0, $max = 999.0) {
-		$rand = rand(1, 358);
-		return $rand * cos($rand);
+		return random_int($min, $max);
 	}
 
 	/**
@@ -39,10 +31,7 @@ class Random {
 	 * @param bool $integerKeys
 	 * @return mixed
 	 */
-	public static function arrayValue($array, $minPosition = null, $maxPosition = null, $integerKeys = false) {
-		if (empty($array)) {
-			return null;
-		}
+	public static function arrayValue(array $array, $minPosition = null, $maxPosition = null, $integerKeys = false) {
 		if ($integerKeys) {
 			$max = count($array) - 1;
 			return $array[static::int(0, $max)];
@@ -54,89 +43,6 @@ class Random {
 	}
 
 	/**
-	 * 1950-01-01 - 2050-12-31
-	 *
-	 * @param int|null $min
-	 * @param int|null $max
-	 * @param bool|null $formatReturn
-	 * @return int|string|null
-	 */
-	public static function date($min = null, $max = null, $formatReturn = null) {
-		if ($min === null && $max === null) {
-			$res = time();
-		} elseif ($min > 0 && $max === null) {
-			$res = $min;
-		} elseif ($min > 0 && $max > 0) {
-			$res = static::int($min, $max);
-		} else {
-			$res = time();
-		}
-
-		$res = 0;
-		$formatReturnAs = FORMAT_DB_DATETIME;
-		if ($formatReturn !== null) {
-			if ($formatReturn === false) {
-				return $res;
-			}
-			$formatReturnAs = $formatReturn;
-		}
-		return date($formatReturnAs);
-	}
-
-	/**
-	 * 00:00:00 - 23:59:59
-	 *
-	 * TODO
-	 *
-	 * @param int|null $min
-	 * @param int|null $max
-	 * @param bool|null $formatReturn
-	 * @return int
-	 */
-	public static function time($min = null, $max = null, $formatReturn = null) {
-		$res = 0;
-		//$returnValueAs = FORMAT_DB_TIME;
-		if ($formatReturn !== null) {
-			if ($formatReturn === false) {
-				return $res;
-			}
-		}
-
-		return $res;
-	}
-
-	/**
-	 * Returns a date of birth within the specified age range
-	 *
-	 * @param int $min minimum age in years
-	 * @param int $max maximum age in years
-	 * @return string Dob a db (ISO) format datetime string
-	 */
-	public static function dob($min = 18, $max = 100) {
-		$dobYear = (int)date('Y') - (static::int($min, $max));
-
-		$dobMonth = static::int(1, 12);
-
-		if ($dobMonth == 2) {
-			// leap year?
-			if ($dobYear % 4 || $dobYear % 400) {
-				$maxDays = 29;
-			} else {
-				$maxDays = 28;
-			}
-		} elseif (in_array($dobMonth, [4, 6, 9, 11])) {
-			$maxDays = 30;
-		} else {
-			$maxDays = 31;
-		}
-
-		$dobDay = static::int(1, $maxDays);
-
-		$dob = sprintf('%4d-%02d-%02d', $dobYear, $dobMonth, $dobDay);
-		return $dob;
-	}
-
-	/**
 	 * Generates a password
 	 *
 	 * @param int $length Password length
@@ -144,13 +50,13 @@ class Random {
 	 * @link https://github.com/CakeDC/users/blob/master/models/user.php#L498
 	 */
 	public static function pronounceablePwd($length = 10) {
-		srand((int)(double)microtime() * 1000000);
+		mt_srand((int)(double)microtime() * 1000000);
 		$password = '';
 		$vowels = ['a', 'e', 'i', 'o', 'u'];
 		$cons = ['b', 'c', 'd', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'u', 'v', 'w', 'tr',
 							'cr', 'br', 'fr', 'th', 'dr', 'ch', 'ph', 'wr', 'st', 'sp', 'sw', 'pr', 'sl', 'cl'];
 		for ($i = 0; $i < $length; $i++) {
-			$password .= $cons[mt_rand(0, 31)] . $vowels[mt_rand(0, 4)];
+			$password .= $cons[random_int(0, 31)] . $vowels[random_int(0, 4)];
 		}
 		return substr($password, 0, $length);
 	}
@@ -171,7 +77,7 @@ class Random {
 		$max = strlen($chars) - 1;
 
 		while ($i < $length) {
-			$password .= $chars[mt_rand(0, $max)];
+			$password .= $chars[random_int(0, $max)];
 			$i++;
 		}
 		return $password;
@@ -236,17 +142,17 @@ class Random {
 		$str = '';
 		for ($i = 0; $i < $length; $i++) {
 			// Select a random character from the pool and add it to the string
-			$str .= $pool[mt_rand(0, $max)];
+			$str .= $pool[random_int(0, $max)];
 		}
 
 		// Make sure alnum strings contain at least one letter and one digit
 		if ($type === 'alnum' && $length > 1) {
 			if (ctype_alpha($str)) {
 				// Add a random digit
-				$str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57));
+				$str[random_int(0, $length - 1)] = chr(random_int(48, 57));
 			} elseif (ctype_digit($str)) {
 				// Add a random letter
-				$str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90));
+				$str[random_int(0, $length - 1)] = chr(random_int(65, 90));
 			}
 		}
 

+ 26 - 15
tests/Fixture/TokensFixture.php

@@ -16,13 +16,14 @@ class TokensFixture extends TestFixture {
 	 */
 	public $fields = [
 		'id' => ['type' => 'integer', 'null' => false, 'default' => null, 'length' => 10],
-		'user_id' => ['type' => 'string', 'null' => true, 'length' => 36, 'comment' => ''],
-		'type' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 10, 'comment' => 'e.g.:activate,reactivate'],
-		'key' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 60, 'comment' => ''],
-		'content' => ['type' => 'string', 'null' => true, 'default' => null, 'comment' => 'can transport some information'],
+		'user_id' => ['type' => 'integer', 'null' => true, 'length' => 10, 'comment' => ''],
+		'type' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 20, 'comment' => 'e.g.:activate,reactivate'],
+		'token' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 60, 'comment' => ''],
+		'content' => ['type' => 'string', 'null' => true, 'length' => 255, 'default' => null, 'comment' => 'can transport some information'],
 		'used' => ['type' => 'integer', 'null' => false, 'default' => '0', 'collate' => null, 'comment' => ''],
 		'created' => ['type' => 'datetime', 'null' => true, 'default' => null, 'collate' => null, 'comment' => ''],
 		'modified' => ['type' => 'datetime', 'null' => true, 'default' => null, 'collate' => null, 'comment' => ''],
+		'unlimited' => ['type' => 'boolean', 'null' => false, 'default' => '0', 'comment' => ''],
 		'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
 	];
 
@@ -33,94 +34,104 @@ class TokensFixture extends TestFixture {
 	 */
 	public $records = [
 		[
-			'user_id' => '1',
+			'user_id' => 1,
 			'type' => 'qlogin',
-			'key' => '7k8qdcizigtudvxn2v9zep',
+			'token' => '7k8qdcizigtudvxn2v9zep',
 			'content' => 'i:1;',
 			'used' => 0,
 			'created' => '2011-08-02 18:00:41',
 			'modified' => '2011-08-02 18:00:41',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '2',
 			'type' => 'qlogin',
-			'key' => '23e32tpkcmdn8x9j8n0n00',
+			'token' => '23e32tpkcmdn8x9j8n0n00',
 			'content' => 'i:2;',
 			'used' => 0,
 			'created' => '2011-08-02 18:00:41',
 			'modified' => '2011-08-02 18:00:41',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '1',
 			'type' => 'qlogin',
-			'key' => '3mpzed7eoewsjvyvg4vy35',
+			'token' => '3mpzed7eoewsjvyvg4vy35',
 			'content' => 'a:3:{s:10:"controller";s:4:"test";s:6:"action";s:3:"foo";i:0;s:3:"bar";}',
 			'used' => 1,
 			'created' => '2011-08-02 18:00:41',
 			'modified' => '2011-08-02 18:00:41',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '2',
 			'type' => 'qlogin',
-			'key' => 'af8ww4y7jxzq5n6npmjpxx',
+			'token' => 'af8ww4y7jxzq5n6npmjpxx',
 			'content' => 's:13:"/test/foo/bar";',
 			'used' => 1,
 			'created' => '2011-08-02 18:00:41',
 			'modified' => '2011-08-02 18:00:41',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '1',
 			'type' => 'qlogin',
-			'key' => '2s7i3zjw0rn009j4no552b',
+			'token' => '2s7i3zjw0rn009j4no552b',
 			'content' => 'i:1;',
 			'used' => 0,
 			'created' => '2011-08-02 18:01:16',
 			'modified' => '2011-08-02 18:01:16',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '2',
 			'type' => 'qlogin',
-			'key' => 'tro596dig63cay0ps09vre',
+			'token' => 'tro596dig63cay0ps09vre',
 			'content' => 'i:2;',
 			'used' => 0,
 			'created' => '2011-08-02 18:01:16',
 			'modified' => '2011-08-02 18:01:16',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '1',
 			'type' => 'qlogin',
-			'key' => 'penfangwc40x550wwvgfmu',
+			'token' => 'penfangwc40x550wwvgfmu',
 			'content' => 'a:3:{s:10:"controller";s:4:"test";s:6:"action";s:3:"foo";i:0;s:3:"bar";}',
 			'used' => 1,
 			'created' => '2011-08-02 18:01:16',
 			'modified' => '2011-08-02 18:01:16',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '2',
 			'type' => 'qlogin',
-			'key' => '2y7m5srasm3ozej0izxbhe',
+			'token' => '2y7m5srasm3ozej0izxbhe',
 			'content' => 's:13:"/test/foo/bar";',
 			'used' => 1,
 			'created' => '2011-08-02 18:01:16',
 			'modified' => '2011-08-02 18:01:16',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '1',
 			'type' => 'qlogin',
-			'key' => '5c6dp2w54ynxii2xo3c50m',
+			'token' => '5c6dp2w54ynxii2xo3c50m',
 			'content' => 'i:1;',
 			'used' => 0,
 			'created' => '2011-08-02 18:01:54',
 			'modified' => '2011-08-02 18:01:54',
+			'unlimited' => false,
 		],
 		[
 			'user_id' => '2',
 			'type' => 'qlogin',
-			'key' => 'fr6a0d4waue2v6hmqeyek5',
+			'token' => 'fr6a0d4waue2v6hmqeyek5',
 			'content' => 'i:2;',
 			'used' => 0,
 			'created' => '2011-08-02 18:01:54',
 			'modified' => '2011-08-02 18:01:54',
+			'unlimited' => false,
 		],
 	];
 

+ 2 - 2
tests/TestCase/Model/Table/TableTest.php

@@ -21,12 +21,12 @@ class TableTest extends TestCase {
 	];
 
 	/**
-	 * @var \Tools\Model\Table\Table;
+	 * @var \Tools\Model\Table\Table
 	 */
 	protected $Users;
 
 	/**
-	 * @var \Tools\Model\Table\Table;
+	 * @var \Tools\Model\Table\Table
 	 */
 	protected $Posts;
 

+ 6 - 6
tests/TestCase/Model/Table/TokensTableTest.php

@@ -4,18 +4,19 @@ namespace Tools\Test\TestCase\Model\Table;
 
 use Cake\ORM\TableRegistry;
 use Shim\TestSuite\TestCase;
+use Tools\Model\Table\TokensTable;
 
 class TokensTableTest extends TestCase {
 
 	/**
-	 * @var array
+	 * @var string[]
 	 */
 	protected $fixtures = [
 		'plugin.Tools.Tokens',
 	];
 
 	/**
-	 * @var \Tools\Model\Table\TokensTable;
+	 * @var \Tools\Model\Table\TokensTable
 	 */
 	protected $Tokens;
 
@@ -32,7 +33,7 @@ class TokensTableTest extends TestCase {
 	 * @return void
 	 */
 	public function testTokenInstance() {
-		$this->assertInstanceOf('Tools\Model\Table\TokensTable', $this->Tokens);
+		$this->assertInstanceOf(TokensTable::class, $this->Tokens);
 	}
 
 	/**
@@ -40,7 +41,6 @@ class TokensTableTest extends TestCase {
 	 */
 	public function testGenerateKey() {
 		$key = $this->Tokens->generateKey(4);
-		//pr($key);
 		$this->assertTrue(!empty($key) && strlen($key) === 4);
 	}
 
@@ -55,7 +55,7 @@ class TokensTableTest extends TestCase {
 		$this->assertTrue(!empty($res));
 
 		$res = $this->Tokens->useKey('test', $key);
-		$this->assertTrue(!empty($res) && !empty($res['used']));
+		$this->assertTrue(!empty($res) && !empty($res->used));
 
 		$res = $this->Tokens->useKey('test', $key . 'x');
 		$this->assertNull($res);
@@ -71,7 +71,7 @@ class TokensTableTest extends TestCase {
 		$data = [
 			'created' => date(FORMAT_DB_DATETIME, time() - 3 * MONTH),
 			'type' => 'y',
-			'key' => 'x',
+			'token' => 'x',
 		];
 		$entity = $this->Tokens->newEntity($data, ['validate' => false]);
 		$this->Tokens->save($entity);

+ 0 - 2
tests/TestCase/Utility/RandomTest.php

@@ -59,6 +59,4 @@ class RandomTest extends TestCase {
 		$this->assertTrue(strlen($is) === 11);
 	}
 
-	//TOOD: other tests
-
 }