Skip to content
This repository has been archived by the owner on Jul 10, 2019. It is now read-only.

Commit

Permalink
Added in almost full support for password reminders.
Browse files Browse the repository at this point in the history
  • Loading branch information
ollieread committed Feb 21, 2014
1 parent 0dac40d commit e545ab0
Show file tree
Hide file tree
Showing 7 changed files with 500 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/Ollieread/Multiauth/MultiManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function __construct($app) {
}
}

public function __call($name, $arguments) {
public function __call($name, $arguments = array()) {
if(array_key_exists($name, $this->providers)) {
return $this->providers[$name];
}
Expand Down
6 changes: 0 additions & 6 deletions src/Ollieread/Multiauth/MultiauthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ public function register() {

return new MultiManager($app);
});

$this->app['command.multiauth.reminders-table'] = $this->app->share(function($app) {
return new RemindersTableCommand($app['files']);
});

$this->commands('command.multiauth.reminders-table');
}

}
183 changes: 183 additions & 0 deletions src/Ollieread/Multiauth/Reminders/DatabaseReminderRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php namespace Ollieread\Multiauth\Reminders;

use Carbon\Carbon;
use Illuminate\Database\Connection;
use Illuminate\Auth\Reminders\RemindableInterface;

class DatabaseReminderRepository implements ReminderRepositoryInterface {

/**
* The database connection instance.
*
* @var \Illuminate\Database\Connection
*/
protected $connection;

/**
* The reminder database table.
*
* @var string
*/
protected $table;

/**
* The hashing key.
*
* @var string
*/
protected $hashKey;

/**
* The number of seconds a reminder should last.
*
* @var int
*/
protected $expires;

/**
* Create a new reminder repository instance.
*
* @param \Illuminate\Database\Connection $connection
* @param string $table
* @param string $hashKey
* @param int $expires
* @return void
*/
public function __construct(Connection $connection, $table, $hashKey, $expires = 60)
{
$this->table = $table;
$this->hashKey = $hashKey;
$this->expires = $expires * 60;
$this->connection = $connection;
}

/**
* Create a new reminder record and token.
*
* @param \Illuminate\Auth\Reminders\RemindableInterface $user
* @return string
*/
public function create(RemindableInterface $user, $type)
{
$email = $user->getReminderEmail();

// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken($user);

$this->getTable()->insert($this->getPayload($email, $token, $type));

return $token;
}

/**
* Build the record payload for the table.
*
* @param string $email
* @param string $token
* @return array
*/
protected function getPayload($email, $token, $type)
{
return array('type' => $type, 'email' => $email, 'token' => $token, 'created_at' => new Carbon);
}

/**
* Determine if a reminder record exists and is valid.
*
* @param \Illuminate\Auth\Reminders\RemindableInterface $user
* @param string $token
* @return bool
*/
public function exists(RemindableInterface $user, $token, $type)
{
$email = $user->getReminderEmail();

$reminder = $this->getTable()->where('email', $email)->where('token', $token)->where('type', $type)->first();

return $reminder && ! $this->reminderExpired($reminder);
}

/**
* Determine if the reminder has expired.
*
* @param object $reminder
* @return bool
*/
protected function reminderExpired($reminder)
{
$createdPlusHour = strtotime($reminder->created_at) + $this->expires;

return $createdPlusHour < $this->getCurrentTime();
}

/**
* Get the current UNIX timestamp.
*
* @return int
*/
protected function getCurrentTime()
{
return time();
}

/**
* Delete a reminder record by token.
*
* @param string $token
* @return void
*/
public function delete($token, $type)
{
$this->getTable()->where('token', $token)->where('type', $type)->delete();
}

/**
* Delete expired reminders.
*
* @return void
*/
public function deleteExpired()
{
$expired = Carbon::now()->subSeconds($this->expires);

$this->getTable()->where('created_at', '<', $expired)->delete();
}

/**
* Create a new token for the user.
*
* @param \Illuminate\Auth\Reminders\RemindableInterface $user
* @return string
*/
public function createNewToken(RemindableInterface $user)
{
$email = $user->getReminderEmail();

$value = str_shuffle(sha1($email.spl_object_hash($this).microtime(true)));

return hash_hmac('sha1', $value, $this->hashKey);
}

/**
* Begin a new database query against the table.
*
* @return \Illuminate\Database\Query\Builder
*/
protected function getTable()
{
return $this->connection->table($this->table);
}

/**
* Get the database connection instance.
*
* @return \Illuminate\Database\Connection
*/
public function getConnection()
{
return $this->connection;
}

}
136 changes: 136 additions & 0 deletions src/Ollieread/Multiauth/Reminders/PasswordBroker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php namespace Ollieread\Multiauth\Reminders;

use Closure;
use Illuminate\Mail\Mailer;
use Illuminate\Auth\UserProviderInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
use Illuminate\Auth\Reminders\PasswordBroker as OriginalPasswordBroker;

class PasswordBroker extends OriginalPasswordBroker {

protected $type;

public function __construct($type,
ReminderRepositoryInterface $reminders,
UserProviderInterface $users,
Mailer $mailer,
$reminderView)
{
$this->users = $users;
$this->mailer = $mailer;
$this->reminders = $reminders;
$this->reminderView = $reminderView;
$this->type = $type;
}

/**
* Send a password reminder to a user.
*
* @param array $credentials
* @param Closure $callback
* @return string
*/
public function remind(array $credentials, Closure $callback = null)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);

if (is_null($user))
{
return self::INVALID_USER;
}

// Once we have the reminder token, we are ready to send a message out to the
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
$token = $this->reminders->create($user, $this->type);

$this->sendReminder($user, $token, $callback);

return self::REMINDER_SENT;
}

/**
* Send the password reminder e-mail.
*
* @param \Illuminate\Auth\Reminders\RemindableInterface $user
* @param string $token
* @param Closure $callback
* @return void
*/
public function sendReminder(RemindableInterface $user, $token, Closure $callback = null)
{
// We will use the reminder view that was given to the broker to display the
// password reminder e-mail. We'll pass a "token" variable into the views
// so that it may be displayed for an user to click for password reset.
$view = $this->reminderView;
$type = $this->type;

return $this->mailer->send($view, compact('token', 'user', 'type'), function($m) use ($user, $token, $type, $callback)
{
$m->to($user->getReminderEmail());

if ( ! is_null($callback)) call_user_func($callback, $m, $user, $type, $token);
});
}

/**
* Reset the password for the given token.
*
* @param array $credentials
* @param Closure $callback
* @return mixed
*/
public function reset(array $credentials, Closure $callback)
{
// If the responses from the validate method is not a user instance, we will
// assume that it is a redirect and simply return it from this method and
// the user is properly redirected having an error message on the post.
$user = $this->validateReset($credentials);

if ( ! $user instanceof RemindableInterface)
{
return $user;
}

$pass = $credentials['password'];

// Once we have called this callback, we will remove this token row from the
// table and return the response from this callback so the user gets sent
// to the destination given by the developers from the callback return.
call_user_func($callback, $user, $pass);

$this->reminders->delete($credentials['token'], $this->type);

return self::PASSWORD_RESET;
}

/**
* Validate a password reset for the given credentials.
*
* @param array $credentials
* @return \Illuminate\Auth\Reminders\RemindableInterface
*/
protected function validateReset(array $credentials)
{
if (is_null($user = $this->getUser($credentials)))
{
return self::INVALID_USER;
}

if ( ! $this->validNewPasswords($credentials))
{
return self::INVALID_PASSWORD;
}

if ( ! $this->reminders->exists($user, $credentials['token'], $this->type))
{
return self::INVALID_TOKEN;
}

return $user;
}

}
27 changes: 27 additions & 0 deletions src/Ollieread/Multiauth/Reminders/PasswordBrokerManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php namespace Ollieread\Multiauth\Reminders;

use Illuminate\Mail\Mailer;

class PasswordBrokerManager {

protected $brokers = array();

public function __construct(ReminderRepositoryInterface $reminders,
Mailer $mailer,
$reminderView,
$providers)
{
foreach($providers as $type => $provider) {
$this->brokers[$type] = new PasswordBroker($type, $reminders, $provider, $mailer, $reminderView);
}
}

public function __call($name, $arguments = array()) {
if(array_key_exists($name, $this->brokers)) {
return $this->brokers[$name];
}
}



}
Loading

0 comments on commit e545ab0

Please sign in to comment.