Merge branch 'master' into feature/namespaced-controllers-5786

Conflicts:
	library/Icinga/Application/Web.php
This commit is contained in:
Eric Lippmann 2015-08-17 12:59:44 +02:00
commit 392eee5a1f
1038 changed files with 61968 additions and 25329 deletions

6
.gitattributes vendored
View File

@ -1,5 +1,11 @@
# Exclude files related to git when generating an archive
.git* export-ignore
# Exclude Vagrant and Puppet related files when generating an archive
.puppet* export-ignore
Vagrantfile export-ignore
# Normalize puppet manifests' line endings to LF on checkin and prevent conversion to CRLF when the files are checked out
.puppet* eol=lf
# Include version information on `git archive'
/application/VERSION export-subst

12
.mailmap Normal file
View File

@ -0,0 +1,12 @@
<alexander.klimov@netways.de> <Alexander.Klimov@netways.de>
<carloscesario@gmail.com> <ccesario@tecnomega.com.br>
<christopher.ruell@netways.de> <Christopher.Ruell@netways.de>
<gunnar.beutner@netways.de> <gunnar@beutner.name>
Jannis Moßhammer <jannis.mosshammer@netways.de>
<matthias.jentsch@netways.de> <mjentsch@localhost.int.netways.de>
<michael.friedrich@netways.de> <Michael.Friedrich@netways.de>
<michael.friedrich@netways.de> <michael.friedrich@gmail.com>
<thomas.gelf@netways.de> <thomas@gelf.net>
Thomas Gelf <thomas.gelf@netways.de> <root@squeeze-devel1.osmc.lab>
Thomas Gelf <thomas.gelf@netways.de> <tgelf@tgelf-web2dep.(none)>
Sylph Lin <sylph.lin@gmail.com>

View File

@ -1,7 +1,8 @@
---
icingaweb2::config: /etc/icingaweb
icingaweb2::log: /var/log/icingaweb/icingaweb.log
icingaweb2::web_path: icingaweb
icingaweb2::db_user: icingaweb
icingaweb2::db_pass: icingaweb
icingaweb2::db_name: icingaweb
icingaweb2::config: /etc/icingaweb2
icingaweb2::log: /var/log/icingaweb2/icingaweb2.log
icingaweb2::web_path: icingaweb2
icingaweb2::db_user: icingaweb2
icingaweb2::db_pass: icingaweb2
icingaweb2::db_name: icingaweb2
icingaweb2::group: icingaweb2

View File

@ -3,7 +3,7 @@
set -e
if which puppet >/dev/null 2>&1; then
exit 0
exit 0
fi
RELEASEVER=$(rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release))
@ -20,7 +20,7 @@ esac
echo "Adding puppet repository.."
rpm --import "https://yum.puppetlabs.com/RPM-GPG-KEY-puppetlabs"
rpm -ivh $PUPPET >/dev/null
rpm -Uvh $PUPPET >/dev/null
echo "Installing puppet.."
yum install -y puppet >/dev/null

View File

@ -12,4 +12,6 @@ node default {
file { '/etc/profile.d/env.sh':
source => 'puppet:////vagrant/.puppet/files/etc/profile.d/env.sh'
}
@user { vagrant: ensure => present }
User <| title == vagrant |> { groups +> hiera('icingaweb2::group') }
}

View File

@ -15,10 +15,10 @@
class epel {
yumrepo { 'epel':
mirrorlist => "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=${::architecture}",
mirrorlist => "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-${::operatingsystemmajrelease}&arch=${::architecture}",
enabled => '1',
gpgcheck => '0',
descr => "Extra Packages for Enterprise Linux 6 - ${::architecture}"
descr => "Extra Packages for Enterprise Linux ${::operatingsystemmajrelease} - ${::architecture}"
}
}

View File

@ -10,29 +10,22 @@
#
# icinga2::feature { 'example-feature'; }
#
define icinga2::feature ($source = undef) {
define icinga2::feature ($ensure = 'present') {
include icinga2
$target = "features-available/${name}"
$cfgpath = '/etc/icinga2'
$path = "${cfgpath}/features-enabled/${name}.conf"
if $source != undef {
icinga2::config { $target:
source => $source,
}
$action = $ensure ? {
/(present)/ => 'enable',
/(absent)/ => 'disable',
}
$test = $ensure ? {
/(present)/ => '-e',
/(absent)/ => '! -e',
}
parent_dirs { $path:
user => 'icinga',
require => [
User['icinga'],
File['icinga2cfgDir']
],
}
-> file { $path:
ensure => link,
target => "${cfgpath}/${target}.conf",
exec { "icinga2-feature-${action}-${name}":
unless => "/usr/bin/test ${test} /etc/icinga2/features-enabled/${name}.conf",
command => "/usr/sbin/icinga2 feature ${action} ${name}",
require => Package['icinga2'],
notify => Service['icinga2'],
}
}

View File

@ -15,7 +15,7 @@ class icinga2 {
include icinga_packages
package { [
'icinga2', 'icinga2-doc', 'icinga2-debuginfo'
'icinga2', 'icinga2-doc'
]:
ensure => latest,
require => Class['icinga_packages'],
@ -35,7 +35,7 @@ class icinga2 {
links => follow,
owner => 'icinga',
group => 'icinga',
mode => 6750,
mode => '6750',
}
icinga2::feature { [ 'statusdata', 'command', 'compatlog' ]: }

View File

@ -28,7 +28,8 @@ class icinga2_mysql {
privileges => 'SELECT,INSERT,UPDATE,DELETE',
schemafile => '/usr/share/icinga2-ido-mysql/schema/mysql.sql',
}
-> icinga2::feature { 'ido-mysql':
-> icinga2::config { 'features-available/ido-mysql':
source => 'puppet:///modules/icinga2_mysql',
}
-> icinga2::feature { 'ido-mysql': }
}

View File

@ -11,7 +11,8 @@ class icinga2_pgsql {
password => 'icinga2',
schemafile => '/usr/share/icinga2-ido-pgsql/schema/pgsql.sql',
}
-> icinga2::feature { 'ido-pgsql':
source => 'puppet:///modules/icinga2_pgsql',
}
# Because Icinga 2 does not handle more than one IDO connection properly, The ido-pgsql will not be enabled by default.
# -> icinga2::feature { 'ido-pgsql':
# source => 'puppet:///modules/icinga2_pgsql',
# }
}

View File

@ -8,7 +8,7 @@
#
class icinga_packages {
yumrepo { 'icinga_packages':
baseurl => 'http://packages.icinga.org/epel/6/snapshot/',
baseurl => "http://packages.icinga.org/epel/${::operatingsystemmajrelease}/snapshot/",
enabled => '1',
gpgcheck => '1',
gpgkey => 'http://packages.icinga.org/icinga.key',

View File

@ -1,14 +1,15 @@
class icingaweb2::config (
$config = hiera('icingaweb2::config')
$config = hiera('icingaweb2::config'),
$web_group = hiera('icingaweb2::group')
) {
group { 'icingaweb':
group { $web_group:
ensure => present,
}
file { [ "${config}", "${config}/enabledModules", "${config}/modules", "${config}/preferences" ]:
ensure => directory,
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => '2770',
}
}

View File

@ -1,15 +1,21 @@
define icingaweb2::config::general (
$source,
$config = hiera('icingaweb2::config'),
$replace = true
$config = hiera('icingaweb2::config'),
$log = hiera('icingaweb2::log'),
$web_path = hiera('icingaweb2::web_path'),
$db_user = hiera('icingaweb2::db_user'),
$db_pass = hiera('icingaweb2::db_pass'),
$db_name = hiera('icingaweb2::db_name'),
$web_group = hiera('icingaweb2::group'),
$replace = true
) {
include icingaweb2::config
file { "${config}/${name}.ini":
content => template("${source}/${name}.ini.erb"),
owner => 'root',
group => 'icingaweb',
mode => 0660,
group => $web_group,
mode => '0660',
replace => $replace,
}
}

View File

@ -1,8 +1,9 @@
define icingaweb2::config::module (
$module,
$source,
$config = hiera('icingaweb2::config'),
$replace = true
$config = hiera('icingaweb2::config'),
$web_group = hiera('icingaweb2::group'),
$replace = true
) {
include icingaweb2::config
@ -10,7 +11,7 @@ define icingaweb2::config::module (
file { "${config}/modules/${module}":
ensure => directory,
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => '2770',
}
}
@ -18,8 +19,8 @@ define icingaweb2::config::module (
file { "${config}/modules/${module}/${name}.ini":
source => "${source}/modules/${module}/${name}.ini",
owner => 'root',
group => 'icingaweb',
mode => 0660,
group => $web_group,
mode => '0660',
replace => $replace,
}
}

View File

@ -1,6 +1,6 @@
# Class: mysql
#
# This class installs the mysql server and client software.
# This class installs the MySQL server and client software on a RHEL or CentOS
#
# Parameters:
#
@ -16,21 +16,39 @@ class mysql {
Exec { path => '/usr/bin' }
if versioncmp($::operatingsystemmajrelease, '7') >= 0 {
$client_package_name = 'mariadb'
$server_package_name = 'mariadb-server'
$server_service_name = 'mariadb'
$cnf = '/etc/my.cnf.d/server.cnf'
$log_error = '/var/log/mariadb/mariadb.log'
$pid_file = '/var/run/mariadb/mariadb.pid'
} else {
$client_package_name = 'mysql'
$server_package_name = 'mysql-server'
$server_service_name = 'mysqld'
$cnf = '/etc/my.cnf'
$log_error = '/var/log/mysqld.log'
$pid_file = '/var/run/mysqld/mysqld.pid'
}
package { [
'mysql', 'mysql-server'
$client_package_name, $server_package_name,
]:
ensure => latest,
}
service { 'mysqld':
ensure => running,
service { $server_service_name:
alias => 'mysqld',
enable => true,
require => Package['mysql-server']
ensure => running,
require => Package[$server_package_name],
}
file { '/etc/my.cnf':
file { $cnf:
content => template('mysql/my.cnf.erb'),
require => Package['mysql-server'],
notify => Service['mysqld']
notify => Service['mysqld'],
recurse => true,
require => Package[$server_package_name],
}
}

View File

@ -104,8 +104,8 @@ innodb_file_per_table
innodb_log_file_size = 64M
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
log-error=<%= @log_error %>
pid-file=<%= @pid_file %>
# Increase the amount of open files allowed per process. Warning: Make
# sure you have set the global system limit high enough! The high value

View File

@ -20,6 +20,15 @@ class openldap {
service { 'slapd':
ensure => running,
require => Package['openldap-servers']
require => Package['openldap-servers'],
}
if versioncmp($::operatingsystemmajrelease, '7') >= 0 {
openldap::schema{ 'core': }
-> openldap::schema{ 'cosine': }
-> openldap::schema{ 'inetorgperson': }
-> openldap::schema{ 'nis': }
-> openldap::schema{ 'misc': }
-> openldap::schema{ 'openldap': }
}
}

View File

@ -0,0 +1,24 @@
# define: openldap::schema
#
# Install a schema.
#
# Parameters:
#
# Actions:
#
# Requires:
#
# Sample Usage:
#
define openldap::schema {
include openldap
exec { "openldap-schema-${name}":
command => "ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/${name}.ldif",
group => 'root',
require => Service['slapd'],
unless => "test -n \"$(find /etc/openldap/slapd.d/cn=config/cn=schema/ -name cn={*}${name}.ldif -print -quit)\"",
user => 'root',
}
}

View File

@ -25,7 +25,7 @@ define pgsql::database::create ($username, $password) {
unless => "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='${username}'\" | grep -q 1",
command => "psql -c \"CREATE ROLE ${username} WITH LOGIN PASSWORD '${password}';\" && \
createdb -O ${username} -E UTF8 -T template0 ${name} && \
createlang plpgsql ${name}",
(createlang plpgsql ${name} || true)",
user => 'postgres',
require => Class['pgsql']
}

View File

@ -71,6 +71,11 @@ local icinga icinga trust
host icinga icinga 127.0.0.1/32 trust
host icinga icinga ::1/128 trust
# icinga2
local icinga2 icinga2 trust
host icinga2 icinga2 127.0.0.1/32 trust
host icinga2 icinga2 ::1/128 trust
# icinga_unittest
local icinga_unittest icinga_unittest trust
host icinga_unittest icinga_unittest 127.0.0.1/32 trust
@ -81,6 +86,11 @@ local icingaweb icingaweb trust
host icingaweb icingaweb 127.0.0.1/32 trust
host icingaweb icingaweb ::1/128 trust
# icingaweb2
local <%= scope.function_hiera(['icingaweb2::db_user']) %> <%= scope.function_hiera(['icingaweb2::db_user']) %> trust
host <%= scope.function_hiera(['icingaweb2::db_user']) %> <%= scope.function_hiera(['icingaweb2::db_user']) %> 127.0.0.1/32 trust
host <%= scope.function_hiera(['icingaweb2::db_user']) %> <%= scope.function_hiera(['icingaweb2::db_user']) %> ::1/128 trust
# "local" is for Unix domain socket connections only
local all all ident
# IPv4 local connections:

View File

@ -20,24 +20,9 @@ class php {
package { 'php':
ensure => latest,
require => Package['apache'],
notify => Service['apache']
}
# TODO(el): Always executed. Should be a resource
-> exec { 'php-timezone':
command => 'sed -re $\'s#^;?(date\\.timezone =).*$#\\1 "UTC"#\' -i /etc/php.ini',
notify => Service['apache'],
require => Package['apache'],
}
file { '/etc/php.d/error_reporting.ini':
content => template('php/error_reporting.ini.erb'),
require => Package['php'],
notify => Service['apache']
}
file { '/etc/php.d/xdebug_settings.ini':
content => template('php/xdebug_settings.ini.erb'),
require => Package['php'],
notify => Service['apache']
}
php::phpd { ['error_reporting', 'timezone', 'xdebug_settings' ]: }
}

View File

@ -0,0 +1,22 @@
# define: php::phpd
#
# Provision php.d config
#
# Parameters:
#
# Actions:
#
# Requires:
#
# Sample Usage:
#
define php::phpd {
include php
file { "/etc/php.d/$name.ini":
content => template("php/$name.ini.erb"),
notify => Service['apache'],
require => Package['php'],
}
}

View File

@ -0,0 +1 @@
date.timezone = "UTC"

View File

@ -0,0 +1,29 @@
# Class: php_imagick
#
# This class installs the ImageMagick PHP module.
#
# Parameters:
#
# Actions:
#
# Requires:
#
# php
#
# Sample Usage:
#
# include php_imagick
#
class php_imagick {
include php
$php_imagick = $::operatingsystem ? {
/(Debian|Ubuntu)/ => 'php5-imagick',
/(RedHat|CentOS|Fedora)/ => 'php-pecl-imagick',
/(SLES|OpenSuSE)/ => 'php5-imagick',
}
package { $php_imagick:
ensure => latest,
}
}

View File

@ -1,11 +1,12 @@
# Class: icinga2_dev
#
# This class installs Icinga 2 w/ MySQL and provides Icinga 2 test configuration.
# This class installs Icinga 2 w/ MySQL and PostgreSQL and provides Icinga 2 test configuration.
#
# Requires:
#
# icinga2_mysql
# icinga2::config
# icinga2::feature
#
# Sample Usage:
#
@ -18,7 +19,17 @@ class icinga2_dev {
include monitoring_test_config
icinga2::config { [
'conf.d/test-config', 'conf.d/commands', 'constants' ]:
'conf.d/test-config', 'conf.d/commands', 'constants'
]:
source => 'puppet:///modules/icinga2_dev',
}
icinga2::feature { 'api':
ensure => absent,
}
icinga2::feature { 'ido-pgsql':
ensure => absent,
require => Class['icinga2_pgsql'],
}
}

View File

@ -6,7 +6,7 @@ olcRootPW: {SSHA}N/2WMqT8q7cElh7KUQz+p9TJbjmKv/u9
replace: olcRootDN
olcRootDN: cn=admin,cn=config
dn: olcDatabase={2}bdb,cn=config
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {SSHA}MxMpLBo2/TSymoIBf/Sb5iQac7Wwiur5

View File

@ -1,13 +1,15 @@
class icingaweb2_dev (
$config = hiera('icingaweb2::config'),
$log = hiera('icingaweb2::log'),
$web_path = hiera('icingaweb2::web_path'),
$db_user = hiera('icingaweb2::db_user'),
$db_pass = hiera('icingaweb2::db_pass'),
$db_name = hiera('icingaweb2::db_name'),
$config = hiera('icingaweb2::config'),
$log = hiera('icingaweb2::log'),
$web_path = hiera('icingaweb2::web_path'),
$db_user = hiera('icingaweb2::db_user'),
$db_pass = hiera('icingaweb2::db_pass'),
$db_name = hiera('icingaweb2::db_name'),
$web_group = hiera('icingaweb2::group'),
) {
include apache
include php
include php_imagick
include icingaweb2::config
include icingacli
include icinga_packages
@ -19,7 +21,7 @@ class icingaweb2_dev (
}
# TODO(el): icinga-gui is not a icingaweb2_dev package
package { [ 'php-pdo', 'php-ldap', 'php-phpunit-PHPUnit', 'icinga-gui' ]:
package { [ 'php-gd', 'php-intl', 'php-pdo', 'php-ldap', 'php-phpunit-PHPUnit', 'icinga-gui' ]:
ensure => latest,
notify => Service['apache'],
require => Class['icinga_packages'],
@ -28,7 +30,7 @@ class icingaweb2_dev (
Exec { path => '/usr/local/bin:/usr/bin:/bin' }
# TODO(el): Enabling/disabling modules should be a resource
User <| alias == apache |> { groups +> 'icingaweb' }
User <| alias == apache |> { groups +> $web_group }
-> exec { 'enable-monitoring-module':
command => 'icingacli module enable monitoring',
user => 'apache',
@ -50,7 +52,7 @@ class icingaweb2_dev (
file { $log_dir:
ensure => directory,
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => '2775'
}
@ -88,7 +90,7 @@ class icingaweb2_dev (
source => $name,
}
icingaweb2::config::general { [ 'config', 'resources' ]:
icingaweb2::config::general { [ 'config', 'resources', 'roles' ]:
source => $name,
replace => false,
}

View File

@ -1,5 +1,5 @@
[autologin]
backend = autologin
backend = external
[icingaweb-mysql]
backend = db

View File

@ -1,23 +1,38 @@
Alias /<%= @web_path %> /vagrant/public
Alias /<%= @web_path %> "/vagrant/public"
<Directory "/vagrant/public/">
Options FollowSymLinks
<Directory "/vagrant/public">
Options SymLinksIfOwnerMatch
AllowOverride None
Order allow,deny
Allow from all
# SetEnv ICINGAWEB_CONFIGDIR <%= @config %>
<IfModule mod_authz_core.c>
# Apache 2.4
<RequireAll>
Require all granted
</RequireAll>
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order allow,deny
Allow from all
</IfModule>
SetEnv ICINGAWEB_CONFIGDIR <%= @config %>
EnableSendfile Off
RewriteEngine on
RewriteBase /<%= @web_path %>/
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /<%= @web_path %>/
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
</IfModule>
php_value xdebug.idekey PHPSTORM
<IfModule !mod_rewrite.c>
DirectoryIndex error_norewrite.html
ErrorDocument 404 /error_norewrite.html
</IfModule>
</Directory>

View File

@ -0,0 +1,3 @@
[admins]
users = icingaadmin
permissions = *

32
AUTHORS Normal file
View File

@ -0,0 +1,32 @@
Alexander A. Klimov <alexander.klimov@netways.de>
Alexander Fuhr <alexander.fuhr@netways.de>
ayoubabid <ayoubabid@users.noreply.github.com>
baufrecht <baufrecht@users.noreply.github.com>
Bence Nagy <bence@underyx.me>
Bernd Erk <bernd.erk@icinga.org>
Boden Garman <boden.garman@spintel.net.au>
Carlos Cesario <carloscesario@gmail.com>
Chris Rüll <christopher.ruell@netways.de>
Daniel Shirley <aditaa@ig2ad.com>
Davide Demuru <davide.demuru@buongiorno.com>
Eric Lippmann <eric.lippmann@netways.de>
Goran Rakic <grakic@devbase.net>
Gunnar Beutner <gunnar.beutner@netways.de>
Jannis Moßhammer <jannis.mosshammer@netways.de>
Johannes Meyer <johannes.meyer@netways.de>
Louis Sautier <sautier.louis@gmail.com>
Marcus Cobden <marcus@marcuscobden.co.uk>
Marius Hein <marius.hein@netways.de>
Markus Frosch <markus@lazyfrosch.de>
Matthias Jentsch <matthias.jentsch@netways.de>
Michael Friedrich <michael.friedrich@netways.de>
Paul Richards <paul@minimoo.org>
rbelinsky <rbelinsky@dalet.com>
Rene Moser <rene.moser@swisstxt.ch>
rkcpi <thieme.sandra@gmail.com>
Susanne Vestner-Ludwig <susanne.vestner-ludwig@inserteffect.com>
Sylph Lin <sylph.lin@gmail.com>
Thomas Gelf <thomas.gelf@netways.de>
Tim Helfensdörfer <tim@visualappeal.de>
Tom Ford <exptom@users.noreply.github.com>
Ulf Lange <mopp@gmx.net>

130
ChangeLog Normal file
View File

@ -0,0 +1,130 @@
## What's New in Version 2.0.0-rc1
### Changes
* Improve layout and look and feel in many ways
* Apply host, service and custom variable restrictions to all monitoring objects
* Add fullscreen mode (?showFullscreen)
* User and group management
* Comment and Downtime Detail View
* Show icon_image in host/service views
* Show Icinga program version in monitoring health
#### Features
* Feature 4139: Notify monitoring backend availability problems
* Feature 4498: Allow to add columns to monitoring views via URL
* Feature 6392: Resolve Icinga 2 runtime macros in action and notes URLs
* Feature 6729: Fullscreen mode
* Feature 7343: Fetch user groups from LDAP
* Feature 7595: Remote connection resource configuration
* Feature 7614: Right-align icons
* Feature 7651: Add module information (module.info) to all core modules
* Feature 8054: Host Groups should list number of hosts (as well as services)
* Feature 8235: Show host and service notes in the host and service detail view
* Feature 8247: Move notifications to the bottom of the page
* Feature 8281: Improve layout of comments and downtimes in the host and service detail views
* Feature 8310: Improve layout of performance data and check statistics in the host and service detail views
* Feature 8565: Improve look and feel of the monitoring multi-select views
* Feature 8613: IDO queries related to concrete objects should not depend on collations
* Feature 8665: Show icon_image in the host and service detail views
* Feature 8781: Automatically deselect rows when closing the detail area
* Feature 8826: User and group management
* Feature 8849: Show only three (or four) significant digits (e.g. in check execution time)
* Feature 8877: Allow module developers to implement new/custom authentication methods
* Feature 8886: Require mandatory parameters in controller actions and CLI commands
* Feature 8902: Downtime detail view
* Feature 8903: Comment detail view
* Feature 9009: Apply host and service restrictions to related views as well
* Feature 9203: Wizard: Validate that a resource is actually an IDO instance
* Feature 9207: Show icinga program version in Monitoring Health
* Feature 9223: Show the active ido endpoint in the monitoring health view
* Feature 9284: Create a ServiceActionsHook
* Feature 9300: Support icon_image_alt
* Feature 9361: Refine UI for RC1
* Feature 9377: Permission and restriction documentation
* Feature 9379: Provide an about.md
#### Bugfixes
* Bug 6281: ShowController's hostAction() and serviceAction() do not respond with 400 for invalid/missing parameters and with 404 if the host or service wasn't found
* Bug 6778: Duration and history time formatting isn't correct
* Bug 6952: Unauthenticated users are provided helpful error messages
* Bug 7151: Play nice with form-button-double-clickers
* Bug 7165: Invalid host address leads to exception w/ PostgreSQL
* Bug 7447: Commands sent over SSH are missing the -i option when using a ssh user aside from the webserver's user
* Bug 7491: Switching from MySQL to PostgreSQL and vice versa doesn't change the port in the resource configuration
* Bug 7642: Monitoring menu renderers should be moved to the monitoring module
* Bug 7658: MenuItemRenderer is not so easy to extend
* Bug 7876: Not all views can be added to the dashboard w/o breaking the layout
* Bug 7931: Can't acknowledge multiple selected services which are in downtime
* Bug 7997: Service-Detail-View tabs are changing their context when clicking the Host-Tab
* Bug 7998: Navigating to the Services-Tab in the Service-Detail-View displays only the selected service
* Bug 8006: Beautify command transport error exceptions
* Bug 8205: List views should not show more than the five worst pies
* Bug 8241: Take display_name into account when searching for host and service names
* Bug 8334: Perfdata details partially hidden depending on the resolution
* Bug 8339: Lib: SimpleQuery::paginate() must not fetch page and limit from request but use them from parameters
* Bug 8343: Status summary does not respect restrictions
* Bug 8363: Updating dashlets corrupts their URLs
* Bug 8453: The filter column "_dev" is not allowed here
* Bug 8472: Missing support for command line arguments in the format --arg=<value>
* Bug 8474: Improve layout of dictionaries in the host and service detail views
* Bug 8624: Delete multiple downtimes and comments at once
* Bug 8696: Can't search for Icinga 2 custom variables
* Bug 8705: Show all shell commands required to get ready in the setup wizard
* Bug 8706: INI files should end with a newline character and should not contain superfluous newlines
* Bug 8707: Wizard: setup seems to fail with just one DB user
* Bug 8711: JS is logging "ugly" side exceptions
* Bug 8731: Apply host restrictions to service views
* Bug 8744: Performance data metrics with value 0 are not displayed
* Bug 8747: Icinga 2 boolean variables not shown in the host and service detail views
* Bug 8777: Server error: Service not found exception when service name begins or ends with whitespaces
* Bug 8815: Only the first external command is sent over SSH when submitting commands for multiple selected hosts or services
* Bug 8847: Missing indication that nothing was found in the docs when searching
* Bug 8860: Host group view calculates states from service states; but states should be calculated from host states instead
* Bug 8927: Tactical overview does not respect restrictions
* Bug 8928: Host and service groups views do not respect restrictions
* Bug 8929: Setup wizard does not validate whether the PostgreSQL user for creating the database owns the CREATE ROLE system privilege
* Bug 8930: Error message about refused connection to the PostgreSQL database server displayed twice in the setup wizard
* Bug 8934: Status text for ok/up becomes white when hovered
* Bug 8941: Long plugin output makes the whole container horizontally scrollable instead of just the row containing the long plugin output
* Bug 8950: Improve English for "The last one occured %s ago"
* Bug 8953: LDAP encryption settings have no effect
* Bug 8956: Can't login when creating the database connection for the preferences store fails
* Bug 8957: Fall back on syslog if the logger's type directive is misconfigured
* Bug 8958: Switching LDAP encryption to LDAPS doesn't change the port in the resource configuration
* Bug 8960: Remove exclamation mark from the notification "Authentication order updated!"
* Bug 8966: Show custom variables visually separated in the host and service detail views
* Bug 8967: Remove right petrol border from plugin output in the host and service detail views
* Bug 8972: Can't view Icinga Web 2's log file
* Bug 8994: Uncaught exception on empty session.save_path()
* Bug 9000: Only the first line of a stack trace is shown in the applications log view
* Bug 9007: Misspelled host and service names in commands are not accepted by icinga
* Bug 9008: Notification overview does not respect restrictions
* Bug 9022: Browser title does not change in case of an error
* Bug 9023: Toggling feature...
* Bug 9025: A tooltip of the service grid's x-axe makes it difficult to click the title of the currently hovered column
* Bug 9026: Add To Dashboard ... on the dashboard
* Bug 9046: Detail View: Downtimes description misses space between duration and comment text
* Bug 9056: Filter for host/servicegroup search doesn't work anymore
* Bug 9057: contact_notify_host_timeperiod
* Bug 9059: Can't initiate an ascending sort by host or service severity
* Bug 9198: monitoring/command/feature/object does not grant the correct permissions
* Bug 9202: The config\* permission does not permit to navigate to the configuration
* Bug 9211: Empty filters are being rendered to SQL which leads to syntax errors
* Bug 9214: Detect multitple icinga_instances entries and warn the user
* Bug 9220: Centralize submission and apply handling of sort rules
* Bug 9224: Allow anonymous LDAP binding
* Bug 9281: Problem with Icingaweb 2 after PHP Upgrade 5.6.8 -> 5.6.9
* Bug 9317: Web 2's ListController inherits from the monitoring module's base controller
* Bug 9319: Downtimes overview does not respect restrictions
* Bug 9350: Menu disappears in user group management view
* Bug 9351: Timeline links are broken
* Bug 9352: User list should be sorted
* Bug 9353: Searching for users fails, at least with LDAP backend
* Bug 9355: msldap seems not to be a first-class citizen
* Bug 9378: Rpm calls usermod w/ invalid option on openSUSE
* Bug 9384: Timeline+Role problem
* Bug 9392: Command links seem to be broken

263
README.md
View File

@ -2,267 +2,26 @@
## Table of Contents
0. [General Information](#general information)
0. [About](#about)
1. [Installation](#installation)
2. [Support](#support)
3. [Vagrant - Virtual development environment](#vagrant)
## General Information
## About
`Icinga Web 2` is the next generation monitoring web interface, framework
and CLI tool developed by the [Icinga Project](https://www.icinga.org/community/team/).
**Icinga Web 2** is the next generation open source monitoring web interface, framework
and command-line interface developed by the [Icinga Project](https://www.icinga.org/), supporting Icinga 2,
Icinga Core and any other monitoring backend compatible with the Livestatus Protocol.
Responsive and fast, rewritten from scratch supporting multiple backends and
providing a CLI tool. Compatible with Icinga Core 2.x and 1.x.
Check the Icinga website for some [insights](https://www.icinga.org/icinga/screenshots/icinga-web-2/).
> **Note**
>
> `Icinga Web 2` is still in development and not meant for production deployment.
> Watch the [development roadmap](https://dev.icinga.org/projects/icingaweb2/roadmap)
> and [Icinga website](https://www.icinga.org/) for release schedule updates!
![Icinga Web 2](https://www.icinga.org/wp-content/uploads/2014/06/service_detail.png "Icinga Web 2")
## Installation
Please navigate to [doc/installation.md](doc/installation.md) for updated details.
For installing Icinga Web 2 please read [doc/installation.md](doc/installation.md).
## Support
Please head over to the [community support channels](https://www.icinga.org/icinga/faq/get-help/)
in case of questions, bugs, etc.
Please make sure to provide the following details:
* OS, distribution, version
* PHP and/or MySQL/PostgreSQL version
* Which browser and its version
* Screenshot and problem description
## Vagrant
### Requirements
* Vagrant 1.2+
* Virtualbox 4.2.16+
* a fairly powerful hardware (quad core, 4gb ram, fast hdd)
> **Note**
>
> The deployment of the virtual machine is tested against Vagrant starting with version 1.2.
> Unfortunately older versions will not work.
### General
The Icinga Web 2 project ships with a Vagrant virtual machine that integrates
the source code with various services and example data in a controlled
environment. This enables developers and users to test Livestatus, status.dat,
MySQL and PostgreSQL backends as well as the LDAP authentication. All you
have to do is install Vagrant and run:
vagrant up
> **Note**
>
> The first boot of the vm takes a fairly long time because
> you'll download a plain CentOS base box and Vagrant will automatically
> provision the environment on the first go.
After you should be able to browse [localhost:8080/icingaweb](http://localhost:8080/icingaweb).
### Environment
**Forwarded ports**:
<table>
<tr>
<th>Proctocol</th>
<th>Local port (virtual machine host)</th>
<th>Remote port (the virtual machine)</th>
</tr>
<tr>
<td>SSH</td>
<td>2222</td>
<td>22</td>
</tr>
<tr>
<td>HTTP</td>
<td>8080</td>
<td>80</td>
</tr>
</table>
**Installed packages**:
* Apache2 with PHP enabled
* PHP with MySQL and PostgreSQL libraries
* MySQL server and client software
* PostgreSQL server and client software
* [Icinga prerequisites](http://docs.icinga.org/latest/en/quickstart-idoutils.html#installpackages)
* OpenLDAP servers and clients
**Installed users and groups**:
* User icinga with group icinga and icinga-cmd
* Webserver user added to group icinga-cmd
**Installed software**:
* Icinga with IDOUtils using a MySQL database
* Icinga with IDOUtils using a PostgreSQL database
* Icinga 2
**Installed files**:
* `/usr/share/icinga/htpasswd.users` account information for logging into the Icinga classic web interface for both icinga instances
* `/usr/lib64/nagios/plugins` Monitoring Plugins for all Icinga instances
#### Icinga with IDOUtils using a MySQL database
**Installation path**: `/usr/local/icinga-mysql`
**Services**:
* `icinga-mysql`
* `ido2db-mysql`
Connect to the **icinga mysql database** using the following command:
mysql -u icinga -p icinga icinga
Access the **Classic UI** (CGIs) via [localhost:8080/icinga-mysql](http://localhost:8080/icinga-mysql).
For **logging into** the Icinga classic web interface use user *icingaadmin* with password *icinga*.
#### Icinga with IDOUtils using a PostgreSQL database
**Installation path**: `/usr/local/icinga-pgsql`
**Services**:
* `icinga-pgsql`
* `ido2db-pgsql`
Connect to the **icinga mysql database** using the following command:
sudo -u postgres psql -U icinga -d icinga
Access the **Classic UI** (CGIs) via [localhost:8080/icinga-pgsql](http://localhost:8080/icinga-pgsql).
For **logging into** the Icinga classic web interface use user *icingaadmin* with password *icinga*.
#### Monitoring Test Config
Test config is added to both the MySQL and PostgreSQL Icinga instance utilizing the Perl module
**Monitoring::Generator::TestConfig** to generate test config to **/usr/local/share/misc/monitoring_test_config**
which is then copied to **<instance>/etc/conf.d/test_config/**.
Configuration can be adjusted and recreated with **/usr/local/share/misc/monitoring_test_config/recreate.pl**.
**Note** that you have to run
vagrant provision
in the host after any modification to the script just mentioned.
#### MK Livestatus
MK Livestatus is added to the Icinga installation using a MySQL database.
**Installation path**:
* `/usr/local/icinga-mysql/bin/unixcat`
* `/usr/local/icinga-mysql/lib/mk-livestatus/livecheck`
* `/usr/local/icinga-mysql/lib/mk-livestatus/livestatus.o`
* `/usr/local/icinga-mysql/etc/modules/mk-livestatus.cfg`
* `/usr/local/icinga-mysql/var/rw/live`
**Example usage**:
echo "GET hosts" | /usr/local/icinga-mysql/bin/unixcat /usr/local/icinga-mysql/var/rw/live
#### LDAP example data
The environment includes a openldap server with example data. *Domain* suffix is **dc=icinga,dc=org**.
Administrator (*rootDN*) of the slapd configuration database is **cn=admin,cn=config** and the
administrator (*rootDN*) of our database instance is **cn=admin,dc=icinga,dc=org**. Both share
the *password* `admin`.
Examples to query the slapd configuration database:
ldapsearch -x -W -LLL -D cn=admin,cn=config -b cn=config dn
ldapsearch -Y EXTERNAL -H ldapi:/// -LLL -b cn=config dn
Examples to query our database instance:
ldapsearch -x -W -LLL -D cn=admin,dc=icinga,dc=org -b dc=icinga,dc=org dn
ldapsearch -Y EXTERNAL -H ldapi:/// -LLL -b dc=icinga,dc=org dn
This is what the **dc=icinga,dc=org** *DIT* looks like:
> dn: dc=icinga,dc=org
>
> dn: ou=people,dc=icinga,dc=org
>
> dn: ou=groups,dc=icinga,dc=org
>
> dn: cn=Users,ou=groups,dc=icinga,dc=org
> cn: Users
> uniqueMember: cn=Jon Doe,ou=people,dc=icinga,dc=org
> uniqueMember: cn=Jane Smith,ou=people,dc=icinga,dc=org
> uniqueMember: cn=John Q. Public,ou=people,dc=icinga,dc=org
> uniqueMember: cn=Richard Roe,ou=people,dc=icinga,dc=org
>
> dn: cn=John Doe,ou=people,dc=icinga,dc=org
> cn: John Doe
> uid: jdoe
>
> dn: cn=Jane Smith,ou=people,dc=icinga,dc=org
> cn: Jane Smith
> uid: jsmith
>
> dn: cn=John Q. Public,ou=people,dc=icinga,dc=org
> cn: John Q. Public
> uid: jqpublic
>
> dn: cn=Richard Roe,ou=people,dc=icinga,dc=org
> cn: Richard Roe
> uid: rroe
All users share the password `password`.
#### Testing the code
All software required to run tests is installed in the virtual machine.
In order to run all tests you have to execute the following commands:
vagrant ssh -c /vagrant/test/php/runtests
vagrant ssh -c /vagrant/test/php/checkswag
vagrant ssh -c /vagrant/test/js/runtests
vagrant ssh -c /vagrant/test/js/checkswag
vagrant ssh -c /vagrant/test/frontend/runtests
`runtests` will execute unit and regression tests and `checkswag` will report
code style issues.
#### Icinga 2
Installed from the Icinga [snapshot package repository](http://packages.icinga.org/epel/).
The configuration is located in `/etc/icinga2`.
**Example usage**:
/etc/init.d/icinga2 (start|stop|restart|reload)
## Log into Icinga Web 2
If you've configure LDAP as authentication backend (which is the default) use the following login credentials:
> **Username**: jdoe
> **Password**: password
Have a look at [LDAP example data](#ldap example data) for more accounts.
Using MySQL as backend:
> **Username**: icingaadmin
> **Password**: icinga
If you come across problems at some time, the [community support channels](https://support.icinga.org/)
are good places to ask for advice from other users and give some in return.
For status updates check the [Icinga website](https://www.icinga.org/) and the
[Icinga Web 2 development roadmap](https://dev.icinga.org/projects/icingaweb2/roadmap).

64
RELEASE.md Normal file
View File

@ -0,0 +1,64 @@
# Quality Assurance
Review and test the changes and issues for this version.
https://dev.icinga.org/projects/icingaweb2/roadmap
# Release Workflow
Update the [.mailmap](.mailmap) and [AUTHORS](AUTHORS) files:
$ git log --use-mailmap | grep ^Author: | cut -f2- -d' ' | sort | uniq > AUTHORS
Update the version number in the [icingaweb2.spec] and [VERSION] files.
Update the [ChangeLog](ChangeLog) file using
the changelog.py script.
Changelog:
$ ./changelog.py --version 2.0.0-rc1
Wordpress:
$ ./changelog.py --version 2.0.0-rc1 --html --links
Commit these changes to the "master" branch:
$ git commit -v -a -m "Release version <VERSION>"
For minor releases: Cherry-pick this commit into the "support" branch.
Create a signed tag (tags/v<VERSION>) on the "master" branch (for major
releases) or the "support" branch (for minor releases).
$ git tag -m "Version <VERSION>" v<VERSION>
Push the tag.
$ git push --tags
For major releases: Create a new "support" branch:
$ git checkout master
$ git checkout -b support/2.x
$ git push -u origin support/2.x
# External Dependencies
## Build Server
### Linux
* Build the newly created git tag for Debian/RHEL/SuSE.
* Provision the vagrant boxes and test the release packages.
## Github Release
Create a new release from the newly created git tag.
https://github.com/Icinga/icingaweb2/releases
## Announcement
* Create a new blog post on www.icinga.org/blog
* Send announcement mail to icinga-announce@lists.icinga.org
* Social media: [Twitter](https://twitter.com/icinga), [Facebook](https://www.facebook.com/icinga), [G+](http://plus.google.com/+icinga), [Xing](https://www.xing.com/communities/groups/icinga-da4b-1060043), [LinkedIn](https://www.linkedin.com/groups/Icinga-1921830/about)

View File

@ -1 +1 @@
v2.0.0-beta1
v2.0.0-rc1

10
Vagrantfile vendored
View File

@ -1,10 +1,11 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+
VAGRANTFILE_API_VERSION = "2"
VAGRANT_REQUIRED_VERSION = "1.5.0"
# Require 1.2.x at least
if ! defined? Vagrant.require_version
if Gem::Version.new(Vagrant::VERSION) < Gem::Version.new(VAGRANT_REQUIRED_VERSION)
puts "Vagrant >= " + VAGRANT_REQUIRED_VERSION + " required. Your version is " + Vagrant::VERSION
@ -21,13 +22,15 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision :shell, :path => ".puppet/manifests/puppet.sh"
config.vm.provider :virtualbox do |v, override|
override.vm.box = "puppetlabs/centos-6.5-64-puppet"
override.vm.box = "centos-71-x64-vbox"
override.vm.box_url = "http://boxes.icinga.org/centos-71-x64-vbox.box"
v.customize ["modifyvm", :id, "--memory", "1024"]
v.customize ["modifyvm", :id, "--cpus", "2"]
end
config.vm.provider :parallels do |p, override|
override.vm.box = "parallels/centos-6.5"
override.vm.box = "parallels/centos-7.1"
p.name = "Icinga Web 2 Development"
@ -46,5 +49,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
puppet.module_path = [ ".puppet/modules", ".puppet/profiles" ]
puppet.manifests_path = ".puppet/manifests"
puppet.manifest_file = "site.pp"
puppet.options = "--parser=future"
end
end

1
application/VERSION Normal file
View File

@ -0,0 +1 @@
$Format:%H%d %ci$

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Clicommands;

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Clicommands;

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Clicommands;

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Clicommands;

View File

@ -0,0 +1,15 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers;
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Version;
class AboutController extends ActionController
{
public function indexAction()
{
$this->view->version = Version::get();
}
}

View File

@ -1,145 +1,53 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers;
use Icinga\Authentication\Backend\AutoLoginBackend;
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Icinga;
use Icinga\Forms\Authentication\LoginForm;
use Icinga\Authentication\AuthChain;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Exception\AuthenticationException;
use Icinga\Exception\NotReadableError;
use Icinga\Exception\ConfigurationError;
use Icinga\User;
use Icinga\Web\Controller;
use Icinga\Web\Cookie;
use Icinga\Web\Url;
/**
* Application wide controller for authentication
*/
class AuthenticationController extends ActionController
class AuthenticationController extends Controller
{
/**
* This controller does not require authentication
*
* @var bool
* {@inheritdoc}
*/
protected $requiresAuthentication = false;
/**
* {@inheritdoc}
*/
protected $innerLayout = 'inline';
/**
* Log into the application
*/
public function loginAction()
{
if (@file_exists(Config::resolvePath('setup.token')) && !@file_exists(Config::resolvePath('config.ini'))) {
$icinga = Icinga::app();
if (($requiresSetup = $icinga->requiresSetup()) && $icinga->setupTokenExists()) {
$this->redirectNow(Url::fromPath('setup'));
}
$auth = $this->Auth();
$this->view->form = $form = new LoginForm();
$this->view->title = $this->translate('Icingaweb Login');
try {
$redirectUrl = $this->view->form->getValue('redirect');
if ($redirectUrl) {
$redirectUrl = Url::fromPath($redirectUrl);
} else {
$redirectUrl = Url::fromPath('dashboard');
}
if ($auth->isAuthenticated()) {
$this->rerenderLayout()->redirectNow($redirectUrl);
}
try {
$config = Config::app('authentication');
} catch (NotReadableError $e) {
throw new ConfigurationError(
$this->translate('Could not read your authentication.ini, no authentication methods are available.'),
0,
$e
);
}
$chain = new AuthChain($config);
$request = $this->getRequest();
if ($request->isPost() && $this->view->form->isValid($request->getPost())) {
$user = new User($this->view->form->getValue('username'));
$password = $this->view->form->getValue('password');
$backendsTried = 0;
$backendsWithError = 0;
$redirectUrl = $form->getValue('redirect');
if ($redirectUrl) {
$redirectUrl = Url::fromPath($redirectUrl);
} else {
$redirectUrl = Url::fromPath('dashboard');
}
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
continue;
}
++$backendsTried;
try {
$authenticated = $backend->authenticate($user, $password);
} catch (AuthenticationException $e) {
Logger::error($e);
++$backendsWithError;
continue;
}
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->rerenderLayout()->redirectNow($redirectUrl);
}
}
if ($backendsTried === 0) {
$this->view->form->addError(
$this->translate(
'No authentication methods available. Did you create'
. ' authentication.ini when setting up Icinga Web 2?'
)
);
} else if ($backendsTried === $backendsWithError) {
$this->view->form->addError(
$this->translate(
'All configured authentication methods failed.'
. ' Please check the system log or Icinga Web 2 log for more information.'
)
);
} elseif ($backendsWithError) {
$this->view->form->addError(
$this->translate(
'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.'
)
);
}
if ($backendsTried > 0 && $backendsTried !== $backendsWithError) {
$this->view->form->getElement('password')->addError($this->translate('Incorrect username or password'));
}
} elseif ($request->isGet()) {
$user = new User('');
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
$authenticated = $backend->authenticate($user);
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->rerenderLayout()->redirectNow(
Url::fromPath(Url::fromRequest()->getParam('redirect', 'dashboard'))
);
}
}
}
}
} catch (Exception $e) {
$this->view->errorInfo = $e->getMessage();
$form = new LoginForm();
if ($this->Auth()->isAuthenticated()) {
$this->redirectNow($form->getRedirectUrl());
}
$this->view->configMissing = is_dir(Config::$configDir) === false;
if (! $requiresSetup) {
if (! $this->getRequest()->hasCookieSupport()) {
echo $this->translate("Cookies must be enabled to run this application.\n");
$this->getResponse()->setHttpResponseCode(403)->sendHeaders();
exit();
}
$form->handleRequest();
}
$this->view->form = $form;
$this->view->title = $this->translate('Icinga Web 2 Login');
$this->view->requiresSetup = $requiresSetup;
}
/**
@ -151,10 +59,12 @@ class AuthenticationController extends ActionController
if (! $auth->isAuthenticated()) {
$this->redirectToLogin();
}
$isRemoteUser = $auth->getUser()->isRemoteUser();
// Get info whether the user is externally authenticated before removing authorization which destroys the
// session and the user object
$isExternalUser = $auth->getUser()->isExternalUser();
$auth->removeAuthorization();
if ($isRemoteUser === true) {
$this->_response->setHttpResponseCode(401);
if ($isExternalUser) {
$this->getResponse()->setHttpResponseCode(401);
} else {
$this->redirectToLogin();
}

View File

@ -1,41 +1,68 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Notification;
use Icinga\Application\Modules\Module;
use Icinga\Web\Widget;
use Icinga\Application\Icinga;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Modules\Module;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\UserBackendConfigForm;
use Icinga\Forms\Config\UserBackendReorderForm;
use Icinga\Forms\Config\GeneralConfigForm;
use Icinga\Forms\Config\AuthenticationBackendReorderForm;
use Icinga\Forms\Config\AuthenticationBackendConfigForm;
use Icinga\Forms\Config\ResourceConfigForm;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Data\ResourceFactory;
use Icinga\Security\SecurityException;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget;
/**
* Application wide controller for application preferences
* Application and module configuration
*/
class ConfigController extends ActionController
class ConfigController extends Controller
{
public function init()
/**
* Create and return the tabs to display when showing application configuration
*/
public function createApplicationTabs()
{
$this->view->tabs = Widget::create('tabs')->add('index', array(
'title' => $this->translate('Application'),
'url' => 'config'
))->add('authentication', array(
'title' => $this->translate('Authentication'),
'url' => 'config/authentication'
))->add('resources', array(
'title' => $this->translate('Resources'),
'url' => 'config/resource'
))->add('roles', array(
'title' => $this->translate('Roles'),
'url' => 'roles'
$tabs = $this->getTabs();
$tabs->add('general', array(
'title' => $this->translate('Adjust the general configuration of Icinga Web 2'),
'label' => $this->translate('General'),
'url' => 'config/general',
'baseTarget' => '_main'
));
$tabs->add('resource', array(
'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'),
'label' => $this->translate('Resources'),
'url' => 'config/resource',
'baseTarget' => '_main'
));
return $tabs;
}
/**
* Create and return the tabs to display when showing authentication configuration
*/
public function createAuthenticationTabs()
{
$tabs = $this->getTabs();
$tabs->add('userbackend', array(
'title' => $this->translate('Configure how users authenticate with and log into Icinga Web 2'),
'label' => $this->translate('User Backends'),
'url' => 'config/userbackend',
'baseTarget' => '_main'
));
$tabs->add('usergroupbackend', array(
'title' => $this->translate('Configure how users are associated with groups by Icinga Web 2'),
'label' => $this->translate('User Group Backends'),
'url' => 'usergroupbackend/list',
'baseTarget' => '_main'
));
return $tabs;
}
public function devtoolsAction()
@ -44,16 +71,27 @@ class ConfigController extends ActionController
}
/**
* Index action, entry point for configuration
* Redirect to the general configuration
*/
public function indexAction()
{
$this->redirectNow('config/general');
}
/**
* General configuration
*
* @throws SecurityException If the user lacks the permission for configuring the general configuration
*/
public function generalAction()
{
$this->assertPermission('config/application/general');
$form = new GeneralConfigForm();
$form->setIniConfig(Config::app());
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('index');
$this->createApplicationTabs()->activate('general');
}
/**
@ -61,32 +99,50 @@ class ConfigController extends ActionController
*/
public function modulesAction()
{
$this->view->tabs = Widget::create('tabs')->add('modules', array(
'title' => $this->translate('Modules'),
'url' => 'config/modules'
));
$this->view->tabs->activate('modules');
$this->assertPermission('config/modules');
// Overwrite tabs created in init
// @TODO(el): This seems not natural to me. Module configuration should have its own controller.
$this->view->tabs = Widget::create('tabs')
->add('modules', array(
'label' => $this->translate('Modules'),
'title' => $this->translate('List intalled modules'),
'url' => 'config/modules'
))
->activate('modules');
$this->view->modules = Icinga::app()->getModuleManager()->select()
->from('modules')
->order('enabled', 'desc')
->order('name')->paginate();
->order('name');
$this->setupLimitControl();
$this->setupPaginationControl($this->view->modules);
// TODO: Not working
/*$this->setupSortControl(array(
'name' => $this->translate('Modulename'),
'path' => $this->translate('Installation Path'),
'enabled' => $this->translate('State')
));*/
}
public function moduleAction()
{
$name = $this->getParam('name');
$this->assertPermission('config/modules');
$app = Icinga::app();
$manager = $app->getModuleManager();
$name = $this->getParam('name');
if ($manager->hasInstalled($name)) {
$this->view->moduleData = Icinga::app()->getModuleManager()->select()
->from('modules')->where('name', $name)->fetchRow();
$module = new Module($app, $name, $manager->getModuleDir($name));
$this->view->moduleData = $manager->select()->from('modules')->where('name', $name)->fetchRow();
if ($manager->hasLoaded($name)) {
$module = $manager->getModule($name);
} else {
$module = new Module($app, $name, $manager->getModuleDir($name));
}
$this->view->module = $module;
$this->view->tabs = $module->getConfigTabs()->activate('info');
} else {
$this->view->module = false;
$this->view->tabs = null;
}
$this->view->tabs = $module->getConfigTabs()->activate('info');
}
/**
@ -94,11 +150,11 @@ class ConfigController extends ActionController
*/
public function moduleenableAction()
{
$this->assertPermission('config/modules');
$module = $this->getParam('name');
$manager = Icinga::app()->getModuleManager();
try {
$manager->enableModule($module);
$manager->loadModule($module);
Notification::success(sprintf($this->translate('Module "%s" enabled'), $module));
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
} catch (Exception $e) {
@ -114,6 +170,7 @@ class ConfigController extends ActionController
*/
public function moduledisableAction()
{
$this->assertPermission('config/modules');
$module = $this->getParam('name');
$manager = Icinga::app()->getModuleManager();
try {
@ -129,85 +186,144 @@ class ConfigController extends ActionController
}
/**
* Action for listing and reordering authentication backends
* Action for listing and reordering user backends
*/
public function authenticationAction()
public function userbackendAction()
{
$form = new AuthenticationBackendReorderForm();
$this->assertPermission('config/application/userbackend');
$form = new UserBackendReorderForm();
$form->setIniConfig(Config::app('authentication'));
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$this->render('authentication/reorder');
$this->createAuthenticationTabs()->activate('userbackend');
$this->render('userbackend/reorder');
}
/**
* Action for creating a new authentication backend
* Create a new user backend
*/
public function createauthenticationbackendAction()
public function createuserbackendAction()
{
$form = new AuthenticationBackendConfigForm();
$form->setIniConfig(Config::app('authentication'));
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
$form->setRedirectUrl('config/authentication');
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$this->render('authentication/create');
}
/**
* Action for editing authentication backends
*/
public function editauthenticationbackendAction()
{
$form = new AuthenticationBackendConfigForm();
$form->setIniConfig(Config::app('authentication'));
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
$form->setRedirectUrl('config/authentication');
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$this->render('authentication/modify');
}
/**
* Action for removing a backend from the authentication list
*/
public function removeauthenticationbackendAction()
{
$form = new ConfirmRemovalForm(array(
'onSuccess' => function ($form) {
$configForm = new AuthenticationBackendConfigForm();
$configForm->setIniConfig(Config::app('authentication'));
$authBackend = $form->getRequest()->getQuery('auth_backend');
try {
$configForm->remove($authBackend);
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
}
if ($configForm->save()) {
Notification::success(sprintf(
t('Authentication backend "%s" has been successfully removed'),
$authBackend
));
} else {
return false;
}
}
$this->assertPermission('config/application/userbackend');
$form = new UserBackendConfigForm();
$form->setRedirectUrl('config/userbackend');
$form->setTitle($this->translate('Create New User Backend'));
$form->addDescription($this->translate(
'Create a new backend for authenticating your users. This backend'
. ' will be added at the end of your authentication order.'
));
$form->setRedirectUrl('config/authentication');
$form->setIniConfig(Config::app('authentication'));
try {
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
} catch (ConfigurationError $e) {
if ($this->hasPermission('config/application/resources')) {
Notification::error($e->getMessage());
$this->redirectNow('config/createresource');
}
throw $e; // No permission for resource configuration, show the error
}
$form->setOnSuccess(function (UserBackendConfigForm $form) {
try {
$form->add(array_filter($form->getValues()));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(t('User backend successfully created'));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$this->render('authentication/remove');
$this->render('form');
}
/**
* Edit a user backend
*/
public function edituserbackendAction()
{
$this->assertPermission('config/application/userbackend');
$backendName = $this->params->getRequired('backend');
$form = new UserBackendConfigForm();
$form->setRedirectUrl('config/userbackend');
$form->setTitle(sprintf($this->translate('Edit User Backend %s'), $backendName));
$form->setIniConfig(Config::app('authentication'));
$form->setOnSuccess(function (UserBackendConfigForm $form) use ($backendName) {
try {
$form->edit($backendName, array_map(
function ($v) {
return $v !== '' ? $v : null;
},
$form->getValues()
));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(sprintf(t('User backend "%s" successfully updated'), $backendName));
return true;
}
return false;
});
try {
$form->load($backendName);
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('User backend "%s" not found'), $backendName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Display a confirmation form to remove the backend identified by the 'backend' parameter
*/
public function removeuserbackendAction()
{
$this->assertPermission('config/application/userbackend');
$backendName = $this->params->getRequired('backend');
$backendForm = new UserBackendConfigForm();
$backendForm->setIniConfig(Config::app('authentication'));
$form = new ConfirmRemovalForm();
$form->setRedirectUrl('config/userbackend');
$form->setTitle(sprintf($this->translate('Remove User Backend %s'), $backendName));
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($backendName, $backendForm) {
try {
$backendForm->delete($backendName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($backendForm->save()) {
Notification::success(sprintf(t('User backend "%s" successfully removed'), $backendName));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
@ -215,8 +331,9 @@ class ConfigController extends ActionController
*/
public function resourceAction()
{
$this->assertPermission('config/application/resources');
$this->view->resources = Config::app('resources', true)->keys();
$this->view->tabs->activate('resources');
$this->createApplicationTabs()->activate('resource');
}
/**
@ -224,7 +341,10 @@ class ConfigController extends ActionController
*/
public function createresourceAction()
{
$this->assertPermission('config/application/resources');
$form = new ResourceConfigForm();
$form->setTitle($this->translate('Create A New Resource'));
$form->addDescription($this->translate('Resources are entities that provide data to Icinga Web 2.'));
$form->setIniConfig(Config::app('resources'));
$form->setRedirectUrl('config/resource');
$form->handleRequest();
@ -238,7 +358,9 @@ class ConfigController extends ActionController
*/
public function editresourceAction()
{
$this->assertPermission('config/application/resources');
$form = new ResourceConfigForm();
$form->setTitle($this->translate('Edit Existing Resource'));
$form->setIniConfig(Config::app('resources'));
$form->setRedirectUrl('config/resource');
$form->handleRequest();
@ -252,6 +374,7 @@ class ConfigController extends ActionController
*/
public function removeresourceAction()
{
$this->assertPermission('config/application/resources');
$form = new ConfirmRemovalForm(array(
'onSuccess' => function ($form) {
$configForm = new ResourceConfigForm();
@ -262,7 +385,7 @@ class ConfigController extends ActionController
$configForm->remove($resource);
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
return false;
}
if ($configForm->save()) {
@ -272,6 +395,7 @@ class ConfigController extends ActionController
}
}
));
$form->setTitle($this->translate('Remove Existing Resource'));
$form->setRedirectUrl('config/resource');
$form->handleRequest();
@ -280,9 +404,9 @@ class ConfigController extends ActionController
$authConfig = Config::app('authentication');
foreach ($authConfig as $backendName => $config) {
if ($config->get('resource') === $resource) {
$form->addError(sprintf(
$form->addDescription(sprintf(
$this->translate(
'The resource "%s" is currently in use by the authentication backend "%s". ' .
'The resource "%s" is currently utilized for authentication by user backend "%s". ' .
'Removing the resource can result in noone being able to log in any longer.'
),
$resource,

View File

@ -1,15 +1,12 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Application\Logger;
use Icinga\Exception\ProgrammingError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Dashboard\DashletForm;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Request;
use Icinga\Web\Url;
use Icinga\Web\Widget\Dashboard;
use Icinga\Web\Widget\Tabextension\DashboardSettings;
@ -25,11 +22,11 @@ class DashboardController extends ActionController
* @var Dashboard;
*/
private $dashboard;
public function init()
{
$this->dashboard = new Dashboard();
$this->dashboard->setUser($this->getRequest()->getUser());
$this->dashboard->setUser($this->Auth()->getUser());
$this->dashboard->load();
}
@ -56,17 +53,19 @@ class DashboardController extends ActionController
$dashlet = new Dashboard\Dashlet($form->getValue('dashlet'), $form->getValue('url'), $pane);
$dashlet->setUserWidget();
$pane->addDashlet($dashlet);
$dashboardConfig = $dashboard->getConfig();
try {
$dashboard->write();
} catch (\Zend_Config_Exception $e) {
$dashboardConfig->saveIni();
} catch (Exception $e) {
$action->view->error = $e;
$action->view->config = $dashboard->createWriter();
$action->view->config = $dashboardConfig;
$action->render('error');
return false;
}
Notification::success(t('Dashlet created'));
return true;
});
$form->setTitle($this->translate('Add Dashlet To Dashboard'));
$form->setRedirectUrl('dashboard');
$form->handleRequest();
$this->view->form = $form;
@ -78,7 +77,7 @@ class DashboardController extends ActionController
$dashboard = $this->dashboard;
$form = new DashletForm();
$form->setDashboard($dashboard);
$form->setSubmitLabel(t('Update Dashlet'));
$form->setSubmitLabel($this->translate('Update Dashlet'));
if (! $this->_request->getParam('pane')) {
throw new Zend_Controller_Action_Exception(
'Missing parameter "pane"',
@ -117,17 +116,19 @@ class DashboardController extends ActionController
$oldPane = $dashboard->getPane($form->getValue('org_pane'));
$oldPane->removeDashlet($dashlet->getTitle());
}
$dashboardConfig = $dashboard->getConfig();
try {
$dashboard->write();
} catch (\Zend_Config_Exception $e) {
$dashboardConfig->saveIni();
} catch (Exception $e) {
$action->view->error = $e;
$action->view->config = $dashboard->createWriter();
$action->view->config = $dashboardConfig;
$action->render('error');
return false;
}
Notification::success(t('Dashlet updated'));
return true;
});
$form->setTitle($this->translate('Edit Dashlet'));
$form->setRedirectUrl('dashboard/settings');
$form->handleRequest();
$pane = $dashboard->getPane($this->getParam('pane'));
@ -158,15 +159,16 @@ class DashboardController extends ActionController
$dashlet = $this->_request->getParam('dashlet');
$action = $this;
$form->setOnSuccess(function (Form $form) use ($dashboard, $dashlet, $pane, $action) {
$pane = $dashboard->getPane($pane);
$pane->removeDashlet($dashlet);
$dashboardConfig = $dashboard->getConfig();
try {
$pane = $dashboard->getPane($pane);
$pane->removeDashlet($dashlet);
$dashboard->write();
$dashboardConfig->saveIni();
Notification::success(t('Dashlet has been removed from') . ' ' . $pane->getTitle());
return true;
} catch (\Zend_Config_Exception $e) {
} catch (Exception $e) {
$action->view->error = $e;
$action->view->config = $dashboard->createWriter();
$action->view->config = $dashboardConfig;
$action->render('error');
return false;
} catch (ProgrammingError $e) {
@ -175,6 +177,7 @@ class DashboardController extends ActionController
}
return false;
});
$form->setTitle($this->translate('Remove Dashlet From Dashboard'));
$form->setRedirectUrl('dashboard/settings');
$form->handleRequest();
$this->view->pane = $pane;
@ -196,15 +199,16 @@ class DashboardController extends ActionController
$pane = $this->_request->getParam('pane');
$action = $this;
$form->setOnSuccess(function (Form $form) use ($dashboard, $pane, $action) {
$pane = $dashboard->getPane($pane);
$dashboard->removePane($pane->getTitle());
$dashboardConfig = $dashboard->getConfig();
try {
$pane = $dashboard->getPane($pane);
$dashboard->removePane($pane->getTitle());
$dashboard->write();
$dashboardConfig->saveIni();
Notification::success(t('Dashboard has been removed') . ': ' . $pane->getTitle());
return true;
} catch (\Zend_Config_Exception $e) {
} catch (Exception $e) {
$action->view->error = $e;
$action->view->config = $dashboard->createWriter();
$action->view->config = $dashboardConfig;
$action->render('error');
return false;
} catch (ProgrammingError $e) {
@ -213,6 +217,7 @@ class DashboardController extends ActionController
}
return false;
});
$form->setTitle($this->translate('Remove Dashboard'));
$form->setRedirectUrl('dashboard/settings');
$form->handleRequest();
$this->view->pane = $pane;
@ -241,14 +246,15 @@ class DashboardController extends ActionController
$this->view->title = $this->dashboard->getActivePane()->getTitle() . ' :: Dashboard';
if ($this->hasParam('remove')) {
$this->dashboard->getActivePane()->removeDashlet($this->getParam('remove'));
$this->dashboard->write();
$this->dashboard->getConfig()->saveIni();
$this->redirectNow(URL::fromRequest()->remove('remove'));
}
$this->view->tabs->add(
'Add',
array(
'title' => '+',
'url' => Url::fromPath('dashboard/new-dashlet')
'label' => '+',
'title' => 'Add a dashlet to an existing or new dashboard',
'url' => Url::fromPath('dashboard/new-dashlet')
)
);
$this->view->dashboard = $this->dashboard;

View File

@ -1,12 +1,13 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
// namespace Icinga\Application\Controllers;
use Icinga\Application\Logger;
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Exception\Http\HttpMethodNotAllowedException;
use Icinga\Exception\Http\HttpNotFoundException;
use Icinga\Exception\MissingParameterException;
use Icinga\Security\SecurityException;
use Icinga\Web\Controller\ActionController;
/**
* Application wide controller for displaying exceptions
@ -36,7 +37,7 @@ class ErrorController extends ActionController
$path = array_shift($path);
$this->getResponse()->setHttpResponseCode(404);
$this->view->message = $this->translate('Page not found.');
if ($modules->hasInstalled($path) && ! $modules->hasEnabled($path)) {
if ($this->Auth()->isAuthenticated() && $modules->hasInstalled($path) && ! $modules->hasEnabled($path)) {
$this->view->message .= ' ' . sprintf(
$this->translate('Enabling the "%s" module might help!'),
$path
@ -45,11 +46,30 @@ class ErrorController extends ActionController
break;
default:
$title = preg_replace('/\r?\n.*$/s', '', $exception->getMessage());
$this->getResponse()->setHttpResponseCode(500);
$this->view->title = 'Server error: ' . $title;
switch (true) {
case $exception instanceof HttpMethodNotAllowedException:
$this->getResponse()->setHttpResponseCode(405);
$this->getResponse()->setHeader('Allow', $exception->getAllowedMethods());
break;
case $exception instanceof HttpNotFoundException:
$this->getResponse()->setHttpResponseCode(404);
break;
case $exception instanceof MissingParameterException:
$this->getResponse()->setHttpResponseCode(400);
$this->getResponse()->setHeader(
'X-Status-Reason',
'Missing parameter ' . $exception->getParameter()
);
break;
case $exception instanceof SecurityException:
$this->getResponse()->setHttpResponseCode(403);
break;
default:
$this->getResponse()->setHttpResponseCode(500);
break;
}
$this->view->message = $exception->getMessage();
if ($this->getInvokeArg('displayExceptions') == true) {
if ($this->getInvokeArg('displayExceptions')) {
$this->view->stackTrace = $exception->getTraceAsString();
}
break;

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
use Icinga\Filter\Filter;

View File

@ -0,0 +1,369 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Data\Reducible;
use Icinga\Data\Filter\Filter;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\UserGroup\AddMemberForm;
use Icinga\Forms\Config\UserGroup\UserGroupForm;
use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget;
class GroupController extends AuthBackendController
{
/**
* List all user groups of a single backend
*/
public function listAction()
{
$this->assertPermission('config/authentication/groups/show');
$this->createListTabs()->activate('group/list');
$backendNames = array_map(
function ($b) { return $b->getName(); },
$this->loadUserGroupBackends('Icinga\Data\Selectable')
);
if (empty($backendNames)) {
return;
}
$this->view->backendSelection = new Form();
$this->view->backendSelection->setAttrib('class', 'backend-selection');
$this->view->backendSelection->setUidDisabled();
$this->view->backendSelection->setMethod('GET');
$this->view->backendSelection->setTokenDisabled();
$this->view->backendSelection->addElement(
'select',
'backend',
array(
'autosubmit' => true,
'label' => $this->translate('Usergroup Backend'),
'multiOptions' => array_combine($backendNames, $backendNames),
'value' => $this->params->get('backend')
)
);
$backend = $this->getUserGroupBackend($this->params->get('backend'));
if ($backend === null) {
$this->view->backend = null;
return;
}
$query = $backend->select(array('group_name'));
$filterEditor = Widget::create('filterEditor')
->setQuery($query)
->setSearchColumns(array('group', 'user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend')
->ignoreParams('page')
->handleRequest($this->getRequest());
$query->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->view->groups = $query;
$this->view->backend = $backend;
$this->setupPaginationControl($query);
$this->setupLimitControl();
$this->setupSortControl(
array(
'group_name' => $this->translate('Group'),
'created_at' => $this->translate('Created at'),
'last_modified' => $this->translate('Last modified')
),
$query
);
}
/**
* Show a group
*/
public function showAction()
{
$this->assertPermission('config/authentication/groups/show');
$groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'));
$group = $backend->select(array(
'group_name',
'created_at',
'last_modified'
))->where('group_name', $groupName)->fetchRow();
if ($group === false) {
$this->httpNotFound(sprintf($this->translate('Group "%s" not found'), $groupName));
}
$members = $backend
->select()
->from('group_membership', array('user_name'))
->where('group_name', $groupName);
$filterEditor = Widget::create('filterEditor')
->setQuery($members)
->setSearchColumns(array('user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'group')
->ignoreParams('page')
->handleRequest($this->getRequest());
$members->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->setupPaginationControl($members);
$this->setupLimitControl();
$this->setupSortControl(
array(
'user_name' => $this->translate('Username'),
'created_at' => $this->translate('Created at'),
'last_modified' => $this->translate('Last modified')
),
$members
);
$this->view->group = $group;
$this->view->backend = $backend;
$this->view->members = $members;
$this->createShowTabs($backend->getName(), $groupName)->activate('group/show');
if ($this->hasPermission('config/authentication/groups/edit') && $backend instanceof Reducible) {
$removeForm = new Form();
$removeForm->setUidDisabled();
$removeForm->setAction(
Url::fromPath('group/removemember', array('backend' => $backend->getName(), 'group' => $groupName))
);
$removeForm->addElement('hidden', 'user_name', array(
'isArray' => true,
'decorators' => array('ViewHelper')
));
$removeForm->addElement('hidden', 'redirect', array(
'value' => Url::fromPath('group/show', array(
'backend' => $backend->getName(),
'group' => $groupName
)),
'decorators' => array('ViewHelper')
));
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),
'title' => $this->translate('Remove this member')
));
$this->view->removeForm = $removeForm;
}
}
/**
* Add a group
*/
public function addAction()
{
$this->assertPermission('config/authentication/groups/add');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible');
$form = new UserGroupForm();
$form->setRedirectUrl(Url::fromPath('group/list', array('backend' => $backend->getName())));
$form->setRepository($backend);
$form->add()->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Edit a group
*/
public function editAction()
{
$this->assertPermission('config/authentication/groups/edit');
$groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Updatable');
$form = new UserGroupForm();
$form->setRedirectUrl(
Url::fromPath('group/show', array('backend' => $backend->getName(), 'group' => $groupName))
);
$form->setRepository($backend);
try {
$form->edit($groupName)->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Group "%s" not found'), $groupName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Remove a group
*/
public function removeAction()
{
$this->assertPermission('config/authentication/groups/remove');
$groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible');
$form = new UserGroupForm();
$form->setRedirectUrl(Url::fromPath('group/list', array('backend' => $backend->getName())));
$form->setRepository($backend);
try {
$form->remove($groupName)->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Group "%s" not found'), $groupName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Add a group member
*/
public function addmemberAction()
{
$this->assertPermission('config/authentication/groups/edit');
$groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible');
$form = new AddMemberForm();
$form->setDataSource($this->fetchUsers())
->setBackend($backend)
->setGroupName($groupName)
->setRedirectUrl(
Url::fromPath('group/show', array('backend' => $backend->getName(), 'group' => $groupName))
)
->setUidDisabled();
try {
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Group "%s" not found'), $groupName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Remove a group member
*/
public function removememberAction()
{
$this->assertPermission('config/authentication/groups/edit');
$this->assertHttpMethod('POST');
$groupName = $this->params->getRequired('group');
$backend = $this->getUserGroupBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible');
$form = new Form(array(
'onSuccess' => function ($form) use ($groupName, $backend) {
foreach ($form->getValue('user_name') as $userName) {
try {
$backend->delete(
'group_membership',
Filter::matchAll(
Filter::where('group_name', $groupName),
Filter::where('user_name', $userName)
)
);
Notification::success(sprintf(
t('User "%s" has been removed from group "%s"'),
$userName,
$groupName
));
} catch (NotFoundError $e) {
throw $e;
} catch (Exception $e) {
Notification::error($e->getMessage());
}
}
$redirect = $form->getValue('redirect');
if (! empty($redirect)) {
$form->setRedirectUrl(htmlspecialchars_decode($redirect));
}
return true;
}
));
$form->setUidDisabled();
$form->setSubmitLabel('btn_submit'); // Required to ensure that isSubmitted() is called
$form->addElement('hidden', 'user_name', array('required' => true, 'isArray' => true));
$form->addElement('hidden', 'redirect');
try {
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Group "%s" not found'), $groupName));
}
}
/**
* Fetch and return all users from all user backends
*
* @return ArrayDatasource
*/
protected function fetchUsers()
{
$users = array();
foreach ($this->loadUserBackends('Icinga\Data\Selectable') as $backend) {
try {
foreach ($backend->select(array('user_name')) as $row) {
$users[] = $row;
}
} catch (Exception $e) {
Logger::error($e);
Notification::warning(sprintf(
$this->translate('Failed to fetch any users from backend %s. Please check your log'),
$backend->getName()
));
}
}
return new ArrayDatasource($users);
}
/**
* Create the tabs to display when showing a group
*
* @param string $backendName
* @param string $groupName
*/
protected function createShowTabs($backendName, $groupName)
{
$tabs = $this->getTabs();
$tabs->add(
'group/show',
array(
'title' => sprintf($this->translate('Show group %s'), $groupName),
'label' => $this->translate('Group'),
'icon' => 'users',
'url' => Url::fromPath('group/show', array('backend' => $backendName, 'group' => $groupName))
)
);
return $tabs;
}
/**
* Create the tabs to display when listing groups
*/
protected function createListTabs()
{
$tabs = $this->getTabs();
$tabs->add(
'group/list',
array(
'title' => $this->translate('List groups of user group backends'),
'label' => $this->translate('Usergroups'),
'icon' => 'users',
'url' => 'group/list'
)
);
return $tabs;
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers;

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\MenuRenderer;
use Icinga\Web\Controller\ActionController;
@ -25,24 +24,4 @@ class LayoutController extends ActionController
$menu = new MenuRenderer(Menu::load(), $url->getRelativeUrl());
$this->view->menuRenderer = $menu->useCustomRenderer();
}
/**
* Render the top bar
*/
public function topbarAction()
{
$topbarHtmlParts = array();
/** @var Hook\TopBarHook $hook */
$hook = null;
foreach (Hook::all('TopBar') as $hook) {
$topbarHtmlParts[] = $hook->getHtml($this->getRequest());
}
$this->view->topbarHtmlParts = $topbarHtmlParts;
$this->renderScript('parts/topbar.phtml');
}
}

View File

@ -1,13 +1,14 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Module\Monitoring\Controller;
use Icinga\Web\Url;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Protocol\File\FileReader;
use \Zend_Controller_Action_Exception as ActionError;
use Icinga\Web\Controller;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
/**
* Class ListController
@ -24,12 +25,12 @@ class ListController extends Controller
protected function addTitleTab($action)
{
$this->getTabs()->add($action, array(
'title' => ucfirst($action),
'label' => ucfirst($action),
'url' => Url::fromPath(
'list/'
. str_replace(' ', '', $action)
)
))->activate($action);
))->extend(new OutputFormat())->extend(new DashboardAction())->activate($action);
}
/**
@ -38,20 +39,20 @@ class ListController extends Controller
public function applicationlogAction()
{
if (! Logger::writesToFile()) {
throw new ActionError('Site not found', 404);
$this->httpNotFound('Page not found');
}
$this->addTitleTab('application log');
$pattern = '/^(?<datetime>[0-9]{4}(-[0-9]{2}){2}' // date
. 'T[0-9]{2}(:[0-9]{2}){2}([\\+\\-][0-9]{2}:[0-9]{2})?)' // time
. ' - (?<loglevel>[A-Za-z]+)' // loglevel
. ' - (?<message>.*)$/'; // message
$loggerWriter = Logger::getInstance()->getWriter();
$resource = new FileReader(new ConfigObject(array(
'filename' => $loggerWriter->getPath(),
'fields' => $pattern
'filename' => Config::app()->get('logging', 'file'),
'fields' => '/(?<!.)(?<datetime>[0-9]{4}(?:-[0-9]{2}){2}' // date
. 'T[0-9]{2}(?::[0-9]{2}){2}(?:[\+\-][0-9]{2}:[0-9]{2})?)' // time
. ' - (?<loglevel>[A-Za-z]+) - (?<message>.*)(?!.)/msS' // loglevel, message
)));
$this->view->logData = $resource->select()->order('DESC')->paginate();
$this->view->logData = $resource->select()->order('DESC');
$this->setupLimitControl();
$this->setupPaginationControl($this->view->logData);
}
}

View File

@ -1,13 +1,12 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\BasePreferenceController;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tab;
use Icinga\Application\Config;
use Icinga\Forms\PreferenceForm;
use Icinga\Exception\ConfigurationError;
use Icinga\Data\ConfigObject;
use Icinga\User\Preferences\PreferencesStore;
/**
@ -27,8 +26,9 @@ class PreferenceController extends BasePreferenceController
return array(
'preferences' => new Tab(
array(
'title' => t('Preferences'),
'url' => Url::fromPath('/preference')
'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'),
'label' => t('Preferences'),
'url' => Url::fromPath('/preference')
)
)
);
@ -39,15 +39,17 @@ class PreferenceController extends BasePreferenceController
*/
public function indexAction()
{
$storeConfig = Config::app()->getSection('preferences');
if ($storeConfig->isEmpty()) {
throw new ConfigurationError(t('You need to configure how to store preferences first.'));
}
$config = Config::app()->getSection('global');
$user = $this->getRequest()->getUser();
$form = new PreferenceForm();
$form->setPreferences($user->getPreferences());
$form->setStore(PreferencesStore::create($storeConfig, $user));
if ($config->get('config_backend', 'ini') !== 'none') {
$form->setStore(PreferencesStore::create(new ConfigObject(array(
'store' => $config->get('config_backend', 'ini'),
'resource' => $config->config_resource
)), $user));
}
$form->handleRequest();
$this->view->form = $form;

View File

@ -1,41 +1,30 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Application\Config;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Security\RoleForm;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Notification;
use Icinga\Web\Widget;
class RolesController extends ActionController
class RoleController extends AuthBackendController
{
public function init()
/**
* List roles
*/
public function listAction()
{
$this->view->tabs = Widget::create('tabs')->add('index', array(
'title' => $this->translate('Application'),
'url' => 'config'
))->add('authentication', array(
'title' => $this->translate('Authentication'),
'url' => 'config/authentication'
))->add('resources', array(
'title' => $this->translate('Resources'),
'url' => 'config/resource'
))->add('roles', array(
'title' => $this->translate('Roles'),
'url' => 'roles'
));
}
public function indexAction()
{
$this->view->tabs->activate('roles');
$this->assertPermission('config/authentication/roles/show');
$this->createListTabs()->activate('role/list');
$this->view->roles = Config::app('roles', true);
}
public function newAction()
/**
* Create a new role
*/
public function addAction()
{
$this->assertPermission('config/authentication/roles/add');
$role = new RoleForm(array(
'onSuccess' => function (RoleForm $role) {
$name = $role->getElement('name')->getValue();
@ -54,15 +43,23 @@ class RolesController extends ActionController
}
));
$role
->setTitle($this->translate('New Role'))
->setSubmitLabel($this->translate('Create Role'))
->setIniConfig(Config::app('roles', true))
->setRedirectUrl('roles')
->setRedirectUrl('role/list')
->handleRequest();
$this->view->form = $role;
$this->render('form');
}
public function updateAction()
/**
* Update a role
*
* @throws Zend_Controller_Action_Exception If the required parameter 'role' is missing or the role does not exist
*/
public function editAction()
{
$this->assertPermission('config/authentication/roles/edit');
$name = $this->_request->getParam('role');
if (empty($name)) {
throw new Zend_Controller_Action_Exception(
@ -71,6 +68,7 @@ class RolesController extends ActionController
);
}
$role = new RoleForm();
$role->setTitle(sprintf($this->translate('Update Role %s'), $name));
$role->setSubmitLabel($this->translate('Update Role'));
try {
$role
@ -99,14 +97,20 @@ class RolesController extends ActionController
}
return false;
})
->setRedirectUrl('roles')
->setRedirectUrl('role/list')
->handleRequest();
$this->view->name = $name;
$this->view->form = $role;
$this->render('form');
}
/**
* Remove a role
*
* @throws Zend_Controller_Action_Exception If the required parameter 'role' is missing or the role does not exist
*/
public function removeAction()
{
$this->assertPermission('config/authentication/roles/remove');
$name = $this->_request->getParam('role');
if (empty($name)) {
throw new Zend_Controller_Action_Exception(
@ -141,10 +145,32 @@ class RolesController extends ActionController
}
));
$confirmation
->setTitle(sprintf($this->translate('Remove Role %s'), $name))
->setSubmitLabel($this->translate('Remove Role'))
->setRedirectUrl('roles')
->setRedirectUrl('role/list')
->handleRequest();
$this->view->name = $name;
$this->view->form = $confirmation;
$this->render('form');
}
/**
* Create the tabs to display when listing roles
*/
protected function createListTabs()
{
$tabs = $this->getTabs();
$tabs->add(
'role/list',
array(
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
),
'label' => $this->translate('Roles'),
'url' => 'role/list',
'baseTarget' => '_main'
)
);
return $tabs;
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Widget;
@ -13,7 +12,9 @@ class SearchController extends ActionController
{
public function indexAction()
{
$this->view->dashboard = SearchDashboard::search($this->params->get('q'));
$searchDashboard = new SearchDashboard();
$searchDashboard->setUser($this->Auth()->getUser());
$this->view->dashboard = $searchDashboard->search($this->params->get('q'));
// NOTE: This renders the dashboard twice. Remove this once we can catch exceptions thrown in view scripts.
$this->view->dashboard->render();

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Icinga;

View File

@ -0,0 +1,328 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Application\Logger;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\User\CreateMembershipForm;
use Icinga\Forms\Config\User\UserForm;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\User;
use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget;
class UserController extends AuthBackendController
{
/**
* List all users of a single backend
*/
public function listAction()
{
$this->assertPermission('config/authentication/users/show');
$this->createListTabs()->activate('user/list');
$backendNames = array_map(
function ($b) { return $b->getName(); },
$this->loadUserBackends('Icinga\Data\Selectable')
);
if (empty($backendNames)) {
return;
}
$this->view->backendSelection = new Form();
$this->view->backendSelection->setAttrib('class', 'backend-selection');
$this->view->backendSelection->setUidDisabled();
$this->view->backendSelection->setMethod('GET');
$this->view->backendSelection->setTokenDisabled();
$this->view->backendSelection->addElement(
'select',
'backend',
array(
'autosubmit' => true,
'label' => $this->translate('Authentication Backend'),
'multiOptions' => array_combine($backendNames, $backendNames),
'value' => $this->params->get('backend')
)
);
$backend = $this->getUserBackend($this->params->get('backend'));
if ($backend === null) {
$this->view->backend = null;
return;
}
$query = $backend->select(array('user_name'));
$filterEditor = Widget::create('filterEditor')
->setQuery($query)
->setSearchColumns(array('user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend')
->ignoreParams('page')
->handleRequest($this->getRequest());
$query->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->view->users = $query;
$this->view->backend = $backend;
$this->setupPaginationControl($query);
$this->setupLimitControl();
$this->setupSortControl(
array(
'user_name' => $this->translate('Username'),
'is_active' => $this->translate('Active'),
'created_at' => $this->translate('Created at'),
'last_modified' => $this->translate('Last modified')
),
$query
);
}
/**
* Show a user
*/
public function showAction()
{
$this->assertPermission('config/authentication/users/show');
$userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend'));
$user = $backend->select(array(
'user_name',
'is_active',
'created_at',
'last_modified'
))->where('user_name', $userName)->fetchRow();
if ($user === false) {
$this->httpNotFound(sprintf($this->translate('User "%s" not found'), $userName));
}
$memberships = $this->loadMemberships(new User($userName))->select();
$filterEditor = Widget::create('filterEditor')
->setQuery($memberships)
->setSearchColumns(array('group_name'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'user')
->ignoreParams('page')
->handleRequest($this->getRequest());
$memberships->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->setupPaginationControl($memberships);
$this->setupLimitControl();
$this->setupSortControl(
array(
'group_name' => $this->translate('Group')
),
$memberships
);
if ($this->hasPermission('config/authentication/groups/edit')) {
$extensibleBackends = $this->loadUserGroupBackends('Icinga\Data\Extensible');
$this->view->showCreateMembershipLink = ! empty($extensibleBackends);
} else {
$this->view->showCreateMembershipLink = false;
}
$this->view->user = $user;
$this->view->backend = $backend;
$this->view->memberships = $memberships;
$this->createShowTabs($backend->getName(), $userName)->activate('user/show');
if ($this->hasPermission('config/authentication/groups/edit')) {
$removeForm = new Form();
$removeForm->setUidDisabled();
$removeForm->addElement('hidden', 'user_name', array(
'isArray' => true,
'value' => $userName,
'decorators' => array('ViewHelper')
));
$removeForm->addElement('hidden', 'redirect', array(
'value' => Url::fromPath('user/show', array(
'backend' => $backend->getName(),
'user' => $userName
)),
'decorators' => array('ViewHelper')
));
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),
'title' => $this->translate('Cancel this membership')
));
$this->view->removeForm = $removeForm;
}
}
/**
* Add a user
*/
public function addAction()
{
$this->assertPermission('config/authentication/users/add');
$backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Extensible');
$form = new UserForm();
$form->setRedirectUrl(Url::fromPath('user/list', array('backend' => $backend->getName())));
$form->setRepository($backend);
$form->add()->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Edit a user
*/
public function editAction()
{
$this->assertPermission('config/authentication/users/edit');
$userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Updatable');
$form = new UserForm();
$form->setRedirectUrl(Url::fromPath('user/show', array('backend' => $backend->getName(), 'user' => $userName)));
$form->setRepository($backend);
try {
$form->edit($userName)->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('User "%s" not found'), $userName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Remove a user
*/
public function removeAction()
{
$this->assertPermission('config/authentication/users/remove');
$userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend'), 'Icinga\Data\Reducible');
$form = new UserForm();
$form->setRedirectUrl(Url::fromPath('user/list', array('backend' => $backend->getName())));
$form->setRepository($backend);
try {
$form->remove($userName)->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('User "%s" not found'), $userName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Create a membership for a user
*/
public function createmembershipAction()
{
$this->assertPermission('config/authentication/groups/edit');
$userName = $this->params->getRequired('user');
$backend = $this->getUserBackend($this->params->getRequired('backend'));
if ($backend->select()->where('user_name', $userName)->count() === 0) {
$this->httpNotFound(sprintf($this->translate('User "%s" not found'), $userName));
}
$backends = $this->loadUserGroupBackends('Icinga\Data\Extensible');
if (empty($backends)) {
throw new ConfigurationError($this->translate(
'You\'ll need to configure at least one user group backend first that allows to create new memberships'
));
}
$form = new CreateMembershipForm();
$form->setBackends($backends)
->setUsername($userName)
->setRedirectUrl(Url::fromPath('user/show', array('backend' => $backend->getName(), 'user' => $userName)))
->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Fetch and return the given user's groups from all user group backends
*
* @param User $user
*
* @return ArrayDatasource
*/
protected function loadMemberships(User $user)
{
$groups = $alreadySeen = array();
foreach ($this->loadUserGroupBackends() as $backend) {
try {
foreach ($backend->getMemberships($user) as $groupName) {
if (array_key_exists($groupName, $alreadySeen)) {
continue; // Ignore duplicate memberships
}
$alreadySeen[$groupName] = null;
$groups[] = (object) array(
'group_name' => $groupName,
'backend' => $backend
);
}
} catch (Exception $e) {
Logger::error($e);
Notification::warning(sprintf(
$this->translate('Failed to fetch memberships from backend %s. Please check your log'),
$backend->getName()
));
}
}
return new ArrayDatasource($groups);
}
/**
* Create the tabs to display when showing a user
*
* @param string $backendName
* @param string $userName
*/
protected function createShowTabs($backendName, $userName)
{
$tabs = $this->getTabs();
$tabs->add(
'user/show',
array(
'title' => sprintf($this->translate('Show user %s'), $userName),
'label' => $this->translate('User'),
'icon' => 'user',
'url' => Url::fromPath('user/show', array('backend' => $backendName, 'user' => $userName))
)
);
return $tabs;
}
/**
* Create the tabs to display when listing users
*/
protected function createListTabs()
{
$tabs = $this->getTabs();
$tabs->add(
'user/list',
array(
'title' => $this->translate('List users of authentication backends'),
'label' => $this->translate('Users'),
'icon' => 'user',
'url' => 'user/list'
)
);
return $tabs;
}
}

View File

@ -0,0 +1,167 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Application\Config;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Config\UserGroup\UserGroupBackendForm;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
use Icinga\Web\Url;
/**
* Controller to configure user group backends
*/
class UsergroupbackendController extends Controller
{
/**
* Initialize this controller
*/
public function init()
{
$this->assertPermission('config/application/usergroupbackend');
}
/**
* Redirect to this controller's list action
*/
public function indexAction()
{
$this->redirectNow('usergroupbackend/list');
}
/**
* Show a list of all user group backends
*/
public function listAction()
{
$this->view->backendNames = Config::app('groups')->keys();
$this->createListTabs()->activate('usergroupbackend');
}
/**
* Create a new user group backend
*/
public function createAction()
{
$form = new UserGroupBackendForm();
$form->setRedirectUrl('usergroupbackend/list');
$form->setTitle($this->translate('Create New User Group Backend'));
$form->addDescription($this->translate('Create a new backend to associate users and groups with.'));
$form->setIniConfig(Config::app('groups'));
$form->setOnSuccess(function (UserGroupBackendForm $form) {
try {
$form->add(array_filter($form->getValues()));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(t('User group backend successfully created'));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Edit an user group backend
*/
public function editAction()
{
$backendName = $this->params->getRequired('backend');
$form = new UserGroupBackendForm();
$form->setRedirectUrl('usergroupbackend/list');
$form->setTitle(sprintf($this->translate('Edit User Group Backend %s'), $backendName));
$form->setIniConfig(Config::app('groups'));
$form->setOnSuccess(function (UserGroupBackendForm $form) use ($backendName) {
try {
$form->edit($backendName, array_map(
function ($v) {
return $v !== '' ? $v : null;
},
$form->getValues()
));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(sprintf(t('User group backend "%s" successfully updated'), $backendName));
return true;
}
return false;
});
try {
$form->load($backendName);
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('User group backend "%s" not found'), $backendName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Remove a user group backend
*/
public function removeAction()
{
$backendName = $this->params->getRequired('backend');
$backendForm = new UserGroupBackendForm();
$backendForm->setIniConfig(Config::app('groups'));
$form = new ConfirmRemovalForm();
$form->setRedirectUrl('usergroupbackend/list');
$form->setTitle(sprintf($this->translate('Remove User Group Backend %s'), $backendName));
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($backendName, $backendForm) {
try {
$backendForm->delete($backendName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($backendForm->save()) {
Notification::success(sprintf(t('User group backend "%s" successfully removed'), $backendName));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Create the tabs for the application configuration
*/
protected function createListTabs()
{
$tabs = $this->getTabs();
$tabs->add('userbackend', array(
'title' => $this->translate('Configure how users authenticate with and log into Icinga Web 2'),
'label' => $this->translate('User Backends'),
'url' => 'config/userbackend'
));
$tabs->add('usergroupbackend', array(
'title' => $this->translate('Configure how users are associated with groups by Icinga Web 2'),
'label' => $this->translate('User Group Backends'),
'url' => 'usergroupbackend/list'
));
return $tabs;
}
}

View File

@ -37,3 +37,21 @@ Font license info
Homepage: http://www.entypo.com
## Fontelico
Copyright (C) 2012 by Fontello project
Author: Crowdsourced, for Fontello project
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://fontello.com
## Typicons
(c) Stephen Hutchings 2012
Author: Stephen Hutchings
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://typicons.com/

View File

@ -11,7 +11,7 @@ webfont pack. Details available in LICENSE.txt file.
- If your project is open-source, usually, it will be ok to make LICENSE.txt
file publically available in your repository.
- Fonts, used in Fontello, don't require to make clickable links on your site.
- Fonts, used in Fontello, don't require a clickable link on your site.
But any kind of additional authors crediting is welcome.
================================================================================
@ -29,8 +29,8 @@ Comments on archive content
- LICENSE.txt - license info about source fonts, used to build your one.
- config.json - keeps your settings. You can import it back to fontello anytime,
to continue your work
- config.json - keeps your settings. You can import it back into fontello
anytime, to continue your work
Why so many CSS files ?
@ -38,17 +38,17 @@ Why so many CSS files ?
Because we like to fit all your needs :)
- basic file, <your_font_name>.css - is usually enougth, in contains @font-face
and character codes definition
- basic file, <your_font_name>.css - is usually enough, it contains @font-face
and character code definitions
- *-ie7.css - if you need IE7 support, but still don't wish to put char codes
directly into html
- *-codes.css and *-ie7-codes.css - if you like to use your own @font-face
rules, but still wish to benefit of css generation. That can be very
convenient for automated assets build systems. When you need to update font -
no needs to manually edit files, just override old version with archive
content. See fontello source codes for example.
rules, but still wish to benefit from css generation. That can be very
convenient for automated asset build systems. When you need to update font -
no need to manually edit files, just override old version with archive
content. See fontello source code for examples.
- *-embedded.css - basic css file, but with embedded WOFF font, to avoid
CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain.
@ -63,11 +63,11 @@ Because we like to fit all your needs :)
Attention for server setup
--------------------------
You MUST setup server to reply with proper `mime-types` for font files. In other
case, some browsers will fail to show fonts.
You MUST setup server to reply with proper `mime-types` for font files -
otherwise some browsers will fail to show fonts.
Usually, `apache` already has necessary settings, but `nginx` and other
webservers should be tuned. Here is list of mime types for our file extentions:
webservers should be tuned. Here is list of mime types for our file extensions:
- `application/vnd.ms-fontobject` - eot
- `application/x-font-woff` - woff

View File

@ -6,6 +6,12 @@
"units_per_em": 1000,
"ascent": 850,
"glyphs": [
{
"uid": "9bc2902722abb366a213a052ade360bc",
"css": "spin6",
"code": 59508,
"src": "fontelico"
},
{
"uid": "9dd9e835aebe1060ba7190ad2b2ed951",
"css": "search",
@ -666,6 +672,30 @@
"code": 59492,
"src": "entypo"
},
{
"uid": "c16a63e911bc47b46dc2a7129d2f0c46",
"css": "down-small",
"code": 59509,
"src": "typicons"
},
{
"uid": "58b78b6ca784d5c3db5beefcd9e18061",
"css": "left-small",
"code": 59510,
"src": "typicons"
},
{
"uid": "877a233d7fdca8a1d82615b96ed0d7a2",
"css": "right-small",
"code": 59511,
"src": "typicons"
},
{
"uid": "62bc6fe2a82e4864e2b94d4c0985ee0c",
"css": "up-small",
"code": 59512,
"src": "typicons"
},
{
"uid": "b90d80c250a9bbdd6cd3fe00e6351710",
"css": "ok",

View File

@ -114,4 +114,9 @@
.icon-chart-area:before { content: '\e870'; } /* '' */
.icon-chart-bar:before { content: '\e871'; } /* '' */
.icon-beaker:before { content: '\e872'; } /* '' */
.icon-magic:before { content: '\e873'; } /* '' */
.icon-magic:before { content: '\e873'; } /* '' */
.icon-spin6:before { content: '\e874'; } /* '' */
.icon-down-small:before { content: '\e875'; } /* '' */
.icon-left-small:before { content: '\e876'; } /* '' */
.icon-right-small:before { content: '\e877'; } /* '' */
.icon-up-small:before { content: '\e878'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -114,4 +114,9 @@
.icon-chart-area { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe870;&nbsp;'); }
.icon-chart-bar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe871;&nbsp;'); }
.icon-beaker { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe872;&nbsp;'); }
.icon-magic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe873;&nbsp;'); }
.icon-magic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe873;&nbsp;'); }
.icon-spin6 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe874;&nbsp;'); }
.icon-down-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe875;&nbsp;'); }
.icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe876;&nbsp;'); }
.icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe877;&nbsp;'); }
.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe878;&nbsp;'); }

View File

@ -125,4 +125,9 @@
.icon-chart-area { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe870;&nbsp;'); }
.icon-chart-bar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe871;&nbsp;'); }
.icon-beaker { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe872;&nbsp;'); }
.icon-magic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe873;&nbsp;'); }
.icon-magic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe873;&nbsp;'); }
.icon-spin6 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe874;&nbsp;'); }
.icon-down-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe875;&nbsp;'); }
.icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe876;&nbsp;'); }
.icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe877;&nbsp;'); }
.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe878;&nbsp;'); }

View File

@ -1,10 +1,10 @@
@font-face {
font-family: 'ifont';
src: url('../font/ifont.eot?81587324');
src: url('../font/ifont.eot?81587324#iefix') format('embedded-opentype'),
url('../font/ifont.woff?81587324') format('woff'),
url('../font/ifont.ttf?81587324') format('truetype'),
url('../font/ifont.svg?81587324#ifont') format('svg');
src: url('../font/ifont.eot?54745533');
src: url('../font/ifont.eot?54745533#iefix') format('embedded-opentype'),
url('../font/ifont.woff?54745533') format('woff'),
url('../font/ifont.ttf?54745533') format('truetype'),
url('../font/ifont.svg?54745533#ifont') format('svg');
font-weight: normal;
font-style: normal;
}
@ -14,7 +14,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'ifont';
src: url('../font/ifont.svg?81587324#ifont') format('svg');
src: url('../font/ifont.svg?54745533#ifont') format('svg');
}
}
*/
@ -35,7 +35,7 @@
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
@ -46,6 +46,10 @@
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
@ -165,4 +169,9 @@
.icon-chart-area:before { content: '\e870'; } /* '' */
.icon-chart-bar:before { content: '\e871'; } /* '' */
.icon-beaker:before { content: '\e872'; } /* '' */
.icon-magic:before { content: '\e873'; } /* '' */
.icon-magic:before { content: '\e873'; } /* '' */
.icon-spin6:before { content: '\e874'; } /* '' */
.icon-down-small:before { content: '\e875'; } /* '' */
.icon-left-small:before { content: '\e876'; } /* '' */
.icon-right-small:before { content: '\e877'; } /* '' */
.icon-up-small:before { content: '\e878'; } /* '' */

View File

@ -227,8 +227,54 @@ body {
.i-code {
display: none;
}
</style>
<link rel="stylesheet" href="css/ifont.css">
@font-face {
font-family: 'ifont';
src: url('./font/ifont.eot?11424534');
src: url('./font/ifont.eot?11424534#iefix') format('embedded-opentype'),
url('./font/ifont.woff?11424534') format('woff'),
url('./font/ifont.ttf?11424534') format('truetype'),
url('./font/ifont.svg?11424534#ifont') format('svg');
font-weight: normal;
font-style: normal;
}
.demo-icon
{
font-family: "ifont";
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* You can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
</style>
<link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><link rel="stylesheet" href="css/ifont-ie7.css"><![endif]-->
<script>
function toggleCodes(on) {
@ -255,178 +301,187 @@ body {
</div>
<div id="icons" class="container">
<div class="row">
<div title="Code: 0xe800" class="the-icons span3"><i class="icon-dashboard"></i> <span class="i-name">icon-dashboard</span><span class="i-code">0xe800</span></div>
<div title="Code: 0xe801" class="the-icons span3"><i class="icon-user"></i> <span class="i-name">icon-user</span><span class="i-code">0xe801</span></div>
<div title="Code: 0xe802" class="the-icons span3"><i class="icon-users"></i> <span class="i-name">icon-users</span><span class="i-code">0xe802</span></div>
<div title="Code: 0xe803" class="the-icons span3"><i class="icon-ok"></i> <span class="i-name">icon-ok</span><span class="i-code">0xe803</span></div>
<div title="Code: 0xe800" class="the-icons span3"><i class="demo-icon icon-dashboard">&#xe800;</i> <span class="i-name">icon-dashboard</span><span class="i-code">0xe800</span></div>
<div title="Code: 0xe801" class="the-icons span3"><i class="demo-icon icon-user">&#xe801;</i> <span class="i-name">icon-user</span><span class="i-code">0xe801</span></div>
<div title="Code: 0xe802" class="the-icons span3"><i class="demo-icon icon-users">&#xe802;</i> <span class="i-name">icon-users</span><span class="i-code">0xe802</span></div>
<div title="Code: 0xe803" class="the-icons span3"><i class="demo-icon icon-ok">&#xe803;</i> <span class="i-name">icon-ok</span><span class="i-code">0xe803</span></div>
</div>
<div class="row">
<div title="Code: 0xe804" class="the-icons span3"><i class="icon-cancel"></i> <span class="i-name">icon-cancel</span><span class="i-code">0xe804</span></div>
<div title="Code: 0xe805" class="the-icons span3"><i class="icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe805</span></div>
<div title="Code: 0xe806" class="the-icons span3"><i class="icon-minus"></i> <span class="i-name">icon-minus</span><span class="i-code">0xe806</span></div>
<div title="Code: 0xe807" class="the-icons span3"><i class="icon-folder-empty"></i> <span class="i-name">icon-folder-empty</span><span class="i-code">0xe807</span></div>
<div title="Code: 0xe804" class="the-icons span3"><i class="demo-icon icon-cancel">&#xe804;</i> <span class="i-name">icon-cancel</span><span class="i-code">0xe804</span></div>
<div title="Code: 0xe805" class="the-icons span3"><i class="demo-icon icon-plus">&#xe805;</i> <span class="i-name">icon-plus</span><span class="i-code">0xe805</span></div>
<div title="Code: 0xe806" class="the-icons span3"><i class="demo-icon icon-minus">&#xe806;</i> <span class="i-name">icon-minus</span><span class="i-code">0xe806</span></div>
<div title="Code: 0xe807" class="the-icons span3"><i class="demo-icon icon-folder-empty">&#xe807;</i> <span class="i-name">icon-folder-empty</span><span class="i-code">0xe807</span></div>
</div>
<div class="row">
<div title="Code: 0xe808" class="the-icons span3"><i class="icon-download"></i> <span class="i-name">icon-download</span><span class="i-code">0xe808</span></div>
<div title="Code: 0xe809" class="the-icons span3"><i class="icon-upload"></i> <span class="i-name">icon-upload</span><span class="i-code">0xe809</span></div>
<div title="Code: 0xe80a" class="the-icons span3"><i class="icon-git"></i> <span class="i-name">icon-git</span><span class="i-code">0xe80a</span></div>
<div title="Code: 0xe80b" class="the-icons span3"><i class="icon-cubes"></i> <span class="i-name">icon-cubes</span><span class="i-code">0xe80b</span></div>
<div title="Code: 0xe808" class="the-icons span3"><i class="demo-icon icon-download">&#xe808;</i> <span class="i-name">icon-download</span><span class="i-code">0xe808</span></div>
<div title="Code: 0xe809" class="the-icons span3"><i class="demo-icon icon-upload">&#xe809;</i> <span class="i-name">icon-upload</span><span class="i-code">0xe809</span></div>
<div title="Code: 0xe80a" class="the-icons span3"><i class="demo-icon icon-git">&#xe80a;</i> <span class="i-name">icon-git</span><span class="i-code">0xe80a</span></div>
<div title="Code: 0xe80b" class="the-icons span3"><i class="demo-icon icon-cubes">&#xe80b;</i> <span class="i-name">icon-cubes</span><span class="i-code">0xe80b</span></div>
</div>
<div class="row">
<div title="Code: 0xe80c" class="the-icons span3"><i class="icon-database"></i> <span class="i-name">icon-database</span><span class="i-code">0xe80c</span></div>
<div title="Code: 0xe80d" class="the-icons span3"><i class="icon-gauge"></i> <span class="i-name">icon-gauge</span><span class="i-code">0xe80d</span></div>
<div title="Code: 0xe80e" class="the-icons span3"><i class="icon-sitemap"></i> <span class="i-name">icon-sitemap</span><span class="i-code">0xe80e</span></div>
<div title="Code: 0xe80f" class="the-icons span3"><i class="icon-sort-name-up"></i> <span class="i-name">icon-sort-name-up</span><span class="i-code">0xe80f</span></div>
<div title="Code: 0xe80c" class="the-icons span3"><i class="demo-icon icon-database">&#xe80c;</i> <span class="i-name">icon-database</span><span class="i-code">0xe80c</span></div>
<div title="Code: 0xe80d" class="the-icons span3"><i class="demo-icon icon-gauge">&#xe80d;</i> <span class="i-name">icon-gauge</span><span class="i-code">0xe80d</span></div>
<div title="Code: 0xe80e" class="the-icons span3"><i class="demo-icon icon-sitemap">&#xe80e;</i> <span class="i-name">icon-sitemap</span><span class="i-code">0xe80e</span></div>
<div title="Code: 0xe80f" class="the-icons span3"><i class="demo-icon icon-sort-name-up">&#xe80f;</i> <span class="i-name">icon-sort-name-up</span><span class="i-code">0xe80f</span></div>
</div>
<div class="row">
<div title="Code: 0xe810" class="the-icons span3"><i class="icon-sort-name-down"></i> <span class="i-name">icon-sort-name-down</span><span class="i-code">0xe810</span></div>
<div title="Code: 0xe811" class="the-icons span3"><i class="icon-megaphone"></i> <span class="i-name">icon-megaphone</span><span class="i-code">0xe811</span></div>
<div title="Code: 0xe812" class="the-icons span3"><i class="icon-bug"></i> <span class="i-name">icon-bug</span><span class="i-code">0xe812</span></div>
<div title="Code: 0xe813" class="the-icons span3"><i class="icon-tasks"></i> <span class="i-name">icon-tasks</span><span class="i-code">0xe813</span></div>
<div title="Code: 0xe810" class="the-icons span3"><i class="demo-icon icon-sort-name-down">&#xe810;</i> <span class="i-name">icon-sort-name-down</span><span class="i-code">0xe810</span></div>
<div title="Code: 0xe811" class="the-icons span3"><i class="demo-icon icon-megaphone">&#xe811;</i> <span class="i-name">icon-megaphone</span><span class="i-code">0xe811</span></div>
<div title="Code: 0xe812" class="the-icons span3"><i class="demo-icon icon-bug">&#xe812;</i> <span class="i-name">icon-bug</span><span class="i-code">0xe812</span></div>
<div title="Code: 0xe813" class="the-icons span3"><i class="demo-icon icon-tasks">&#xe813;</i> <span class="i-name">icon-tasks</span><span class="i-code">0xe813</span></div>
</div>
<div class="row">
<div title="Code: 0xe814" class="the-icons span3"><i class="icon-filter"></i> <span class="i-name">icon-filter</span><span class="i-code">0xe814</span></div>
<div title="Code: 0xe815" class="the-icons span3"><i class="icon-off"></i> <span class="i-name">icon-off</span><span class="i-code">0xe815</span></div>
<div title="Code: 0xe816" class="the-icons span3"><i class="icon-book"></i> <span class="i-name">icon-book</span><span class="i-code">0xe816</span></div>
<div title="Code: 0xe817" class="the-icons span3"><i class="icon-paste"></i> <span class="i-name">icon-paste</span><span class="i-code">0xe817</span></div>
<div title="Code: 0xe814" class="the-icons span3"><i class="demo-icon icon-filter">&#xe814;</i> <span class="i-name">icon-filter</span><span class="i-code">0xe814</span></div>
<div title="Code: 0xe815" class="the-icons span3"><i class="demo-icon icon-off">&#xe815;</i> <span class="i-name">icon-off</span><span class="i-code">0xe815</span></div>
<div title="Code: 0xe816" class="the-icons span3"><i class="demo-icon icon-book">&#xe816;</i> <span class="i-name">icon-book</span><span class="i-code">0xe816</span></div>
<div title="Code: 0xe817" class="the-icons span3"><i class="demo-icon icon-paste">&#xe817;</i> <span class="i-name">icon-paste</span><span class="i-code">0xe817</span></div>
</div>
<div class="row">
<div title="Code: 0xe818" class="the-icons span3"><i class="icon-scissors"></i> <span class="i-name">icon-scissors</span><span class="i-code">0xe818</span></div>
<div title="Code: 0xe819" class="the-icons span3"><i class="icon-globe"></i> <span class="i-name">icon-globe</span><span class="i-code">0xe819</span></div>
<div title="Code: 0xe81a" class="the-icons span3"><i class="icon-cloud"></i> <span class="i-name">icon-cloud</span><span class="i-code">0xe81a</span></div>
<div title="Code: 0xe81b" class="the-icons span3"><i class="icon-flash"></i> <span class="i-name">icon-flash</span><span class="i-code">0xe81b</span></div>
<div title="Code: 0xe818" class="the-icons span3"><i class="demo-icon icon-scissors">&#xe818;</i> <span class="i-name">icon-scissors</span><span class="i-code">0xe818</span></div>
<div title="Code: 0xe819" class="the-icons span3"><i class="demo-icon icon-globe">&#xe819;</i> <span class="i-name">icon-globe</span><span class="i-code">0xe819</span></div>
<div title="Code: 0xe81a" class="the-icons span3"><i class="demo-icon icon-cloud">&#xe81a;</i> <span class="i-name">icon-cloud</span><span class="i-code">0xe81a</span></div>
<div title="Code: 0xe81b" class="the-icons span3"><i class="demo-icon icon-flash">&#xe81b;</i> <span class="i-name">icon-flash</span><span class="i-code">0xe81b</span></div>
</div>
<div class="row">
<div title="Code: 0xe81c" class="the-icons span3"><i class="icon-barchart"></i> <span class="i-name">icon-barchart</span><span class="i-code">0xe81c</span></div>
<div title="Code: 0xe81d" class="the-icons span3"><i class="icon-down-dir"></i> <span class="i-name">icon-down-dir</span><span class="i-code">0xe81d</span></div>
<div title="Code: 0xe81e" class="the-icons span3"><i class="icon-up-dir"></i> <span class="i-name">icon-up-dir</span><span class="i-code">0xe81e</span></div>
<div title="Code: 0xe81f" class="the-icons span3"><i class="icon-left-dir"></i> <span class="i-name">icon-left-dir</span><span class="i-code">0xe81f</span></div>
<div title="Code: 0xe81c" class="the-icons span3"><i class="demo-icon icon-barchart">&#xe81c;</i> <span class="i-name">icon-barchart</span><span class="i-code">0xe81c</span></div>
<div title="Code: 0xe81d" class="the-icons span3"><i class="demo-icon icon-down-dir">&#xe81d;</i> <span class="i-name">icon-down-dir</span><span class="i-code">0xe81d</span></div>
<div title="Code: 0xe81e" class="the-icons span3"><i class="demo-icon icon-up-dir">&#xe81e;</i> <span class="i-name">icon-up-dir</span><span class="i-code">0xe81e</span></div>
<div title="Code: 0xe81f" class="the-icons span3"><i class="demo-icon icon-left-dir">&#xe81f;</i> <span class="i-name">icon-left-dir</span><span class="i-code">0xe81f</span></div>
</div>
<div class="row">
<div title="Code: 0xe820" class="the-icons span3"><i class="icon-right-dir"></i> <span class="i-name">icon-right-dir</span><span class="i-code">0xe820</span></div>
<div title="Code: 0xe821" class="the-icons span3"><i class="icon-down-open"></i> <span class="i-name">icon-down-open</span><span class="i-code">0xe821</span></div>
<div title="Code: 0xe822" class="the-icons span3"><i class="icon-right-open"></i> <span class="i-name">icon-right-open</span><span class="i-code">0xe822</span></div>
<div title="Code: 0xe823" class="the-icons span3"><i class="icon-up-open"></i> <span class="i-name">icon-up-open</span><span class="i-code">0xe823</span></div>
<div title="Code: 0xe820" class="the-icons span3"><i class="demo-icon icon-right-dir">&#xe820;</i> <span class="i-name">icon-right-dir</span><span class="i-code">0xe820</span></div>
<div title="Code: 0xe821" class="the-icons span3"><i class="demo-icon icon-down-open">&#xe821;</i> <span class="i-name">icon-down-open</span><span class="i-code">0xe821</span></div>
<div title="Code: 0xe822" class="the-icons span3"><i class="demo-icon icon-right-open">&#xe822;</i> <span class="i-name">icon-right-open</span><span class="i-code">0xe822</span></div>
<div title="Code: 0xe823" class="the-icons span3"><i class="demo-icon icon-up-open">&#xe823;</i> <span class="i-name">icon-up-open</span><span class="i-code">0xe823</span></div>
</div>
<div class="row">
<div title="Code: 0xe824" class="the-icons span3"><i class="icon-left-open"></i> <span class="i-name">icon-left-open</span><span class="i-code">0xe824</span></div>
<div title="Code: 0xe825" class="the-icons span3"><i class="icon-up-big"></i> <span class="i-name">icon-up-big</span><span class="i-code">0xe825</span></div>
<div title="Code: 0xe826" class="the-icons span3"><i class="icon-right-big"></i> <span class="i-name">icon-right-big</span><span class="i-code">0xe826</span></div>
<div title="Code: 0xe827" class="the-icons span3"><i class="icon-left-big"></i> <span class="i-name">icon-left-big</span><span class="i-code">0xe827</span></div>
<div title="Code: 0xe824" class="the-icons span3"><i class="demo-icon icon-left-open">&#xe824;</i> <span class="i-name">icon-left-open</span><span class="i-code">0xe824</span></div>
<div title="Code: 0xe825" class="the-icons span3"><i class="demo-icon icon-up-big">&#xe825;</i> <span class="i-name">icon-up-big</span><span class="i-code">0xe825</span></div>
<div title="Code: 0xe826" class="the-icons span3"><i class="demo-icon icon-right-big">&#xe826;</i> <span class="i-name">icon-right-big</span><span class="i-code">0xe826</span></div>
<div title="Code: 0xe827" class="the-icons span3"><i class="demo-icon icon-left-big">&#xe827;</i> <span class="i-name">icon-left-big</span><span class="i-code">0xe827</span></div>
</div>
<div class="row">
<div title="Code: 0xe828" class="the-icons span3"><i class="icon-down-big"></i> <span class="i-name">icon-down-big</span><span class="i-code">0xe828</span></div>
<div title="Code: 0xe829" class="the-icons span3"><i class="icon-resize-full-alt"></i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xe829</span></div>
<div title="Code: 0xe82a" class="the-icons span3"><i class="icon-resize-full"></i> <span class="i-name">icon-resize-full</span><span class="i-code">0xe82a</span></div>
<div title="Code: 0xe82b" class="the-icons span3"><i class="icon-resize-small"></i> <span class="i-name">icon-resize-small</span><span class="i-code">0xe82b</span></div>
<div title="Code: 0xe828" class="the-icons span3"><i class="demo-icon icon-down-big">&#xe828;</i> <span class="i-name">icon-down-big</span><span class="i-code">0xe828</span></div>
<div title="Code: 0xe829" class="the-icons span3"><i class="demo-icon icon-resize-full-alt">&#xe829;</i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xe829</span></div>
<div title="Code: 0xe82a" class="the-icons span3"><i class="demo-icon icon-resize-full">&#xe82a;</i> <span class="i-name">icon-resize-full</span><span class="i-code">0xe82a</span></div>
<div title="Code: 0xe82b" class="the-icons span3"><i class="demo-icon icon-resize-small">&#xe82b;</i> <span class="i-name">icon-resize-small</span><span class="i-code">0xe82b</span></div>
</div>
<div class="row">
<div title="Code: 0xe82c" class="the-icons span3"><i class="icon-move"></i> <span class="i-name">icon-move</span><span class="i-code">0xe82c</span></div>
<div title="Code: 0xe82d" class="the-icons span3"><i class="icon-resize-horizontal"></i> <span class="i-name">icon-resize-horizontal</span><span class="i-code">0xe82d</span></div>
<div title="Code: 0xe82e" class="the-icons span3"><i class="icon-resize-vertical"></i> <span class="i-name">icon-resize-vertical</span><span class="i-code">0xe82e</span></div>
<div title="Code: 0xe82f" class="the-icons span3"><i class="icon-zoom-in"></i> <span class="i-name">icon-zoom-in</span><span class="i-code">0xe82f</span></div>
<div title="Code: 0xe82c" class="the-icons span3"><i class="demo-icon icon-move">&#xe82c;</i> <span class="i-name">icon-move</span><span class="i-code">0xe82c</span></div>
<div title="Code: 0xe82d" class="the-icons span3"><i class="demo-icon icon-resize-horizontal">&#xe82d;</i> <span class="i-name">icon-resize-horizontal</span><span class="i-code">0xe82d</span></div>
<div title="Code: 0xe82e" class="the-icons span3"><i class="demo-icon icon-resize-vertical">&#xe82e;</i> <span class="i-name">icon-resize-vertical</span><span class="i-code">0xe82e</span></div>
<div title="Code: 0xe82f" class="the-icons span3"><i class="demo-icon icon-zoom-in">&#xe82f;</i> <span class="i-name">icon-zoom-in</span><span class="i-code">0xe82f</span></div>
</div>
<div class="row">
<div title="Code: 0xe830" class="the-icons span3"><i class="icon-block"></i> <span class="i-name">icon-block</span><span class="i-code">0xe830</span></div>
<div title="Code: 0xe831" class="the-icons span3"><i class="icon-zoom-out"></i> <span class="i-name">icon-zoom-out</span><span class="i-code">0xe831</span></div>
<div title="Code: 0xe832" class="the-icons span3"><i class="icon-lightbulb"></i> <span class="i-name">icon-lightbulb</span><span class="i-code">0xe832</span></div>
<div title="Code: 0xe833" class="the-icons span3"><i class="icon-clock"></i> <span class="i-name">icon-clock</span><span class="i-code">0xe833</span></div>
<div title="Code: 0xe830" class="the-icons span3"><i class="demo-icon icon-block">&#xe830;</i> <span class="i-name">icon-block</span><span class="i-code">0xe830</span></div>
<div title="Code: 0xe831" class="the-icons span3"><i class="demo-icon icon-zoom-out">&#xe831;</i> <span class="i-name">icon-zoom-out</span><span class="i-code">0xe831</span></div>
<div title="Code: 0xe832" class="the-icons span3"><i class="demo-icon icon-lightbulb">&#xe832;</i> <span class="i-name">icon-lightbulb</span><span class="i-code">0xe832</span></div>
<div title="Code: 0xe833" class="the-icons span3"><i class="demo-icon icon-clock">&#xe833;</i> <span class="i-name">icon-clock</span><span class="i-code">0xe833</span></div>
</div>
<div class="row">
<div title="Code: 0xe834" class="the-icons span3"><i class="icon-volume-up"></i> <span class="i-name">icon-volume-up</span><span class="i-code">0xe834</span></div>
<div title="Code: 0xe835" class="the-icons span3"><i class="icon-volume-down"></i> <span class="i-name">icon-volume-down</span><span class="i-code">0xe835</span></div>
<div title="Code: 0xe836" class="the-icons span3"><i class="icon-volume-off"></i> <span class="i-name">icon-volume-off</span><span class="i-code">0xe836</span></div>
<div title="Code: 0xe837" class="the-icons span3"><i class="icon-mute"></i> <span class="i-name">icon-mute</span><span class="i-code">0xe837</span></div>
<div title="Code: 0xe834" class="the-icons span3"><i class="demo-icon icon-volume-up">&#xe834;</i> <span class="i-name">icon-volume-up</span><span class="i-code">0xe834</span></div>
<div title="Code: 0xe835" class="the-icons span3"><i class="demo-icon icon-volume-down">&#xe835;</i> <span class="i-name">icon-volume-down</span><span class="i-code">0xe835</span></div>
<div title="Code: 0xe836" class="the-icons span3"><i class="demo-icon icon-volume-off">&#xe836;</i> <span class="i-name">icon-volume-off</span><span class="i-code">0xe836</span></div>
<div title="Code: 0xe837" class="the-icons span3"><i class="demo-icon icon-mute">&#xe837;</i> <span class="i-name">icon-mute</span><span class="i-code">0xe837</span></div>
</div>
<div class="row">
<div title="Code: 0xe838" class="the-icons span3"><i class="icon-mic"></i> <span class="i-name">icon-mic</span><span class="i-code">0xe838</span></div>
<div title="Code: 0xe839" class="the-icons span3"><i class="icon-endtime"></i> <span class="i-name">icon-endtime</span><span class="i-code">0xe839</span></div>
<div title="Code: 0xe83a" class="the-icons span3"><i class="icon-starttime"></i> <span class="i-name">icon-starttime</span><span class="i-code">0xe83a</span></div>
<div title="Code: 0xe83b" class="the-icons span3"><i class="icon-calendar-empty"></i> <span class="i-name">icon-calendar-empty</span><span class="i-code">0xe83b</span></div>
<div title="Code: 0xe838" class="the-icons span3"><i class="demo-icon icon-mic">&#xe838;</i> <span class="i-name">icon-mic</span><span class="i-code">0xe838</span></div>
<div title="Code: 0xe839" class="the-icons span3"><i class="demo-icon icon-endtime">&#xe839;</i> <span class="i-name">icon-endtime</span><span class="i-code">0xe839</span></div>
<div title="Code: 0xe83a" class="the-icons span3"><i class="demo-icon icon-starttime">&#xe83a;</i> <span class="i-name">icon-starttime</span><span class="i-code">0xe83a</span></div>
<div title="Code: 0xe83b" class="the-icons span3"><i class="demo-icon icon-calendar-empty">&#xe83b;</i> <span class="i-name">icon-calendar-empty</span><span class="i-code">0xe83b</span></div>
</div>
<div class="row">
<div title="Code: 0xe83c" class="the-icons span3"><i class="icon-calendar"></i> <span class="i-name">icon-calendar</span><span class="i-code">0xe83c</span></div>
<div title="Code: 0xe83d" class="the-icons span3"><i class="icon-wrench"></i> <span class="i-name">icon-wrench</span><span class="i-code">0xe83d</span></div>
<div title="Code: 0xe83e" class="the-icons span3"><i class="icon-sliders"></i> <span class="i-name">icon-sliders</span><span class="i-code">0xe83e</span></div>
<div title="Code: 0xe83f" class="the-icons span3"><i class="icon-services"></i> <span class="i-name">icon-services</span><span class="i-code">0xe83f</span></div>
<div title="Code: 0xe83c" class="the-icons span3"><i class="demo-icon icon-calendar">&#xe83c;</i> <span class="i-name">icon-calendar</span><span class="i-code">0xe83c</span></div>
<div title="Code: 0xe83d" class="the-icons span3"><i class="demo-icon icon-wrench">&#xe83d;</i> <span class="i-name">icon-wrench</span><span class="i-code">0xe83d</span></div>
<div title="Code: 0xe83e" class="the-icons span3"><i class="demo-icon icon-sliders">&#xe83e;</i> <span class="i-name">icon-sliders</span><span class="i-code">0xe83e</span></div>
<div title="Code: 0xe83f" class="the-icons span3"><i class="demo-icon icon-services">&#xe83f;</i> <span class="i-name">icon-services</span><span class="i-code">0xe83f</span></div>
</div>
<div class="row">
<div title="Code: 0xe840" class="the-icons span3"><i class="icon-service"></i> <span class="i-name">icon-service</span><span class="i-code">0xe840</span></div>
<div title="Code: 0xe841" class="the-icons span3"><i class="icon-phone"></i> <span class="i-name">icon-phone</span><span class="i-code">0xe841</span></div>
<div title="Code: 0xe842" class="the-icons span3"><i class="icon-file-pdf"></i> <span class="i-name">icon-file-pdf</span><span class="i-code">0xe842</span></div>
<div title="Code: 0xe843" class="the-icons span3"><i class="icon-file-word"></i> <span class="i-name">icon-file-word</span><span class="i-code">0xe843</span></div>
<div title="Code: 0xe840" class="the-icons span3"><i class="demo-icon icon-service">&#xe840;</i> <span class="i-name">icon-service</span><span class="i-code">0xe840</span></div>
<div title="Code: 0xe841" class="the-icons span3"><i class="demo-icon icon-phone">&#xe841;</i> <span class="i-name">icon-phone</span><span class="i-code">0xe841</span></div>
<div title="Code: 0xe842" class="the-icons span3"><i class="demo-icon icon-file-pdf">&#xe842;</i> <span class="i-name">icon-file-pdf</span><span class="i-code">0xe842</span></div>
<div title="Code: 0xe843" class="the-icons span3"><i class="demo-icon icon-file-word">&#xe843;</i> <span class="i-name">icon-file-word</span><span class="i-code">0xe843</span></div>
</div>
<div class="row">
<div title="Code: 0xe844" class="the-icons span3"><i class="icon-file-excel"></i> <span class="i-name">icon-file-excel</span><span class="i-code">0xe844</span></div>
<div title="Code: 0xe845" class="the-icons span3"><i class="icon-doc-text"></i> <span class="i-name">icon-doc-text</span><span class="i-code">0xe845</span></div>
<div title="Code: 0xe846" class="the-icons span3"><i class="icon-trash"></i> <span class="i-name">icon-trash</span><span class="i-code">0xe846</span></div>
<div title="Code: 0xe847" class="the-icons span3"><i class="icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xe847</span></div>
<div title="Code: 0xe844" class="the-icons span3"><i class="demo-icon icon-file-excel">&#xe844;</i> <span class="i-name">icon-file-excel</span><span class="i-code">0xe844</span></div>
<div title="Code: 0xe845" class="the-icons span3"><i class="demo-icon icon-doc-text">&#xe845;</i> <span class="i-name">icon-doc-text</span><span class="i-code">0xe845</span></div>
<div title="Code: 0xe846" class="the-icons span3"><i class="demo-icon icon-trash">&#xe846;</i> <span class="i-name">icon-trash</span><span class="i-code">0xe846</span></div>
<div title="Code: 0xe847" class="the-icons span3"><i class="demo-icon icon-comment-empty">&#xe847;</i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xe847</span></div>
</div>
<div class="row">
<div title="Code: 0xe848" class="the-icons span3"><i class="icon-comment"></i> <span class="i-name">icon-comment</span><span class="i-code">0xe848</span></div>
<div title="Code: 0xe849" class="the-icons span3"><i class="icon-chat"></i> <span class="i-name">icon-chat</span><span class="i-code">0xe849</span></div>
<div title="Code: 0xe84a" class="the-icons span3"><i class="icon-chat-empty"></i> <span class="i-name">icon-chat-empty</span><span class="i-code">0xe84a</span></div>
<div title="Code: 0xe84b" class="the-icons span3"><i class="icon-bell"></i> <span class="i-name">icon-bell</span><span class="i-code">0xe84b</span></div>
<div title="Code: 0xe848" class="the-icons span3"><i class="demo-icon icon-comment">&#xe848;</i> <span class="i-name">icon-comment</span><span class="i-code">0xe848</span></div>
<div title="Code: 0xe849" class="the-icons span3"><i class="demo-icon icon-chat">&#xe849;</i> <span class="i-name">icon-chat</span><span class="i-code">0xe849</span></div>
<div title="Code: 0xe84a" class="the-icons span3"><i class="demo-icon icon-chat-empty">&#xe84a;</i> <span class="i-name">icon-chat-empty</span><span class="i-code">0xe84a</span></div>
<div title="Code: 0xe84b" class="the-icons span3"><i class="demo-icon icon-bell">&#xe84b;</i> <span class="i-name">icon-bell</span><span class="i-code">0xe84b</span></div>
</div>
<div class="row">
<div title="Code: 0xe84c" class="the-icons span3"><i class="icon-bell-alt"></i> <span class="i-name">icon-bell-alt</span><span class="i-code">0xe84c</span></div>
<div title="Code: 0xe84d" class="the-icons span3"><i class="icon-attention-alt"></i> <span class="i-name">icon-attention-alt</span><span class="i-code">0xe84d</span></div>
<div title="Code: 0xe84e" class="the-icons span3"><i class="icon-print"></i> <span class="i-name">icon-print</span><span class="i-code">0xe84e</span></div>
<div title="Code: 0xe84f" class="the-icons span3"><i class="icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe84f</span></div>
<div title="Code: 0xe84c" class="the-icons span3"><i class="demo-icon icon-bell-alt">&#xe84c;</i> <span class="i-name">icon-bell-alt</span><span class="i-code">0xe84c</span></div>
<div title="Code: 0xe84d" class="the-icons span3"><i class="demo-icon icon-attention-alt">&#xe84d;</i> <span class="i-name">icon-attention-alt</span><span class="i-code">0xe84d</span></div>
<div title="Code: 0xe84e" class="the-icons span3"><i class="demo-icon icon-print">&#xe84e;</i> <span class="i-name">icon-print</span><span class="i-code">0xe84e</span></div>
<div title="Code: 0xe84f" class="the-icons span3"><i class="demo-icon icon-edit">&#xe84f;</i> <span class="i-name">icon-edit</span><span class="i-code">0xe84f</span></div>
</div>
<div class="row">
<div title="Code: 0xe850" class="the-icons span3"><i class="icon-forward"></i> <span class="i-name">icon-forward</span><span class="i-code">0xe850</span></div>
<div title="Code: 0xe851" class="the-icons span3"><i class="icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xe851</span></div>
<div title="Code: 0xe852" class="the-icons span3"><i class="icon-reply-all"></i> <span class="i-name">icon-reply-all</span><span class="i-code">0xe852</span></div>
<div title="Code: 0xe853" class="the-icons span3"><i class="icon-eye"></i> <span class="i-name">icon-eye</span><span class="i-code">0xe853</span></div>
<div title="Code: 0xe850" class="the-icons span3"><i class="demo-icon icon-forward">&#xe850;</i> <span class="i-name">icon-forward</span><span class="i-code">0xe850</span></div>
<div title="Code: 0xe851" class="the-icons span3"><i class="demo-icon icon-reply">&#xe851;</i> <span class="i-name">icon-reply</span><span class="i-code">0xe851</span></div>
<div title="Code: 0xe852" class="the-icons span3"><i class="demo-icon icon-reply-all">&#xe852;</i> <span class="i-name">icon-reply-all</span><span class="i-code">0xe852</span></div>
<div title="Code: 0xe853" class="the-icons span3"><i class="demo-icon icon-eye">&#xe853;</i> <span class="i-name">icon-eye</span><span class="i-code">0xe853</span></div>
</div>
<div class="row">
<div title="Code: 0xe854" class="the-icons span3"><i class="icon-tag"></i> <span class="i-name">icon-tag</span><span class="i-code">0xe854</span></div>
<div title="Code: 0xe855" class="the-icons span3"><i class="icon-tags"></i> <span class="i-name">icon-tags</span><span class="i-code">0xe855</span></div>
<div title="Code: 0xe856" class="the-icons span3"><i class="icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xe856</span></div>
<div title="Code: 0xe857" class="the-icons span3"><i class="icon-lock-open"></i> <span class="i-name">icon-lock-open</span><span class="i-code">0xe857</span></div>
<div title="Code: 0xe854" class="the-icons span3"><i class="demo-icon icon-tag">&#xe854;</i> <span class="i-name">icon-tag</span><span class="i-code">0xe854</span></div>
<div title="Code: 0xe855" class="the-icons span3"><i class="demo-icon icon-tags">&#xe855;</i> <span class="i-name">icon-tags</span><span class="i-code">0xe855</span></div>
<div title="Code: 0xe856" class="the-icons span3"><i class="demo-icon icon-lock-open-alt">&#xe856;</i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xe856</span></div>
<div title="Code: 0xe857" class="the-icons span3"><i class="demo-icon icon-lock-open">&#xe857;</i> <span class="i-name">icon-lock-open</span><span class="i-code">0xe857</span></div>
</div>
<div class="row">
<div title="Code: 0xe858" class="the-icons span3"><i class="icon-lock"></i> <span class="i-name">icon-lock</span><span class="i-code">0xe858</span></div>
<div title="Code: 0xe859" class="the-icons span3"><i class="icon-home"></i> <span class="i-name">icon-home</span><span class="i-code">0xe859</span></div>
<div title="Code: 0xe85a" class="the-icons span3"><i class="icon-info"></i> <span class="i-name">icon-info</span><span class="i-code">0xe85a</span></div>
<div title="Code: 0xe85b" class="the-icons span3"><i class="icon-help"></i> <span class="i-name">icon-help</span><span class="i-code">0xe85b</span></div>
<div title="Code: 0xe858" class="the-icons span3"><i class="demo-icon icon-lock">&#xe858;</i> <span class="i-name">icon-lock</span><span class="i-code">0xe858</span></div>
<div title="Code: 0xe859" class="the-icons span3"><i class="demo-icon icon-home">&#xe859;</i> <span class="i-name">icon-home</span><span class="i-code">0xe859</span></div>
<div title="Code: 0xe85a" class="the-icons span3"><i class="demo-icon icon-info">&#xe85a;</i> <span class="i-name">icon-info</span><span class="i-code">0xe85a</span></div>
<div title="Code: 0xe85b" class="the-icons span3"><i class="demo-icon icon-help">&#xe85b;</i> <span class="i-name">icon-help</span><span class="i-code">0xe85b</span></div>
</div>
<div class="row">
<div title="Code: 0xe85c" class="the-icons span3"><i class="icon-search"></i> <span class="i-name">icon-search</span><span class="i-code">0xe85c</span></div>
<div title="Code: 0xe85d" class="the-icons span3"><i class="icon-flapping"></i> <span class="i-name">icon-flapping</span><span class="i-code">0xe85d</span></div>
<div title="Code: 0xe85e" class="the-icons span3"><i class="icon-rewind"></i> <span class="i-name">icon-rewind</span><span class="i-code">0xe85e</span></div>
<div title="Code: 0xe85f" class="the-icons span3"><i class="icon-chart-line"></i> <span class="i-name">icon-chart-line</span><span class="i-code">0xe85f</span></div>
<div title="Code: 0xe85c" class="the-icons span3"><i class="demo-icon icon-search">&#xe85c;</i> <span class="i-name">icon-search</span><span class="i-code">0xe85c</span></div>
<div title="Code: 0xe85d" class="the-icons span3"><i class="demo-icon icon-flapping">&#xe85d;</i> <span class="i-name">icon-flapping</span><span class="i-code">0xe85d</span></div>
<div title="Code: 0xe85e" class="the-icons span3"><i class="demo-icon icon-rewind">&#xe85e;</i> <span class="i-name">icon-rewind</span><span class="i-code">0xe85e</span></div>
<div title="Code: 0xe85f" class="the-icons span3"><i class="demo-icon icon-chart-line">&#xe85f;</i> <span class="i-name">icon-chart-line</span><span class="i-code">0xe85f</span></div>
</div>
<div class="row">
<div title="Code: 0xe860" class="the-icons span3"><i class="icon-bell-off"></i> <span class="i-name">icon-bell-off</span><span class="i-code">0xe860</span></div>
<div title="Code: 0xe861" class="the-icons span3"><i class="icon-bell-off-empty"></i> <span class="i-name">icon-bell-off-empty</span><span class="i-code">0xe861</span></div>
<div title="Code: 0xe862" class="the-icons span3"><i class="icon-plug"></i> <span class="i-name">icon-plug</span><span class="i-code">0xe862</span></div>
<div title="Code: 0xe863" class="the-icons span3"><i class="icon-eye-off"></i> <span class="i-name">icon-eye-off</span><span class="i-code">0xe863</span></div>
<div title="Code: 0xe860" class="the-icons span3"><i class="demo-icon icon-bell-off">&#xe860;</i> <span class="i-name">icon-bell-off</span><span class="i-code">0xe860</span></div>
<div title="Code: 0xe861" class="the-icons span3"><i class="demo-icon icon-bell-off-empty">&#xe861;</i> <span class="i-name">icon-bell-off-empty</span><span class="i-code">0xe861</span></div>
<div title="Code: 0xe862" class="the-icons span3"><i class="demo-icon icon-plug">&#xe862;</i> <span class="i-name">icon-plug</span><span class="i-code">0xe862</span></div>
<div title="Code: 0xe863" class="the-icons span3"><i class="demo-icon icon-eye-off">&#xe863;</i> <span class="i-name">icon-eye-off</span><span class="i-code">0xe863</span></div>
</div>
<div class="row">
<div title="Code: 0xe864" class="the-icons span3"><i class="icon-reschedule"></i> <span class="i-name">icon-reschedule</span><span class="i-code">0xe864</span></div>
<div title="Code: 0xe865" class="the-icons span3"><i class="icon-cw"></i> <span class="i-name">icon-cw</span><span class="i-code">0xe865</span></div>
<div title="Code: 0xe866" class="the-icons span3"><i class="icon-host"></i> <span class="i-name">icon-host</span><span class="i-code">0xe866</span></div>
<div title="Code: 0xe867" class="the-icons span3"><i class="icon-thumbs-up"></i> <span class="i-name">icon-thumbs-up</span><span class="i-code">0xe867</span></div>
<div title="Code: 0xe864" class="the-icons span3"><i class="demo-icon icon-reschedule">&#xe864;</i> <span class="i-name">icon-reschedule</span><span class="i-code">0xe864</span></div>
<div title="Code: 0xe865" class="the-icons span3"><i class="demo-icon icon-cw">&#xe865;</i> <span class="i-name">icon-cw</span><span class="i-code">0xe865</span></div>
<div title="Code: 0xe866" class="the-icons span3"><i class="demo-icon icon-host">&#xe866;</i> <span class="i-name">icon-host</span><span class="i-code">0xe866</span></div>
<div title="Code: 0xe867" class="the-icons span3"><i class="demo-icon icon-thumbs-up">&#xe867;</i> <span class="i-name">icon-thumbs-up</span><span class="i-code">0xe867</span></div>
</div>
<div class="row">
<div title="Code: 0xe868" class="the-icons span3"><i class="icon-thumbs-down"></i> <span class="i-name">icon-thumbs-down</span><span class="i-code">0xe868</span></div>
<div title="Code: 0xe869" class="the-icons span3"><i class="icon-spinner"></i> <span class="i-name">icon-spinner</span><span class="i-code">0xe869</span></div>
<div title="Code: 0xe86a" class="the-icons span3"><i class="icon-attach"></i> <span class="i-name">icon-attach</span><span class="i-code">0xe86a</span></div>
<div title="Code: 0xe86b" class="the-icons span3"><i class="icon-keyboard"></i> <span class="i-name">icon-keyboard</span><span class="i-code">0xe86b</span></div>
<div title="Code: 0xe868" class="the-icons span3"><i class="demo-icon icon-thumbs-down">&#xe868;</i> <span class="i-name">icon-thumbs-down</span><span class="i-code">0xe868</span></div>
<div title="Code: 0xe869" class="the-icons span3"><i class="demo-icon icon-spinner">&#xe869;</i> <span class="i-name">icon-spinner</span><span class="i-code">0xe869</span></div>
<div title="Code: 0xe86a" class="the-icons span3"><i class="demo-icon icon-attach">&#xe86a;</i> <span class="i-name">icon-attach</span><span class="i-code">0xe86a</span></div>
<div title="Code: 0xe86b" class="the-icons span3"><i class="demo-icon icon-keyboard">&#xe86b;</i> <span class="i-name">icon-keyboard</span><span class="i-code">0xe86b</span></div>
</div>
<div class="row">
<div title="Code: 0xe86c" class="the-icons span3"><i class="icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xe86c</span></div>
<div title="Code: 0xe86d" class="the-icons span3"><i class="icon-wifi"></i> <span class="i-name">icon-wifi</span><span class="i-code">0xe86d</span></div>
<div title="Code: 0xe86e" class="the-icons span3"><i class="icon-moon"></i> <span class="i-name">icon-moon</span><span class="i-code">0xe86e</span></div>
<div title="Code: 0xe86f" class="the-icons span3"><i class="icon-chart-pie"></i> <span class="i-name">icon-chart-pie</span><span class="i-code">0xe86f</span></div>
<div title="Code: 0xe86c" class="the-icons span3"><i class="demo-icon icon-menu">&#xe86c;</i> <span class="i-name">icon-menu</span><span class="i-code">0xe86c</span></div>
<div title="Code: 0xe86d" class="the-icons span3"><i class="demo-icon icon-wifi">&#xe86d;</i> <span class="i-name">icon-wifi</span><span class="i-code">0xe86d</span></div>
<div title="Code: 0xe86e" class="the-icons span3"><i class="demo-icon icon-moon">&#xe86e;</i> <span class="i-name">icon-moon</span><span class="i-code">0xe86e</span></div>
<div title="Code: 0xe86f" class="the-icons span3"><i class="demo-icon icon-chart-pie">&#xe86f;</i> <span class="i-name">icon-chart-pie</span><span class="i-code">0xe86f</span></div>
</div>
<div class="row">
<div title="Code: 0xe870" class="the-icons span3"><i class="icon-chart-area"></i> <span class="i-name">icon-chart-area</span><span class="i-code">0xe870</span></div>
<div title="Code: 0xe871" class="the-icons span3"><i class="icon-chart-bar"></i> <span class="i-name">icon-chart-bar</span><span class="i-code">0xe871</span></div>
<div title="Code: 0xe872" class="the-icons span3"><i class="icon-beaker"></i> <span class="i-name">icon-beaker</span><span class="i-code">0xe872</span></div>
<div title="Code: 0xe873" class="the-icons span3"><i class="icon-magic"></i> <span class="i-name">icon-magic</span><span class="i-code">0xe873</span></div>
<div title="Code: 0xe870" class="the-icons span3"><i class="demo-icon icon-chart-area">&#xe870;</i> <span class="i-name">icon-chart-area</span><span class="i-code">0xe870</span></div>
<div title="Code: 0xe871" class="the-icons span3"><i class="demo-icon icon-chart-bar">&#xe871;</i> <span class="i-name">icon-chart-bar</span><span class="i-code">0xe871</span></div>
<div title="Code: 0xe872" class="the-icons span3"><i class="demo-icon icon-beaker">&#xe872;</i> <span class="i-name">icon-beaker</span><span class="i-code">0xe872</span></div>
<div title="Code: 0xe873" class="the-icons span3"><i class="demo-icon icon-magic">&#xe873;</i> <span class="i-name">icon-magic</span><span class="i-code">0xe873</span></div>
</div>
<div class="row">
<div title="Code: 0xe874" class="the-icons span3"><i class="demo-icon icon-spin6 animate-spin">&#xe874;</i> <span class="i-name">icon-spin6</span><span class="i-code">0xe874</span></div>
<div title="Code: 0xe875" class="the-icons span3"><i class="demo-icon icon-down-small">&#xe875;</i> <span class="i-name">icon-down-small</span><span class="i-code">0xe875</span></div>
<div title="Code: 0xe876" class="the-icons span3"><i class="demo-icon icon-left-small">&#xe876;</i> <span class="i-name">icon-left-small</span><span class="i-code">0xe876</span></div>
<div title="Code: 0xe877" class="the-icons span3"><i class="demo-icon icon-right-small">&#xe877;</i> <span class="i-name">icon-right-small</span><span class="i-code">0xe877</span></div>
</div>
<div class="row">
<div title="Code: 0xe878" class="the-icons span3"><i class="demo-icon icon-up-small">&#xe878;</i> <span class="i-name">icon-up-small</span><span class="i-code">0xe878</span></div>
</div>
</div>
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>

View File

@ -0,0 +1,9 @@
# fontello-ifont font files moved
New target is: public/font
The font directory has been moved to the public structure because of
Internet Explorer version 8 compatibility. The common way for browsers is to
include the binary embeded font type in the javascript. IE8 falls back and
include one of the provided font sources. Therefore it is important to have
the font files available public and exported by the HTTP server.

View File

@ -1,28 +1,36 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Authentication;
use Icinga\Authentication\Auth;
use Icinga\Authentication\User\ExternalBackend;
use Icinga\User;
use Icinga\Web\Form;
use Icinga\Web\Url;
/**
* Class LoginForm
* Form for user authentication
*/
class LoginForm extends Form
{
/**
* Initialize this login form
* Redirect URL
*/
const REDIRECT_URL = 'dashboard';
/**
* {@inheritdoc}
*/
public function init()
{
$this->setRequiredCue(null);
$this->setName('form_login');
$this->setSubmitLabel(t('Login'));
$this->setSubmitLabel($this->translate('Login'));
}
/**
* @see Form::createElements()
* {@inheritdoc}
*/
public function createElements(array $formData)
{
@ -31,8 +39,8 @@ class LoginForm extends Form
'username',
array(
'required' => true,
'label' => t('Username'),
'placeholder' => t('Please enter your username...'),
'label' => $this->translate('Username'),
'placeholder' => $this->translate('Please enter your username...'),
'class' => false === isset($formData['username']) ? 'autofocus' : ''
)
);
@ -41,8 +49,8 @@ class LoginForm extends Form
'password',
array(
'required' => true,
'label' => t('Password'),
'placeholder' => t('...and your password'),
'label' => $this->translate('Password'),
'placeholder' => $this->translate('...and your password'),
'class' => isset($formData['username']) ? 'autofocus' : ''
)
);
@ -54,4 +62,83 @@ class LoginForm extends Form
)
);
}
/**
* {@inheritdoc}
*/
public function getRedirectUrl()
{
$redirect = null;
if ($this->created) {
$redirect = $this->getElement('redirect')->getValue();
}
if (empty($redirect)) {
$redirect = static::REDIRECT_URL;
}
return Url::fromPath($redirect);
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
$auth = Auth::getInstance();
$authChain = $auth->getAuthChain();
$authChain->setSkipExternalBackends(true);
$user = new User($this->getElement('username')->getValue());
$password = $this->getElement('password')->getValue();
$authenticated = $authChain->authenticate($user, $password);
if ($authenticated) {
$auth->setAuthenticated($user);
$this->getResponse()->setRerenderLayout(true);
return true;
}
switch ($authChain->getError()) {
case $authChain::EEMPTY:
$this->addError($this->translate(
'No authentication methods available.'
. ' Did you create authentication.ini when setting up Icinga Web 2?'
));
break;
case $authChain::EFAIL:
$this->addError($this->translate(
'All configured authentication methods failed.'
. ' Please check the system log or Icinga Web 2 log for more information.'
));
break;
/** @noinspection PhpMissingBreakStatementInspection */
case $authChain::ENOTALL:
$this->addError($this->translate(
'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.'
));
// Move to default
default:
$this->getElement('password')->addError($this->translate('Incorrect username or password'));
break;
}
return false;
}
/**
* {@inheritdoc}
*/
public function onRequest()
{
$auth = Auth::getInstance();
$onlyExternal = true;
// TODO(el): This may be set on the auth chain once iterated. See Auth::authExternal().
foreach ($auth->getAuthChain() as $backend) {
if (! $backend instanceof ExternalBackend) {
$onlyExternal = false;
}
}
if ($onlyExternal) {
$this->addError($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 login.'
));
}
}
}

View File

@ -0,0 +1,83 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms;
use Icinga\Application\Logger;
use Icinga\User\Preferences;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Session;
use Icinga\Web\Url;
/**
* Form class to adjust user auto refresh preferences
*/
class AutoRefreshForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_auto_refresh');
// Post against the current location
$this->setAction('');
}
/**
* Adjust preferences and persist them
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
/** @var Preferences $preferences */
$preferences = $this->getRequest()->getUser()->getPreferences();
$icingaweb = $preferences->get('icingaweb');
if ((bool) $preferences->getValue('icingaweb', 'auto_refresh', true) === false) {
$icingaweb['auto_refresh'] = '1';
$notification = $this->translate('Auto refresh successfully enabled');
} else {
$icingaweb['auto_refresh'] = '0';
$notification = $this->translate('Auto refresh successfully disabled');
}
$preferences->icingaweb = $icingaweb;
Session::getSession()->user->setPreferences($preferences);
Notification::success($notification);
$this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes');
$this->setRedirectUrl(Url::fromRequest()->without('renderLayout'));
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$preferences = $this->getRequest()->getUser()->getPreferences();
if ((bool) $preferences->getValue('icingaweb', 'auto_refresh', true) === false) {
$value = $this->translate('Enable auto refresh');
} else {
$value = $this->translate('Disable auto refresh');
}
$this->addElements(array(
array(
'button',
'btn_submit',
array(
'ignore' => true,
'type' => 'submit',
'value' => $value,
'decorators' => array('ViewHelper'),
'escape' => false,
'class' => 'link-like'
)
)
));
}
}

View File

@ -1,90 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Forms\Config\Authentication;
use Zend_Validate_Callback;
use Icinga\Web\Form;
/**
* Form class for adding/modifying autologin authentication backends
*/
class AutologinBackendForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_autologin');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => 'The backend name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
);
$this->addElement(
'text',
'strip_username_regexp',
array(
'label' => t('Filter Pattern'),
'description' => t('The regular expression to use to strip specific parts off from usernames. Leave empty if you do not want to strip off anything'),
'value' => '/\@[^$]+$/',
'validators' => array(
new Zend_Validate_Callback(function ($value) {
return @preg_match($value, '') !== false;
})
)
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true,
'value' => 'autologin'
)
);
return $this;
}
/**
* Validate the configuration by creating a backend and requesting the user count
*
* Returns always true as autologin backends are just "passive" backends. (The webserver authenticates users.)
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public static function isValidAuthenticationBackend(Form $form)
{
return true;
}
}

View File

@ -1,130 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Forms\Config\Authentication;
use Exception;
use Icinga\Web\Form;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Authentication\Backend\DbUserBackend;
/**
* Form class for adding/modifying database authentication backends
*/
class DbBackendForm extends Form
{
/**
* The database resource names the user can choose from
*
* @var array
*/
protected $resources;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_db');
}
/**
* Set the resource names the user can choose from
*
* @param array $resources The resources to choose from
*
* @return self
*/
public function setResources(array $resources)
{
$this->resources = $resources;
return $this;
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t(
'The name of this authentication provider that is used to differentiate it from others'
),
)
);
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => t('Database Connection'),
'description' => t('The database connection to use for authenticating with this provider'),
'multiOptions' => false === empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true,
'value' => 'db'
)
);
return $this;
}
/**
* Validate that the selected resource is a valid database authentication backend
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
if (false === static::isValidAuthenticationBackend($this)) {
return false;
}
}
/**
* Validate the configuration by creating a backend and requesting the user count
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public static function isValidAuthenticationBackend(Form $form)
{
try {
$dbUserBackend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig()));
if ($dbUserBackend->count() < 1) {
$form->addError(t('No users found under the specified database backend'));
return false;
}
} catch (Exception $e) {
$form->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
return false;
}
return true;
}
/**
* Return the configuration for the chosen resource
*
* @return ConfigObject
*/
public function getResourceConfig()
{
return ResourceFactory::getResourceConfig($this->getValue('resource'));
}
}

View File

@ -1,165 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Forms\Config\Authentication;
use Exception;
use Icinga\Web\Form;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\AuthenticationException;
use Icinga\Authentication\Backend\LdapUserBackend;
/**
* Form class for adding/modifying LDAP authentication backends
*/
class LdapBackendForm extends Form
{
/**
* The ldap resource names the user can choose from
*
* @var array
*/
protected $resources;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_ldap');
}
/**
* Set the resource names the user can choose from
*
* @param array $resources The resources to choose from
*
* @return self
*/
public function setResources(array $resources)
{
$this->resources = $resources;
return $this;
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t(
'The name of this authentication provider that is used to differentiate it from others'
)
)
);
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => t('LDAP Resource'),
'description' => t('The resource to use for authenticating with this provider'),
'multiOptions' => false === empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
)
);
$this->addElement(
'text',
'user_class',
array(
'required' => true,
'label' => t('LDAP User Object Class'),
'description' => t('The object class used for storing users on the ldap server'),
'value' => 'inetOrgPerson'
)
);
$this->addElement(
'text',
'user_name_attribute',
array(
'required' => true,
'label' => t('LDAP User Name Attribute'),
'description' => t('The attribute name used for storing the user name on the ldap server'),
'value' => 'uid'
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true,
'value' => 'ldap'
)
);
$this->addElement(
'text',
'base_dn',
array(
'required' => false,
'label' => t('Base DN'),
'description' => t('The path where users can be found on the ldap server. ' .
' Leave empty to select all users available on the specified resource.')
)
);
return $this;
}
/**
* Validate that the selected resource is a valid ldap authentication backend
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
if (false === static::isValidAuthenticationBackend($this)) {
return false;
}
}
/**
* Validate the configuration by creating a backend and requesting the user count
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public static function isValidAuthenticationBackend(Form $form)
{
try {
$ldapUserBackend = new LdapUserBackend(
ResourceFactory::createResource($form->getResourceConfig()),
$form->getElement('user_class')->getValue(),
$form->getElement('user_name_attribute')->getValue(),
$form->getElement('base_dn')->getValue()
);
$ldapUserBackend->assertAuthenticationPossible();
} catch (AuthenticationException $e) {
$form->addError($e->getMessage());
return false;
} catch (Exception $e) {
$form->addError(sprintf(t('Unable to validate authentication: %s'), $e->getMessage()));
return false;
}
return true;
}
/**
* Return the configuration for the chosen resource
*
* @return ConfigObject
*/
public function getResourceConfig()
{
return ResourceFactory::getResourceConfig($this->getValue('resource'));
}
}

View File

@ -1,344 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Forms\Config;
use InvalidArgumentException;
use Icinga\Forms\ConfigForm;
use Icinga\Web\Notification;
use Icinga\Application\Config;
use Icinga\Application\Platform;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Forms\Config\Authentication\DbBackendForm;
use Icinga\Forms\Config\Authentication\LdapBackendForm;
use Icinga\Forms\Config\Authentication\AutologinBackendForm;
class AuthenticationBackendConfigForm extends ConfigForm
{
/**
* The available resources split by type
*
* @var array
*/
protected $resources;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend');
$this->setSubmitLabel(t('Save Changes'));
}
/**
* Set the resource configuration to use
*
* @param Config $resources The resource configuration
*
* @return self
*/
public function setResourceConfig(Config $resourceConfig)
{
$resources = array();
foreach ($resourceConfig as $name => $resource) {
$resources[strtolower($resource->type)][] = $name;
}
$this->resources = $resources;
return $this;
}
/**
* Return a form object for the given backend type
*
* @param string $type The backend type for which to return a form
*
* @return Form
*/
public function getBackendForm($type)
{
if ($type === 'db') {
$form = new DbBackendForm();
$form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array());
} elseif ($type === 'ldap') {
$form = new LdapBackendForm();
$form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array());
} elseif ($type === 'autologin') {
$form = new AutologinBackendForm();
} else {
throw new InvalidArgumentException(sprintf(t('Invalid backend type "%s" provided'), $type));
}
return $form;
}
/**
* Add a particular authentication backend
*
* The backend to add is identified by the array-key `name'.
*
* @param array $values The values to extend the configuration with
*
* @return self
*
* @throws InvalidArgumentException In case the backend does already exist
*/
public function add(array $values)
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
} elseif ($this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Authentication backend already exists'));
}
unset($values['name']);
$this->config->setSection($name, $values);
return $this;
}
/**
* Edit a particular authentication backend
*
* @param string $name The name of the backend to edit
* @param array $values The values to edit the configuration with
*
* @return array The edited backend configuration
*
* @throws InvalidArgumentException In case the backend does not exist
*/
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(t('Old authentication backend name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(t('New authentication backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
}
$backendConfig = $this->config->getSection($name);
if ($newName !== $name) {
// Only remove the old entry if it has changed as the order gets screwed when editing backend names
$this->config->removeSection($name);
}
unset($values['name']);
$this->config->setSection($newName, $backendConfig->merge($values));
return $backendConfig;
}
/**
* Remove the given authentication backend
*
* @param string $name The name of the backend to remove
*
* @return array The removed backend configuration
*
* @throws InvalidArgumentException In case the backend does not exist
*/
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
}
$backendConfig = $this->config->getSection($name);
$this->config->removeSection($name);
return $backendConfig;
}
/**
* Move the given authentication backend up or down in order
*
* @param string $name The name of the backend to be moved
* @param int $position The new (absolute) position of the backend
*
* @return self
*
* @throws InvalidArgumentException In case the backend does not exist
*/
public function move($name, $position)
{
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
}
$backendOrder = $this->config->keys();
array_splice($backendOrder, array_search($name, $backendOrder), 1);
array_splice($backendOrder, $position, 0, $name);
$newConfig = array();
foreach ($backendOrder as $backendName) {
$newConfig[$backendName] = $this->config->getSection($backendName);
}
$config = Config::fromArray($newConfig);
$this->config = $config->setConfigFile($this->config->getConfigFile());
return $this;
}
/**
* Add or edit an authentication backend and save the configuration
*
* Performs a connectivity validation using the submitted values. A checkbox is
* added to the form to skip the check if it fails and redirection is aborted.
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
$backendForm = $this->getBackendForm($this->getElement('type')->getValue());
if (false === $backendForm::isValidAuthenticationBackend($this)) {
$this->addElement($this->getForceCreationCheckbox());
return false;
}
}
$authBackend = $this->request->getQuery('auth_backend');
try {
if ($authBackend === null) { // create new backend
$this->add($this->getValues());
$message = t('Authentication backend "%s" has been successfully created');
} else { // edit existing backend
$this->edit($authBackend, $this->getValues());
$message = t('Authentication backend "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
}
if ($this->save()) {
Notification::success(sprintf($message, $this->getElement('name')->getValue()));
} else {
return false;
}
}
/**
* Populate the form in case an authentication backend is being edited
*
* @see Form::onRequest()
*
* @throws ConfigurationError In case the backend name is missing in the request or is invalid
*/
public function onRequest()
{
$authBackend = $this->request->getQuery('auth_backend');
if ($authBackend !== null) {
if ($authBackend === '') {
throw new ConfigurationError(t('Authentication backend name missing'));
} elseif (! $this->config->hasSection($authBackend)) {
throw new ConfigurationError(t('Unknown authentication backend provided'));
} elseif ($this->config->getSection($authBackend)->backend === null) {
throw new ConfigurationError(sprintf(t('Backend "%s" has no `backend\' setting'), $authBackend));
}
$configValues = $this->config->getSection($authBackend)->toArray();
$configValues['type'] = $configValues['backend'];
$configValues['name'] = $authBackend;
$this->populate($configValues);
} elseif (empty($this->resources)) {
$autologinBackends = array_filter(
$this->config->toArray(),
function ($authBackendCfg) {
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
}
);
if (false === empty($autologinBackends)) {
throw new ConfigurationError(t('Could not find any resources for authentication'));
}
}
}
/**
* Return a checkbox to be displayed at the beginning of the form
* which allows the user to skip the connection validation
*
* @return Zend_Form_Element
*/
protected function getForceCreationCheckbox()
{
return $this->createElement(
'checkbox',
'force_creation',
array(
'order' => 0,
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
)
);
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$backendTypes = array();
$backendType = isset($formData['type']) ? $formData['type'] : null;
if (isset($this->resources['db'])) {
$backendTypes['db'] = t('Database');
}
if (isset($this->resources['ldap']) && ($backendType === 'ldap' || Platform::extensionLoaded('ldap'))) {
$backendTypes['ldap'] = 'LDAP';
}
$autologinBackends = array_filter(
$this->config->toArray(),
function ($authBackendCfg) {
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
}
);
if ($backendType === 'autologin' || empty($autologinBackends)) {
$backendTypes['autologin'] = t('Autologin');
}
if ($backendType === null) {
$backendType = key($backendTypes);
}
$this->addElement(
'select',
'type',
array(
'ignore' => true,
'required' => true,
'autosubmit' => true,
'label' => t('Backend Type'),
'description' => t('The type of the resource to use for this authenticaton provider'),
'multiOptions' => $backendTypes
)
);
if (isset($formData['force_creation']) && $formData['force_creation']) {
// In case another error occured and the checkbox was displayed before
$this->addElement($this->getForceCreationCheckbox());
}
$this->addElements($this->getBackendForm($backendType)->createElements($formData)->getElements());
}
/**
* Return the configuration for the chosen resource
*
* @return ConfigObject
*/
public function getResourceConfig()
{
return ResourceFactory::getResourceConfig($this->getValue('resource'));
}
}

View File

@ -1,16 +1,12 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\General;
use DateTimeZone;
use Icinga\Application\Icinga;
use Icinga\Data\ResourceFactory;
use Icinga\Util\Translator;
use Icinga\Web\Form;
/**
* Form class to modify the general application configuration
*/
@ -33,10 +29,10 @@ class ApplicationConfigForm extends Form
'text',
'global_module_path',
array(
'label' => t('Module Path'),
'label' => $this->translate('Module Path'),
'required' => true,
'value' => implode(':', Icinga::app()->getModuleManager()->getModuleDirs()),
'description' => t(
'description' => $this->translate(
'Contains the directories that will be searched for available modules, separated by '
. 'colons. Modules that don\'t exist in these directories can still be symlinked in '
. 'the module folder, but won\'t show up in the list of disabled modules.'
@ -46,19 +42,19 @@ class ApplicationConfigForm extends Form
$this->addElement(
'select',
'preferences_type',
'global_config_backend',
array(
'required' => true,
'autosubmit' => true,
'label' => t('User Preference Storage Type'),
'label' => $this->translate('User Preference Storage Type'),
'multiOptions' => array(
'ini' => t('File System (INI Files)'),
'db' => t('Database'),
'null' => t('Don\'t Store Preferences')
'ini' => $this->translate('File System (INI Files)'),
'db' => $this->translate('Database'),
'none' => $this->translate('Don\'t Store Preferences')
)
)
);
if (isset($formData['preferences_type']) && $formData['preferences_type'] === 'db') {
if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'db') {
$backends = array();
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
if ($resource['type'] === 'db') {
@ -68,11 +64,11 @@ class ApplicationConfigForm extends Form
$this->addElement(
'select',
'preferences_resource',
'global_config_resource',
array(
'required' => true,
'multiOptions' => $backends,
'label' => t('Database Connection')
'label' => $this->translate('Database Connection')
)
);
}

View File

@ -1,13 +1,10 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\General;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\WritablePathValidator;
class LoggingConfigForm extends Form
{
@ -31,12 +28,12 @@ class LoggingConfigForm extends Form
array(
'required' => true,
'autosubmit' => true,
'label' => t('Logging Type'),
'description' => t('The type of logging to utilize.'),
'label' => $this->translate('Logging Type'),
'description' => $this->translate('The type of logging to utilize.'),
'multiOptions' => array(
'syslog' => 'Syslog',
'file' => t('File', 'app.config.logging.type'),
'none' => t('None', 'app.config.logging.type')
'file' => $this->translate('File', 'app.config.logging.type'),
'none' => $this->translate('None', 'app.config.logging.type')
)
)
);
@ -47,13 +44,13 @@ class LoggingConfigForm extends Form
'logging_level',
array(
'required' => true,
'label' => t('Logging Level'),
'description' => t('The maximum logging level to emit.'),
'label' => $this->translate('Logging Level'),
'description' => $this->translate('The maximum logging level to emit.'),
'multiOptions' => array(
Logger::$levels[Logger::ERROR] => t('Error', 'app.config.logging.level'),
Logger::$levels[Logger::WARNING] => t('Warning', 'app.config.logging.level'),
Logger::$levels[Logger::INFO] => t('Information', 'app.config.logging.level'),
Logger::$levels[Logger::DEBUG] => t('Debug', 'app.config.logging.level')
Logger::$levels[Logger::ERROR] => $this->translate('Error', 'app.config.logging.level'),
Logger::$levels[Logger::WARNING] => $this->translate('Warning', 'app.config.logging.level'),
Logger::$levels[Logger::INFO] => $this->translate('Information', 'app.config.logging.level'),
Logger::$levels[Logger::DEBUG] => $this->translate('Debug', 'app.config.logging.level')
)
)
);
@ -65,9 +62,12 @@ class LoggingConfigForm extends Form
'logging_application',
array(
'required' => true,
'label' => t('Application Prefix'),
'description' => t('The name of the application by which to prefix syslog messages.'),
'value' => 'icingaweb',
'label' => $this->translate('Application Prefix'),
'description' => $this->translate(
'The name of the application by which to prefix syslog messages.'
),
'requirement' => $this->translate('The application prefix must not contain whitespace.'),
'value' => 'icingaweb2',
'validators' => array(
array(
'Regex',
@ -75,7 +75,9 @@ class LoggingConfigForm extends Form
array(
'pattern' => '/^[^\W]+$/',
'messages' => array(
'regexNotMatch' => 'The application prefix cannot contain any whitespaces.'
'regexNotMatch' => $this->translate(
'The application prefix must not contain whitespace.'
)
)
)
)
@ -91,8 +93,8 @@ class LoggingConfigForm extends Form
// 'logging_facility',
// array(
// 'required' => true,
// 'label' => t('Facility'),
// 'description' => t('The syslog facility to utilize.'),
// 'label' => $this->translate('Facility'),
// 'description' => $this->translate('The syslog facility to utilize.'),
// 'multiOptions' => array(
// 'user' => 'LOG_USER'
// )
@ -104,24 +106,14 @@ class LoggingConfigForm extends Form
'logging_file',
array(
'required' => true,
'label' => t('File path'),
'description' => t('The full path to the log file to write messages to.'),
'value' => $this->getDefaultLogDir(),
'validators' => array(new WritablePathValidator())
'label' => $this->translate('File path'),
'description' => $this->translate('The full path to the log file to write messages to.'),
'value' => '/var/log/icingaweb2/icingaweb2.log',
'validators' => array('WritablePathValidator')
)
);
}
return $this;
}
/**
* Return the default logging directory for type 'file'
*
* @return string
*/
protected function getDefaultLogDir()
{
return realpath(Icinga::app()->getApplicationDir('../var/log/icingaweb.log'));
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config;
@ -20,7 +19,8 @@ class GeneralConfigForm extends ConfigForm
public function init()
{
$this->setName('form_config_general');
$this->setSubmitLabel(t('Save Changes'));
$this->setSubmitLabel($this->translate('Save Changes'));
$this->setTitle($this->translate('General Configuration'));
}
/**
@ -52,7 +52,7 @@ class GeneralConfigForm extends ConfigForm
}
if ($this->save()) {
Notification::success(t('New configuration has successfully been stored'));
Notification::success($this->translate('New configuration has successfully been stored'));
} else {
return false;
}

View File

@ -1,13 +1,9 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Resource;
use Exception;
use Icinga\Web\Form;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Application\Platform;
/**
@ -24,7 +20,9 @@ class DbResourceForm extends Form
}
/**
* @see Form::createElements()
* Create and add elements to this form
*
* @param array $formData The data sent by the user
*/
public function createElements(array $formData)
{
@ -41,8 +39,8 @@ class DbResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -50,8 +48,9 @@ class DbResourceForm extends Form
'db',
array(
'required' => true,
'label' => t('Database Type'),
'description' => t('The type of SQL database'),
'autosubmit' => true,
'label' => $this->translate('Database Type'),
'description' => $this->translate('The type of SQL database'),
'multiOptions' => $dbChoices
)
);
@ -60,8 +59,8 @@ class DbResourceForm extends Form
'host',
array (
'required' => true,
'label' => t('Host'),
'description' => t('The hostname of the database'),
'label' => $this->translate('Host'),
'description' => $this->translate('The hostname of the database'),
'value' => 'localhost'
)
);
@ -69,10 +68,11 @@ class DbResourceForm extends Form
'number',
'port',
array(
'required' => true,
'label' => t('Port'),
'description' => t('The port to use'),
'value' => 3306
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'),
'value' => ! array_key_exists('db', $formData) || $formData['db'] === 'mysql' ? 3306 : 5432
)
);
$this->addElement(
@ -80,8 +80,8 @@ class DbResourceForm extends Form
'dbname',
array(
'required' => true,
'label' => t('Database Name'),
'description' => t('The name of the database to use')
'label' => $this->translate('Database Name'),
'description' => $this->translate('The name of the database to use')
)
);
$this->addElement(
@ -89,8 +89,8 @@ class DbResourceForm extends Form
'username',
array (
'required' => true,
'label' => t('Username'),
'description' => t('The user name to use for authentication')
'label' => $this->translate('Username'),
'description' => $this->translate('The user name to use for authentication')
)
);
$this->addElement(
@ -99,43 +99,11 @@ class DbResourceForm extends Form
array(
'required' => true,
'renderPassword' => true,
'label' => t('Password'),
'description' => t('The password to use for authentication')
'label' => $this->translate('Password'),
'description' => $this->translate('The password to use for authentication')
)
);
return $this;
}
/**
* Validate that the current configuration points to a valid resource
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
if (false === static::isValidResource($this)) {
return false;
}
}
/**
* Validate the resource configuration by trying to connect with it
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public static function isValidResource(Form $form)
{
try {
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
$resource->getConnection()->getConnection();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
return false;
}
return true;
}
}

View File

@ -1,11 +1,10 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Resource;
use Zend_Validate_Callback;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\ReadablePathValidator;
/**
* Form class for adding/modifying file resources
@ -30,8 +29,8 @@ class FileResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -39,18 +38,27 @@ class FileResourceForm extends Form
'filename',
array(
'required' => true,
'label' => t('Filepath'),
'description' => t('The filename to fetch information from'),
'validators' => array(new ReadablePathValidator())
'label' => $this->translate('Filepath'),
'description' => $this->translate('The filename to fetch information from'),
'validators' => array('ReadablePathValidator')
)
);
$callbackValidator = new Zend_Validate_Callback(function ($value) {
return @preg_match($value, '') !== false;
});
$callbackValidator->setMessage(
$this->translate('"%value%" is not a valid regular expression.'),
Zend_Validate_Callback::INVALID_VALUE
);
$this->addElement(
'text',
'fields',
array(
'required' => true,
'label' => t('Pattern'),
'description' => t('The regular expression by which to identify columns')
'label' => $this->translate('Pattern'),
'description' => $this->translate('The pattern by which to identify columns.'),
'requirement' => $this->translate('The column pattern must be a valid regular expression.'),
'validators' => array($callbackValidator)
)
);

View File

@ -1,13 +1,10 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Resource;
use Exception;
use Icinga\Web\Form;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Protocol\Ldap\LdapConnection;
/**
* Form class for adding/modifying ldap resources
@ -23,17 +20,23 @@ class LdapResourceForm extends Form
}
/**
* @see Form::createElements()
* Create and add elements to this form
*
* @param array $formData The data sent by the user
*/
public function createElements(array $formData)
{
$defaultPort = ! array_key_exists('encryption', $formData) || $formData['encryption'] !== LdapConnection::LDAPS
? 389
: 636;
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -41,8 +44,10 @@ class LdapResourceForm extends Form
'hostname',
array(
'required' => true,
'label' => t('Host'),
'description' => t('The hostname or address of the LDAP server to use for authentication'),
'label' => $this->translate('Host'),
'description' => $this->translate(
'The hostname or address of the LDAP server to use for authentication'
),
'value' => 'localhost'
)
);
@ -50,79 +55,80 @@ class LdapResourceForm extends Form
'number',
'port',
array(
'required' => true,
'label' => t('Port'),
'description' => t('The port of the LDAP server to use for authentication'),
'value' => 389
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port of the LDAP server to use for authentication'),
'value' => $defaultPort
)
);
$this->addElement(
'select',
'encryption',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Encryption'),
'description' => $this->translate(
'Whether to encrypt communication. Choose STARTTLS or LDAPS for encrypted communication or'
. ' none for unencrypted communication'
),
'multiOptions' => array(
'none' => $this->translate('None', 'resource.ldap.encryption'),
LdapConnection::STARTTLS => 'STARTTLS',
LdapConnection::LDAPS => 'LDAPS'
)
)
);
if (isset($formData['encryption']) && $formData['encryption'] !== 'none') {
// TODO(jom): Do not show this checkbox unless the connection is actually failing due to certificate errors
$this->addElement(
'checkbox',
'reqcert',
array(
'required' => true,
'label' => $this->translate('Require Certificate'),
'description' => $this->translate(
'When checked, the LDAP server must provide a valid and known (trusted) certificate.'
),
'value' => 1
)
);
}
$this->addElement(
'text',
'root_dn',
array(
'required' => true,
'label' => t('Root DN'),
'description' => t('Only the root and its child nodes will be accessible on this resource.')
'label' => $this->translate('Root DN'),
'description' => $this->translate(
'Only the root and its child nodes will be accessible on this resource.'
)
)
);
$this->addElement(
'text',
'bind_dn',
array(
'required' => true,
'label' => t('Bind DN'),
'description' => t('The user dn to use for querying the ldap server')
'label' => $this->translate('Bind DN'),
'description' => $this->translate(
'The user dn to use for querying the ldap server. Leave the dn and password empty for attempting'
. ' an anonymous bind'
)
)
);
$this->addElement(
'password',
'bind_pw',
array(
'required' => true,
'renderPassword' => true,
'label' => t('Bind Password'),
'description' => t('The password to use for querying the ldap server')
'label' => $this->translate('Bind Password'),
'description' => $this->translate('The password to use for querying the ldap server')
)
);
return $this;
}
/**
* Validate that the current configuration points to a valid resource
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
if (false === static::isValidResource($this)) {
return false;
}
}
/**
* Validate the resource configuration by trying to connect with it
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public static function isValidResource(Form $form)
{
try {
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
if (false === $resource->testCredentials(
$form->getElement('bind_dn')->getValue(),
$form->getElement('bind_pw')->getValue()
)
) {
throw new Exception();
}
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
return false;
}
return true;
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Resource;
@ -33,8 +32,8 @@ class LivestatusResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -42,9 +41,9 @@ class LivestatusResourceForm extends Form
'socket',
array(
'required' => true,
'label' => t('Socket'),
'description' => t('The path to your livestatus socket used for querying monitoring data'),
'value' => realpath(Icinga::app()->getApplicationDir() . '/../var/rw/livestatus')
'label' => $this->translate('Socket'),
'description' => $this->translate('The path to your livestatus socket used for querying monitoring data'),
'value' => '/var/run/icinga2/cmd/livestatus'
)
);
@ -75,8 +74,10 @@ class LivestatusResourceForm extends Form
try {
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
$resource->connect()->disconnect();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
} catch (Exception $_) {
$form->addError(
$form->translate('Connectivity validation failed, connection to the given resource not possible.')
);
return false;
}

View File

@ -0,0 +1,147 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Resource;
use Icinga\Application\Icinga;
use Icinga\Data\ConfigObject;
use Icinga\Forms\Config\ResourceConfigForm;
use Icinga\Web\Form;
use Icinga\Util\File;
use Zend_Validate_Callback;
/**
* Form class for adding/modifying ssh identity resources
*/
class SshResourceForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_resource_ssh');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
'text',
'user',
array(
'required' => true,
'label' => $this->translate('User'),
'description' => $this->translate(
'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be'
. ' possible for this user'
)
)
);
if ($this->getRequest()->getActionName() != 'editresource') {
$callbackValidator = new Zend_Validate_Callback(function ($value) {
if (openssl_pkey_get_private($value) === false) {
return false;
}
return true;
});
$callbackValidator->setMessage(
$this->translate('The given SSH key is invalid'),
Zend_Validate_Callback::INVALID_VALUE
);
$this->addElement(
'textarea',
'private_key',
array(
'required' => true,
'label' => $this->translate('Private Key'),
'description' => $this->translate('The private key which will be used for the SSH connections'),
'class' => 'resource ssh-identity',
'validators' => array($callbackValidator)
)
);
} else {
$resourceName = $formData['name'];
$this->addElement(
'note',
'private_key_note',
array(
'escape' => false,
'label' => $this->translate('Private Key'),
'value' => sprintf(
'<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>',
$this->getView()->url('config/removeresource', array('resource' => $resourceName)),
sprintf($this->translate(
'Remove the %s resource'
), $resourceName),
$this->translate('To modify the private key you must recreate this resource.')
)
)
);
}
return $this;
}
/**
* Remove the assigned key to the resource
*
* @param ConfigObject $config
*
* @return bool
*/
public static function beforeRemove(ConfigObject $config)
{
$file = $config->private_key;
if (file_exists($file)) {
unlink($file);
return true;
}
return false;
}
/**
* Creates the assigned key to the resource
*
* @param ResourceConfigForm $form
*
* @return bool
*/
public static function beforeAdd(ResourceConfigForm $form)
{
$configDir = Icinga::app()->getConfigDir();
$user = $form->getElement('user')->getValue();
$filePath = $configDir . '/ssh/' . $user;
if (! file_exists($filePath)) {
$file = File::create($filePath, 0600);
} else {
$form->error(
sprintf($form->translate('The private key for the user "%s" is already exists.'), $user)
);
return false;
}
$file->fwrite($form->getElement('private_key')->getValue());
$form->getElement('private_key')->setValue($configDir . '/ssh/' . $user);
return true;
}
}

View File

@ -1,18 +1,23 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config;
use InvalidArgumentException;
use Icinga\Web\Notification;
use Icinga\Application\Platform;
use Icinga\Exception\ConfigurationError;
use Icinga\Data\ConfigObject;
use Icinga\Data\Inspectable;
use Icinga\Data\Inspection;
use Icinga\Data\ResourceFactory;
use Icinga\Forms\ConfigForm;
use Icinga\Forms\Config\Resource\DbResourceForm;
use Icinga\Forms\Config\Resource\FileResourceForm;
use Icinga\Forms\Config\Resource\LdapResourceForm;
use Icinga\Forms\Config\Resource\LivestatusResourceForm;
use Icinga\Application\Platform;
use Icinga\Exception\ConfigurationError;
use Icinga\Forms\Config\Resource\SshResourceForm;
use Icinga\Web\Form;
use Icinga\Web\Notification;
class ResourceConfigForm extends ConfigForm
{
@ -22,7 +27,8 @@ class ResourceConfigForm extends ConfigForm
public function init()
{
$this->setName('form_config_resource');
$this->setSubmitLabel(t('Save Changes'));
$this->setSubmitLabel($this->translate('Save Changes'));
$this->setValidatePartial(true);
}
/**
@ -42,8 +48,10 @@ class ResourceConfigForm extends ConfigForm
return new LivestatusResourceForm();
} elseif ($type === 'file') {
return new FileResourceForm();
} elseif ($type === 'ssh') {
return new SshResourceForm();
} else {
throw new InvalidArgumentException(sprintf(t('Invalid resource type "%s" provided'), $type));
throw new InvalidArgumentException(sprintf($this->translate('Invalid resource type "%s" provided'), $type));
}
}
@ -54,17 +62,17 @@ class ResourceConfigForm extends ConfigForm
*
* @param array $values The values to extend the configuration with
*
* @return self
* @return $this
*
* @thrwos InvalidArgumentException In case the resource does already exist
* @throws InvalidArgumentException In case the resource does already exist
*/
public function add(array $values)
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(t('Resource name missing'));
throw new InvalidArgumentException($this->translate('Resource name missing'));
} elseif ($this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Resource already exists'));
throw new InvalidArgumentException($this->translate('Resource already exists'));
}
unset($values['name']);
@ -85,11 +93,11 @@ class ResourceConfigForm extends ConfigForm
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(t('Old resource name missing'));
throw new InvalidArgumentException($this->translate('Old resource name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(t('New resource name missing'));
throw new InvalidArgumentException($this->translate('New resource name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown resource provided'));
throw new InvalidArgumentException($this->translate('Unknown resource provided'));
}
$resourceConfig = $this->config->getSection($name);
@ -111,12 +119,17 @@ class ResourceConfigForm extends ConfigForm
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(t('Resource name missing'));
throw new InvalidArgumentException($this->translate('Resource name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown resource provided'));
throw new InvalidArgumentException($this->translate('Unknown resource provided'));
}
$resourceConfig = $this->config->getSection($name);
$resourceForm = $this->getResourceForm($resourceConfig->type);
if (method_exists($resourceForm, 'beforeRemove')) {
$resourceForm::beforeRemove($resourceConfig);
}
$this->config->removeSection($name);
return $resourceConfig;
}
@ -131,9 +144,12 @@ class ResourceConfigForm extends ConfigForm
*/
public function onSuccess()
{
$resourceForm = $this->getResourceForm($this->getElement('type')->getValue());
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
$resourceForm = $this->getResourceForm($this->getElement('type')->getValue());
if (method_exists($resourceForm, 'isValidResource') && false === $resourceForm::isValidResource($this)) {
$inspection = static::inspectResource($this);
if ($inspection !== null && $inspection->hasError()) {
$this->error($inspection->getError());
$this->addElement($this->getForceCreationCheckbox());
return false;
}
@ -142,15 +158,20 @@ class ResourceConfigForm extends ConfigForm
$resource = $this->request->getQuery('resource');
try {
if ($resource === null) { // create new resource
if (method_exists($resourceForm, 'beforeAdd')) {
if (! $resourceForm::beforeAdd($this)) {
return false;
}
}
$this->add($this->getValues());
$message = t('Resource "%s" has been successfully created');
$message = $this->translate('Resource "%s" has been successfully created');
} else { // edit existing resource
$this->edit($resource, $this->getValues());
$message = t('Resource "%s" has been successfully changed');
$message = $this->translate('Resource "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
return false;
}
if ($this->save()) {
@ -172,9 +193,9 @@ class ResourceConfigForm extends ConfigForm
$resource = $this->request->getQuery('resource');
if ($resource !== null) {
if ($resource === '') {
throw new ConfigurationError(t('Resource name missing'));
throw new ConfigurationError($this->translate('Resource name missing'));
} elseif (! $this->config->hasSection($resource)) {
throw new ConfigurationError(t('Unknown resource provided'));
throw new ConfigurationError($this->translate('Unknown resource provided'));
}
$configValues = $this->config->getSection($resource)->toArray();
@ -197,8 +218,8 @@ class ResourceConfigForm extends ConfigForm
array(
'order' => 0,
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
'label' => $this->translate('Force Changes'),
'description' => $this->translate('Check this box to enforce changes without connectivity validation')
)
);
}
@ -211,14 +232,15 @@ class ResourceConfigForm extends ConfigForm
$resourceType = isset($formData['type']) ? $formData['type'] : 'db';
$resourceTypes = array(
'file' => t('File'),
'file' => $this->translate('File'),
'livestatus' => 'Livestatus',
'ssh' => $this->translate('SSH Identity'),
);
if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) {
$resourceTypes['ldap'] = 'LDAP';
}
if ($resourceType === 'db' || Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
$resourceTypes['db'] = t('SQL Database');
$resourceTypes['db'] = $this->translate('SQL Database');
}
$this->addElement(
@ -227,8 +249,8 @@ class ResourceConfigForm extends ConfigForm
array(
'required' => true,
'autosubmit' => true,
'label' => t('Resource Type'),
'description' => t('The type of resource'),
'label' => $this->translate('Resource Type'),
'description' => $this->translate('The type of resource'),
'multiOptions' => $resourceTypes,
'value' => $resourceType
)
@ -241,4 +263,103 @@ class ResourceConfigForm extends ConfigForm
$this->addElements($this->getResourceForm($resourceType)->createElements($formData)->getElements());
}
/**
* Create a resource by using the given form's values and return its inspection results
*
* @param Form $form
*
* @return Inspection
*/
public static function inspectResource(Form $form)
{
if ($form->getValue('type') !== 'ssh') {
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
if ($resource instanceof Inspectable) {
return $resource->inspect();
}
}
}
/**
* Run the configured resource's inspection checks and show the result, if necessary
*
* This will only run any validation if the user pushed the 'resource_validation' button.
*
* @param array $formData
*
* @return bool
*/
public function isValidPartial(array $formData)
{
if ($this->getElement('resource_validation')->isChecked() && parent::isValid($formData)) {
$inspection = static::inspectResource($this);
if ($inspection !== null) {
$join = function ($e) use (& $join) {
return is_string($e) ? $e : join("\n", array_map($join, $e));
};
$this->addElement(
'note',
'inspection_output',
array(
'order' => 0,
'value' => '<strong>' . $this->translate('Validation Log') . "</strong>\n\n"
. join("\n", array_map($join, $inspection->toArray())),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'pre', 'class' => 'log-output')),
)
)
);
if ($inspection->hasError()) {
$this->warning(sprintf(
$this->translate('Failed to successfully validate the configuration: %s'),
$inspection->getError()
));
return false;
}
}
$this->info($this->translate('The configuration has been successfully validated.'));
}
return true;
}
/**
* Add a submit button to this form and one to manually validate the configuration
*
* Calls parent::addSubmitButton() to add the submit button.
*
* @return $this
*/
public function addSubmitButton()
{
parent::addSubmitButton()
->getElement('btn_submit')
->setDecorators(array('ViewHelper'));
$this->addElement(
'submit',
'resource_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'decorators' => array('ViewHelper')
)
);
$this->addDisplayGroup(
array('btn_submit', 'resource_validation'),
'submit_validation',
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group'))
)
)
);
return $this;
}
}

View File

@ -0,0 +1,191 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\User;
use Exception;
use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Web\Form;
use Icinga\Web\Notification;
/**
* Form for creating one or more group memberships
*/
class CreateMembershipForm extends Form
{
/**
* The user group backends to fetch groups from
*
* Each backend must implement the Icinga\Data\Extensible and Icinga\Data\Selectable interface.
*
* @var array
*/
protected $backends;
/**
* The username to create memberships for
*
* @var string
*/
protected $userName;
/**
* Set the user group backends to fetch groups from
*
* @param array $backends
*
* @return $this
*/
public function setBackends($backends)
{
$this->backends = $backends;
return $this;
}
/**
* Set the username to create memberships for
*
* @param string $userName
*
* @return $this
*/
public function setUsername($userName)
{
$this->userName = $userName;
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData The data sent by the user
*/
public function createElements(array $formData)
{
$query = $this->createDataSource()->select()->from('group', array('group_name', 'backend_name'));
$options = array();
foreach ($query as $row) {
$options[$row->backend_name . ';' . $row->group_name] = $row->group_name . ' (' . $row->backend_name . ')';
}
$this->addElement(
'multiselect',
'groups',
array(
'required' => true,
'multiOptions' => $options,
'label' => $this->translate('Groups'),
'description' => sprintf(
$this->translate('Select one or more groups where to add %s as member'),
$this->userName
),
'class' => 'grant-permissions'
)
);
$this->setTitle(sprintf($this->translate('Create memberships for %s'), $this->userName));
$this->setSubmitLabel($this->translate('Create'));
}
/**
* Instantly redirect back in case the user is already a member of all groups
*/
public function onRequest()
{
if ($this->createDataSource()->select()->from('group')->count() === 0) {
Notification::info(sprintf($this->translate('User %s is already a member of all groups'), $this->userName));
$this->getResponse()->redirectAndExit($this->getRedirectUrl());
}
}
/**
* Create the memberships for the user
*
* @return bool
*/
public function onSuccess()
{
$backendMap = array();
foreach ($this->backends as $backend) {
$backendMap[$backend->getName()] = $backend;
}
$single = null;
foreach ($this->getValue('groups') as $backendAndGroup) {
list($backendName, $groupName) = explode(';', $backendAndGroup, 2);
try {
$backendMap[$backendName]->insert(
'group_membership',
array(
'group_name' => $groupName,
'user_name' => $this->userName
)
);
} catch (Exception $e) {
Notification::error(sprintf(
$this->translate('Failed to add "%s" as group member for "%s"'),
$this->userName,
$groupName
));
$this->error($e->getMessage());
return false;
}
$single = $single === null;
}
if ($single) {
Notification::success(
sprintf($this->translate('Membership for group %s created successfully'), $groupName)
);
} else {
Notification::success($this->translate('Memberships created successfully'));
}
return true;
}
/**
* Create and return a data source to fetch all groups from all backends where the user is not already a member of
*
* @return ArrayDatasource
*/
protected function createDataSource()
{
$groups = $failures = array();
foreach ($this->backends as $backend) {
try {
$memberships = $backend
->select()
->from('group_membership', array('group_name'))
->where('user_name', $this->userName)
->fetchColumn();
foreach ($backend->select(array('group_name')) as $row) {
if (! in_array($row->group_name, $memberships)) { // TODO(jom): Apply this as native query filter
$row->backend_name = $backend->getName();
$groups[] = $row;
}
}
} catch (Exception $e) {
$failures[] = array($backend->getName(), $e);
}
}
if (empty($groups) && !empty($failures)) {
// In case there are only failures, throw the very first exception again
throw $failures[0][1];
} elseif (! empty($failures)) {
foreach ($failures as $failure) {
Logger::error($failure[1]);
Notification::warning(sprintf(
$this->translate('Failed to fetch any groups from backend %s. Please check your log'),
$failure[0]
));
}
}
return new ArrayDatasource($groups);
}
}

View File

@ -0,0 +1,175 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\User;
use Icinga\Data\Filter\Filter;
use Icinga\Forms\RepositoryForm;
class UserForm extends RepositoryForm
{
/**
* Create and add elements to this form to insert or update a user
*
* @param array $formData The data sent by the user
*/
protected function createInsertElements(array $formData)
{
$this->addElement(
'checkbox',
'is_active',
array(
'required' => true,
'value' => true,
'label' => $this->translate('Active'),
'description' => $this->translate('Prevents the user from logging in if unchecked')
)
);
$this->addElement(
'text',
'user_name',
array(
'required' => true,
'label' => $this->translate('Username')
)
);
$this->addElement(
'text',
'password',
array(
'required' => true,
'label' => $this->translate('Password')
)
);
$this->setTitle($this->translate('Add a new user'));
$this->setSubmitLabel($this->translate('Add'));
}
/**
* Create and add elements to this form to update a user
*
* @param array $formData The data sent by the user
*/
protected function createUpdateElements(array $formData)
{
$this->createInsertElements($formData);
$this->addElement(
'text',
'password',
array(
'label' => $this->translate('Password')
)
);
$this->setTitle(sprintf($this->translate('Edit user %s'), $this->getIdentifier()));
$this->setSubmitLabel($this->translate('Save'));
}
/**
* Update a user
*
* @return bool
*/
protected function onUpdateSuccess()
{
if (parent::onUpdateSuccess()) {
if (($newName = $this->getValue('user_name')) !== $this->getIdentifier()) {
$this->getRedirectUrl()->setParam('user', $newName);
}
return true;
}
return false;
}
/**
* Retrieve all form element values
*
* Strips off the password if null or the empty string.
*
* @param bool $suppressArrayNotation
*
* @return array
*/
public function getValues($suppressArrayNotation = false)
{
$values = parent::getValues($suppressArrayNotation);
if (! $values['password']) {
unset($values['password']);
}
return $values;
}
/**
* Create and add elements to this form to delete a user
*
* @param array $formData The data sent by the user
*/
protected function createDeleteElements(array $formData)
{
$this->setTitle(sprintf($this->translate('Remove user %s?'), $this->getIdentifier()));
$this->setSubmitLabel($this->translate('Yes'));
}
/**
* Create and return a filter to use when updating or deleting a user
*
* @return Filter
*/
protected function createFilter()
{
return Filter::where('user_name', $this->getIdentifier());
}
/**
* Return a notification message to use when inserting a user
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
protected function getInsertMessage($success)
{
if ($success) {
return $this->translate('User added successfully');
} else {
return $this->translate('Failed to add user');
}
}
/**
* Return a notification message to use when updating a user
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
protected function getUpdateMessage($success)
{
if ($success) {
return sprintf($this->translate('User "%s" has been edited'), $this->getIdentifier());
} else {
return sprintf($this->translate('Failed to edit user "%s"'), $this->getIdentifier());
}
}
/**
* Return a notification message to use when deleting a user
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
protected function getDeleteMessage($success)
{
if ($success) {
return sprintf($this->translate('User "%s" has been removed'), $this->getIdentifier());
} else {
return sprintf($this->translate('Failed to remove user "%s"'), $this->getIdentifier());
}
}
}

View File

@ -0,0 +1,96 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserBackend;
use Icinga\Web\Form;
/**
* Form class for adding/modifying database user backends
*/
class DbBackendForm extends Form
{
/**
* The database resource names the user can choose from
*
* @var array
*/
protected $resources;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_db');
}
/**
* Set the resource names the user can choose from
*
* @param array $resources The resources to choose from
*
* @return $this
*/
public function setResources(array $resources)
{
$this->resources = $resources;
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider 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 \':\'.'
)
)
)
)
)
)
);
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => $this->translate('Database Connection'),
'description' => $this->translate(
'The database connection to use for authenticating with this provider'
),
'multiOptions' => !empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true,
'value' => 'db'
)
);
}
}

View File

@ -0,0 +1,97 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserBackend;
use Zend_Validate_Callback;
use Icinga\Web\Form;
/**
* Form class for adding/modifying user backends of type "external"
*/
class ExternalBackendForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_external');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The backend name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
$callbackValidator = new Zend_Validate_Callback(function ($value) {
return @preg_match($value, '') !== false;
});
$callbackValidator->setMessage(
$this->translate('"%value%" is not a valid regular expression.'),
Zend_Validate_Callback::INVALID_VALUE
);
$this->addElement(
'text',
'strip_username_regexp',
array(
'label' => $this->translate('Filter Pattern'),
'description' => $this->translate(
'The filter to use to strip specific parts off from usernames.'
. ' Leave empty if you do not want to strip off anything.'
),
'requirement' => $this->translate('The filter pattern must be a valid regular expression.'),
'validators' => array($callbackValidator)
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true,
'value' => 'external'
)
);
return $this;
}
/**
* Validate the configuration by creating a backend and requesting the user count
*
* Returns always true as backends of type "external" are just "passive" backends.
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public static function isValidUserBackend(Form $form)
{
return true;
}
}

View File

@ -0,0 +1,241 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserBackend;
use Exception;
use Icinga\Data\ResourceFactory;
use Icinga\Web\Form;
/**
* Form class for adding/modifying LDAP user backends
*/
class LdapBackendForm extends Form
{
/**
* The ldap resource names the user can choose from
*
* @var array
*/
protected $resources;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_ldap');
}
/**
* Set the resource names the user can choose from
*
* @param array $resources The resources to choose from
*
* @return $this
*/
public function setResources(array $resources)
{
$this->resources = $resources;
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
$isAd = isset($formData['type']) ? $formData['type'] === 'msldap' : false;
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider 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 \':\'.'
)
)
)
)
)
)
);
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => $this->translate('LDAP Connection'),
'description' => $this->translate(
'The LDAP connection to use for authenticating with this provider.'
),
'multiOptions' => !empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
)
);
$baseDn = null;
$hasAdOid = false;
if (! $isAd && !empty($this->resources)) {
$this->addElement(
'button',
'discovery_btn',
array(
'type' => 'submit',
'value' => 'discovery_btn',
'label' => $this->translate('Discover', 'A button to discover LDAP capabilities'),
'title' => $this->translate(
'Push to fill in the chosen connection\'s default settings.'
),
'decorators' => array(
array('ViewHelper', array('separator' => '')),
array('HtmlTag', array('tag' => 'div', 'class' => 'element'))
),
'formnovalidate' => 'formnovalidate'
)
);
$this->addDisplayGroup(
array('resource', 'discovery_btn'),
'connection_discovery',
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group'))
)
)
);
if ($this->getElement('discovery_btn')->isChecked()) {
$connection = ResourceFactory::create(
isset($formData['resource']) ? $formData['resource'] : reset($this->resources)
);
try {
$capabilities = $connection->bind()->getCapabilities();
$baseDn = $capabilities->getDefaultNamingContext();
$hasAdOid = $capabilities->isActiveDirectory();
} catch (Exception $e) {
$this->warning(sprintf(
$this->translate('Failed to discover the chosen LDAP connection: %s'),
$e->getMessage()
));
}
}
}
if ($isAd || $hasAdOid) {
// ActiveDirectory defaults
$userClass = 'user';
$filter = '!(objectClass=computer)';
$userNameAttribute = 'sAMAccountName';
} else {
// OpenLDAP defaults
$userClass = 'inetOrgPerson';
$filter = null;
$userNameAttribute = 'uid';
}
$this->addElement(
'text',
'user_class',
array(
'preserveDefault' => true,
'required' => ! $isAd,
'ignore' => $isAd,
'disabled' => $isAd ?: null,
'label' => $this->translate('LDAP User Object Class'),
'description' => $this->translate('The object class used for storing users on the LDAP server.'),
'value' => $userClass
)
);
$this->addElement(
'text',
'filter',
array(
'preserveDefault' => true,
'allowEmpty' => true,
'value' => $filter,
'label' => $this->translate('LDAP Filter'),
'description' => $this->translate(
'An additional filter to use when looking up users using the specified connection. '
. 'Leave empty to not to use any additional filter rules.'
),
'requirement' => $this->translate(
'The filter needs to be expressed as standard LDAP expression.'
. ' (e.g. &(foo=bar)(bar=foo) or foo=bar)'
),
'validators' => array(
array(
'Callback',
false,
array(
'callback' => function ($v) {
// This is not meant to be a full syntax check. It will just
// ensure that we can safely strip unnecessary parentheses.
$v = trim($v);
return ! $v || $v[0] !== '(' || (
strpos($v, ')(') !== false ? substr($v, -2) === '))' : substr($v, -1) === ')'
);
},
'messages' => array(
'callbackValue' => $this->translate('The filter is invalid. Please check your syntax.')
)
)
)
)
)
);
$this->addElement(
'text',
'user_name_attribute',
array(
'preserveDefault' => true,
'required' => ! $isAd,
'ignore' => $isAd,
'disabled' => $isAd ?: null,
'label' => $this->translate('LDAP User Name Attribute'),
'description' => $this->translate(
'The attribute name used for storing the user name on the LDAP server.'
),
'value' => $userNameAttribute
)
);
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true,
'value' => $isAd ? 'msldap' : 'ldap'
)
);
$this->addElement(
'text',
'base_dn',
array(
'preserveDefault' => true,
'required' => false,
'label' => $this->translate('LDAP Base DN'),
'description' => $this->translate(
'The path where users can be found on the LDAP server. Leave ' .
'empty to select all users available using the specified connection.'
),
'value' => $baseDn
)
);
}
}

View File

@ -0,0 +1,485 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config;
use InvalidArgumentException;
use Icinga\Application\Config;
use Icinga\Authentication\User\UserBackend;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Data\ConfigObject;
use Icinga\Data\Inspectable;
use Icinga\Data\Inspection;
use Icinga\Forms\ConfigForm;
use Icinga\Forms\Config\UserBackend\ExternalBackendForm;
use Icinga\Forms\Config\UserBackend\DbBackendForm;
use Icinga\Forms\Config\UserBackend\LdapBackendForm;
use Icinga\Web\Form;
/**
* Form for managing user backends
*/
class UserBackendConfigForm extends ConfigForm
{
/**
* The available user backend resources split by type
*
* @var array
*/
protected $resources;
/**
* The backend to load when displaying the form for the first time
*
* @var string
*/
protected $backendToLoad;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend');
$this->setSubmitLabel($this->translate('Save Changes'));
$this->setValidatePartial(true);
}
/**
* Set the resource configuration to use
*
* @param Config $resourceConfig The resource configuration
*
* @return $this
*
* @throws ConfigurationError In case there are no valid resources for authentication available
*/
public function setResourceConfig(Config $resourceConfig)
{
$resources = array();
foreach ($resourceConfig as $name => $resource) {
if (in_array($resource->type, array('db', 'ldap'))) {
$resources[$resource->type][] = $name;
}
}
if (empty($resources)) {
$externalBackends = $this->config->toArray();
array_walk(
$externalBackends,
function (& $authBackendCfg) {
if (! isset($authBackendCfg['backend']) || $authBackendCfg['backend'] !== 'external') {
$authBackendCfg = null;
}
}
);
if (count(array_filter($externalBackends)) > 0 && (
$this->backendToLoad === null || !isset($externalBackends[$this->backendToLoad])
)) {
throw new ConfigurationError($this->translate(
'Could not find any valid user backend resources.'
. ' Please configure a resource for authentication first.'
));
}
}
$this->resources = $resources;
return $this;
}
/**
* Return a form object for the given backend type
*
* @param string $type The backend type for which to return a form
*
* @return Form
*
* @throws InvalidArgumentException In case the given backend type is invalid
*/
public function getBackendForm($type)
{
switch ($type)
{
case 'db':
$form = new DbBackendForm();
$form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array());
break;
case 'ldap':
case 'msldap':
$form = new LdapBackendForm();
$form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array());
break;
case 'external':
$form = new ExternalBackendForm();
break;
default:
throw new InvalidArgumentException(
sprintf($this->translate('Invalid backend type "%s" provided'), $type)
);
}
return $form;
}
/**
* Populate the form with the given backend's config
*
* @param string $name
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function load($name)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No user backend called "%s" found', $name);
}
$this->backendToLoad = $name;
return $this;
}
/**
* Add a new user backend
*
* The backend to add is identified by the array-key `name'.
*
* @param array $data
*
* @return $this
*
* @throws InvalidArgumentException In case $data does not contain a backend name
* @throws IcingaException In case a backend with the same name already exists
*/
public function add(array $data)
{
if (! isset($data['name'])) {
throw new InvalidArgumentException('Key \'name\' missing');
}
$backendName = $data['name'];
if ($this->config->hasSection($backendName)) {
throw new IcingaException(
$this->translate('A user backend with the name "%s" does already exist'),
$backendName
);
}
unset($data['name']);
$this->config->setSection($backendName, $data);
return $this;
}
/**
* Edit a user backend
*
* @param string $name
* @param array $data
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function edit($name, array $data)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No user backend called "%s" found', $name);
}
$backendConfig = $this->config->getSection($name);
if (isset($data['name'])) {
if ($data['name'] !== $name) {
$this->config->removeSection($name);
$name = $data['name'];
}
unset($data['name']);
}
$backendConfig->merge($data);
foreach ($backendConfig->toArray() as $k => $v) {
if ($v === null) {
unset($backendConfig->$k);
}
}
$this->config->setSection($name, $backendConfig);
return $this;
}
/**
* Remove a user backend
*
* @param string $name
*
* @return $this
*/
public function delete($name)
{
$this->config->removeSection($name);
return $this;
}
/**
* Move the given user backend up or down in order
*
* @param string $name The name of the backend to be moved
* @param int $position The new (absolute) position of the backend
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function move($name, $position)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No user backend called "%s" found', $name);
}
$backendOrder = $this->config->keys();
array_splice($backendOrder, array_search($name, $backendOrder), 1);
array_splice($backendOrder, $position, 0, $name);
$newConfig = array();
foreach ($backendOrder as $backendName) {
$newConfig[$backendName] = $this->config->getSection($backendName);
}
$config = Config::fromArray($newConfig);
$this->config = $config->setConfigFile($this->config->getConfigFile());
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
$backendTypes = array();
$backendType = isset($formData['type']) ? $formData['type'] : null;
if (isset($this->resources['db'])) {
$backendTypes['db'] = $this->translate('Database');
}
if (isset($this->resources['ldap'])) {
$backendTypes['ldap'] = 'LDAP';
$backendTypes['msldap'] = 'ActiveDirectory';
}
$externalBackends = array_filter(
$this->config->toArray(),
function ($authBackendCfg) {
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'external';
}
);
if ($backendType === 'external' || empty($externalBackends)) {
$backendTypes['external'] = $this->translate('External');
}
if ($backendType === null) {
$backendType = key($backendTypes);
}
$this->addElement(
'select',
'type',
array(
'ignore' => true,
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Backend Type'),
'description' => $this->translate(
'The type of the resource to use for this authenticaton provider'
),
'multiOptions' => $backendTypes
)
);
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
// In case another error occured and the checkbox was displayed before
$this->addSkipValidationCheckbox();
}
$this->addSubForm($this->getBackendForm($backendType)->create($formData), 'backend_form');
}
/**
* Populate the configuration of the backend to load
*/
public function onRequest()
{
if ($this->backendToLoad) {
$data = $this->config->getSection($this->backendToLoad)->toArray();
$data['name'] = $this->backendToLoad;
$data['type'] = $data['backend'];
$this->populate($data);
}
}
/**
* 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;
}
/**
* Return whether the given values are valid
*
* @param array $formData The data to validate
*
* @return bool
*/
public function isValid($formData)
{
if (! parent::isValid($formData)) {
return false;
}
if (($el = $this->getElement('skip_validation')) === null || false === $el->isChecked()) {
$inspection = static::inspectUserBackend($this);
if ($inspection && $inspection->hasError()) {
$this->error($inspection->getError());
if ($el === null) {
$this->addSkipValidationCheckbox();
}
return false;
}
}
return true;
}
/**
* Create a user backend by using the given form's values and return its inspection results
*
* Returns null for non-inspectable backends.
*
* @param Form $form
*
* @return Inspection|null
*/
public static function inspectUserBackend(Form $form)
{
$backend = UserBackend::create(null, new ConfigObject($form->getValues()));
if ($backend instanceof Inspectable) {
return $backend->inspect();
}
}
/**
* Add a checkbox to the form by which the user can skip the connection validation
*/
protected function addSkipValidationCheckbox()
{
$this->addElement(
'checkbox',
'skip_validation',
array(
'order' => 0,
'ignore' => true,
'required' => true,
'label' => $this->translate('Skip Validation'),
'description' => $this->translate(
'Check this box to enforce changes without validating that authentication is possible.'
)
)
);
}
/**
* Run the configured backend's inspection checks and show the result, if necessary
*
* This will only run any validation if the user pushed the 'backend_validation' button.
*
* @param array $formData
*
* @return bool
*/
public function isValidPartial(array $formData)
{
if ($this->getElement('backend_validation')->isChecked() && parent::isValid($formData)) {
$inspection = static::inspectUserBackend($this);
if ($inspection !== null) {
$join = function ($e) use (& $join) {
return is_string($e) ? $e : join("\n", array_map($join, $e));
};
$this->addElement(
'note',
'inspection_output',
array(
'order' => 0,
'value' => '<strong>' . $this->translate('Validation Log') . "</strong>\n\n"
. join("\n", array_map($join, $inspection->toArray())),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'pre', 'class' => 'log-output')),
)
)
);
if ($inspection->hasError()) {
$this->warning(sprintf(
$this->translate('Failed to successfully validate the configuration: %s'),
$inspection->getError()
));
return false;
}
}
$this->info($this->translate('The configuration has been successfully validated.'));
}
return true;
}
/**
* Add a submit button to this form and one to manually validate the configuration
*
* Calls parent::addSubmitButton() to add the submit button.
*
* @return $this
*/
public function addSubmitButton()
{
parent::addSubmitButton()
->getElement('btn_submit')
->setDecorators(array('ViewHelper'));
$this->addElement(
'submit',
'backend_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'decorators' => array('ViewHelper')
)
);
$this->addDisplayGroup(
array('btn_submit', 'backend_validation'),
'submit_validation',
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group'))
)
)
);
return $this;
}
}

View File

@ -1,14 +1,13 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config;
use InvalidArgumentException;
use Icinga\Web\Notification;
use Icinga\Forms\ConfigForm;
use Icinga\Exception\NotFoundError;
use Icinga\Web\Notification;
class AuthenticationBackendReorderForm extends ConfigForm
class UserBackendReorderForm extends ConfigForm
{
/**
* Initialize this form
@ -30,7 +29,9 @@ class AuthenticationBackendReorderForm extends ConfigForm
}
/**
* @see Form::createElements()
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
@ -39,9 +40,7 @@ class AuthenticationBackendReorderForm extends ConfigForm
}
/**
* Update the authentication backend order and save the configuration
*
* @see Form::onSuccess()
* Update the user backend order and save the configuration
*/
public function onSuccess()
{
@ -52,24 +51,24 @@ class AuthenticationBackendReorderForm extends ConfigForm
try {
if ($configForm->move($backendName, $position)->save()) {
Notification::success(t('Authentication order updated!'));
Notification::success($this->translate('Authentication order updated'));
} else {
return false;
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
} catch (NotFoundError $_) {
Notification::error(sprintf($this->translate('User backend "%s" not found'), $backendName));
}
}
}
/**
* Return the config form for authentication backends
* Return the config form for user backends
*
* @return ConfigForm
*/
protected function getConfigForm()
{
$form = new AuthenticationBackendConfigForm();
$form = new UserBackendConfigForm();
$form->setIniConfig($this->config);
return $form;
}

View File

@ -0,0 +1,182 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserGroup;
use Exception;
use Icinga\Data\Extensible;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Selectable;
use Icinga\Exception\NotFoundError;
use Icinga\Web\Form;
use Icinga\Web\Notification;
/**
* Form for adding one or more group members
*/
class AddMemberForm extends Form
{
/**
* The data source to fetch users from
*
* @var Selectable
*/
protected $ds;
/**
* The user group backend to use
*
* @var Extensible
*/
protected $backend;
/**
* The group to add members for
*
* @var string
*/
protected $groupName;
/**
* Set the data source to fetch users from
*
* @param Selectable $ds
*
* @return $this
*/
public function setDataSource(Selectable $ds)
{
$this->ds = $ds;
return $this;
}
/**
* Set the user group backend to use
*
* @param Extensible $backend
*
* @return $this
*/
public function setBackend(Extensible $backend)
{
$this->backend = $backend;
return $this;
}
/**
* Set the group to add members for
*
* @param string $groupName
*
* @return $this
*/
public function setGroupName($groupName)
{
$this->groupName = $groupName;
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData The data sent by the user
*/
public function createElements(array $formData)
{
// TODO(jom): Fetching already existing members to prevent the user from mistakenly creating duplicate
// memberships (no matter whether the data source permits it or not, a member does never need to be
// added more than once) should be kept at backend level (GroupController::fetchUsers) but this does
// not work currently as our ldap protocol stuff is unable to handle our filter implementation..
$members = $this->backend
->select()
->from('group_membership', array('user_name'))
->where('group_name', $this->groupName)
->fetchColumn();
$filter = empty($members) ? Filter::matchAll() : Filter::not(Filter::where('user_name', $members));
$users = $this->ds->select()->from('user', array('user_name'))->applyFilter($filter)->fetchColumn();
if (! empty($users)) {
$this->addElement(
'multiselect',
'user_name',
array(
'multiOptions' => array_combine($users, $users),
'label' => $this->translate('Backend Users'),
'description' => $this->translate(
'Select one or more users (fetched from your user backends) to add as group member'
),
'class' => 'grant-permissions'
)
);
}
$this->addElement(
'textarea',
'users',
array(
'required' => empty($users),
'label' => $this->translate('Users'),
'description' => $this->translate(
'Provide one or more usernames separated by comma to add as group member'
)
)
);
$this->setTitle(sprintf($this->translate('Add members for group %s'), $this->groupName));
$this->setSubmitLabel($this->translate('Add'));
}
/**
* Insert the members for the group
*
* @return bool
*/
public function onSuccess()
{
$userNames = $this->getValue('user_name') ?: array();
if (($users = $this->getValue('users'))) {
$userNames = array_merge($userNames, array_map('trim', explode(',', $users)));
}
if (empty($userNames)) {
$this->info($this->translate(
'Please provide at least one username, either by choosing one '
. 'in the list or by manually typing one in the text box below'
));
return false;
}
$single = null;
foreach ($userNames as $userName) {
try {
$this->backend->insert(
'group_membership',
array(
'group_name' => $this->groupName,
'user_name' => $userName
)
);
} catch (NotFoundError $e) {
throw $e; // Trigger 404, the group name is initially accessed as GET parameter
} catch (Exception $e) {
Notification::error(sprintf(
$this->translate('Failed to add "%s" as group member for "%s"'),
$userName,
$this->groupName
));
$this->error($e->getMessage());
return false;
}
$single = $single === null;
}
if ($single) {
Notification::success(sprintf($this->translate('Group member "%s" added successfully'), $userName));
} else {
Notification::success($this->translate('Group members added successfully'));
}
return true;
}
}

View File

@ -0,0 +1,93 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserGroup;
use Icinga\Data\ResourceFactory;
use Icinga\Web\Form;
/**
* Form for managing database user group backends
*/
class DbUserGroupBackendForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_dbusergroupbackend');
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
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',
'resource',
array(
'required' => true,
'label' => $this->translate('Database Connection'),
'description' => $this->translate('The database connection to use for this backend'),
'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'
)
);
}
/**
* Return the names of all configured database resources
*
* @return array
*/
protected function getDatabaseResourceNames()
{
$names = array();
foreach (ResourceFactory::getResourceConfigs() as $name => $config) {
if (strtolower($config->type) === 'db') {
$names[] = $name;
}
}
return $names;
}
}

View File

@ -0,0 +1,331 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserGroup;
use Icinga\Authentication\User\UserBackend;
use Icinga\Authentication\UserGroup\LdapUserGroupBackend;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Protocol\Ldap\LdapConnection;
use Icinga\Web\Form;
use Icinga\Web\Notification;
/**
* Form for managing LDAP user group backends
*/
class LdapUserGroupBackendForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_ldapusergroupbackend');
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
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',
'resource',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('LDAP Connection'),
'description' => $this->translate('The LDAP connection to use for this backend.'),
'multiOptions' => array_combine($resourceNames, $resourceNames)
)
);
$resource = ResourceFactory::create(
isset($formData['resource']) && in_array($formData['resource'], $resourceNames)
? $formData['resource']
: $resourceNames[0]
);
$userBackendNames = $this->getLdapUserBackendNames($resource);
if (! empty($userBackendNames)) {
$userBackends = array_combine($userBackendNames, $userBackendNames);
$userBackends['none'] = $this->translate('None', 'usergroupbackend.ldap.user_backend');
} else {
$userBackends = array('none' => $this->translate('None', 'usergroupbackend.ldap.user_backend'));
}
$this->addElement(
'select',
'user_backend',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('User Backend'),
'description' => $this->translate('The user backend to link with this user group backend.'),
'multiOptions' => $userBackends
)
);
$groupBackend = new LdapUserGroupBackend($resource);
if ($formData['type'] === 'ldap') {
$defaults = $groupBackend->getOpenLdapDefaults();
$groupConfigDisabled = $userConfigDisabled = null; // MUST BE null, do NOT change this to false!
} else { // $formData['type'] === 'msldap'
$defaults = $groupBackend->getActiveDirectoryDefaults();
$groupConfigDisabled = $userConfigDisabled = true;
}
$this->createGroupConfigElements($defaults, $groupConfigDisabled);
if (count($userBackends) === 1 || (isset($formData['user_backend']) && $formData['user_backend'] === 'none')) {
$this->createUserConfigElements($defaults, $userConfigDisabled);
}
$this->addElement(
'hidden',
'backend',
array(
'disabled' => true, // Prevents the element from being submitted, see #7717
'value' => $formData['type']
)
);
}
/**
* Create and add all elements to this form required for the group configuration
*
* @param ConfigObject $defaults
* @param null|bool $disabled
*/
protected function createGroupConfigElements(ConfigObject $defaults, $disabled)
{
$this->addElement(
'text',
'group_class',
array(
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP Group Object Class'),
'description' => $this->translate('The object class used for storing groups on the LDAP server.'),
'value' => $defaults->group_class
)
);
$this->addElement(
'text',
'group_filter',
array(
'preserveDefault' => true,
'allowEmpty' => true,
'label' => $this->translate('LDAP Group Filter'),
'description' => $this->translate(
'An additional filter to use when looking up groups using the specified connection. '
. 'Leave empty to not to use any additional filter rules.'
),
'requirement' => $this->translate(
'The filter needs to be expressed as standard LDAP expression, without'
. ' outer parentheses. (e.g. &(foo=bar)(bar=foo) or foo=bar)'
),
'validators' => array(
array(
'Callback',
false,
array(
'callback' => function ($v) {
return strpos($v, '(') !== 0;
},
'messages' => array(
'callbackValue' => $this->translate('The filter must not be wrapped in parantheses.')
)
)
)
),
'value' => $defaults->group_filter
)
);
$this->addElement(
'text',
'group_name_attribute',
array(
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP Group Name Attribute'),
'description' => $this->translate(
'The attribute name used for storing a group\'s name on the LDAP server.'
),
'value' => $defaults->group_name_attribute
)
);
$this->addElement(
'text',
'base_dn',
array(
'preserveDefault' => true,
'label' => $this->translate('LDAP Group Base DN'),
'description' => $this->translate(
'The path where groups can be found on the LDAP server. Leave ' .
'empty to select all users available using the specified connection.'
),
'value' => $defaults->base_dn
)
);
}
/**
* Create and add all elements to this form required for the user configuration
*
* @param ConfigObject $defaults
* @param null|bool $disabled
*/
protected function createUserConfigElements(ConfigObject $defaults, $disabled)
{
$this->addElement(
'text',
'user_class',
array(
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP User Object Class'),
'description' => $this->translate('The object class used for storing users on the LDAP server.'),
'value' => $defaults->user_class
)
);
$this->addElement(
'text',
'user_filter',
array(
'preserveDefault' => true,
'allowEmpty' => true,
'label' => $this->translate('LDAP User Filter'),
'description' => $this->translate(
'An additional filter to use when looking up users using the specified connection. '
. 'Leave empty to not to use any additional filter rules.'
),
'requirement' => $this->translate(
'The filter needs to be expressed as standard LDAP expression, without'
. ' outer parentheses. (e.g. &(foo=bar)(bar=foo) or foo=bar)'
),
'validators' => array(
array(
'Callback',
false,
array(
'callback' => function ($v) {
return strpos($v, '(') !== 0;
},
'messages' => array(
'callbackValue' => $this->translate('The filter must not be wrapped in parantheses.')
)
)
)
),
'value' => $defaults->user_filter
)
);
$this->addElement(
'text',
'user_name_attribute',
array(
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP User Name Attribute'),
'description' => $this->translate(
'The attribute name used for storing a user\'s name on the LDAP server.'
),
'value' => $defaults->user_name_attribute
)
);
$this->addElement(
'text',
'user_base_dn',
array(
'preserveDefault' => true,
'label' => $this->translate('LDAP User Base DN'),
'description' => $this->translate(
'The path where users can be found on the LDAP server. Leave ' .
'empty to select all users available using the specified connection.'
),
'value' => $defaults->user_base_dn
)
);
}
/**
* Return the names of all configured LDAP resources
*
* @return array
*/
protected function getLdapResourceNames()
{
$names = array();
foreach (ResourceFactory::getResourceConfigs() as $name => $config) {
if (in_array(strtolower($config->type), array('ldap', 'msldap'))) {
$names[] = $name;
}
}
if (empty($names)) {
Notification::error(
$this->translate('No LDAP resources available. Please configure an LDAP resource first.')
);
$this->getResponse()->redirectAndExit('config/createresource');
}
return $names;
}
/**
* Return the names of all configured LDAP user backends
*
* @param LdapConnection $resource
*
* @return array
*/
protected function getLdapUserBackendNames(LdapConnection $resource)
{
$names = array();
foreach (UserBackend::getBackendConfigs() as $name => $config) {
if (in_array(strtolower($config->backend), array('ldap', 'msldap'))) {
$backendResource = ResourceFactory::create($config->resource);
if (
$backendResource->getHostname() === $resource->getHostname()
&& $backendResource->getPort() === $resource->getPort()
) {
$names[] = $name;
}
}
}
return $names;
}
}

View File

@ -0,0 +1,216 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserGroup;
use InvalidArgumentException;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
/**
* Form for managing user group backends
*/
class UserGroupBackendForm extends ConfigForm
{
/**
* The backend to load when displaying the form for the first time
*
* @var string
*/
protected $backendToLoad;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_usergroupbackend');
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* Return a form object for the given backend type
*
* @param string $type The backend type for which to return a form
*
* @return Form
*
* @throws InvalidArgumentException In case the given backend type is invalid
*/
public function getBackendForm($type)
{
switch ($type)
{
case 'db':
return new DbUserGroupBackendForm();
case 'ldap':
case 'msldap':
return new LdapUserGroupBackendForm();
default:
throw new InvalidArgumentException(
sprintf($this->translate('Invalid backend type "%s" provided'), $type)
);
}
}
/**
* Populate the form with the given backend's config
*
* @param string $name
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function load($name)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No user group backend called "%s" found', $name);
}
$this->backendToLoad = $name;
return $this;
}
/**
* Add a new user group backend
*
* The backend to add is identified by the array-key `name'.
*
* @param array $data
*
* @return $this
*
* @throws InvalidArgumentException In case $data does not contain a backend name
* @throws IcingaException In case a backend with the same name already exists
*/
public function add(array $data)
{
if (! isset($data['name'])) {
throw new InvalidArgumentException('Key \'name\' missing');
}
$backendName = $data['name'];
if ($this->config->hasSection($backendName)) {
throw new IcingaException('A user group backend with the name "%s" does already exist', $backendName);
}
unset($data['name']);
$this->config->setSection($backendName, $data);
return $this;
}
/**
* Edit a user group backend
*
* @param string $name
* @param array $data
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function edit($name, array $data)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No user group backend called "%s" found', $name);
}
$backendConfig = $this->config->getSection($name);
if (isset($data['name'])) {
if ($data['name'] !== $name) {
$this->config->removeSection($name);
$name = $data['name'];
}
unset($data['name']);
}
$backendConfig->merge($data);
foreach ($backendConfig->toArray() as $k => $v) {
if ($v === null) {
unset($backendConfig->$k);
}
}
$this->config->setSection($name, $backendConfig);
return $this;
}
/**
* Remove a user group backend
*
* @param string $name
*
* @return $this
*/
public function delete($name)
{
$this->config->removeSection($name);
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
// TODO(jom): We did not think about how to configure custom group backends yet!
$backendTypes = array(
'db' => $this->translate('Database'),
'ldap' => 'LDAP',
'msldap' => 'ActiveDirectory'
);
$backendType = isset($formData['type']) ? $formData['type'] : null;
if ($backendType === null) {
$backendType = key($backendTypes);
}
$this->addElement(
'select',
'type',
array(
'ignore' => true,
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Backend Type'),
'description' => $this->translate('The type of this user group backend'),
'multiOptions' => $backendTypes
)
);
$this->addSubForm($this->getBackendForm($backendType)->create($formData), 'backend_form');
}
/**
* Populate the configuration of the backend to load
*/
public function onRequest()
{
if ($this->backendToLoad) {
$data = $this->config->getSection($this->backendToLoad)->toArray();
$data['type'] = $data['backend'];
$data['name'] = $this->backendToLoad;
$this->populate($data);
}
}
/**
* 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

@ -0,0 +1,126 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\UserGroup;
use Icinga\Data\Filter\Filter;
use Icinga\Forms\RepositoryForm;
class UserGroupForm extends RepositoryForm
{
/**
* Create and add elements to this form to insert or update a group
*
* @param array $formData The data sent by the user
*/
protected function createInsertElements(array $formData)
{
$this->addElement(
'text',
'group_name',
array(
'required' => true,
'label' => $this->translate('Group Name')
)
);
if ($this->shouldInsert()) {
$this->setTitle($this->translate('Add a new group'));
$this->setSubmitLabel($this->translate('Add'));
} else { // $this->shouldUpdate()
$this->setTitle(sprintf($this->translate('Edit group %s'), $this->getIdentifier()));
$this->setSubmitLabel($this->translate('Save'));
}
}
/**
* Update a group
*
* @return bool
*/
protected function onUpdateSuccess()
{
if (parent::onUpdateSuccess()) {
if (($newName = $this->getValue('group_name')) !== $this->getIdentifier()) {
$this->getRedirectUrl()->setParam('group', $newName);
}
return true;
}
return false;
}
/**
* Create and add elements to this form to delete a group
*
* @param array $formData The data sent by the user
*/
protected function createDeleteElements(array $formData)
{
$this->setTitle(sprintf($this->translate('Remove group %s?'), $this->getIdentifier()));
$this->addDescription($this->translate(
'Note that all users that are currently a member of this group will'
. ' have their membership cleared automatically.'
));
$this->setSubmitLabel($this->translate('Yes'));
}
/**
* Create and return a filter to use when updating or deleting a group
*
* @return Filter
*/
protected function createFilter()
{
return Filter::where('group_name', $this->getIdentifier());
}
/**
* Return a notification message to use when inserting a group
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
protected function getInsertMessage($success)
{
if ($success) {
return $this->translate('Group added successfully');
} else {
return $this->translate('Failed to add group');
}
}
/**
* Return a notification message to use when updating a group
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
protected function getUpdateMessage($success)
{
if ($success) {
return sprintf($this->translate('Group "%s" has been edited'), $this->getIdentifier());
} else {
return sprintf($this->translate('Failed to edit group "%s"'), $this->getIdentifier());
}
}
/**
* Return a notification message to use when deleting a group
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
protected function getDeleteMessage($success)
{
if ($success) {
return sprintf($this->translate('Group "%s" has been removed'), $this->getIdentifier());
} else {
return sprintf($this->translate('Failed to remove group "%s"'), $this->getIdentifier());
}
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms;
@ -8,7 +7,6 @@ use Exception;
use Zend_Form_Decorator_Abstract;
use Icinga\Web\Form;
use Icinga\Application\Config;
use Icinga\File\Ini\IniWriter;
/**
* Form base-class providing standard functionality for configuration forms
@ -27,7 +25,7 @@ class ConfigForm extends Form
*
* @param Config $config The configuration to use
*
* @return self
* @return $this
*/
public function setIniConfig(Config $config)
{
@ -44,21 +42,14 @@ class ConfigForm extends Form
*/
public function save()
{
$writer = new IniWriter(
array(
'config' => $this->config,
'filename' => $this->config->getConfigFile()
)
);
try {
$writer->write();
$this->config->saveIni();
} catch (Exception $e) {
$this->addDecorator('ViewScript', array(
'viewModule' => 'default',
'viewScript' => 'showConfiguration.phtml',
'errorMessage' => $e->getMessage(),
'configString' => $writer->render(),
'configString' => $this->config,
'filePath' => $this->config->getConfigFile(),
'placement' => Zend_Form_Decorator_Abstract::PREPEND
));

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms;
@ -12,11 +11,11 @@ use Icinga\Web\Form;
class ConfirmRemovalForm extends Form
{
/**
* Initalize this form
* Initialize this form
*/
public function init()
{
$this->setName('form_confirm_removal');
$this->setSubmitLabel(t('Confirm Removal'));
$this->getSubmitLabel() ?: $this->setSubmitLabel($this->translate('Confirm Removal'));
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Dashboard;
@ -26,7 +25,7 @@ class DashletForm extends Form
{
$this->setName('form_dashboard_addurl');
if (! $this->getSubmitLabel()) {
$this->setSubmitLabel(t('Add To Dashboard'));
$this->setSubmitLabel($this->translate('Add To Dashboard'));
}
$this->setAction(URL::fromRequest());
}
@ -66,9 +65,10 @@ class DashletForm extends Form
'url',
array(
'required' => true,
'label' => t('Url'),
'description' =>
t('Enter url being loaded in the dashlet. You can paste the full URL, including filters.')
'label' => $this->translate('Url'),
'description' => $this->translate(
'Enter url being loaded in the dashlet. You can paste the full URL, including filters.'
)
)
);
$this->addElement(
@ -76,8 +76,22 @@ class DashletForm extends Form
'dashlet',
array(
'required' => true,
'label' => t('Dashlet Title'),
'description' => t('Enter a title for the dashlet.')
'label' => $this->translate('Dashlet Title'),
'description' => $this->translate('Enter a title for the dashlet.'),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\' or \']\'.'
)
)
)
)
)
)
);
$this->addElement(
@ -95,9 +109,8 @@ class DashletForm extends Form
'pane',
array(
'required' => true,
'label' => t("New Dashboard Title"),
'description' =>
t('Enter a title for the new pane.')
'label' => $this->translate("New Dashboard Title"),
'description' => $this->translate('Enter a title for the new pane.')
)
);
} else {
@ -106,10 +119,9 @@ class DashletForm extends Form
'pane',
array(
'required' => true,
'label' => t('Dashboard'),
'label' => $this->translate('Dashboard'),
'multiOptions' => $panes,
'description' =>
t('Select a pane you want to add the dashlet.')
'description' => $this->translate('Select a pane you want to add the dashlet.')
)
);
}
@ -118,10 +130,10 @@ class DashletForm extends Form
'checkbox',
'create_new_pane',
array(
'autosubmit' => true,
'required' => false,
'label' => t('New dashboard'),
'class' => 'autosubmit',
'description' => t('Check this box if you want to add the dashlet to a new dashboard')
'label' => $this->translate('New dashboard'),
'description' => $this->translate('Check this box if you want to add the dashlet to a new dashboard')
)
);
}
@ -150,9 +162,9 @@ class DashletForm extends Form
$this->populate(array(
'pane' => $dashlet->getPane()->getName(),
'org_pane' => $dashlet->getPane()->getName(),
'dashlet' => $dashlet->getTitle(),
'org_dashlet' => $dashlet->getTitle(),
'url' => $dashlet->getUrl()
'dashlet' => $dashlet->getTitle(),
'org_dashlet' => $dashlet->getTitle(),
'url' => $dashlet->getUrl()->getRelativeUrl()
));
}
}

View File

@ -1,6 +1,5 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms;
@ -25,51 +24,11 @@ class LdapDiscoveryForm extends Form
'text',
'domain',
array(
'required' => true,
'label' => t('Search Domain'),
'description' => t('Search this domain for records of available servers.'),
'label' => $this->translate('Search Domain'),
'description' => $this->translate('Search this domain for records of available servers.'),
)
);
if (false) {
$this->addElement(
'note',
'additional_description',
array(
'value' => t('No Ldap servers found on this domain.'
. ' You can try to specify host and port and try again, or just skip this step and '
. 'configure the server manually.'
)
)
);
$this->addElement(
'text',
'hostname',
array(
'required' => false,
'label' => t('Host'),
'description' => t('IP or host name to search.'),
)
);
$this->addElement(
'text',
'port',
array(
'required' => false,
'label' => t('Port'),
'description' => t('Port', 389),
)
);
}
return $this;
}
public function isValid($data)
{
if (false === parent::isValid($data)) {
return false;
}
return true;
}
}

View File

@ -1,13 +1,12 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms;
use Exception;
use DateTimeZone;
use Icinga\Application\Logger;
use Icinga\Authentication\Manager;
use Icinga\Authentication\Auth;
use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore;
use Icinga\Util\TimezoneDetect;
@ -41,6 +40,7 @@ class PreferenceForm extends Form
public function init()
{
$this->setName('form_config_preferences');
$this->setTitle($this->translate('Preferences'));
}
/**
@ -48,7 +48,7 @@ class PreferenceForm extends Form
*
* @param Preferences $preferences The preferences to work with
*
* @return self
* @return $this
*/
public function setPreferences(Preferences $preferences)
{
@ -61,17 +61,18 @@ class PreferenceForm extends Form
*
* @param PreferencesStore $store The preference store to use
*
* @return self
* @return $this
*/
public function setStore(PreferencesStore $store)
{
$this->store = $store;
return $this;
}
/**
* Persist preferences
*
* @return self
* @return $this
*/
public function save()
{
@ -86,7 +87,7 @@ class PreferenceForm extends Form
*/
public function onSuccess()
{
$this->preferences = new Preferences($this->store->load());
$this->preferences = new Preferences($this->store ? $this->store->load() : array());
$webPreferences = $this->preferences->get('icingaweb', array());
foreach ($this->getValues() as $key => $value) {
@ -103,11 +104,11 @@ class PreferenceForm extends Form
Session::getSession()->user->setPreferences($this->preferences);
try {
if ($this->getElement('btn_submit_preferences')->isChecked()) {
if ($this->store && $this->getElement('btn_submit_preferences')->isChecked()) {
$this->save();
Notification::success(t('Preferences successfully saved'));
Notification::success($this->translate('Preferences successfully saved'));
} else {
Notification::success(t('Preferences successfully saved for the current session'));
Notification::success($this->translate('Preferences successfully saved for the current session'));
}
} catch (Exception $e) {
Logger::error($e);
@ -122,7 +123,7 @@ class PreferenceForm extends Form
*/
public function onRequest()
{
$auth = Manager::getInstance();
$auth = Auth::getInstance();
$values = $auth->getUser()->getPreferences()->get('icingaweb');
if (! isset($values['language'])) {
@ -142,13 +143,13 @@ class PreferenceForm extends Form
public function createElements(array $formData)
{
$languages = array();
$languages['autodetect'] = sprintf(t('Browser (%s)', 'preferences.form'), $this->getLocale());
$languages['autodetect'] = sprintf($this->translate('Browser (%s)', 'preferences.form'), $this->getLocale());
foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language;
}
$tzList = array();
$tzList['autodetect'] = sprintf(t('Browser (%s)', 'preferences.form'), $this->getDefaultTimezone());
$tzList['autodetect'] = sprintf($this->translate('Browser (%s)', 'preferences.form'), $this->getDefaultTimezone());
foreach (DateTimeZone::listIdentifiers() as $tz) {
$tzList[$tz] = $tz;
}
@ -158,8 +159,8 @@ class PreferenceForm extends Form
'language',
array(
'required' => true,
'label' => t('Your Current Language'),
'description' => t('Use the following language to display texts and messages'),
'label' => $this->translate('Your Current Language'),
'description' => $this->translate('Use the following language to display texts and messages'),
'multiOptions' => $languages,
'value' => substr(setlocale(LC_ALL, 0), 0, 5)
)
@ -170,8 +171,8 @@ class PreferenceForm extends Form
'timezone',
array(
'required' => true,
'label' => t('Your Current Timezone'),
'description' => t('Use the following timezone for dates and times'),
'label' => $this->translate('Your Current Timezone'),
'description' => $this->translate('Use the following timezone for dates and times'),
'multiOptions' => $tzList,
'value' => $this->getDefaultTimezone()
)
@ -182,33 +183,40 @@ class PreferenceForm extends Form
'show_benchmark',
array(
'required' => true,
'label' => t('Use benchmark')
'label' => $this->translate('Use benchmark')
)
);
$this->addElement(
'submit',
'btn_submit_preferences',
'checkbox',
'auto_refresh',
array(
'ignore' => true,
'label' => t('Save to the Preferences'),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div'))
)
'required' => false,
'label' => $this->translate('Enable auto refresh'),
'description' => $this->translate('This option allows you to enable or to disable the global page content auto refresh'),
'value' => 1
)
);
if ($this->store) {
$this->addElement(
'submit',
'btn_submit_preferences',
array(
'ignore' => true,
'label' => $this->translate('Save to the Preferences'),
'decorators' => array('ViewHelper')
)
);
}
$this->addElement(
'submit',
'btn_submit_session',
array(
'ignore' => true,
'label' => t('Save for the current Session'),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div'))
)
'label' => $this->translate('Save for the current Session'),
'decorators' => array('ViewHelper')
)
);

Some files were not shown because too many files have changed in this diff Show More