mirror of
https://github.com/humhub/humhub.git
synced 2025-01-17 14:18:27 +01:00
* Enh #6164: Invitation by link: when registering within an SSO, the email should only be requested on the service provider * Updated with fix #6202: Invite by link is not possible for a user already invited by email * new AuthChoice() was causing HeadersAlreadySentException * Allow registration by email with an SSO even if new user cannot register (in the settings) * Added and * Fixed broken tests * global token was given instead of space token * Convert Invite when using OAuth to keep Space * Use Session instead of Return Parameter * Fix tests * Fixed invalidToken Test * Test: Fixed HTTP return code on disabled link invite --------- Co-authored-by: Marc Farré <contact@marc.fun>
This commit is contained in:
parent
78e09961c8
commit
933773f856
@ -15,6 +15,8 @@ HumHub Changelog
|
||||
- Enh #6236: Logging: Show log entries from migrations with category migration
|
||||
- Fix #6216: Spaces icon in admin menu
|
||||
- Fix #6229: Bug on saving forms: Zend OPcache API is restricted by "restrict_api"
|
||||
- Enh #6240: Add ability to set showAtDashboard in SpaceMembership::addMember method
|
||||
- Enh #6164: Invitation by link: when registering within an SSO, the email should only be requested on the service provider
|
||||
- Enh #6240: Add ability to set showAtDashboard in SpaceMembership::addMember method
|
||||
- Enh #5668: Allow Admin to sort the Spaces in a custom order
|
||||
- Enh #29: AutoStart Tour for new Users
|
||||
|
@ -7,9 +7,9 @@ use humhub\modules\admin\permissions\ManageUsers;
|
||||
use humhub\modules\space\jobs\AddUsersToSpaceJob;
|
||||
use humhub\modules\space\models\Membership;
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\user\models\Invite;
|
||||
use humhub\modules\user\models\User;
|
||||
use humhub\modules\user\Module;
|
||||
use humhub\modules\user\services\LinkRegistrationService;
|
||||
use Yii;
|
||||
use yii\base\Exception;
|
||||
use yii\base\Model;
|
||||
@ -103,7 +103,7 @@ class InviteForm extends Model
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if(!$this->validate()) {
|
||||
if (!$this->validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -354,11 +354,13 @@ class InviteForm extends Model
|
||||
*/
|
||||
public function getInviteLink($forceResetToken = false)
|
||||
{
|
||||
$token = $this->space->settings->get('inviteToken');
|
||||
$linkRegistrationService = new LinkRegistrationService(null, $this->space);
|
||||
|
||||
$token = $linkRegistrationService->getStoredToken();
|
||||
if ($forceResetToken || !$token) {
|
||||
$token = Yii::$app->security->generateRandomString(Invite::LINK_TOKEN_LENGTH);
|
||||
$this->space->settings->set('inviteToken', $token);
|
||||
$token = $linkRegistrationService->setNewToken();
|
||||
}
|
||||
|
||||
return Url::to(['/user/registration/by-link', 'token' => $token, 'spaceId' => $this->space->id], true);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ use Yii;
|
||||
*/
|
||||
class AuthAction extends \yii\authclient\AuthAction
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
@ -29,13 +28,14 @@ class AuthAction extends \yii\authclient\AuthAction
|
||||
*/
|
||||
public function auth($client, $authUrlParams = [])
|
||||
{
|
||||
Yii::$app->session->set('loginRememberMe', (boolean) Yii::$app->request->get('rememberMe'));
|
||||
$rememberMe = (bool)Yii::$app->request->get('rememberMe');
|
||||
Yii::$app->session->set('loginRememberMe', $rememberMe);
|
||||
|
||||
if ($client instanceof StandaloneAuthClient) {
|
||||
return $client->authAction($this);
|
||||
}
|
||||
|
||||
return parent::auth($client);
|
||||
return parent::auth($client, $authUrlParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,18 +11,20 @@ namespace humhub\modules\user\controllers;
|
||||
use humhub\components\access\ControllerAccess;
|
||||
use humhub\components\Controller;
|
||||
use humhub\components\Response;
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\user\models\User;
|
||||
use humhub\modules\user\authclient\AuthAction;
|
||||
use humhub\modules\user\events\UserEvent;
|
||||
use humhub\modules\user\models\Invite;
|
||||
use humhub\modules\user\models\forms\Login;
|
||||
use humhub\modules\user\authclient\interfaces\ApprovalBypass;
|
||||
use humhub\modules\user\authclient\BaseFormAuth;
|
||||
use humhub\modules\user\models\Session;
|
||||
use humhub\modules\user\services\AuthClientService;
|
||||
use humhub\modules\user\services\AuthClientUserService;
|
||||
use humhub\modules\user\Module;
|
||||
use humhub\modules\user\services\InviteRegistrationService;
|
||||
use humhub\modules\user\services\LinkRegistrationService;
|
||||
use Yii;
|
||||
use yii\captcha\CaptchaAction;
|
||||
use yii\web\Cookie;
|
||||
use yii\authclient\BaseClient;
|
||||
use yii\web\HttpException;
|
||||
@ -71,7 +73,7 @@ class AuthController extends Controller
|
||||
{
|
||||
return [
|
||||
'captcha' => [
|
||||
'class' => 'yii\captcha\CaptchaAction',
|
||||
'class' => CaptchaAction::class,
|
||||
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
|
||||
],
|
||||
'external' => [
|
||||
@ -146,8 +148,6 @@ class AuthController extends Controller
|
||||
*/
|
||||
public function onAuthSuccess(BaseClient $authClient)
|
||||
{
|
||||
$attributes = $authClient->getUserAttributes();
|
||||
|
||||
// User already logged in - Add new authclient to existing user
|
||||
if (!Yii::$app->user->isGuest) {
|
||||
Yii::$app->user->getAuthClientUserService()->add($authClient);
|
||||
@ -162,45 +162,69 @@ class AuthController extends Controller
|
||||
return $this->redirect(['/user/auth/login']);
|
||||
}
|
||||
|
||||
// Check if e-mail is already in use with another auth method
|
||||
if ($user === null && isset($attributes['email'])) {
|
||||
$user = User::findOne(['email' => $attributes['email']]);
|
||||
if ($user !== null) {
|
||||
// Map current auth method to user with same e-mail address
|
||||
(new AuthClientUserService($user))->add($authClient);
|
||||
}
|
||||
}
|
||||
$authClientService->autoMapToExistingUser();
|
||||
|
||||
if ($user !== null) {
|
||||
return $this->login($user, $authClient);
|
||||
}
|
||||
|
||||
if (!$authClient instanceof ApprovalBypass && !Yii::$app->getModule('user')->settings->get('auth.anonymousRegistration')) {
|
||||
Yii::warning('Could not register user automatically: Anonymous registration disabled. AuthClient: ' . get_class($authClient), 'user');
|
||||
Yii::$app->session->setFlash('error', Yii::t('UserModule.base', "You're not registered."));
|
||||
return $this->redirect(['/user/auth/login']);
|
||||
}
|
||||
return $this->register($authClient);
|
||||
}
|
||||
|
||||
// Check if E-Mail is given
|
||||
if (!isset($attributes['email']) && Yii::$app->getModule('user')->emailRequired) {
|
||||
|
||||
/**
|
||||
* Try to register (automatic user creation or start the registration process) after successful authentication
|
||||
* without found related user account
|
||||
*
|
||||
* @param BaseClient $authClient
|
||||
* @return Response|\yii\console\Response|\yii\web\Response
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function register(BaseClient $authClient)
|
||||
{
|
||||
$attributes = $authClient->getUserAttributes();
|
||||
|
||||
// Check if E-Mail is given by the AuthClient
|
||||
if (!isset($attributes['email']) && $this->module->emailRequired) {
|
||||
Yii::warning('Could not register user automatically: AuthClient ' . get_class($authClient) . ' provided no E-Mail attribute.', 'user');
|
||||
Yii::$app->session->setFlash('error', Yii::t('UserModule.base', 'Missing E-Mail Attribute from AuthClient.'));
|
||||
return $this->redirect(['/user/auth/login']);
|
||||
}
|
||||
|
||||
// Check if AuthClient provide a ID for the user (mandatory)
|
||||
if (!isset($attributes['id'])) {
|
||||
Yii::warning('Could not register user automatically: AuthClient ' . get_class($authClient) . ' provided no ID attribute.', 'user');
|
||||
Yii::$app->session->setFlash('error', Yii::t('UserModule.base', 'Missing ID AuthClient Attribute from AuthClient.'));
|
||||
return $this->redirect(['/user/auth/login']);
|
||||
}
|
||||
|
||||
// Try automatically create user & login user
|
||||
$user = $authClientService->createUser();
|
||||
$authClientService = new AuthClientService($authClient);
|
||||
$tokenRegistrationService = new InviteRegistrationService((string) Yii::$app->request->get('token'));
|
||||
$linkRegistrationService = LinkRegistrationService::createFromRequest();
|
||||
|
||||
if (!$tokenRegistrationService->isValid() && !$linkRegistrationService->isValid() && !$authClientService->allowSelfRegistration()) {
|
||||
Yii::warning('Could not register user automatically: Anonymous registration disabled. AuthClient: ' . get_class($authClient), 'user');
|
||||
Yii::$app->session->setFlash('error', Yii::t('UserModule.base', "You're not registered."));
|
||||
return $this->redirect(['/user/auth/login']);
|
||||
}
|
||||
|
||||
if ($linkRegistrationService->isValid() && !empty($attributes['email'])) {
|
||||
$linkRegistrationService->convertToInvite($attributes['email']);
|
||||
}
|
||||
|
||||
// Try automatic user creation
|
||||
$user = $authClientService->createUser();
|
||||
if ($user !== null) {
|
||||
return $this->login($user, $authClient);
|
||||
}
|
||||
|
||||
// Start Registration
|
||||
return $this->redirectToRegistration($authClient);
|
||||
}
|
||||
|
||||
|
||||
private function redirectToRegistration(BaseClient $authClient)
|
||||
{
|
||||
if ($authClient instanceof \humhub\modules\user\authclient\BaseClient) {
|
||||
/** @var \humhub\modules\user\authclient\BaseClient $authClient */
|
||||
$authClient->beforeSerialize();
|
||||
@ -209,10 +233,10 @@ class AuthController extends Controller
|
||||
// Store authclient in session - for registration controller
|
||||
Yii::$app->session->set('authClient', $authClient);
|
||||
|
||||
// Start registration process
|
||||
return $this->redirect(['/user/registration']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login user
|
||||
*
|
||||
|
@ -9,10 +9,13 @@
|
||||
namespace humhub\modules\user\controllers;
|
||||
|
||||
use humhub\components\access\ControllerAccess;
|
||||
use humhub\modules\space\models\forms\InviteForm;
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\user\Module;
|
||||
use humhub\modules\user\services\LinkRegistrationService;
|
||||
use humhub\modules\user\services\InviteRegistrationService;
|
||||
use humhub\modules\user\widgets\AuthChoice;
|
||||
use Yii;
|
||||
use yii\authclient\BaseClient;
|
||||
use yii\base\Exception;
|
||||
use yii\db\StaleObjectException;
|
||||
use yii\web\HttpException;
|
||||
@ -67,16 +70,21 @@ class RegistrationController extends Controller
|
||||
$registration = new Registration();
|
||||
|
||||
/**
|
||||
* @var \yii\authclient\BaseClient
|
||||
* @var BaseClient
|
||||
*/
|
||||
$authClient = null;
|
||||
$inviteToken = Yii::$app->request->get('token', '');
|
||||
$showAuthClients = AuthChoice::hasClients();
|
||||
|
||||
if ($inviteToken != '') {
|
||||
$this->handleInviteRegistration($inviteToken, $registration);
|
||||
if (Yii::$app->request->get('token')) {
|
||||
$inviteRegistrationService = new InviteRegistrationService(Yii::$app->request->get('token'));
|
||||
if (!$inviteRegistrationService->isValid()) {
|
||||
throw new HttpException(404, 'Invalid registration token!');
|
||||
}
|
||||
$inviteRegistrationService->populateRegistration($registration);
|
||||
} elseif (Yii::$app->session->has('authClient')) {
|
||||
$authClient = Yii::$app->session->get('authClient');
|
||||
$this->handleAuthClientRegistration($authClient, $registration);
|
||||
$showAuthClients = false;
|
||||
} else {
|
||||
Yii::warning('Registration failed: No token (query) or authclient (session) found!', 'user');
|
||||
Yii::$app->session->setFlash('error', 'Registration failed.');
|
||||
@ -102,7 +110,10 @@ class RegistrationController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('index', ['hForm' => $registration]);
|
||||
return $this->render('index', [
|
||||
'hForm' => $registration,
|
||||
'showAuthClients' => $showAuthClients,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -115,85 +126,37 @@ class RegistrationController extends Controller
|
||||
* @throws \Throwable
|
||||
* @throws StaleObjectException
|
||||
*/
|
||||
public function actionByLink($token = null, $spaceId = null)
|
||||
public function actionByLink(?string $token = null, $spaceId = null)
|
||||
{
|
||||
if (empty($this->module->settings->get('auth.internalUsersCanInviteByLink'))) {
|
||||
throw new HttpException(400, 'Invite by link is disabled!');
|
||||
$linkRegistrationService = new LinkRegistrationService($token, Space::findOne(['id' => (int)$spaceId]));
|
||||
|
||||
if (!$linkRegistrationService->isEnabled()) {
|
||||
throw new HttpException(404);
|
||||
}
|
||||
|
||||
if ($spaceId !== null) {
|
||||
// If invited by link from a space
|
||||
$space = Space::findOne(['id' => (int)$spaceId]);
|
||||
if ($space === null || $space->settings->get('inviteToken') !== $token) {
|
||||
throw new HttpException(404, 'Invalid registration token!');
|
||||
}
|
||||
|
||||
Yii::$app->setLanguage($space->ownerUser->language);
|
||||
} else {
|
||||
// If invited by link globally
|
||||
if ($this->module->settings->get('registration.inviteToken') !== $token) {
|
||||
throw new HttpException(404, 'Invalid registration token!');
|
||||
}
|
||||
if ($token === null || !$linkRegistrationService->isValid()) {
|
||||
throw new HttpException(400, 'Invalid token provided!');
|
||||
}
|
||||
|
||||
$invite = new Invite([
|
||||
'source' => Invite::SOURCE_INVITE_BY_LINK,
|
||||
'space_invite_id' => $spaceId,
|
||||
'scenario' => 'invite',
|
||||
'language' => Yii::$app->language,
|
||||
]);
|
||||
$linkRegistrationService->storeInSession();
|
||||
|
||||
if ($invite->load(Yii::$app->request->post())) {
|
||||
// Deleting any previous email invitation or abandoned link invitation
|
||||
$oldInvite = Invite::findOne(['email' => $invite->email]);
|
||||
if ($oldInvite !== null) {
|
||||
$oldInvite->delete();
|
||||
}
|
||||
if ($invite->save()) {
|
||||
$invite->sendInviteMail();
|
||||
return $this->render('@user/views/auth/register_success', ['model' => $invite]);
|
||||
}
|
||||
$form = new Invite(['source' => Invite::SOURCE_INVITE_BY_LINK]);
|
||||
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
|
||||
$invite = $linkRegistrationService->convertToInvite($form->email);
|
||||
$invite->sendInviteMail();
|
||||
return $this->render('@user/views/auth/register_success', ['model' => $invite]);
|
||||
}
|
||||
|
||||
return $this->render('byLink', [
|
||||
'invite' => $invite,
|
||||
'invite' => $form,
|
||||
'showAuthClients' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $inviteToken
|
||||
* @param Registration $form
|
||||
* @throws HttpException
|
||||
*/
|
||||
protected function handleInviteByEmailRegistration($inviteToken, Registration $form)
|
||||
{
|
||||
$userInvite = Invite::findOne(['token' => $inviteToken]);
|
||||
if (!$userInvite) {
|
||||
throw new HttpException(404, 'Invalid registration token!');
|
||||
}
|
||||
Yii::$app->setLanguage($userInvite->language);
|
||||
$form->getUser()->email = $userInvite->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $inviteToken
|
||||
* @param Registration $form
|
||||
* @throws HttpException
|
||||
*/
|
||||
protected function handleInviteRegistration($inviteToken, Registration $form)
|
||||
{
|
||||
$userInvite = Invite::findOne(['token' => $inviteToken]);
|
||||
if (!$userInvite) {
|
||||
throw new HttpException(404, 'Invalid registration token!');
|
||||
}
|
||||
Yii::$app->setLanguage($userInvite->language);
|
||||
$form->getUser()->email = $userInvite->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Already all registration data gathered
|
||||
*
|
||||
* @param \yii\authclient\BaseClient $authClient
|
||||
* @param BaseClient $authClient
|
||||
* @param Registration $registration
|
||||
* @throws Exception
|
||||
*/
|
||||
|
@ -10,9 +10,13 @@
|
||||
namespace humhub\modules\user\helpers;
|
||||
|
||||
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\user\models\forms\Registration;
|
||||
use humhub\modules\user\models\Invite;
|
||||
use humhub\modules\user\models\User;
|
||||
use humhub\modules\user\Module;
|
||||
use Yii;
|
||||
use yii\web\HttpException;
|
||||
|
||||
/**
|
||||
* Class AuthHelper
|
||||
@ -88,4 +92,5 @@ class AuthHelper
|
||||
|
||||
return $username . $usernameRandomSuffix;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,6 +46,11 @@ class Invite extends ActiveRecord
|
||||
|
||||
public $captcha;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $skipCaptchaValidation = false;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@ -238,8 +243,13 @@ class Invite extends ActiveRecord
|
||||
return (!Yii::$app->settings->get('maintenanceMode') && Yii::$app->getModule('user')->settings->get('auth.anonymousRegistration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function showCaptureInRegisterForm()
|
||||
{
|
||||
return (Yii::$app->getModule('user')->settings->get('auth.showCaptureInRegisterForm'));
|
||||
return
|
||||
!$this->skipCaptchaValidation
|
||||
&& (Yii::$app->getModule('user')->settings->get('auth.showCaptureInRegisterForm'));
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ namespace humhub\modules\user\models\forms;
|
||||
use humhub\modules\admin\permissions\ManageGroups;
|
||||
use humhub\modules\admin\permissions\ManageUsers;
|
||||
use humhub\modules\user\Module;
|
||||
use humhub\modules\user\services\LinkRegistrationService;
|
||||
use Yii;
|
||||
use yii\base\Exception;
|
||||
use yii\base\InvalidConfigException;
|
||||
@ -123,15 +124,12 @@ class Invite extends Model
|
||||
*/
|
||||
public function getInviteLink($forceResetToken = false)
|
||||
{
|
||||
/* @var $module Module */
|
||||
$module = Yii::$app->getModule('user');
|
||||
$settings = $module->settings;
|
||||
|
||||
$token = $settings->get('registration.inviteToken');
|
||||
$linkRegistrationService = new LinkRegistrationService();
|
||||
$token = $linkRegistrationService->getStoredToken();
|
||||
if ($forceResetToken || !$token) {
|
||||
$token = Yii::$app->security->generateRandomString(\humhub\modules\user\models\Invite::LINK_TOKEN_LENGTH);
|
||||
$settings->set('registration.inviteToken', $token);
|
||||
$token = $linkRegistrationService->setNewToken();
|
||||
}
|
||||
|
||||
return Url::to(['/user/registration/by-link', 'token' => $token], true);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use humhub\modules\user\helpers\AuthHelper;
|
||||
use humhub\modules\user\models\Auth;
|
||||
use humhub\modules\user\models\forms\Registration;
|
||||
use humhub\modules\user\models\User;
|
||||
use humhub\modules\user\Module;
|
||||
use Yii;
|
||||
use yii\authclient\ClientInterface;
|
||||
use yii\helpers\VarDumper;
|
||||
@ -187,4 +188,40 @@ class AuthClientService
|
||||
|
||||
return $authClientCollection;
|
||||
}
|
||||
|
||||
public function autoMapToExistingUser(): void
|
||||
{
|
||||
$attributes = $this->authClient->getUserAttributes();
|
||||
|
||||
// Check if e-mail is already in use with another auth method
|
||||
if ($this->getUser() === null && isset($attributes['email'])) {
|
||||
$user = User::findOne(['email' => $attributes['email']]);
|
||||
if ($user !== null) {
|
||||
// Map current auth method to user with same e-mail address
|
||||
(new AuthClientUserService($user))->add($this->authClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 1.15
|
||||
*/
|
||||
public function allowSelfRegistration(): bool
|
||||
{
|
||||
// Always also AuthClients like LDAP to automatic registration
|
||||
if ($this->authClient instanceof ApprovalBypass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Module $module */
|
||||
$module = Yii::$app->getModule('user');
|
||||
|
||||
// Anonymous Registration is enabled
|
||||
if ($module->settings->get('auth.anonymousRegistration')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\user\services;
|
||||
|
||||
use humhub\modules\user\models\forms\Registration;
|
||||
use humhub\modules\user\models\Invite;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* InviteRegistrationService is responsible for registrations (Global or by Space) using a mail invite.
|
||||
*
|
||||
* @since 1.15
|
||||
*/
|
||||
final class InviteRegistrationService
|
||||
{
|
||||
private ?string $token;
|
||||
|
||||
public function __construct(?string $token)
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return ($this->getInvite() !== null);
|
||||
}
|
||||
|
||||
private function getInvite(): ?Invite
|
||||
{
|
||||
return Invite::findOne(['token' => $this->token]);
|
||||
}
|
||||
|
||||
public function populateRegistration(Registration $registration): void
|
||||
{
|
||||
$invite = $this->getInvite();
|
||||
if ($invite !== null) {
|
||||
Yii::$app->setLanguage($invite->language);
|
||||
$registration->getUser()->email = $invite->email;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\modules\user\services;
|
||||
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\user\models\Invite;
|
||||
use humhub\modules\user\Module;
|
||||
use Yii;
|
||||
use yii\base\Exception;
|
||||
|
||||
/**
|
||||
* LinkRegistrationService is responsible for registrations (Global or per Space) using an Invite Link.
|
||||
*
|
||||
* @since 1.15
|
||||
*/
|
||||
final class LinkRegistrationService
|
||||
{
|
||||
const SETTING_VAR_ENABLED = 'auth.internalUsersCanInviteByLink';
|
||||
const SETTING_VAR_SPACE_TOKEN = 'inviteToken';
|
||||
const SETTING_VAR_TOKEN = 'registration.inviteToken';
|
||||
private ?Space $space;
|
||||
private ?string $token;
|
||||
|
||||
public static function createFromRequest(): LinkRegistrationService
|
||||
{
|
||||
$token = (string)Yii::$app->request->get('token', null);
|
||||
$spaceId = (int)Yii::$app->request->get('spaceId');
|
||||
|
||||
if (!$token && Yii::$app->session->has(LinkRegistrationService::class . '::token')) {
|
||||
$token = Yii::$app->session->get(LinkRegistrationService::class . '::token');
|
||||
$spaceId = Yii::$app->session->get(LinkRegistrationService::class . '::spaceId', null);
|
||||
}
|
||||
|
||||
return new LinkRegistrationService($token, Space::findOne(['id' => $spaceId]));
|
||||
}
|
||||
|
||||
|
||||
public function __construct(?string $token = null, ?Space $space = null)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->space = $space;
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return ($this->isEnabled() && $this->getStoredToken() === $this->token);
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
/** @var Module $module */
|
||||
$module = Yii::$app->getModule('user');
|
||||
|
||||
return (!empty($module->settings->get(self::SETTING_VAR_ENABLED)));
|
||||
}
|
||||
|
||||
public function getStoredToken(): ?string
|
||||
{
|
||||
if ($this->space) {
|
||||
// TODO: Find better solution
|
||||
Yii::$app->setLanguage($this->space->ownerUser->language);
|
||||
|
||||
return $this->space->settings->get(self::SETTING_VAR_SPACE_TOKEN);
|
||||
}
|
||||
|
||||
/** @var Module $module */
|
||||
$module = Yii::$app->getModule('user');
|
||||
|
||||
return $module->settings->get(self::SETTING_VAR_TOKEN);
|
||||
}
|
||||
|
||||
public function setNewToken(): string
|
||||
{
|
||||
$newToken = Yii::$app->security->generateRandomString(Invite::LINK_TOKEN_LENGTH);
|
||||
if ($this->space) {
|
||||
$this->space->settings->set(self::SETTING_VAR_SPACE_TOKEN, $newToken);
|
||||
} else {
|
||||
/** @var Module $module */
|
||||
$module = Yii::$app->getModule('user');
|
||||
|
||||
$module->settings->set(self::SETTING_VAR_TOKEN, $newToken);
|
||||
}
|
||||
|
||||
return $newToken;
|
||||
}
|
||||
|
||||
public function convertToInvite(string $email): Invite
|
||||
{
|
||||
// Deleting any previous email invitation or abandoned link invitation
|
||||
$oldInvite = Invite::findOne(['email' => $email]);
|
||||
if ($oldInvite !== null) {
|
||||
$oldInvite->delete();
|
||||
}
|
||||
|
||||
$invite = new Invite([
|
||||
'email' => $email,
|
||||
'scenario' => 'invite',
|
||||
'language' => Yii::$app->language,
|
||||
]);
|
||||
$invite->skipCaptchaValidation = true;
|
||||
$invite->source = Invite::SOURCE_INVITE_BY_LINK;
|
||||
if ($this->space) {
|
||||
$invite->space_invite_id = $this->space->id;
|
||||
}
|
||||
|
||||
if (!$invite->save()) {
|
||||
throw new Exception('Could not create invite!');
|
||||
}
|
||||
|
||||
return $invite;
|
||||
}
|
||||
|
||||
public function storeInSession()
|
||||
{
|
||||
Yii::$app->session->set(get_class($this) . '::token', $this->token);
|
||||
Yii::$app->session->set(get_class($this) . '::spaceId', $this->space->id ?? null);
|
||||
}
|
||||
|
||||
}
|
@ -15,7 +15,7 @@ class LinkInviteCest
|
||||
$inviteUrl = $inviteForm->getInviteLink();
|
||||
|
||||
$I->amOnPage($inviteUrl);
|
||||
$I->seeResponseCodeIs(400);
|
||||
$I->seeResponseCodeIs(404);
|
||||
}
|
||||
|
||||
public function testInvalidToken(FunctionalTester $I)
|
||||
@ -25,7 +25,7 @@ class LinkInviteCest
|
||||
Yii::$app->getModule('user')->settings->set('auth.internalUsersCanInviteByLink', 1);
|
||||
|
||||
$I->amOnRoute('/user/registration/by-link', ['token' => 'abcd', 'spaceId' => 1]);
|
||||
$I->seeResponseCodeIs(404);
|
||||
$I->seeResponseCodeIs(400);
|
||||
}
|
||||
|
||||
public function testValidTokenDifferentSpaceId(FunctionalTester $I)
|
||||
@ -34,17 +34,25 @@ class LinkInviteCest
|
||||
|
||||
Yii::$app->getModule('user')->settings->set('auth.internalUsersCanInviteByLink', 1);
|
||||
|
||||
|
||||
// Generate Token
|
||||
$space = \humhub\modules\space\models\Space::findOne(['name' => 'Space 2']);
|
||||
$inviteForm = new \humhub\modules\space\models\forms\InviteForm();
|
||||
$inviteForm->space = $space;
|
||||
$inviteUrl = $inviteForm->getInviteLink();
|
||||
|
||||
$I->amOnRoute('/user/registration/by-link', ['token' => $space->settings->get('inviteToken'), 'spaceId' => $space->id]);
|
||||
$linkRegistrationService = new \humhub\modules\user\services\LinkRegistrationService(null, $space);
|
||||
$I->amOnRoute('/user/registration/by-link', ['token' => $linkRegistrationService->getStoredToken(), 'spaceId' => $space->id]);
|
||||
$I->seeResponseCodeIs(200);
|
||||
|
||||
$I->amOnRoute('/user/registration/by-link', ['token' => $space->settings->get('inviteToken'), 'spaceId' => 1]);
|
||||
$I->amOnRoute('/user/registration/by-link', ['token' => $linkRegistrationService->getStoredToken(), 'spaceId' => 1]);
|
||||
$I->seeResponseCodeIs(400);
|
||||
|
||||
|
||||
Yii::$app->getModule('user')->settings->set('auth.internalUsersCanInviteByLink', 0);
|
||||
$I->amOnRoute('/user/registration/by-link', ['token' => 'abc', 'spaceId' => 1]);
|
||||
$I->seeResponseCodeIs(404);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,7 +7,10 @@ use humhub\libs\Html;
|
||||
use humhub\modules\user\models\Invite;
|
||||
use yii\captcha\Captcha;
|
||||
|
||||
/* @var $invite Invite */
|
||||
/**
|
||||
* @var $invite Invite
|
||||
* @var $showAuthClients bool
|
||||
*/
|
||||
|
||||
$this->pageTitle = Yii::t('UserModule.auth', 'Create Account');
|
||||
?>
|
||||
@ -22,6 +25,10 @@ $this->pageTitle = Yii::t('UserModule.auth', 'Create Account');
|
||||
<?= Yii::t('UserModule.auth', '<strong>Account</strong> registration') ?>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<?php if ($showAuthClients && AuthChoice::hasClients()): ?>
|
||||
<?= AuthChoice::widget() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (Yii::$app->session->hasFlash('error')): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<?= Yii::$app->session->getFlash('error') ?>
|
||||
|
@ -1,10 +1,16 @@
|
||||
<?php
|
||||
|
||||
use humhub\modules\user\models\forms\Registration;
|
||||
use humhub\widgets\SiteLogo;
|
||||
use yii\bootstrap\ActiveForm;
|
||||
use humhub\modules\user\widgets\AuthChoice;
|
||||
use humhub\libs\Html;
|
||||
|
||||
/**
|
||||
* @var $hForm Registration
|
||||
* @var $showAuthClients bool
|
||||
*/
|
||||
|
||||
$this->pageTitle = Yii::t('UserModule.auth', 'Create Account');
|
||||
?>
|
||||
|
||||
@ -18,7 +24,7 @@ $this->pageTitle = Yii::t('UserModule.auth', 'Create Account');
|
||||
<?= Yii::t('UserModule.auth', '<strong>Account</strong> registration') ?>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<?php if (AuthChoice::hasClients()): ?>
|
||||
<?php if ($showAuthClients): ?>
|
||||
<?= AuthChoice::widget() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user