1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-06 06:07:31 +02:00

Version 1.1.6 User Role, Fieldsets and Refactoring

This commit is contained in:
Sebastian
2018-05-22 23:03:27 +02:00
parent aefb9709cb
commit e346625e32
32 changed files with 1073 additions and 657 deletions

View File

@@ -7,6 +7,7 @@ use Slim\Http\Request;
use Slim\Http\Response;
use Typemill\Models\Validation;
use Typemill\Models\User;
use Typemill\Models\WriteYaml;
class AuthController extends Controller
{
@@ -34,9 +35,37 @@ class AuthController extends Controller
public function show(Request $request, Response $response, $args)
{
$this->c->view->render($response, '/auth/login.twig');
}
$data = array();
/* check previous login attemps */
$yaml = new WriteYaml();
$logins = $yaml->getYaml('settings/users', '.logins');
$userIP = $this->getUserIP();
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
if($userLogins)
{
/* get the latest */
$lastLogin = intval($logins[$userIP][$userLogins-1]);
/* if last login is longer than 60 seconds ago, clear it. */
if(time() - $lastLogin > 60)
{
unset($logins[$userIP]);
$yaml->updateYaml('settings/users', '.logins', $logins);
}
/* Did the user made three login attemps that failed? */
elseif($userLogins >= 3)
{
$timeleft = 60 - (time() - $lastLogin);
$data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.'));
}
}
$this->c->view->render($response, '/auth/login.twig', $data);
}
/**
* signin an existing user
*
@@ -47,6 +76,36 @@ class AuthController extends Controller
public function login(Request $request, Response $response)
{
/* log user attemps to authenticate */
$yaml = new WriteYaml();
$logins = $yaml->getYaml('settings/users', '.logins');
$userIP = $this->getUserIP();
$userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false;
/* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */
if($userLogins)
{
/* get the latest */
$lastLogin = intval($logins[$userIP][$userLogins-1]);
/* if last login is longer than 60 seconds ago, clear it and add this attempt */
if(time() - $lastLogin > 60)
{
unset($logins[$userIP]);
$yaml->updateYaml('settings/users', '.logins', $logins);
}
/* Did the user made three login attemps that failed? */
elseif($userLogins >= 2)
{
$logins[$userIP][] = time();
$yaml->updateYaml('settings/users', '.logins', $logins);
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
}
/* authentication */
$params = $request->getParams();
$validation = new Validation();
@@ -58,14 +117,26 @@ class AuthController extends Controller
if($userdata && password_verify($params['password'], $userdata['password']))
{
$user->login($userdata['username']);
/* clear the user login attemps */
if($userLogins)
{
unset($logins[$userIP]);
$yaml->updateYaml('settings/users', '.logins', $logins);
}
return $response->withRedirect($this->c->router->pathFor('settings.show'));
}
}
/* if authentication failed, add attempt to log file */
$logins[$userIP][] = time();
$yaml->updateYaml('settings/users', '.logins', $logins);
$this->c->flash->addMessage('error', 'Ups, credentials were wrong, please try again.');
$this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.');
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
/**
* log out a user
*
@@ -82,5 +153,27 @@ class AuthController extends Controller
}
return $response->withRedirect($this->c->router->pathFor('auth.show'));
}
}
private function getUserIP()
{
$client = @$_SERVER['HTTP_CLIENT_IP'];
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
$remote = $_SERVER['REMOTE_ADDR'];
if(filter_var($client, FILTER_VALIDATE_IP))
{
$ip = $client;
}
elseif(filter_var($forward, FILTER_VALIDATE_IP))
{
$ip = $forward;
}
else
{
$ip = $remote;
}
return $ip;
}
}

View File

@@ -20,6 +20,11 @@ abstract class Controller
$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
unset($_SESSION['old']);
$response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN');
$response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block');
return $this->c->view->render($response, $route, $data);
}

View File

@@ -90,7 +90,7 @@ class SettingsController extends Controller
if(isset($themeSettings['forms']['fields']))
{
$fields = $this->getFields($userSettings, 'themes', $themeName, $themeSettings);
/* overwrite original theme form definitions with enhanced form objects */
$themedata[$themeName]['forms']['fields'] = $fields;
}
@@ -177,57 +177,73 @@ class SettingsController extends Controller
/* then iterate through the fields */
foreach($objectSettings['forms']['fields'] as $fieldName => $fieldConfigs)
{
/* and create a new field object with the field name and the field configurations. */
$field = new Field($fieldName, $fieldConfigs);
/* you have to prefil the value for the field with default settings, user settings or old user-input from form */
$userValue = false;
/* first, add the default values from the original plugin or theme settings. Ignore checkboxes, otherwiese they might be always checked */
if(isset($objectSettings['settings'][$fieldName]))
if($fieldConfigs['type'] == 'fieldset')
{
$userValue = $objectSettings['settings'][$fieldName];
}
/* now overwrite them with the local stored user settings */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$userValue = $userSettings[$objectType][$objectName][$fieldName];
}
/* overwrite it with old input in form, if exists */
if(isset($_SESSION['old'][$objectName][$fieldName]))
{
$userValue = $_SESSION['old'][$objectName][$fieldName];
}
/* now we have set the uservalue for the field. Prepopulate the field object with it now */
if($field->getType() == "textarea")
{
if($userValue)
{
$field->setContent($userValue);
}
}
elseif($field->getType() == "checkbox")
{
/* needs special treatment, because field does not exist in settings if unchecked by user */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$field->setAttribute('checked', 'checked');
}
else
{
$field->unsetAttribute('checked');
}
/* Create an array for the subsettings of the fieldset with the same structure and data as the original settings array */
$subSettings = $objectSettings;
$subSettings['forms'] = $fieldConfigs;
$fieldset = array();
$fieldset['type'] = 'fieldset';
$fieldset['legend'] = $fieldConfigs['legend'];
$fieldset['fields'] = $this->getFields($userSettings, $objectType, $objectName, $subSettings);
$fields[] = $fieldset;
}
else
{
$field->setAttributeValue('value', $userValue);
}
/* and create a new field object with the field name and the field configurations. */
$field = new Field($fieldName, $fieldConfigs);
/* you have to prefil the value for the field with default settings, user settings or old user-input from form */
$userValue = false;
/* add the field to the field-List with the plugin-name as key */
$fields[] = $field;
/* first, add the default values from the original plugin or theme settings. Ignore checkboxes, otherwiese they might be always checked */
if(isset($objectSettings['settings'][$fieldName]))
{
$userValue = $objectSettings['settings'][$fieldName];
}
/* now overwrite them with the local stored user settings */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$userValue = $userSettings[$objectType][$objectName][$fieldName];
}
/* overwrite it with old input in form, if exists */
if(isset($_SESSION['old'][$objectName][$fieldName]))
{
$userValue = $_SESSION['old'][$objectName][$fieldName];
}
/* now we have set the uservalue for the field. Prepopulate the field object with it now */
if($field->getType() == "textarea")
{
if($userValue)
{
$field->setContent($userValue);
}
}
elseif($field->getType() == "checkbox")
{
/* needs special treatment, because field does not exist in settings if unchecked by user */
if(isset($userSettings[$objectType][$objectName][$fieldName]))
{
$field->setAttribute('checked', 'checked');
}
else
{
$field->unsetAttribute('checked');
}
}
else
{
$field->setAttributeValue('value', $userValue);
}
/* add the field to the field-List with the plugin-name as key */
$fields[] = $field;
}
}
return $fields;
@@ -331,13 +347,31 @@ class SettingsController extends Controller
/* fetch the original settings from the folder (plugin or theme) to get the field definitions */
$originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName);
if($originalSettings)
if(isset($originalSettings['forms']['fields']))
{
/* flaten the multi-dimensional array with fieldsets to a one-dimensional array */
$originalFields = array();
foreach($originalSettings['forms']['fields'] as $fieldName => $fieldValue)
{
if(isset($fieldValue['fields']))
{
foreach($fieldValue['fields'] as $subFieldName => $subFieldValue)
{
$originalFields[$subFieldName] = $subFieldValue;
}
}
else
{
$originalFields[$fieldName] = $fieldValue;
}
}
/* take the user input data and iterate over all fields and values */
foreach($userInput as $fieldName => $fieldValue)
{
/* get the corresponding field definition from original plugin settings */
$fieldDefinition = isset($originalSettings['forms']['fields'][$fieldName]) ? $originalSettings['forms']['fields'][$fieldName] : false;
$fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false;
if($fieldDefinition)
{
/* validate user input for this field */
@@ -345,18 +379,23 @@ class SettingsController extends Controller
}
if(!$fieldDefinition && $fieldName != 'active')
{
$_SESSION['errors'][$objectName][$fieldName] = 'This field is not defined!';
$_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!');
}
}
}
}
/***********************
** USER MANAGEMENT **
***********************/
public function showUser($request, $response, $args)
{
if($_SESSION['role'] == 'editor' && $_SESSION['user'] !== $args['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] ));
}
$validate = new Validation();
if($validate->username($args['username']))
@@ -428,12 +467,25 @@ class SettingsController extends Controller
public function updateUser($request, $response, $args)
{
if($request->isPost())
{
{
$params = $request->getParams();
$user = new User();
$userroles = $user->getUserroles();
$validate = new Validation();
/* non admins have different update rights */
if($_SESSION['role'] !== 'administrator')
{
/* if an editor tries to update other userdata than its own */
if($_SESSION['user'] !== $params['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] ));
}
/* non admins cannot change his userrole */
$params['userrole'] = $_SESSION['role'];
}
if($validate->existingUser($params, $userroles))
{
$userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole']);
@@ -442,14 +494,14 @@ class SettingsController extends Controller
{
$user->updateUser($userdata);
$this->c->flash->addMessage('info', 'Saved all changes');
return $response->withRedirect($this->c->router->pathFor('user.list'));
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
}
elseif($validate->newPassword($params))
{
$userdata['password'] = $params['newpassword'];
$user->updateUser($userdata);
$this->c->flash->addMessage('info', 'Saved all changes');
return $response->withRedirect($this->c->router->pathFor('user.list'));
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']]));
}
}
@@ -465,6 +517,16 @@ class SettingsController extends Controller
$params = $request->getParams();
$validate = new Validation();
$user = new User();
/* non admins have different update rights */
if($_SESSION['role'] !== 'administrator')
{
/* if an editor tries to delete other user than its own */
if($_SESSION['user'] !== $params['username'])
{
return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] ));
}
}
if($validate->username($params['username']))
{