diff --git a/src/Ollieread/Multiauth/MultiManager.php b/src/Ollieread/Multiauth/MultiManager.php index b3271a9..cb14aa8 100644 --- a/src/Ollieread/Multiauth/MultiManager.php +++ b/src/Ollieread/Multiauth/MultiManager.php @@ -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]; } diff --git a/src/Ollieread/Multiauth/MultiauthServiceProvider.php b/src/Ollieread/Multiauth/MultiauthServiceProvider.php index a3fd640..dfc7508 100644 --- a/src/Ollieread/Multiauth/MultiauthServiceProvider.php +++ b/src/Ollieread/Multiauth/MultiauthServiceProvider.php @@ -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'); } } diff --git a/src/Ollieread/Multiauth/Reminders/DatabaseReminderRepository.php b/src/Ollieread/Multiauth/Reminders/DatabaseReminderRepository.php new file mode 100644 index 0000000..7832d0c --- /dev/null +++ b/src/Ollieread/Multiauth/Reminders/DatabaseReminderRepository.php @@ -0,0 +1,183 @@ +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; + } + +} \ No newline at end of file diff --git a/src/Ollieread/Multiauth/Reminders/PasswordBroker.php b/src/Ollieread/Multiauth/Reminders/PasswordBroker.php new file mode 100644 index 0000000..407be83 --- /dev/null +++ b/src/Ollieread/Multiauth/Reminders/PasswordBroker.php @@ -0,0 +1,136 @@ +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; + } + +} diff --git a/src/Ollieread/Multiauth/Reminders/PasswordBrokerManager.php b/src/Ollieread/Multiauth/Reminders/PasswordBrokerManager.php new file mode 100644 index 0000000..cd6392b --- /dev/null +++ b/src/Ollieread/Multiauth/Reminders/PasswordBrokerManager.php @@ -0,0 +1,27 @@ + $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]; + } + } + + + +} diff --git a/src/Ollieread/Multiauth/Reminders/ReminderRepositoryInterface.php b/src/Ollieread/Multiauth/Reminders/ReminderRepositoryInterface.php new file mode 100644 index 0000000..0fbbaaf --- /dev/null +++ b/src/Ollieread/Multiauth/Reminders/ReminderRepositoryInterface.php @@ -0,0 +1,39 @@ +registerPasswordBroker(); + + $this->registerReminderRepository(); + + $this->registerCommands(); + } + + /** + * Register the password broker instance. + * + * @return void + */ + protected function registerPasswordBroker() + { + $this->app->bindShared('auth.reminder', function($app) + { + // The reminder repository is responsible for storing the user e-mail addresses + // and password reset tokens. It will be used to verify the tokens are valid + // for the given e-mail addresses. We will resolve an implementation here. + $reminders = $app['auth.reminder.repository']; + + $providers = array(); + + foreach($app['config']['auth.multi'] as $type => $config) { + $providers[$type] = $app['auth']->$type()->driver()->getProvider(); + } + + $view = $app['config']['auth.reminder.email']; + + // The password broker uses the reminder repository to validate tokens and send + // reminder e-mails, as well as validating that password reset process as an + // aggregate service of sorts providing a convenient interface for resets. + return new PasswordBrokerManager( + + $reminders, $app['mailer'], $view, $providers + + ); + }); + } + + /** + * Register the reminder repository implementation. + * + * @return void + */ + protected function registerReminderRepository() + { + $this->app->bindShared('auth.reminder.repository', function($app) + { + $connection = $app['db']->connection(); + + // The database reminder repository is an implementation of the reminder repo + // interface, and is responsible for the actual storing of auth tokens and + // their e-mail addresses. We will inject this table and hash key to it. + $table = $app['config']['auth.reminder.table']; + + $key = $app['config']['app.key']; + + $expire = $app['config']->get('auth.reminder.expire', 60); + + return new DbRepository($connection, $table, $key, $expire); + }); + } + + /** + * Register the multiauth related console commands. + * + * @return void + */ + protected function registerCommands() + { + $this->app->bindShared('command.multiauth.reminders', function($app) + { + return new RemindersTableCommand($app['files']); + }); + + $this->commands( + 'command.multiauth.reminders' + ); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array('auth.reminder'); + } + +}