diff --git a/application/controllers/GroupController.php b/application/controllers/GroupController.php index 4dd05fdef..21fba5ebb 100644 --- a/application/controllers/GroupController.php +++ b/application/controllers/GroupController.php @@ -44,7 +44,7 @@ class GroupController extends AuthBackendController 'backend', array( 'autosubmit' => true, - 'label' => $this->translate('Usergroup Backend'), + 'label' => $this->translate('User Group Backend'), 'multiOptions' => array_combine($backendNames, $backendNames), 'value' => $this->params->get('backend') ) @@ -66,7 +66,7 @@ class GroupController extends AuthBackendController $this->setupLimitControl(); $this->setupSortControl( array( - 'group_name' => $this->translate('Group'), + 'group_name' => $this->translate('User Group'), 'created_at' => $this->translate('Created at'), 'last_modified' => $this->translate('Last modified') ), @@ -134,7 +134,7 @@ class GroupController extends AuthBackendController $removeForm->addElement('button', 'btn_submit', array( 'escape' => false, 'type' => 'submit', - 'class' => 'link-like spinner', + 'class' => 'link-button spinner', 'value' => 'btn_submit', 'decorators' => array('ViewHelper'), 'label' => $this->view->icon('trash'), diff --git a/application/controllers/UserController.php b/application/controllers/UserController.php index d080c1dae..69d4a71a5 100644 --- a/application/controllers/UserController.php +++ b/application/controllers/UserController.php @@ -44,7 +44,7 @@ class UserController extends AuthBackendController 'backend', array( 'autosubmit' => true, - 'label' => $this->translate('Authentication Backend'), + 'label' => $this->translate('User Backend'), 'multiOptions' => array_combine($backendNames, $backendNames), 'value' => $this->params->get('backend') ) @@ -140,7 +140,7 @@ class UserController extends AuthBackendController $removeForm->addElement('button', 'btn_submit', array( 'escape' => false, 'type' => 'submit', - 'class' => 'link-like spinner', + 'class' => 'link-button spinner', 'value' => 'btn_submit', 'decorators' => array('ViewHelper'), 'label' => $this->view->icon('trash'), diff --git a/application/views/scripts/group/list.phtml b/application/views/scripts/group/list.phtml index 01bf3f7dd..b2bc9b920 100644 --- a/application/views/scripts/group/list.phtml +++ b/application/views/scripts/group/list.phtml @@ -4,30 +4,24 @@ use Icinga\Data\Extensible; use Icinga\Data\Reducible; if (! $this->compact): ?> -
- tabs; ?> -
-
- limiter ?> +
+ +
+ limiter ?> + paginator ?> + sortBox ?>
-
- paginator ?> +
+ backendSelection; ?> + filterEditor; ?>
-
- sortBox ?> -
-
-
- backendSelection; ?> - filterEditor; ?> -
-
+
translate('No backend found which is able to list groups') . '
'; + echo $this->translate('No backend found which is able to list user groups') . '
'; return; } else { $extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible; @@ -37,64 +31,68 @@ if (! isset($backend)) { qlink( - $this->translate('Add a New User Group') , + $this->translate('Add a New User Group'), 'group/add', array('backend' => $backend->getName()), array( 'class' => 'button-link', 'data-base-target' => '_next', - 'icon' => 'plus', - 'title' => $this->translate('Create a new user group') + 'icon' => 'plus' ) ) ?> - - - - - - - - - - - - - - - - - - - - - -hasResult()): ?> - -
translate('Group'); ?>translate('Remove'); ?>
qlink($group->group_name, 'group/show', array( - 'backend' => $backend->getName(), - 'group' => $group->group_name - ), array( - 'title' => sprintf($this->translate('Show detailed information for group %s'), $group->group_name) - )); ?> - qlink( - null, - 'group/remove', - array( - 'backend' => $backend->getName(), - 'group' => $group->group_name - ), - array( - 'title' => sprintf($this->translate('Remove group %s'), $group->group_name), - 'icon' => 'trash' - ) - ); ?> -
- -

translate('No groups found matching the filter'); ?>

- +hasResult()): ?> +

translate('No user groups found matching the filter'); ?>

+ + + + + + + + + + + + + + + + + + + + + +
translate('User Group'); ?>translate('Remove'); ?>
+ qlink( + $group->group_name, + 'group/show', + array( + 'backend' => $backend->getName(), + 'group' => $group->group_name + ), + array( + 'title' => sprintf( + $this->translate('Show detailed information for user group %s'), + $group->group_name + ) + ) + ); ?> + + qlink( + null, + 'group/remove', + array( + 'backend' => $backend->getName(), + 'group' => $group->group_name + ), + array( + 'title' => sprintf($this->translate('Remove user group %s'), $group->group_name), + 'icon' => 'trash' + ) + ); ?> +
+
\ No newline at end of file diff --git a/application/views/scripts/group/show.phtml b/application/views/scripts/group/show.phtml index f76ecdf3f..fb850a2a5 100644 --- a/application/views/scripts/group/show.phtml +++ b/application/views/scripts/group/show.phtml @@ -2,7 +2,6 @@ use Icinga\Data\Extensible; use Icinga\Data\Updatable; -use Icinga\Data\Selectable; $extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible; @@ -24,92 +23,86 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan } ?> -
- compact): ?> - - -

escape($group->group_name) ?>

- - - - - - - - - -
translate('Created at'); ?>created_at === null ? '-' : $this->formatDateTime($group->created_at); ?>
translate('Last modified'); ?>last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?>
-

translate('Members'); ?>

+
compact): ?> - sortBox; ?> + - limiter; ?> - paginator; ?> +

escape($group->group_name) ?>

+ + + + + + + + + +
translate('Created at'); ?>created_at === null ? '-' : $this->formatDateTime($group->created_at); ?>
translate('Last modified'); ?>last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?>
compact): ?> - filterEditor; ?> +

translate('Members'); ?>

+
+ limiter; ?> + paginator; ?> + sortBox; ?> +
+ filterEditor; ?>
-
+
qlink( - $this->translate('Add User to Group') , + $this->translate('Add New Member'), 'group/addmember', - null, array( - 'class' => 'button-link', - 'data-base-target' => '_next', - 'icon' => 'plus', - 'title' => $this->translate('Add user to group') + 'backend' => $backend->getName(), + 'group' => $group->group_name + ), + array( + 'icon' => 'plus', + 'class' => 'button-link' ) ) ?> - - - - - - - - - - - - - - - - - - - - - -hasResult()): ?> - -
translate('Username'); ?>translate('Remove'); ?>
- hasPermission('config/authentication/users/show') - && method_exists($backend, 'getUserBackend') - && ($userBackend = $backend->getUserBackend()) !== null - && $userBackend instanceof Selectable - ): ?> - qlink($member->user_name, 'user/show', array( - 'backend' => $userBackend->getName(), - 'user' => $member->user_name - ), array( - 'title' => sprintf($this->translate('Show detailed information about %s'), $member->user_name) - )); ?> - - escape($member->user_name); ?> - - - getElement('user_name')->setValue($member->user_name); echo $removeForm; ?> -
- +hasResult()): ?>

translate('No group member found matching the filter'); ?>

-
+ + + + + + + + + + + + + + + + + + + + + +
translate('Username'); ?>translate('Remove'); ?>
+ hasPermission('config/authentication/users/show') + && ($userBackend = $backend->getUserBackendName($member->user_name)) !== null + ): ?> + qlink($member->user_name, 'user/show', array( + 'backend' => $userBackend, + 'user' => $member->user_name + ), array( + 'title' => sprintf($this->translate('Show detailed information about %s'), $member->user_name) + )); ?> + + escape($member->user_name); ?> + + + getElement('user_name')->setValue($member->user_name); echo $removeForm; ?> +
+
\ No newline at end of file diff --git a/application/views/scripts/user/list.phtml b/application/views/scripts/user/list.phtml index c89ea82e8..26114d657 100644 --- a/application/views/scripts/user/list.phtml +++ b/application/views/scripts/user/list.phtml @@ -1,20 +1,15 @@ compact): ?> -
- tabs ?> -
-
- limiter ?> -
-
- paginator ?> -
-
- sortBox ?> -
+
+ +
+ limiter ?> + paginator ?> + sortBox ?>
backendSelection ?> @@ -22,20 +17,18 @@ if (! $this->compact): ?>
-
- -

translate('No backend found which is able to list users.') ?>

-
- -hasResult()): ?> -

translate('No users found matching the filter.') ?>

-
- hasPermission('config/authentication/users/add') && $backend instanceof Extensible; -$reducible = $this->hasPermission('config/authentication/users/remove') && $backend instanceof Reducible; + +if (! isset($backend)) { + echo $this->translate('No backend found which is able to list users') . '
'; + return; +} else { + $extensible = $this->hasPermission('config/authentication/users/add') && $backend instanceof Extensible; + $reducible = $this->hasPermission('config/authentication/users/remove') && $backend instanceof Reducible; +} ?> + qlink( $this->translate('Add a New User') , @@ -44,12 +37,17 @@ $reducible = $this->hasPermission('config/authentication/users/remove') && $back array( 'class' => 'button-link', 'data-base-target' => '_next', - 'icon' => 'plus', - 'title' => $this->translate('Create a new user') + 'icon' => 'plus' ) ) ?> - + +hasResult()): ?> +

translate('No users found matching the filter') ?>

+ + + +
@@ -91,4 +89,4 @@ $reducible = $this->hasPermission('config/authentication/users/remove') && $back
translate('Username') ?>
-
+
\ No newline at end of file diff --git a/application/views/scripts/user/show.phtml b/application/views/scripts/user/show.phtml index ee3aa6809..2fbdab551 100644 --- a/application/views/scripts/user/show.phtml +++ b/application/views/scripts/user/show.phtml @@ -22,93 +22,90 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc } ?> -
+
compact): ?> - + -

escape($user->user_name) ?>

- - - - - - - - - - - - - -
translate('State'); ?>is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?>
translate('Created at'); ?>created_at === null ? '-' : $this->formatDateTime($user->created_at); ?>
translate('Last modified'); ?>last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?>
-

translate('Group Memberships'); ?>

+

escape($user->user_name) ?>

+ + + + + + + + + + + + + +
translate('State'); ?>is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?>
translate('Created at'); ?>created_at === null ? '-' : $this->formatDateTime($user->created_at); ?>
translate('Last modified'); ?>last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?>
compact): ?> - sortBox; ?> - - limiter; ?> - paginator; ?> -compact): ?> - filterEditor; ?> +

translate('Group Memberships'); ?>

+
+ limiter; ?> + paginator; ?> + sortBox; ?> +
+ filterEditor; ?>
-
- +
+ qlink( - $this->translate('Add User to Group') , + $this->translate('Create New Membership'), 'user/createmembership', - array('backend' => $backend->getName()), array( - 'class' => 'button-link', - 'data-base-target' => '_next', - 'icon' => 'plus', - 'title' => $this->translate('Add user to user group') + 'backend' => $backend->getName(), + 'user' => $user->user_name + ), + array( + 'icon' => 'plus', + 'class' => 'button-link' ) ) ?> - - - - - - - - - - - - - - - - - - -hasResult()): ?> - -
translate('Group'); ?>translate('Cancel', 'group.membership'); ?>
- hasPermission('config/authentication/groups/show') && $membership->backend instanceof Selectable): ?> - qlink($membership->group_name, 'group/show', array( - 'backend' => $membership->backend->getName(), - 'group' => $membership->group_name - ), array( - 'title' => sprintf($this->translate('Show detailed information for group %s'), $membership->group_name) - )); ?> - - escape($membership->group_name); ?> - - - backend instanceof Reducible): ?> - setAction($this->url('group/removemember', array( - 'backend' => $membership->backend->getName(), - 'group' => $membership->group_name - ))); ?> - - - - -
- -

translate('No memberships found matching the filter'); ?>

+ +hasResult()): ?> +

translate('No memberships found matching the filter'); ?>

+ + + + + + + + + + + + + + + + + +
translate('Group'); ?>translate('Cancel', 'group.membership'); ?>
+ hasPermission('config/authentication/groups/show') && $membership->backend instanceof Selectable): ?> + qlink($membership->group_name, 'group/show', array( + 'backend' => $membership->backend->getName(), + 'group' => $membership->group_name + ), array( + 'title' => sprintf($this->translate('Show detailed information for group %s'), $membership->group_name) + )); ?> + + escape($membership->group_name); ?> + + + backend instanceof Reducible): ?> + setAction($this->url('group/removemember', array( + 'backend' => $membership->backend->getName(), + 'group' => $membership->group_name + ))); ?> + + - + +
+
\ No newline at end of file diff --git a/library/Icinga/Authentication/User/DbUserBackend.php b/library/Icinga/Authentication/User/DbUserBackend.php index c0c949fd7..e004a56c0 100644 --- a/library/Icinga/Authentication/User/DbUserBackend.php +++ b/library/Icinga/Authentication/User/DbUserBackend.php @@ -130,6 +130,7 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec */ public function insert($table, array $bind) { + $this->requireTable($table); $bind['created_at'] = date('Y-m-d H:i:s'); $this->ds->insert( $this->prependTablePrefix($table), @@ -150,6 +151,7 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec */ public function update($table, array $bind, Filter $filter = null) { + $this->requireTable($table); $bind['last_modified'] = date('Y-m-d H:i:s'); if ($filter) { $filter = $this->requireFilter($table, $filter); diff --git a/library/Icinga/Authentication/User/LdapUserBackend.php b/library/Icinga/Authentication/User/LdapUserBackend.php index f3a852115..b5bd51f45 100644 --- a/library/Icinga/Authentication/User/LdapUserBackend.php +++ b/library/Icinga/Authentication/User/LdapUserBackend.php @@ -341,6 +341,27 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In return $table; } + /** + * Validate that the given column is a valid query target and return it or the actual name if it's an alias + * + * @param string $table The table where to look for the column or alias + * @param string $name The name or alias of the column to validate + * @param RepositoryQuery $query An optional query to pass as context + * + * @return string The given column's name + * + * @throws QueryException In case the given column is not a valid query column + */ + public function requireQueryColumn($table, $name, RepositoryQuery $query = null) + { + $column = parent::requireQueryColumn($table, $name, $query); + if ($name === 'user_name' && $query !== null) { + $query->getQuery()->setUnfoldAttribute('user_name'); + } + + return $column; + } + /** * Authenticate the given user * diff --git a/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php index a114cc8d1..6f5c2e9dd 100644 --- a/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php @@ -217,6 +217,18 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa return $memberships; } + /** + * Return the name of the backend that is providing the given user + * + * @param string $username Currently unused + * + * @return null|string The name of the backend or null in case this information is not available + */ + public function getUserBackendName($username) + { + return null; // TODO(10373): Store this to the database when inserting and fetch it here + } + /** * Join group into group_membership * @@ -238,7 +250,7 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa */ protected function joinGroupMembership(RepositoryQuery $query) { - $query->getQuery()->join( + $query->getQuery()->joinLeft( $this->requireTable('group_membership'), 'g.id = gm.group_id', array() diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index 2aa615a84..9a4ab53c0 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -5,6 +5,7 @@ namespace Icinga\Authentication\UserGroup; use Icinga\Authentication\User\UserBackend; use Icinga\Authentication\User\LdapUserBackend; +use Icinga\Application\Logger; use Icinga\Data\ConfigObject; use Icinga\Exception\ConfigurationError; use Icinga\Exception\ProgrammingError; @@ -454,12 +455,48 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt ) ); if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) { + $rules['group_membership']['user_name'] = 'user_name'; + $rules['group_membership']['user'] = 'user_name'; $rules['group']['user_name'] = 'user_name'; + $rules['group']['user'] = 'user_name'; } return $rules; } + /** + * Return the distinguished name for the given uid or gid + * + * @param string $name + * + * @return string + */ + protected function persistUserName($name) + { + $userDn = $this->ds + ->select() + ->from($this->userClass, array()) + ->where($this->userNameAttribute, $name) + ->setUsePagedResults(false) + ->fetchDn(); + if ($userDn) { + return $userDn; + } + + $groupDn = $this->ds + ->select() + ->from($this->groupClass, array()) + ->where($this->groupNameAttribute, $name) + ->setUsePagedResults(false) + ->fetchDn(); + if ($groupDn) { + return $groupDn; + } + + Logger::debug('Unable to persist uid or gid "%s" in repository "%s". No DN found.', $name, $this->getName()); + return $name; + } + /** * Return the uid for the given distinguished name * @@ -564,6 +601,21 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt return $groups; } + /** + * Return the name of the backend that is providing the given user + * + * @param string $username Unused + * + * @return null|string The name of the backend or null in case this information is not available + */ + public function getUserBackendName($username) + { + $userBackend = $this->getUserBackend(); + if ($userBackend !== null) { + return $userBackend->getName(); + } + } + /** * Apply the given configuration on this backend * diff --git a/library/Icinga/Authentication/UserGroup/UserGroupBackendInterface.php b/library/Icinga/Authentication/UserGroup/UserGroupBackendInterface.php index a567d1f0a..31dd2c50d 100644 --- a/library/Icinga/Authentication/UserGroup/UserGroupBackendInterface.php +++ b/library/Icinga/Authentication/UserGroup/UserGroupBackendInterface.php @@ -34,4 +34,13 @@ interface UserGroupBackendInterface * @return array */ public function getMemberships(User $user); + + /** + * Return the name of the backend that is providing the given user + * + * @param string $username + * + * @return null|string The name of the backend or null in case this information is not available + */ + public function getUserBackendName($username); } diff --git a/library/Icinga/Repository/DbRepository.php b/library/Icinga/Repository/DbRepository.php index 89701cb9d..27144d097 100644 --- a/library/Icinga/Repository/DbRepository.php +++ b/library/Icinga/Repository/DbRepository.php @@ -297,6 +297,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, */ public function insert($table, array $bind) { + $this->requireTable($table); $this->ds->insert($this->prependTablePrefix($table), $this->requireStatementColumns($table, $bind)); } @@ -309,6 +310,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, */ public function update($table, array $bind, Filter $filter = null) { + $this->requireTable($table); + if ($filter) { $filter = $this->requireFilter($table, $filter); } @@ -324,6 +327,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, */ public function delete($table, Filter $filter = null) { + $this->requireTable($table); + if ($filter) { $filter = $this->requireFilter($table, $filter); } diff --git a/public/css/icinga/main-content.less b/public/css/icinga/main-content.less index 54430ef60..624b21eec 100644 --- a/public/css/icinga/main-content.less +++ b/public/css/icinga/main-content.less @@ -233,83 +233,6 @@ div.content.users { } } -div.content.memberships { - table.membership-list { - th.membership-cancel { - width: 8em; - padding-right: 0.5em; - text-align: right; - } - - td.membership-cancel { - text-align: right; - - form button.link-like { - color: inherit; - } - } - } - - p { - margin-top: 0; - } - - a.membership-create { - display: block; - margin-top: 1em; - } -} - -div.content.groups { - table.group-list { - th.group-remove { - width: 8em; - padding-right: 0.5em; - text-align: right; - } - - td.group-remove { - text-align: right; - } - } - - p { - margin-top: 0; - } - - a.group-add { - display: block; - margin-top: 1em; - } -} - -div.content.members { - table.member-list { - th.member-remove { - width: 8em; - padding-right: 0.5em; - text-align: right; - } - - td.member-remove { - text-align: right; - - form button.link-like { - color: inherit; - } - } - } - - p { - margin-top: 0; - } - - a.member-add { - display: block; - margin-top: 1em; - } -} - form.backend-selection { float: right;