diff --git a/config/default.php b/config/default.php index a78ce55..0432195 100644 --- a/config/default.php +++ b/config/default.php @@ -15,9 +15,6 @@ * SMTP server password: SMTP_PASSWORD */ return [ - 'debug' => [ - 'tokens' => true, - ], 'organisation' => [ 'registration' => [ 'require_approval' => true, diff --git a/src/Database/Models/Organisation.php b/src/Database/Models/Organisation.php index a33682e..849394c 100644 --- a/src/Database/Models/Organisation.php +++ b/src/Database/Models/Organisation.php @@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Capsule\Manager as DB; use UserFrosting\Sprinkle\Core\Database\Models\Model; use UserFrosting\Sprinkle\Organisations\Database\Models\Interfaces\OrganisationInterface; -use UserFrosting\Sprinkle\Organisations\Repository\Interfaces\TokenOwnerInterface; +use UserFrosting\Sprinkle\UFTweaks\Repository\Interfaces\TokenOwnerInterface; /** * Organisation Class. diff --git a/src/Database/Models/OrganisationMember.php b/src/Database/Models/OrganisationMember.php index f4643bc..ab7bf90 100644 --- a/src/Database/Models/OrganisationMember.php +++ b/src/Database/Models/OrganisationMember.php @@ -12,7 +12,7 @@ namespace UserFrosting\Sprinkle\Organisations\Database\Models; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Capsule\Manager as DB; use UserFrosting\Sprinkle\Core\Database\Models\Model; -use UserFrosting\Sprinkle\Organisations\Repository\Interfaces\TokenOwnerInterface; +use UserFrosting\Sprinkle\UFTweaks\Repository\Interfaces\TokenOwnerInterface; /** * OrganisationMember mapping Class. diff --git a/src/Repository/BasicTokenRepository.php b/src/Repository/BasicTokenRepository.php deleted file mode 100644 index 47b158b..0000000 --- a/src/Repository/BasicTokenRepository.php +++ /dev/null @@ -1,492 +0,0 @@ -classMapper = $classMapper; - $this->algorithm = $algorithm; - $this->tokenLogger = ($debug ? $tokenLogger : null); - } - - - /** - * Create a new token. - * - * @param TokenOwnerInterface $tokenOwner The object to associate with this token, be it an organisation, user, group etc. - * @param int $timeout The time, in seconds, after which this token should expire. - * - * @return Model The model (PasswordReset, Verification, etc) object that stores the token. - */ - public function create(TokenOwnerInterface $tokenOwner, $timeout) - { - // Remove any previous tokens for this tokenOwner - $this->removeExisting($tokenOwner); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Creating new token for {{owner}} which expires in {{timeout}} seconds', [ - 'tokenOwner' => $tokenOwner, - 'timeout' => $timeout - ]); - } - - // Compute expiration time - $expiresAt = Carbon::now()->addSeconds($timeout); - - $model = $this->classMapper->createInstance($this->modelIdentifier); - - // Generate a random token - $model->setToken($this->generateRandomToken()); - - // Hash the password reset token for the stored version - $hash = hash($this->algorithm, $model->getToken()); - - $model->fill([ - 'owner_id' => $tokenOwner->getId(), - 'hash' => $hash, - 'completed' => false, - 'expires_at' => ($timeout >= 0 ? $expiresAt : null), - ]); - - $model->save(); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Completed new token', ['model' => $model]); - } - - return $model; - } - - /** - * Cancels a specified token by removing it from the database. - * - * @param int $token The token to remove. - * - * @return Model|false - */ - public function cancel($token) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Cancelling token {{token}}', [ - 'token' => $token - ]); - } - - // Hash the password reset token for the stored version - $hash = hash($this->algorithm, $token); - - // Find an incomplete reset request for the specified hash - $model = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('hash', $hash) - ->where('completed', false) - ->first(); - - if ($model === null) { - if ($this->tokenLogger) { - $this->tokenLogger->warn('Token not found!'); - } - return false; - } - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Deleting matched model', [ - 'model' => $model - ]); - } - - $model->delete(); - - return $model; - } - - /** - * Completes a token-based process, invoking updateTokenOwner() in the child object to do the actual action. - * - * @param int $token The token to complete. - * @param mixed[] $params An optional list of parameters to pass to updateUser(). - * - * @return Model|false - */ - public function complete($token, $params = []) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Completing token for {{token}}', [ - 'token' => $token - ]); - } - - // Hash the token for the stored version - $hash = hash($this->algorithm, $token); - - // Find an unexpired, incomplete token for the specified hash - $model = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('hash', $hash) - ->where('completed', false) - ->where(function($query) { - return $query->where('expires_at', '>', Carbon::now())->orWhereNull('expires_at'); - }) - ->first(); - - if ($model === null) { - if ($this->tokenLogger) { - $this->tokenLogger->warn('Token not found!'); - } - return false; - } - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Found:', [ - 'model' => $model - ]); - } - - $ret = $this->updateTokenOwner($model->owner_id, $model, $params); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Return of updateTokenOwner: {{ret}}', [ - 'ret' => $ret - ]); - } - - $model->fill([ - 'completed' => true, - 'completed_at' => Carbon::now(), - ]); - - $model->save(); - - return $model; - } - - /** - * Completes a token-based process using the owner instead of the token, - * invoking updateTokenOwner() in the child object to do the actual action. - * - * @param int $token The token to complete. - * @param mixed[] $params An optional list of parameters to pass to updateUser(). - * - * @return Model|false - */ - public function completeForOwner(TokenOwnerInterface $tokenOwner, $params = []) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Completing token for {{owner}}', [ - 'owner' => $tokenOwner - ]); - } - - // Find an unexpired, incomplete tokens for the specified owner. - // Using first() works because owners can only have at most 1 active token - $model = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('owner_id', $tokenOwner->getId()) - ->where('completed', false) - ->where(function($query) { - return $query->where('expires_at', '>', Carbon::now())->orWhereNull('expires_at'); - }) - ->first(); - - if ($model === null) { - if ($this->tokenLogger) { - $this->tokenLogger->warn('Token not found!'); - } - return false; - } - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Found:', [ - 'model' => $model - ]); - } - - $ret = $this->updateTokenOwner($model->owner_id, $model, $params); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Return of updateTokenOwner: {{ret}}', [ - 'ret' => $ret - ]); - } - - $model->fill([ - 'completed' => true, - 'completed_at' => Carbon::now(), - ]); - - $model->save(); - - return $model; - } - - /** - * Reverts a completed token request to its previously incomplete state. - * - * @param TokenOwnerInterface $tokenOwner The owner of the token to revert. - * - * @return Model|false - */ - public function revert(TokenOwnerInterface $tokenOwner) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Reverting token for {{owner}}', [ - 'owner' => $tokenOwner - ]); - } - - // Find an unexpired, incomplete tokens for the specified owner. - // Using first() works because owners can only have at most 1 active token - $model = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('owner_id', $tokenOwner->getId()) - ->where('completed', true) - ->where(function($query) { - return $query->where('expires_at', '>', Carbon::now())->orWhereNull('expires_at'); - }) - ->first(); - - if ($model === null) { - if ($this->tokenLogger) { - $this->tokenLogger->warn('Token not found!'); - } - return false; - } - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Found:', [ - 'model' => $model - ]); - } - - $model->fill([ - 'completed' => false, - 'completed_at' => null, - ]); - - $model->save(); - - return $model; - } - - /** - * Determine if a specified token owner has an incomplete and unexpired token. - * - * @param TokenOwnerInterface $tokenOwner The token owner object to look up. - * @param int $token Optionally, try to match a specific token. - * - * @return Model|false - */ - public function exists(TokenOwnerInterface $tokenOwner, $token = null) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Searching for token for {{owner}} (with optional {{token}})', [ - 'owner' => $tokenOwner, - 'token' => $token - ]); - } - - $model = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('owner_id', $tokenOwner->id) - ->where('completed', false) - ->where(function($query) { - return $query->where('expires_at', '>', Carbon::now())->orWhereNull('expires_at'); - }); - - if ($token) { - // get token hash - $hash = hash($this->algorithm, $token); - if ($this->tokenLogger) { - $this->tokenLogger->debug('Token hash: {{hash}}', [ - 'hash' => $hash - ]); - } - $model->where('hash', $hash); - } - - $result = $model->first() ?: false; - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Found:', [ - 'result' => $result - ]); - } - - return $result; - } - - /** - * Find an unexpired and un-completed token for the specified owner. - * - * @param TokenOwnerInterface $tokenOwner The token owner object to look up. - * @param int $token Optionally, try to match a specific token. - * - * @return owner_id|null - */ - public function findOwner($token) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Searching token for owner of {{token}}', [ - 'token' => $token - ]); - } - - // get token hash - $hash = hash($this->algorithm, $token); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Token hash: {{hash}}', [ - 'hash' => $hash - ]); - } - - $model = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('hash', $hash) - ->where('completed', false) - ->where(function($query) { - return $query->where('expires_at', '>', Carbon::now())->orWhereNull('expires_at'); - }) - ->first(); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Found:', [ - 'model' => $model - ]); - } - - return $model ? $model->owner_id : null; - } - - /** - * Delete all existing tokens from the database for a particular owner. - * - * @param TokenOwnerInterface $tokenOwner - * - * @return int - */ - public function removeExisting(TokenOwnerInterface $tokenOwner) - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Removing all tokens for {{owner}}', [ - 'owner' => $tokenOwner - ]); - } - - $result = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('owner_id', $tokenOwner->getId()) - ->delete(); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Deletion result: {{result}}', [ - 'result' => $result - ]); - } - - return $result; - } - - /** - * Remove all expired tokens from the database. - * - * @return bool|null - */ - public function removeExpired() - { - if ($this->tokenLogger) { - $this->tokenLogger->debug('Removing expired tokens...'); - } - - $result = $this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('completed', false) - ->whereNotNull('expires_at') - ->where('expires_at', '<', Carbon::now()) - ->delete(); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Deletion result: {{result}}', [ - 'result' => $result - ]); - } - - return $result; - } - - /** - * Generate a new random token. - * - * This generates a token to use for verifying anything you like, with a unique reference. - * - * @return string - */ - protected function generateRandomToken() - { - do { - $gen = md5(uniqid(mt_rand(), false)); - } while ($this->classMapper->getClassMapping($this->modelIdentifier)::query() - ->where('hash', hash($this->algorithm, $gen)) - ->first()); - - if ($this->tokenLogger) { - $this->tokenLogger->debug('Generated token {{token}}', [ - 'token' => $gen - ]); - } - - return $gen; - } - - /** - * Modify the owner of the token during the token completion process. - * - * This method is called during complete(), and is a way for concrete implementations to modify the owner. - * - * @param integer $owner_id The id of the token owner - * @param mixed[] $args - * - * @return mixed[] $args the list of parameters that were supplied to the call to `complete()` - */ - abstract protected function updateTokenOwner($owner_id, $model, $args); -} diff --git a/src/Repository/Interfaces/TokenOwnerInterface.php b/src/Repository/Interfaces/TokenOwnerInterface.php deleted file mode 100644 index c5cbbca..0000000 --- a/src/Repository/Interfaces/TokenOwnerInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -