diff --git a/app/config/services.php b/app/config/services.php index 85a2866..c8499ef 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -126,6 +126,9 @@ $di->setShared('router', function() { 'action' => 'oauth' ))->setName('oauth'); + $router->add('/oauth/{provider:([a-z]+)}/disconnect', 'User::oauthdisconnect')->setName('oauth-disconnect'); + $router->add('/oauth/{provider:([a-z]+)}/disconnect/{confirm}', 'User::oauthdisconnect')->setName('oauth-disconnect-confirm'); + $router->add('/settings', array( 'controller' => 'user', 'action' => 'settings', diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index 08331e7..08ff5d8 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -8,6 +8,13 @@ use App\Form\Login as LoginForm; class AuthController extends ControllerBase { + public function initialize() + { + // We need event manager here from DI. + $eventManager = $this->di->get('eventsManager'); + $this->setEventsManager($eventManager); + } + public function indexAction() { $form = new LoginForm(); @@ -54,22 +61,42 @@ class AuthController extends ControllerBase // NOTE: Should pass $state here also. $data = $client->authenticate($code); - $result = $this->auth->loginOauth($data); + // If user is authed already, we connect. + $user = $this->auth->getUser(); + if ($user) { - // There was an error when creating the account - if (is_array($result)) { - $msg = ''; - foreach($result as $message) { - $msg .= '
  • ' . $message->getMessage() . '
  • '; + $name = ucfirst($provider_name); + $user->{'set' . $name . 'Id'}($data->getId()); + $user->save(); + + $this->getEventsManager()->fire('user:onOAuthConnected', $user, $data); + + $this->flash->message('success', "{$name} was connected!"); + $this->response->redirect('/settings'); + } + // Perform Auth. + else { + $result = $this->auth->loginOauth($data); + + // There was an error when creating the account + if (is_array($result)) { + $msg = ''; + foreach ($result as $message) { + $msg .= '
  • ' . $message->getMessage() . '
  • '; + } + $this->flash->message('error', "Failed to create account: "); + $this->response->redirect('/login'); + } else { + $this->response->redirect('/'); } - $this->flash->message('error', "Failed to create account: "); - $this->response->redirect('/login'); - } else { - $this->response->redirect('/'); } } catch(\Exception $e) { $this->flash->message('error', 'Failed to authenticate.'); - $this->response->redirect('/login'); + if ($this->auth->getUser()) { + $this->response->redirect('/settings'); + } else { + $this->response->redirect('/login'); + } } } // No code diff --git a/app/controllers/UserController.php b/app/controllers/UserController.php index 0d038d3..e9c5782 100644 --- a/app/controllers/UserController.php +++ b/app/controllers/UserController.php @@ -8,6 +8,13 @@ use App\Controller\ControllerBase, class UserController extends ControllerBase { + public function initialize() + { + // We need event manager here from DI. + $eventManager = $this->di->get('eventsManager'); + $this->setEventsManager($eventManager); + } + public function settingsAction() { $user = $this->_getAuth()->getUser(); @@ -33,6 +40,7 @@ class UserController extends ControllerBase } } + $this->view->user = $user; $this->view->form = $form; } @@ -45,4 +53,51 @@ class UserController extends ControllerBase $this->view->page = $paginator->getPaginate(); $this->view->pagination_url = '/user/activity/'; } + + public function oauthDisconnectAction($provider, $last_unlink_confirmed = false) + { + $user = $this->_getAuth()->getUser(); + + // Check if we are unlinking the last provider + if (count($user->getSocialLinks()) <= 1) { + + // If user does not have a password, we wont allow it. + if (strlen($user->getPassword()) < 1) { + $msg = 'Unlinking your last OAuth provider cannot be done ' + . 'if you don\'t have a password as it would be impossible for you to log in.'; + + $this->flash->message('error', $msg); + $this->response->redirect('/settings'); + return; + } + + // Give a warning to the user about password as the only login option. + if ($last_unlink_confirmed == false) { + + $url = $this->url->get([ + 'for' => 'oauth-disconnect-confirm', + 'provider' => $provider, + 'confirm' => 'confirm', + ]); + + $msg = '

    You are about to unlink the last OAuth provider.' + . ' Your only login option will be password if you do this.

    ' + . '

    Are you sure? Yes

    '; + + $this->flash->message('warning', $msg); + $this->response->redirect('/settings'); + return; + } + } + + $provider = ucfirst($provider); + $user->{'set' . $provider . 'Id'}(null); + $user->save(); + + $this->getEventsManager()->fire('user:onOAuthDisconnect', $user, $provider); + + $this->flash->message('success', "

    {$provider} was disconnected

    "); + + $this->response->redirect('/settings'); + } } diff --git a/app/listeners/ActivityLog.php b/app/listeners/ActivityLog.php index 2b62892..6202aa4 100644 --- a/app/listeners/ActivityLog.php +++ b/app/listeners/ActivityLog.php @@ -6,6 +6,7 @@ use Phalcon\Mvc\User\Plugin, Phalcon\Events\Event, App\Model\Data\User, App\Model\Data\ActivityLog as ActivityLogger, + Httpcb\OAuth\UserData\UserDataInterface as OAuthUserDataInterface, Httpcb\Auth; class ActivityLog extends Plugin @@ -45,6 +46,32 @@ class ActivityLog extends Plugin $this->_log($user, "Changed password"); } + /** + * Fired when a user is connected to a OAuth provider. + * + * @param Event $event + * @param User $user + * @param OAuthUserDataInterface $provider + */ + public function onOAuthConnected(Event $event, User $user, OAuthUserDataInterface $provider) + { + $name = $provider->getProvider(); + + $this->_log($user, "OAuth connected ({$name})"); + } + + /** + * Fired when a user is connected to a OAuth provider. + * + * @param Event $event + * @param User $user + * @param string $providerName + */ + public function onOAuthDisconnect(Event $event, User $user, $providerName) + { + $this->_log($user, "OAuth disconnected ({$providerName})"); + } + protected function _log(User $user, $message) { $ip = (new \Phalcon\Http\Request())->getClientAddress(); diff --git a/app/models/Data/User.php b/app/models/Data/User.php index 717a4dd..be42c2c 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -227,6 +227,17 @@ class User extends Model return $this; } + public function getSocialLinks() + { + $providers = [ + 'github' => $this->getGithubId(), + 'gitlab' => $this->getGitlabId(), + 'google' => $this->getGoogleId() + ]; + + return array_filter($providers); + } + static public function createFromOAuthData(UserDataInterface $data) { $oauth_id = 'set' . $data->getProvider() . 'Id'; diff --git a/app/views/user/settings.volt b/app/views/user/settings.volt index e5e74e4..2c0123d 100644 --- a/app/views/user/settings.volt +++ b/app/views/user/settings.volt @@ -37,9 +37,50 @@
    +
    + +

    Social sign-in

    + +
    + +
    +
    {{ icon('brand/github', [ '3x' ]) }}
    + {% if user.getGithubId() > 0 %} + Disconnect + {% else %} + Connect + {% endif %} +
    + +
    +
    {{ icon('brand/gitlab', [ '3x' ]) }}
    + {% if user.getGitlabId() > 0 %} + Disconnect + {% else %} + Connect + {% endif %} +
    + +
    +
    {{ icon('brand/google', [ '3x' ]) }}
    + {% if user.getGoogleId() > 0 %} + Connect + {% else %} + Connect + {% endif %} + +
    + +
    +
    + +
    +
    +
    {{ form.render('Save') }}
    +