mirror of
https://github.com/typemill/typemill.git
synced 2025-08-01 20:00:37 +02:00
v2.4.2 add loginlink feature
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
127.0.0.1;2024-03-25 21:48:49;login: wrong password
|
||||
127.0.0.1;2024-04-20 12:51:39;login: wrong password
|
||||
127.0.0.1;2024-04-21 19:24:11;login: invalid data
|
||||
127.0.0.1;2024-04-22 14:38:20;loginlink: loginlink for user member is not activated.
|
||||
|
@@ -221,8 +221,15 @@ class ControllerApiSystemUsers extends Controller
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||||
}
|
||||
|
||||
# check if loginlink is activated
|
||||
$loginlink = false;
|
||||
if($userdata['userrole'] == 'member' && isset($this->settings['loginlink']) && $this->settings['loginlink'])
|
||||
{
|
||||
$loginlink = true;
|
||||
}
|
||||
|
||||
# we have to validate again because of additional dynamic fields
|
||||
$formdefinitions = $user->getUserFields($this->c->get('acl'), $request->getAttribute('c_userrole'));
|
||||
$formdefinitions = $user->getUserFields($this->c->get('acl'), $request->getAttribute('c_userrole'), NULL, $loginlink);
|
||||
$validatedOutput = $validate->recursiveValidation($formdefinitions, $userdata);
|
||||
if(!empty($validate->errors))
|
||||
{
|
||||
|
@@ -241,6 +241,136 @@ class ControllerWebAuth extends Controller
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor($redirect))->withStatus(302);
|
||||
}
|
||||
|
||||
public function loginlink(Request $request, Response $response, $args)
|
||||
{
|
||||
if(!isset($this->settings['loginlink']) OR !$this->settings['loginlink'])
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('loginlink: not activated');
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
# optionally check trusted ips
|
||||
$trustedLogin = ( isset($this->settings['trustedloginreferrer']) && !empty($this->settings['trustedloginreferrer']) ) ? explode(",", $this->settings['trustedloginreferrer']) : [];
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'] ?? null;
|
||||
if (
|
||||
!empty($trustedLogin)
|
||||
&& !in_array($ipAddress, $trustedLogin)
|
||||
)
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('loginlink: remote address is not a trusted ip');
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
$input = $request->getQueryParams();
|
||||
$validation = new Validation();
|
||||
$securitylog = $this->settings['securitylog'] ?? false;
|
||||
|
||||
if($validation->signin($input) !== true)
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('loginlink: invalid data');
|
||||
}
|
||||
|
||||
if($this->c->get('flash'))
|
||||
{
|
||||
$this->c->get('flash')->addMessage('error', Translations::translate('Wrong password or username, please try again.'));
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
$user = new User();
|
||||
|
||||
if(!$user->setUserWithPassword($input['username']))
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('loginlink: user not found');
|
||||
}
|
||||
|
||||
if($this->c->get('flash'))
|
||||
{
|
||||
$this->c->get('flash')->addMessage('error', Translations::translate('Wrong password or username, please try again.'));
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
$userdata = $user->getUserData();
|
||||
|
||||
if($userdata['userrole'] != 'member')
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('loginlink: user has not a member role. Only members can use loginlinks.');
|
||||
}
|
||||
|
||||
if($this->c->get('flash'))
|
||||
{
|
||||
$this->c->get('flash')->addMessage('error', Translations::translate('User is not a member.'));
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
if(!isset($userdata['linkaccess']) OR ($userdata['linkaccess'] !== true))
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('loginlink: loginlink for user ' . $userdata['username'] . ' is not activated.');
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
if($userdata && !password_verify($input['password'], $userdata['password']))
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('login: wrong password');
|
||||
}
|
||||
|
||||
if($this->c->get('flash'))
|
||||
{
|
||||
$this->c->get('flash')->addMessage('error', Translations::translate('Wrong password or username, please try again.'));
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
# check if user has confirmed the account
|
||||
if(isset($userdata['optintoken']) && $userdata['optintoken'])
|
||||
{
|
||||
if($securitylog)
|
||||
{
|
||||
\Typemill\Static\Helpers::addLogEntry('login: user not confirmed yet.');
|
||||
}
|
||||
|
||||
if($this->c->get('flash'))
|
||||
{
|
||||
$this->c->get('flash')->addMessage('error', Translations::translate('Your registration is not confirmed yet. Please check your e-mails and use the confirmation link.'));
|
||||
}
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor('auth.show'))->withStatus(302);
|
||||
}
|
||||
|
||||
$user->login();
|
||||
|
||||
$redirect = $this->getRedirectDestination($userdata['userrole']);
|
||||
|
||||
return $response->withHeader('Location', $this->routeParser->urlFor($redirect))->withStatus(302);
|
||||
}
|
||||
|
||||
|
||||
private function getRedirectDestination(string $userrole)
|
||||
{
|
||||
# decide where to redirect after login, configurable in settings -> system.yaml
|
||||
|
@@ -361,7 +361,12 @@ class ControllerWebSystem extends Controller
|
||||
|
||||
$userdata = $user->getUserData();
|
||||
$inspector = $request->getAttribute('c_userrole');
|
||||
$userfields = $user->getUserFields($this->c->get('acl'), $userdata['userrole'], $inspector);
|
||||
$loginlink = false;
|
||||
if($userdata['userrole'] == 'member' && isset($this->settings['loginlink']) && $this->settings['loginlink'])
|
||||
{
|
||||
$loginlink = true;
|
||||
}
|
||||
$userfields = $user->getUserFields($this->c->get('acl'), $userdata['userrole'], $inspector, $loginlink);
|
||||
|
||||
return $this->c->get('view')->render($response, 'system/user.twig', [
|
||||
'settings' => $this->settings,
|
||||
|
@@ -169,7 +169,7 @@ class User
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUserFields($acl, $userrole, $inspectorrole = NULL)
|
||||
public function getUserFields($acl, $userrole, $inspectorrole = NULL, $loginlink = NULL)
|
||||
{
|
||||
$storage = new StorageWrapper('\Typemill\Models\Storage');
|
||||
$userfields = $storage->getYaml('systemSettings', '', 'user.yaml');
|
||||
@@ -217,7 +217,12 @@ class User
|
||||
$userfields['userrole'] = ['label' => Translations::translate('Role'), 'type' => 'select', 'options' => $options];
|
||||
|
||||
# can activate api access
|
||||
$userfields['apiaccess'] = ['label' => Translations::translate('API access'), 'checkboxlabel' => Translations::translate('Activate API access for this user. Use username and password for api calls'), 'type' => 'checkbox'];
|
||||
$userfields['apiaccess'] = ['label' => Translations::translate('API access'), 'checkboxlabel' => Translations::translate('Activate API access for this user. Use username and password for api calls. Whitelist calling domains in the developer settings.'), 'type' => 'checkbox'];
|
||||
|
||||
if($loginlink)
|
||||
{
|
||||
$userfields['linkaccess'] = ['label' => Translations::translate('Link access'), 'checkboxlabel' => Translations::translate('Activate link access for this user (only for member role). Use username and password for the link. Optionally whitelist IPs in the developer settings.'), 'type' => 'checkbox'];
|
||||
}
|
||||
}
|
||||
|
||||
return $userfields;
|
||||
|
@@ -19,6 +19,11 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($settings) {
|
||||
$group->get('/login', ControllerWebAuth::class . ':show')->setName('auth.show');
|
||||
$group->post('/login', ControllerWebAuth::class . ':login')->setName('auth.login');
|
||||
|
||||
if(isset($settings['loginlink']) && $settings['loginlink'])
|
||||
{
|
||||
$group->get('/loginlink', ControllerWebAuth::class . ':loginlink')->setName('auth.link');
|
||||
}
|
||||
|
||||
if(isset($settings['authcode']) && $settings['authcode'])
|
||||
{
|
||||
$group->post('/authcode', ControllerWebAuth::class . ':loginWithAuthcode')->setName('auth.authcode');
|
||||
|
@@ -289,4 +289,11 @@ fieldsetdeveloper:
|
||||
label: "Allowed Domains for API-Access (CORS-Headers)"
|
||||
placeholder: 'https://my-website-that-uses-the-api.org,https://another-website-using-the-api.org'
|
||||
description: "List all domains, separated by comma, that should have access to the Typemill API. Domains will be added to the cors-header."
|
||||
|
||||
loginlink:
|
||||
type: checkbox
|
||||
label: "Login with link"
|
||||
checkboxlabel: "Allow selected users to login with a login link."
|
||||
description: "If activated, you can allow login-links with a checkbox in the user profile. This is only available for member-roles since members have very limited rights. Login with a link can be helpful if you link from your software to a non-public documentation. Be aware of the low protection that this kind of logins have. If you integrate such links in a SaaS-software, then you should restrict access to your ips."
|
||||
trustedloginreferrer:
|
||||
type: text
|
||||
label: "Trusted IPs for the login-link-referrer (comma separated)"
|
@@ -21,6 +21,11 @@ userrole:
|
||||
label: 'Role'
|
||||
type: 'text'
|
||||
readonly: true
|
||||
darkmode:
|
||||
name: darkmode
|
||||
label: 'Darkmode'
|
||||
checkboxlabel: 'Activate the darkmode for me'
|
||||
type: 'checkbox'
|
||||
password:
|
||||
name: password
|
||||
label: 'Actual Password'
|
||||
@@ -31,9 +36,4 @@ newpassword:
|
||||
label: 'New Password'
|
||||
type: 'password'
|
||||
autocomplete: 'new-password'
|
||||
generator: true
|
||||
darkmode:
|
||||
name: darkmode
|
||||
label: 'Darkmode'
|
||||
checkboxlabel: 'Activate the darkmode for me'
|
||||
type: 'checkbox'
|
||||
generator: true
|
Reference in New Issue
Block a user