Merge branch 'bugfix/allow-to-configure-how-to-manage-groups-9609'

fixes #9609
This commit is contained in:
Johannes Meyer 2015-07-30 16:16:24 +02:00
commit eeff0dbfa5
22 changed files with 854 additions and 154 deletions

View File

@ -26,6 +26,32 @@ class DbUserGroupBackendForm extends Form
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
$resourceNames = $this->getDatabaseResourceNames();
$this->addElement(
'select',
@ -37,6 +63,15 @@ class DbUserGroupBackendForm extends Form
'multiOptions' => empty($resourceNames) ? array() : array_combine($resourceNames, $resourceNames)
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true, // Prevents the element from being submitted, see #7717
'value' => 'db'
)
);
}
/**

View File

@ -3,7 +3,6 @@
namespace Icinga\Forms\Config\UserGroup;
use Icinga\Application\Config;
use Icinga\Authentication\User\UserBackend;
use Icinga\Authentication\UserGroup\LdapUserGroupBackend;
use Icinga\Data\ConfigObject;
@ -32,6 +31,32 @@ class LdapUserGroupBackendForm extends Form
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
$resourceNames = $this->getLdapResourceNames();
$this->addElement(
'select',
@ -90,6 +115,15 @@ class LdapUserGroupBackendForm extends Form
$this->createGroupConfigElements($defaults, $groupConfigDisabled);
$this->createUserConfigElements($defaults, $userConfigDisabled, $dnDisabled);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true, // Prevents the element from being submitted, see #7717
'value' => $formData['type']
)
);
}
/**
@ -293,7 +327,7 @@ class LdapUserGroupBackendForm extends Form
protected function getLdapUserBackendNames(LdapConnection $resource)
{
$names = array();
foreach (Config::app('authentication') as $name => $config) {
foreach (UserBackend::getBackendConfigs() as $name => $config) {
if (in_array(strtolower($config->backend), array('ldap', 'msldap'))) {
$backendResource = ResourceFactory::create($config->resource);
if (

View File

@ -158,32 +158,6 @@ class UserGroupBackendForm extends ConfigForm
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
// TODO(jom): We did not think about how to configure custom group backends yet!
$backendTypes = array(
'db' => $this->translate('Database'),
@ -196,15 +170,6 @@ class UserGroupBackendForm extends ConfigForm
$backendType = key($backendTypes);
}
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true, // Prevents the element from being submitted, see #7717
'value' => $backendType
)
);
$this->addElement(
'select',
'type',

View File

@ -7,6 +7,7 @@ use ErrorException;
use Exception;
use LogicException;
use Icinga\Application\Modules\Manager as ModuleManager;
use Icinga\Authentication\User\UserBackend;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
@ -544,6 +545,24 @@ abstract class ApplicationBootstrap
return $this;
}
/**
* Set up the user backend factory
*
* @return $this
*/
protected function setupUserBackendFactory()
{
try {
UserBackend::setConfig(Config::app('authentication'));
} catch (NotReadableError $e) {
Logger::error(
new IcingaException('Cannot load user backend configuration. An exception was thrown:', $e)
);
}
return $this;
}
/**
* Detect the timezone
*

View File

@ -43,6 +43,7 @@ class Cli extends ApplicationBootstrap
->setupLogger()
->setupResourceFactory()
->setupModuleManager()
->setupUserBackendFactory()
->loadSetupModuleIfNecessary();
}

View File

@ -107,6 +107,7 @@ class Web extends ApplicationBootstrap
->setupZendMvc()
->setupFormNamespace()
->setupModuleManager()
->setupUserBackendFactory()
->loadSetupModuleIfNecessary()
->loadEnabledModules()
->setupRoute()

View File

@ -9,11 +9,12 @@ use Icinga\Application\Icinga;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Util\ConfigAwareFactory;
/**
* Factory for user backends
*/
class UserBackend
class UserBackend implements ConfigAwareFactory
{
/**
* The default user backend types provided by Icinga Web 2
@ -34,6 +35,48 @@ class UserBackend
*/
protected static $customBackends;
/**
* User backend configuration
*
* @var Config
*/
private static $backends;
/**
* Set user backend configuration
*
* @param Config $config
*/
public static function setConfig($config)
{
self::$backends = $config;
}
/**
* Return the configuration of all existing user backends
*
* @return Config
*/
public static function getBackendConfigs()
{
self::assertBackendsExist();
return self::$backends;
}
/**
* Check if any user backends exist. If not, throw an error.
*
* @throws ConfigurationError
*/
private static function assertBackendsExist()
{
if (self::$backends === null) {
throw new ConfigurationError(
'User backends not set up. Please contact your Icinga Web administrator'
);
}
}
/**
* Register all custom user backends from all loaded modules
*/
@ -110,9 +153,9 @@ class UserBackend
public static function create($name, ConfigObject $backendConfig = null)
{
if ($backendConfig === null) {
$authConfig = Config::app('authentication');
if ($authConfig->hasSection($name)) {
$backendConfig = $authConfig->getSection($name);
self::assertBackendsExist();
if (self::$backends->hasSection($name)) {
$backendConfig = self::$backends->getSection($name);
} else {
throw new ConfigurationError('User backend "%s" does not exist', $name);
}

View File

@ -437,7 +437,7 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
throw new ProgrammingError('It is required to set a attribute name where to find a group\'s name first');
}
if ($this->ds->getCapabilities()->hasAdOid()) {
if ($this->ds->getCapabilities()->isActiveDirectory()) {
$createdAtAttribute = 'whenCreated';
$lastModifiedAttribute = 'whenChanged';
} else {

View File

@ -138,6 +138,9 @@ class IdoResourcePage extends Form
}
$this->info($this->translate('The configuration has been successfully validated.'));
} elseif (! isset($formData['backend_validation'])) {
// This is usually done by isValid(Partial), but as we're not calling any of these...
$this->populate($formData);
}
return true;

View File

@ -4,10 +4,16 @@
namespace Icinga\Module\Setup\Forms;
use Exception;
use Icinga\Application\Config;
use Icinga\Authentication\User\UserBackend;
use Icinga\Authentication\User\DbUserBackend;
use Icinga\Authentication\User\LdapUserBackend;
use Icinga\Authentication\UserGroup\UserGroupBackend;
use Icinga\Authentication\UserGroup\LdapUserGroupBackend;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Data\Selectable;
use Icinga\Exception\NotImplementedError;
use Icinga\Web\Form;
/**
@ -23,12 +29,19 @@ class AdminAccountPage extends Form
protected $resourceConfig;
/**
* The backend configuration to use
* The user backend configuration to use
*
* @var array
*/
protected $backendConfig;
/**
* The user group backend configuration to use
*
* @var array
*/
protected $groupConfig;
/**
* Initialize this page
*/
@ -37,7 +50,7 @@ class AdminAccountPage extends Form
$this->setName('setup_admin_account');
$this->setTitle($this->translate('Administration', 'setup.page.title'));
$this->addDescription($this->translate(
'Now it\'s time to configure your first administrative account for Icinga Web 2.'
'Now it\'s time to configure your first administrative account or group for Icinga Web 2.'
));
}
@ -55,7 +68,7 @@ class AdminAccountPage extends Form
}
/**
* Set the backend configuration to use
* Set the user backend configuration to use
*
* @param array $config
*
@ -67,6 +80,19 @@ class AdminAccountPage extends Form
return $this;
}
/**
* Set the user group backend configuration to use
*
* @param array $config
*
* @return $this
*/
public function setGroupConfig(array $config = null)
{
$this->groupConfig = $config;
return $this;
}
/**
* @see Form::createElements()
*/
@ -76,6 +102,13 @@ class AdminAccountPage extends Form
if ($this->backendConfig['backend'] !== 'db') {
$choices['by_name'] = $this->translate('By Name', 'setup.admin');
$choice = isset($formData['user_type']) ? $formData['user_type'] : 'by_name';
if (in_array($this->backendConfig['backend'], array('ldap', 'msldap'))) {
$groups = $this->fetchGroups();
if (! empty($groups)) {
$choices['user_group'] = $this->translate('User Group', 'setup.admin');
}
}
} else {
$choices['new_user'] = $this->translate('New User', 'setup.admin');
$choice = isset($formData['user_type']) ? $formData['user_type'] : 'new_user';
@ -128,6 +161,23 @@ class AdminAccountPage extends Form
);
}
if ($choice === 'user_group') {
$this->addElement(
'select',
'user_group',
array(
'required' => true,
'label' => $this->translate('Group Name'),
'description' => $this->translate(
'Choose a user group reported by the LDAP backend'
. ' to permit its members administrative access.',
'setup.admin'
),
'multiOptions' => array_combine($groups, $groups)
)
);
}
if ($choice === 'existing_user') {
$this->addElement(
'select',
@ -234,14 +284,18 @@ class AdminAccountPage extends Form
}
/**
* Return the names of all users the backend currently provides
* Return the names of all users the user backend currently provides
*
* @return array
*/
protected function fetchUsers()
{
try {
return $this->createBackend()->select(array('user_name'))->order('user_name', 'asc', true)->fetchColumn();
return $this
->createUserBackend()
->select(array('user_name'))
->order('user_name', 'asc', true)
->fetchColumn();
} catch (Exception $_) {
// No need to handle anything special here. Error means no users found.
return array();
@ -249,7 +303,7 @@ class AdminAccountPage extends Form
}
/**
* Return whether the backend provides a user with the given name
* Return whether the user backend provides a user with the given name
*
* @param string $username
*
@ -258,21 +312,103 @@ class AdminAccountPage extends Form
protected function hasUser($username)
{
try {
return $this->createBackend()->select()->where('user_name', $username)->count() > 1;
return $this
->createUserBackend()
->select()
->where('user_name', $username)
->count() > 1;
} catch (Exception $_) {
return null;
return false;
}
}
/**
* Create and return the backend
* Create and return the user backend
*
* @return DbUserBackend|LdapUserBackend
*/
protected function createBackend()
protected function createUserBackend()
{
$resourceConfig = new Config();
$resourceConfig->setSection($this->resourceConfig['name'], $this->resourceConfig);
ResourceFactory::setConfig($resourceConfig);
$config = new ConfigObject($this->backendConfig);
$config->resource = $this->resourceConfig;
$config->resource = $this->resourceConfig['name'];
return UserBackend::create(null, $config);
}
/**
* Return the names of all user groups the user group backend currently provides
*
* @return array
*/
protected function fetchGroups()
{
try {
return $this
->createUserGroupBackend()
->select(array('group_name'))
->fetchColumn();
} catch (Exception $_) {
// No need to handle anything special here. Error means no groups found.
return array();
}
}
/**
* Return whether the user group backend provides a user group with the given name
*
* @param string $groupname
*
* @return bool
*/
protected function hasGroup($groupname)
{
try {
return $this
->createUserGroupBackend()
->select()
->where('group_name', $groupname)
->count() > 1;
} catch (Exception $_) {
return false;
}
}
/**
* Create and return the user group backend
*
* @return LdapUserGroupBackend
*/
protected function createUserGroupBackend()
{
$resourceConfig = new Config();
$resourceConfig->setSection($this->resourceConfig['name'], $this->resourceConfig);
ResourceFactory::setConfig($resourceConfig);
$backendConfig = new Config();
$backendConfig->setSection($this->backendConfig['name'], array_merge(
$this->backendConfig,
array('resource' => $this->resourceConfig['name'])
));
UserBackend::setConfig($backendConfig);
if (empty($this->groupConfig)) {
$groupConfig = new ConfigObject(array(
'backend' => $this->backendConfig['backend'], // _Should_ be "db" or "msldap"
'resource' => $this->resourceConfig['name'],
'user_backend' => $this->backendConfig['name'] // Gets ignored if 'backend' is "db"
));
} else {
$groupConfig = new ConfigObject($this->groupConfig);
}
$backend = UserGroupBackend::create(null, $groupConfig);
if (! $backend instanceof Selectable) {
throw new NotImplementedError('Unsupported, until #9772 has been resolved');
}
return $backend;
}
}

View File

@ -71,6 +71,12 @@ class AuthBackendPage extends Form
. 'to do now is defining a name for your first authentication backend.'
));
} elseif ($this->config['type'] === 'ldap') {
$type = null;
if (! isset($formData['type']) && isset($formData['backend'])) {
$type = $formData['backend'];
$formData['type'] = $type;
}
$backendForm = new LdapBackendForm();
$backendForm->setResources(array($this->config['name']));
$backendForm->create($formData);
@ -93,7 +99,8 @@ class AuthBackendPage extends Form
'multiOptions' => array(
'ldap' => 'LDAP',
'msldap' => 'ActiveDirectory'
)
),
'value' => $type
)
);
} else { // $this->config['type'] === 'external'
@ -164,7 +171,10 @@ class AuthBackendPage extends Form
{
if (isset($formData['backend_validation']) && parent::isValid($formData)) {
$self = clone $this;
$self->getSubForm('backend_form')->getElement('resource')->setIgnore(false);
if (($resourceElement = $self->getSubForm('backend_form')->getElement('resource')) !== null) {
$resourceElement->setIgnore(false);
}
$inspection = UserBackendConfigForm::inspectUserBackend($self);
if ($inspection !== null) {
$join = function ($e) use (& $join) {
@ -194,6 +204,9 @@ class AuthBackendPage extends Form
}
$this->info($this->translate('The configuration has been successfully validated.'));
} elseif (! isset($formData['backend_validation'])) {
// This is usually done by isValid(Partial), but as we're not calling any of these...
$this->populate($formData);
}
return true;

View File

@ -31,11 +31,14 @@ class AuthenticationPage extends Form
public function createElements(array $formData)
{
if (isset($formData['type']) && $formData['type'] === 'external' && !isset($_SERVER['REMOTE_USER'])) {
$this->addDescription($this->translate(
'You\'re currently not authenticated using any of the web server\'s authentication '
. 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to '
. 'log into Icinga Web 2.'
));
$this->info(
$this->translate(
'You\'re currently not authenticated using any of the web server\'s authentication '
. 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to '
. 'log into Icinga Web 2.'
),
false
);
}
$backendTypes = array();

View File

@ -109,6 +109,9 @@ class DbResourcePage extends Form
}
$this->info($this->translate('The configuration has been successfully validated.'));
} elseif (! isset($formData['backend_validation'])) {
// This is usually done by isValid(Partial), but as we're not calling any of these...
$this->populate($formData);
}
return true;

View File

@ -124,6 +124,9 @@ class LdapResourcePage extends Form
}
$this->info($this->translate('The configuration has been successfully validated.'));
} elseif (! isset($formData['backend_validation'])) {
// This is usually done by isValid(Partial), but as we're not calling any of these...
$this->populate($formData);
}
return true;

View File

@ -0,0 +1,132 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Setup\Forms;
use Icinga\Application\Config;
use Icinga\Authentication\User\UserBackend;
use Icinga\Data\ResourceFactory;
use Icinga\Forms\Config\UserGroup\LdapUserGroupBackendForm;
use Icinga\Web\Form;
/**
* Wizard page to define user group backend specific details
*/
class UserGroupBackendPage extends Form
{
/**
* The resource configuration to use
*
* @var array
*/
protected $resourceConfig;
/**
* The user backend configuration to use
*
* @var array
*/
protected $backendConfig;
/**
* Initialize this page
*/
public function init()
{
$this->setName('setup_usergroup_backend');
$this->setTitle($this->translate('User Group Backend', 'setup.page.title'));
$this->addDescription($this->translate(
'To allow Icinga Web 2 to associate users and groups, you\'ll need to provide some further information'
. ' about the LDAP Connection that is already going to be used to locate account details.'
));
}
/**
* Set the resource configuration to use
*
* @param array $config
*
* @return $this
*/
public function setResourceConfig(array $config)
{
$this->resourceConfig = $config;
return $this;
}
/**
* Set the user backend configuration to use
*
* @param array $config
*
* @return $this
*/
public function setBackendConfig(array $config)
{
$this->backendConfig = $config;
return $this;
}
/**
* Return the resource configuration as Config object
*
* @return Config
*/
protected function createResourceConfiguration()
{
$config = new Config();
$config->setSection($this->resourceConfig['name'], $this->resourceConfig);
return $config;
}
/**
* Return the user backend configuration as Config object
*
* @return Config
*/
protected function createBackendConfiguration()
{
$config = new Config();
$backendConfig = $this->backendConfig;
$backendConfig['resource'] = $this->resourceConfig['name'];
$config->setSection($this->backendConfig['name'], $backendConfig);
return $config;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
// LdapUserGroupBackendForm requires these factories to provide valid configurations
ResourceFactory::setConfig($this->createResourceConfiguration());
UserBackend::setConfig($this->createBackendConfiguration());
$formData['type'] = 'ldap';
$formData['user_backend'] = $this->backendConfig['name']; // We're forcing the linkage anyway..
$backendForm = new LdapUserGroupBackendForm();
$backendForm->create($formData);
$userBackendOptions = $backendForm->getElement('user_backend')->getMultiOptions();
unset($userBackendOptions['none']);
$backendForm->getElement('name')->setValue('icingaweb2');
$backendForm->getElement('user_backend')->setMultiOptions($userBackendOptions);
$this->addSubForm($backendForm, 'backend_form');
}
/**
* Retrieve all form element values
*
* @param bool $suppressArrayNotation Ignored
*
* @return array
*/
public function getValues($suppressArrayNotation = false)
{
$values = parent::getValues();
$values = array_merge($values, $values['backend_form']);
unset($values['backend_form']);
return $values;
}
}

View File

@ -80,7 +80,10 @@ if ($notifications->hasMessages()) {
$pos < $maxProgress ? ' visited' : ($currentPos > 2 ? ' active' : '')
); ?>
<?php if ($page === $firstPage): ?>
<div class="line left<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-right: 0"></div>
<div class="line left<?= $stateClass; ?>" style="float: left; width: <?= sprintf(
'%.2F',
100 - (count($configPagesLeft) - 1) * $lineWidth
); ?>%; margin-right: 0"></div>
<?php elseif ($page === $lastPage): ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-right: -0.1em;"></div>
<?php else: ?>
@ -109,7 +112,10 @@ if ($notifications->hasMessages()) {
<?php if ($page === $firstPage): ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-left: -0.1em;"></div>
<?php elseif ($page === $lastPage): ?>
<div class="line right<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-left: 0;"></div>
<div class="line right<?= $stateClass; ?>" style="float: left; width: <?= sprintf(
'%.2F',
100 - (count($configPagesRight) - 1) * $lineWidth
); ?>%; margin-left: 0;"></div>
<?php else: ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%;"></div>
<?php endif ?>

View File

@ -27,7 +27,7 @@
); ?>
<?php endif ?>
</div>
<textarea class="report" readonly><?= join("\n\n", array_map(function($a) {
<pre class="log-output"><?= join("\n\n", array_map(function($a) {
return join("\n", $a);
}, $report)); ?></textarea>
}, $report)); ?></pre>
</div>

View File

@ -32,7 +32,7 @@ class AuthenticationStep extends Step
$success &= $this->createAccount();
}
$success &= $this->defineInitialAdmin();
$success &= $this->createRolesIni();
return $success;
}
@ -60,16 +60,26 @@ class AuthenticationStep extends Step
return true;
}
protected function defineInitialAdmin()
protected function createRolesIni()
{
$config = array();
$config['admins'] = array(
'users' => $this->data['adminAccountData']['username'],
'permissions' => '*'
);
if (isset($this->data['adminAccountData']['username'])) {
$config = array(
'users' => $this->data['adminAccountData']['username'],
'permissions' => '*'
);
if ($this->data['backendConfig']['backend'] === 'db') {
$config['groups'] = mt('setup', 'Administrators', 'setup.role.name');
}
} else { // isset($this->data['adminAccountData']['groupname'])
$config = array(
'groups' => $this->data['adminAccountData']['groupname'],
'permissions' => '*'
);
}
try {
Config::fromArray($config)
Config::fromArray(array(mt('setup', 'Administrators', 'setup.role.name') => $config))
->setConfigFile(Config::resolvePath('roles.ini'))
->saveIni();
} catch (Exception $e) {
@ -94,13 +104,13 @@ class AuthenticationStep extends Step
'password' => $this->data['adminAccountData']['password'],
'is_active' => true
));
$this->dbError = false;
}
} catch (Exception $e) {
$this->dbError = $e;
return false;
}
$this->dbError = false;
return true;
}
@ -114,7 +124,9 @@ class AuthenticationStep extends Step
$backendDesc = '<p>' . sprintf(
mt('setup', 'Users will authenticate using %s.', 'setup.summary.auth'),
$authType === 'db' ? mt('setup', 'a database', 'setup.summary.auth.type') : (
$authType === 'ldap' ? 'LDAP' : mt('setup', 'webserver authentication', 'setup.summary.auth.type')
$authType === 'ldap' || $authType === 'msldap' ? 'LDAP' : (
mt('setup', 'webserver authentication', 'setup.summary.auth.type')
)
)
) . '</p>';
@ -125,18 +137,20 @@ class AuthenticationStep extends Step
. '<td><strong>' . t('Backend Name') . '</strong></td>'
. '<td>' . $this->data['backendConfig']['name'] . '</td>'
. '</tr>'
. ($authType === 'ldap' ? (
. ($authType === 'ldap' || $authType === 'msldap' ? (
'<tr>'
. '<td><strong>' . mt('setup', 'User Object Class') . '</strong></td>'
. '<td>' . $this->data['backendConfig']['user_class'] . '</td>'
. '<td>' . ($authType === 'msldap' ? 'user' : $this->data['backendConfig']['user_class']) . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('setup', 'Custom Filter') . '</strong></td>'
. '<td>' . trim($this->data['backendConfig']['filter']) ?: t('None', 'auth.ldap.filter') . '</td>'
. '<td>' . (trim($this->data['backendConfig']['filter']) ?: t('None', 'auth.ldap.filter')) . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('setup', 'User Name Attribute') . '</strong></td>'
. '<td>' . $this->data['backendConfig']['user_name_attribute'] . '</td>'
. '<td>' . ($authType === 'msldap'
? 'sAMAccountName'
: $this->data['backendConfig']['user_name_attribute']) . '</td>'
. '</tr>'
) : ($authType === 'external' ? (
'<tr>'
@ -147,13 +161,20 @@ class AuthenticationStep extends Step
. '</tbody>'
. '</table>';
$adminHtml = '<p>' . (isset($this->data['adminAccountData']['resourceConfig']) ? sprintf(
mt('setup', 'Administrative rights will initially be granted to a new account called "%s".'),
$this->data['adminAccountData']['username']
) : sprintf(
mt('setup', 'Administrative rights will initially be granted to an existing account called "%s".'),
$this->data['adminAccountData']['username']
)) . '</p>';
if (isset($this->data['adminAccountData']['username'])) {
$adminHtml = '<p>' . (isset($this->data['adminAccountData']['resourceConfig']) ? sprintf(
mt('setup', 'Administrative rights will initially be granted to a new account called "%s".'),
$this->data['adminAccountData']['username']
) : sprintf(
mt('setup', 'Administrative rights will initially be granted to an existing account called "%s".'),
$this->data['adminAccountData']['username']
)) . '</p>';
} else { // isset($this->data['adminAccountData']['groupname'])
$adminHtml = '<p>' . sprintf(
mt('setup', 'Administrative rights will initially be granted to members of the user group "%s".'),
$this->data['adminAccountData']['groupname']
) . '</p>';
}
return $pageTitle . '<div class="topic">' . $backendDesc . $backendTitle . $backendHtml . '</div>'
. '<div class="topic">' . $adminTitle . $adminHtml . '</div>';
@ -190,14 +211,23 @@ class AuthenticationStep extends Step
}
if ($this->permIniError === false) {
$report[] = sprintf(
$report[] = isset($this->data['adminAccountData']['username']) ? sprintf(
mt('setup', 'Account "%s" has been successfully defined as initial administrator.'),
$this->data['adminAccountData']['username']
) : sprintf(
mt('setup', 'The members of the user group "%s" were successfully defined as initial administrators.'),
$this->data['adminAccountData']['groupname']
);
} elseif ($this->permIniError !== null) {
$report[] = sprintf(
$report[] = isset($this->data['adminAccountData']['username']) ? sprintf(
mt('setup', 'Unable to define account "%s" as initial administrator. An error occured:'),
$this->data['adminAccountData']['username']
) : sprintf(
mt(
'setup',
'Unable to define the members of the user group "%s" as initial administrators. An error occured:'
),
$this->data['adminAccountData']['groupname']
);
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->permIniError->getMessage());
}

View File

@ -0,0 +1,209 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Setup\Steps;
use Exception;
use Icinga\Application\Config;
use Icinga\Authentication\UserGroup\DbUserGroupBackend;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\IcingaException;
use Icinga\Module\Setup\Step;
class UserGroupStep extends Step
{
protected $data;
protected $groupError;
protected $memberError;
protected $groupIniError;
public function __construct(array $data)
{
$this->data = $data;
}
public function apply()
{
$success = $this->createGroupsIni();
if (isset($this->data['resourceConfig'])) {
$success &= $this->createUserGroup();
if ($success) {
$success &= $this->createMembership();
}
}
return $success;
}
protected function createGroupsIni()
{
$config = array();
if (isset($this->data['groupConfig'])) {
$backendConfig = $this->data['groupConfig'];
$backendName = $backendConfig['name'];
unset($backendConfig['name']);
$config[$backendName] = $backendConfig;
} else {
$backendConfig = array(
'backend' => $this->data['backendConfig']['backend'], // "db" or "msldap"
'resource' => $this->data['resourceName']
);
if ($backendConfig['backend'] === 'msldap') {
$backendConfig['user_backend'] = $this->data['backendConfig']['name'];
}
$config[$this->data['backendConfig']['name']] = $backendConfig;
}
try {
Config::fromArray($config)
->setConfigFile(Config::resolvePath('groups.ini'))
->saveIni();
} catch (Exception $e) {
$this->groupIniError = $e;
return false;
}
$this->groupIniError = false;
return true;
}
protected function createUserGroup()
{
try {
$backend = new DbUserGroupBackend(
ResourceFactory::createResource(new ConfigObject($this->data['resourceConfig']))
);
$groupName = mt('setup', 'Administrators', 'setup.role.name');
if ($backend->select()->where('group_name', $groupName)->count() === 0) {
$backend->insert('group', array(
'group_name' => $groupName
));
$this->groupError = false;
}
} catch (Exception $e) {
$this->groupError = $e;
return false;
}
return true;
}
protected function createMembership()
{
try {
$backend = new DbUserGroupBackend(
ResourceFactory::createResource(new ConfigObject($this->data['resourceConfig']))
);
$groupName = mt('setup', 'Administrators', 'setup.role.name');
$userName = $this->data['username'];
if ($backend
->select()
->from('group_membership')
->where('group_name', $groupName)
->where('user_name', $userName)
->count() === 0
) {
$backend->insert('group_membership', array(
'group_name' => $groupName,
'user_name' => $userName
));
$this->memberError = false;
}
} catch (Exception $e) {
$this->memberError = $e;
return false;
}
return true;
}
public function getSummary()
{
if (! isset($this->data['groupConfig'])) {
return; // It's not necessary to show the user something he didn't configure..
}
$pageTitle = '<h2>' . mt('setup', 'User Groups', 'setup.page.title') . '</h2>';
$backendTitle = '<h3>' . mt('setup', 'User Group Backend', 'setup.page.title') . '</h3>';
$backendHtml = ''
. '<table>'
. '<tbody>'
. '<tr>'
. '<td><strong>' . t('Backend Name') . '</strong></td>'
. '<td>' . $this->data['groupConfig']['name'] . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('setup', 'Group Object Class') . '</strong></td>'
. '<td>' . $this->data['groupConfig']['group_class'] . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('setup', 'Custom Filter') . '</strong></td>'
. '<td>' . (trim($this->data['groupConfig']['group_filter']) ?: t('None', 'auth.ldap.filter')) . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('setup', 'Group Name Attribute') . '</strong></td>'
. '<td>' . $this->data['groupConfig']['group_name_attribute'] . '</td>'
. '</tr>'
. '</tbody>'
. '</table>';
return $pageTitle . '<div class="topic">' . $backendTitle . $backendHtml . '</div>';
}
public function getReport()
{
$report = array();
if ($this->groupIniError === false) {
$report[] = sprintf(
mt('setup', 'User Group Backend configuration has been successfully written to: %s'),
Config::resolvePath('groups.ini')
);
} elseif ($this->groupIniError !== null) {
$report[] = sprintf(
mt('setup', 'User Group Backend configuration could not be written to: %s. An error occured:'),
Config::resolvePath('groups.ini')
);
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->groupIniError));
}
if ($this->groupError === false) {
$report[] = sprintf(
mt('setup', 'User Group "%s" has been successfully created.'),
mt('setup', 'Administrators', 'setup.role.name')
);
} elseif ($this->groupError !== null) {
$report[] = sprintf(
mt('setup', 'Unable to create user group "%s". An error occured:'),
mt('setup', 'Administrators', 'setup.role.name')
);
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->groupError));
}
if ($this->memberError === false) {
$report[] = sprintf(
mt('setup', 'Account "%s" has been successfully added as member to user group "%s".'),
$this->data['username'],
mt('setup', 'Administrators', 'setup.role.name')
);
} elseif ($this->memberError !== null) {
$report[] = sprintf(
mt('setup', 'Unable to add account "%s" as member to user group "%s". An error occured:'),
$this->data['username'],
mt('setup', 'Administrators', 'setup.role.name')
);
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->memberError));
}
return $report;
}
}

View File

@ -22,10 +22,12 @@ use Icinga\Module\Setup\Forms\RequirementsPage;
use Icinga\Module\Setup\Forms\GeneralConfigPage;
use Icinga\Module\Setup\Forms\AuthenticationPage;
use Icinga\Module\Setup\Forms\DatabaseCreationPage;
use Icinga\Module\Setup\Forms\UserGroupBackendPage;
use Icinga\Module\Setup\Steps\DatabaseStep;
use Icinga\Module\Setup\Steps\GeneralConfigStep;
use Icinga\Module\Setup\Steps\ResourceStep;
use Icinga\Module\Setup\Steps\AuthenticationStep;
use Icinga\Module\Setup\Steps\UserGroupStep;
use Icinga\Module\Setup\Utils\EnableModuleStep;
use Icinga\Module\Setup\Utils\DbTool;
use Icinga\Module\Setup\Requirement\OSRequirement;
@ -104,6 +106,7 @@ class WebWizard extends Wizard implements SetupWizard
//$this->addPage(new LdapDiscoveryConfirmPage());
$this->addPage(new LdapResourcePage());
$this->addPage(new AuthBackendPage());
$this->addPage(new UserGroupBackendPage());
$this->addPage(new AdminAccountPage());
$this->addPage(new GeneralConfigPage());
$this->addPage(new DbResourcePage(array('name' => 'setup_config_db_resource')));
@ -135,15 +138,37 @@ class WebWizard extends Wizard implements SetupWizard
} elseif ($authData['type'] === 'ldap') {
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
$suggestions = $this->getPageData('setup_ldap_discovery');
if (isset($suggestions['backend'])) {
$page->populate($suggestions['backend']);
if (! $this->hasPageData('setup_authentication_backend')) {
$suggestions = $this->getPageData('setup_ldap_discovery');
if (isset($suggestions['backend'])) {
$page->populate($suggestions['backend']);
}
}
if ($this->getDirection() === static::FORWARD) {
$backendConfig = $this->getPageData('setup_authentication_backend');
if ($backendConfig !== null && $request->getPost('name') !== $backendConfig['name']) {
$pageData = & $this->getPageData();
unset($pageData['setup_usergroup_backend']);
}
}
}
if ($this->getDirection() === static::FORWARD) {
$backendConfig = $this->getPageData('setup_authentication_backend');
if ($backendConfig !== null && $request->getPost('backend') !== $backendConfig['backend']) {
$pageData = & $this->getPageData();
unset($pageData['setup_usergroup_backend']);
}
}
/*} elseif ($page->getName() === 'setup_ldap_discovery_confirm') {
$page->setResourceConfig($this->getPageData('setup_ldap_discovery'));*/
} elseif ($page->getName() === 'setup_usergroup_backend') {
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
$page->setBackendConfig($this->getPageData('setup_authentication_backend'));
} elseif ($page->getName() === 'setup_admin_account') {
$page->setBackendConfig($this->getPageData('setup_authentication_backend'));
$page->setGroupConfig($this->getPageData('setup_usergroup_backend'));
$authData = $this->getPageData('setup_authentication_type');
if ($authData['type'] === 'db') {
$page->setResourceConfig($this->getPageData('setup_auth_db_resource'));
@ -173,6 +198,14 @@ class WebWizard extends Wizard implements SetupWizard
if (isset($suggestion['resource'])) {
$page->populate($suggestion['resource']);
}
if ($this->getDirection() === static::FORWARD) {
$resourceConfig = $this->getPageData('setup_ldap_resource');
if ($resourceConfig !== null && $request->getPost('name') !== $resourceConfig['name']) {
$pageData = & $this->getPageData();
unset($pageData['setup_usergroup_backend']);
}
}
} elseif ($page->getName() === 'setup_general_config') {
$authData = $this->getPageData('setup_authentication_type');
if ($authData['type'] === 'db') {
@ -233,6 +266,9 @@ class WebWizard extends Wizard implements SetupWizard
} elseif ($newPage->getName() === 'setup_ldap_resource') {
$authData = $this->getPageData('setup_authentication_type');
$skip = $authData['type'] !== 'ldap';
} elseif ($newPage->getName() === 'setup_usergroup_backend') {
$backendConfig = $this->getPageData('setup_authentication_backend');
$skip = $backendConfig['backend'] !== 'ldap';
} elseif ($newPage->getName() === 'setup_config_db_resource') {
$authData = $this->getPageData('setup_authentication_type');
$configData = $this->getPageData('setup_general_config');
@ -401,14 +437,18 @@ class WebWizard extends Wizard implements SetupWizard
);
$adminAccountType = $pageData['setup_admin_account']['user_type'];
$adminAccountData = array('username' => $pageData['setup_admin_account'][$adminAccountType]);
if ($adminAccountType === 'new_user' && !$pageData['setup_auth_db_resource']['skip_validation']
&& (! isset($pageData['setup_auth_db_creation'])
|| !$pageData['setup_auth_db_creation']['skip_validation']
)
) {
$adminAccountData['resourceConfig'] = $pageData['setup_auth_db_resource'];
$adminAccountData['password'] = $pageData['setup_admin_account']['new_user_password'];
if ($adminAccountType === 'user_group') {
$adminAccountData = array('groupname' => $pageData['setup_admin_account'][$adminAccountType]);
} else {
$adminAccountData = array('username' => $pageData['setup_admin_account'][$adminAccountType]);
if ($adminAccountType === 'new_user' && !$pageData['setup_auth_db_resource']['skip_validation']
&& (! isset($pageData['setup_auth_db_creation'])
|| !$pageData['setup_auth_db_creation']['skip_validation']
)
) {
$adminAccountData['resourceConfig'] = $pageData['setup_auth_db_resource'];
$adminAccountData['password'] = $pageData['setup_admin_account']['new_user_password'];
}
}
$authType = $pageData['setup_authentication_type']['type'];
$setup->addStep(
@ -421,6 +461,26 @@ class WebWizard extends Wizard implements SetupWizard
))
);
if ($authType !== 'external') {
$setup->addStep(
new UserGroupStep(array(
'backendConfig' => $pageData['setup_authentication_backend'],
'groupConfig' => isset($pageData['setup_usergroup_backend'])
? $pageData['setup_usergroup_backend']
: null,
'resourceName' => $authType === 'db'
? $pageData['setup_auth_db_resource']['name']
: $pageData['setup_ldap_resource']['name'],
'resourceConfig' => $authType === 'db'
? $pageData['setup_auth_db_resource']
: null,
'username' => $authType === 'db'
? $pageData['setup_admin_account'][$adminAccountType]
: null
))
);
}
if (
isset($pageData['setup_auth_db_resource'])
|| isset($pageData['setup_config_db_resource'])

View File

@ -107,6 +107,53 @@ form.inline {
display: inline;
}
button, .button-like {
font-size: 0.9em;
font-weight: bold;
outline: 0;
color: #fff;
padding: 0.2em;
border: 1px solid;
border-color: @colorPetrol;
background: @colorPetrol;
&[disabled] {
background-color: #666;
border-color: black;
}
&:hover, &:focus, &:active {
background-color: #333;
border-color: #333;
cursor: pointer;
&[disabled] {
background-color: #666;
}
}
&.icon-only {
font-size: 1.5em;
padding: 0;
border: none;
color: inherit;
background-color: transparent;
&:hover, &:focus, &:active {
color: #666;
}
}
}
.button-like {
display: inline-block;
}
a.button-like {
cursor: default;
text-decoration: none;
}
form.link-like input[type="submit"], form.link-like button[type="submit"], input.link-like, button.link-like {
color: @colorLinkDefault;
font-weight: normal;
@ -119,9 +166,16 @@ form.link-like input[type="submit"], form.link-like button[type="submit"], input
form.link-like input[type="submit"]:hover,
form.link-like input[type="submit"]:focus,
form.link-like input[type="submit"]:active,
form.link-like button[type="submit"]:hover,
form.link-like button[type="submit"]:focus,
input.link-like:hover, button.link-like:focus {
form.link-like button[type="submit"]:active,
input.link-like:hover,
input.link-like:focus,
input.link-like:active,
button.link-like:hover,
button.link-like:focus,
button.link-like:active {
text-decoration: underline;
background: none;
color: @colorLinkDefault;
@ -302,47 +356,4 @@ form ul.hints {
height: 0;
clear: both;
}
}
button, .button-like {
font-size: 0.9em;
font-weight: bold;
outline: 0;
color: #fff;
padding: 0.2em;
border: 1px solid;
border-color: @colorPetrol;
background: @colorPetrol;
&[disabled] {
background-color: #666;
border-color: black;
}
&:hover, &:focus, &:active {
background-color: #333;
border-color: #333;
cursor: pointer;
&[disabled] {
background-color: #666;
}
}
&.icon-only {
font-size: 1.5em;
padding: 0;
border: none;
color: inherit;
background-color: transparent;
&:hover, &:focus, &:active {
color: #666;
}
}
}
a.button-like {
cursor: default;
text-decoration: none;
}

View File

@ -117,16 +117,8 @@
left: -1337px;
}
button, .button-like {
&.finish, &.login {
min-width: 25em;
color: #fffafa;
background: @colorPetrol;
&:hover, &:focus, &:active {
background: #666;
}
}
button.finish, a.button-like.login {
min-width: 25em;
}
}
@ -294,13 +286,14 @@ form#setup_requirements {
}
}
textarea.report {
pre.log-output {
width: 66%;
height: 25em;
max-height: none;
}
div.buttons {
margin-top: 0.5em;
margin-top: 0;
text-align: center;
a {