From 3fe09e3eb31d4409ce5cd305260db97abe00e660 Mon Sep 17 00:00:00 2001 From: Antonio Ramirez Date: Fri, 9 Dec 2016 16:41:25 +0100 Subject: [PATCH] added social connection action --- lib/User/Controller/ProfileController.php | 23 ++- lib/User/Controller/SecurityController.php | 147 ++++++++++++++++++ lib/User/Event/FormEvent.php | 2 + lib/User/Event/SocialNetworkAuthEvent.php | 5 + lib/User/Event/UserEvent.php | 2 + .../SocialNetworkAccountConnectService.php | 91 +++++++++++ .../SocialNetworkAccountCreateService.php | 85 ---------- .../SocialNetworkAccountUserLinkService.php | 68 -------- .../SocialNetworkAuthenticateService.php | 132 ++++++++++++++++ 9 files changed, 400 insertions(+), 155 deletions(-) create mode 100644 lib/User/Controller/SecurityController.php create mode 100644 lib/User/Service/SocialNetworkAccountConnectService.php delete mode 100644 lib/User/Service/SocialNetworkAccountCreateService.php delete mode 100644 lib/User/Service/SocialNetworkAccountUserLinkService.php create mode 100644 lib/User/Service/SocialNetworkAuthenticateService.php diff --git a/lib/User/Controller/ProfileController.php b/lib/User/Controller/ProfileController.php index 9170cb1..7b972f8 100644 --- a/lib/User/Controller/ProfileController.php +++ b/lib/User/Controller/ProfileController.php @@ -12,20 +12,39 @@ class ProfileController extends Controller { protected $profileQuery; + /** + * ProfileController constructor. + * + * @param string $id + * @param Module $module + * @param ProfileQuery $profileQuery + * @param array $config + */ public function __construct($id, Module $module, ProfileQuery $profileQuery, array $config) { $this->profileQuery = $profileQuery; parent::__construct($id, $module, $config); } + /** + * @inheritdoc + */ public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ - ['allow' => true, 'actions' => ['index'], 'roles' => ['@']], - ['allow' => true, 'actions' => ['show'], 'roles' => ['?', '@']], + [ + 'allow' => true, + 'actions' => ['index'], + 'roles' => ['@'] + ], + [ + 'allow' => true, + 'actions' => ['show'], + 'roles' => ['?', '@'] + ], ], ], ]; diff --git a/lib/User/Controller/SecurityController.php b/lib/User/Controller/SecurityController.php new file mode 100644 index 0000000..55b4eb2 --- /dev/null +++ b/lib/User/Controller/SecurityController.php @@ -0,0 +1,147 @@ +socialNetworkAccountQuery = $socialNetworkAccountQuery; + parent::__construct($id, $module, $config); + } + + /** + * @inheritdoc + */ + public function behaviors() + { + return [ + 'access' => [ + 'class' => AccessControl::className(), + 'rules' => [ + [ + 'allow' => true, + 'actions' => ['login', 'auth', 'blocked'], + 'roles' => ['?'] + ], + [ + 'allow' => true, + 'actions' => ['login', 'auth', 'logout'], + 'roles' => ['@'] + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'logout' => ['post'], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function actions() + { + return [ + 'auth' => [ + 'class' => AuthAction::className(), + // if user is not logged in, will try to log him in, otherwise + // will try to connect social account to user. + 'successCallback' => Yii::$app->user->isGuest + ? [$this, 'authenticate'] + : [$this, 'connect'], + ], + ]; + } + + public function actionLogin() + { + if (!Yii::$app->user->getIsGuest()) { + return $this->goHome(); + } + /** @var LoginForm $form */ + $form = $this->make(LoginForm::class); + /** @var FormEvent $event */ + $event = $this->make(FormEvent::class, [$form]); + + if ($form->load(Yii::$app->request->post())) { + $this->trigger(FormEvent::EVENT_BEFORE_LOGIN, $event); + if ($form->login()) { + $this->trigger(FormEvent::EVENT_AFTER_LOGIN, $event); + + return $this->goBack(); + } + } + + return $this->render( + 'login', + [ + 'model' => $form, + 'module' => $this->module, + ] + ); + } + + public function actionLogout() + { + $event = $this->make(UserEvent::class, [Yii::$app->getUser()->getIdentity()]); + + $this->trigger(UserEvent::EVENT_BEFORE_LOGOUT, $event); + + if (Yii::$app->getUser()->logout()) { + $this->trigger(UserEvent::EVENT_AFTER_LOGOUT, $event); + } + + return $this->goHome(); + } + + public function authenticate(AuthClientInterface $client) + { + $this->make(SocialNetworkAuthenticateService::class, [$this, $this->action, $client])->run(); + } + + public function connect(AuthClientInterface $client) { + if (Yii::$app->user->isGuest) { + Yii::$app->session->setFlash('danger', Yii::t('user', 'Something went wrong')); + + return; + } + + $this->make(SocialNetworkAccountConnectService::class, [$this, $client])->run(); + } +} diff --git a/lib/User/Event/FormEvent.php b/lib/User/Event/FormEvent.php index 091dd37..8bdd785 100644 --- a/lib/User/Event/FormEvent.php +++ b/lib/User/Event/FormEvent.php @@ -10,6 +10,8 @@ class FormEvent extends Event const EVENT_AFTER_REQUEST = 'afterRequest'; const EVENT_BEFORE_RESEND = 'beforeResend'; const EVENT_AFTER_RESEND = 'afterResend'; + const EVENT_BEFORE_LOGIN = 'beforeLogin'; + const EVENT_AFTER_LOGIN = 'afterLogin'; protected $form; diff --git a/lib/User/Event/SocialNetworkAuthEvent.php b/lib/User/Event/SocialNetworkAuthEvent.php index 1c6a67a..fbe4c8a 100644 --- a/lib/User/Event/SocialNetworkAuthEvent.php +++ b/lib/User/Event/SocialNetworkAuthEvent.php @@ -8,6 +8,11 @@ use yii\base\Event; class SocialNetworkAuthEvent extends Event { + const EVENT_BEFORE_AUTHENTICATE = 'beforeAuthenticate'; + const EVENT_AFTER_AUTHENTICATE = 'afterAuthenticate'; + const EVENT_BEFORE_CONNECT = 'beforeConnect'; + const EVENT_AFTER_CONNECT = 'afterConnect'; + protected $client; protected $account; diff --git a/lib/User/Event/UserEvent.php b/lib/User/Event/UserEvent.php index 49d55c6..5767841 100644 --- a/lib/User/Event/UserEvent.php +++ b/lib/User/Event/UserEvent.php @@ -18,6 +18,8 @@ class UserEvent extends Event const EVENT_AFTER_UNBLOCK = 'afterUnblock'; const EVENT_BEFORE_BLOCK = 'beforeBlock'; const EVENT_AFTER_BLOCK = 'afterBlock'; + const EVENT_BEFORE_LOGOUT = 'beforeLogout'; + const EVENT_AFTER_LOGOUT = 'afterLogout'; protected $user; diff --git a/lib/User/Service/SocialNetworkAccountConnectService.php b/lib/User/Service/SocialNetworkAccountConnectService.php new file mode 100644 index 0000000..4cd773c --- /dev/null +++ b/lib/User/Service/SocialNetworkAccountConnectService.php @@ -0,0 +1,91 @@ +controller = $controller; + $this->client = $client; + $this->socialNetworkAccountQuery = $socialNetworkAccountQuery; + } + + public function run() + { + $account = $this->getSocialNetworkAccount(); + + $event = $this->make(SocialNetworkAuthEvent::class, [$account, $this->client]); + + $this->controller->trigger(SocialNetworkAuthEvent::EVENT_BEFORE_CONNECT, $event); + + if ($account && $account->user === null) { + /** @var User $user */ + $user = Yii::$app->user->identity; + $account->link('user', $user); + Yii::$app->session->setFlash('success', Yii::t('user', 'Your account has been connected')); + $this->controller->trigger(SocialNetworkAuthEvent::EVENT_AFTER_CONNECT, $event); + + return true; + + } else { + Yii::$app->session->setFlash( + 'danger', + Yii::t('user', 'This account has already been connected to another user') + ); + } + + return false; + } + + protected function getSocialNetworkAccount() + { + $account = $this->socialNetworkAccountQuery->whereClient($this->client)->one(); + + if (null === $account) { + $data = $this->client->getUserAttributes(); + + $account = $this->make( + SocialNetworkAccount::class, + [ + 'provider' => $this->client->getId(), + 'client_id' => $data['id'], + 'data' => json_encode($data) + ] + ); + + if ($account->save(false)) { + return $account; + } + } + + return false; + } +} diff --git a/lib/User/Service/SocialNetworkAccountCreateService.php b/lib/User/Service/SocialNetworkAccountCreateService.php deleted file mode 100644 index 2f76896..0000000 --- a/lib/User/Service/SocialNetworkAccountCreateService.php +++ /dev/null @@ -1,85 +0,0 @@ -client = $client; - $this->query = $query; - } - - /** - * @return object - */ - public function run() - { - $data = $this->client->getUserAttributes(); - - /** @var SocialNetworkAccount $account */ - $account = Yii::createObject( - [ - 'class' => SocialNetworkAccount::class, - 'provider' => $this->client->getId(), - 'client_id' => $data['id'], - 'data' => json_encode($data), - 'username' => $this->client->getUserName(), - 'email' => $this->client->getEmail() - ] - ); - - if (($user = $this->getUser($account)) instanceof User) { - $account->user_id = $user->id; - } - - $account->save(false); - - return $account; - } - - protected function getUser(SocialNetworkAccount $account) - { - $user = $this->query->whereEmail($account->email)->one(); - if (null !== $user) { - return $user; - } - /** @var User $user */ - $user = Yii::createObject( - 'User', - [ - 'scenario' => 'connect', - 'username' => $account->username, - 'email' => $account->email - ] - ); - - if (!$user->validate(['email'])) { - $user->email = null; - } - - if (!$user->validate(['username'])) { - $user->username = null; - } - - return Yii::$container->get(UserCreateService::class, [$user])->run() ? $user : false; - } -} diff --git a/lib/User/Service/SocialNetworkAccountUserLinkService.php b/lib/User/Service/SocialNetworkAccountUserLinkService.php deleted file mode 100644 index 344f406..0000000 --- a/lib/User/Service/SocialNetworkAccountUserLinkService.php +++ /dev/null @@ -1,68 +0,0 @@ -client = $client; - $this->query = $query; - } - - public function run() - { - $account = $this->getSocialNetworkAccount(); - - if ($account->user === null) { - /** @var User $user */ - $user = Yii::$app->user->identity; - $account->link('user', $user); - - return true; - } - - return false; - } - - protected function getSocialNetworkAccount() - { - $account = $this->query->whereClient($this->client)->one(); - - if (null === $account) { - $data = $this->client->getUserAttributes(); - - $account = Yii::createObject( - [ - 'class' => SocialNetworkAccount::class, - 'provider' => $this->client->getId(), - 'client_id' => $data['id'], - 'data' => json_encode($data) - ] - ); - - $account->save(false); - } - - return $account; - } -} diff --git a/lib/User/Service/SocialNetworkAuthenticateService.php b/lib/User/Service/SocialNetworkAuthenticateService.php new file mode 100644 index 0000000..5e6e475 --- /dev/null +++ b/lib/User/Service/SocialNetworkAuthenticateService.php @@ -0,0 +1,132 @@ +controller = $controller; + $this->authAction = $authAction; + $this->client = $client; + $this->socialNetworkAccountQuery = $socialNetworkAccountQuery; + $this->userQuery = $userQuery; + } + + + public function run() + { + $account = $this->socialNetworkAccountQuery->whereClient($this->client)->one(); + if (!$this->controller->module->enableRegistration && ($account === null || $account->user === null)) { + Yii::$app->session->setFlash('danger', Yii::t('user', 'Registration on this website is disabled')); + $this->authAction->setSuccessUrl(Url::to(['/usr/security/login'])); + + return false; + } + if ($account === null) { + $account = $this->createAccount(); + if (!$account) { + Yii::$app->session->setFlash('danger', Yii::t('user', 'Unable to create an account.')); + $this->authAction->setSuccessUrl(Url::to(['/usr/security/login'])); + + return false; + } + } + $event = Yii::createObject(SocialNetworkAuthEvent::class, [$this->client]); + + $this->controller->trigger(SocialNetworkAuthEvent::EVENT_BEFORE_AUTHENTICATE, $event); + if ($account->user instanceof User) { + if ($account->user->getIsBlocked()) { + Yii::$app->session->setFlash('danger', Yii::t('user', 'Your account has been blocked.')); + $this->authAction->setSuccessUrl(Url::to(['/user/security/login'])); + } else { + Yii::$app->user->login($account->user, $this->controller->module->rememberLoginLifeSpan); + $this->authAction->setSuccessUrl(Yii::$app->getUser()->getReturnUrl()); + } + } else { + $this->authAction->setSuccessUrl($account->getConnectionUrl()); + } + + $this->controller->trigger(SocialNetworkAuthEvent::EVENT_AFTER_AUTHENTICATE, $event); + } + + protected function createAccount() + { + $data = $this->client->getUserAttributes(); + + /** @var SocialNetworkAccount $account */ + $account = $this->controller->make( + SocialNetworkAccount::class, + [ + 'provider' => $this->client->getId(), + 'client_id' => $data['id'], + 'data' => json_encode($data), + 'username' => $this->client->getUserName(), + 'email' => $this->client->getEmail() + ] + ); + + if (($user = $this->getUser($account)) instanceof User) { + $account->user_id = $user->id; + $account->save(false); + + return $account; + } + + return false; + } + + protected function getUser(SocialNetworkAccount $account) + { + $user = $this->userQuery->whereEmail($account->email)->one(); + if (null !== $user) { + return $user; + } + /** @var User $user */ + $user = $this->controller->make( + User::class, + [ + 'scenario' => 'connect', + 'username' => $account->username, + 'email' => $account->email + ] + ); + + if (!$user->validate(['email'])) { + $user->email = null; + } + + if (!$user->validate(['username'])) { + $user->username = null; + } + + $mailService = MailFactory::makeWelcomeMailerService($user); + + return $this->controller->make(UserCreateService::class, [$user, $mailService])->run() ? $user : false; + } +}