diff --git a/library/Icinga/Authentication/Backend.php b/library/Icinga/Authentication/Backend.php deleted file mode 100644 index 2678e396b..000000000 --- a/library/Icinga/Authentication/Backend.php +++ /dev/null @@ -1,31 +0,0 @@ -config = $config; - $userbackend = ucwords(strtolower($config->users->backend)); - $class = '\\Icinga\\Authentication\\' . $userbackend . 'UserBackend'; - $this->userBackend = new $class($config->users); - } - - public function hasUsername($username) - { - return $this->userBackend->hasUsername($username); - } - - public function authenticate($username, $password = null) - { - return $this->userBackend->authenticate($username, $password); - } -} diff --git a/library/Icinga/Authentication/Backend/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php index afaf13ec5..fbd4d9c63 100644 --- a/library/Icinga/Authentication/Backend/LdapUserBackend.php +++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php @@ -10,15 +10,35 @@ use Icinga\Authentication\Credentials; use Icinga\Protocol\Ldap; use Icinga\Application\Config; +/** +* User authentication backend (@see Icinga\Authentication\UserBackend) for +* authentication of users via LDAP. The attributes and location of the +* user is configurable via the application.ini +* +* See the UserBackend class (@see Icinga\Authentication\UserBackend) for +* usage information +**/ class LdapUserBackend implements UserBackend { + /** + * @var Ldap\Connection + **/ protected $connection; - + + /** + * Creates a new Authentication backend using the + * connection information provided in $config + * + * @param object $config The ldap connection information + **/ public function __construct($config) { $this->connection = new Ldap\Connection($config); } + /** + * @see Icinga\Authentication\UserBackend::hasUsername + **/ public function hasUsername(Credentials $credential) { return $this->connection->fetchOne( @@ -26,11 +46,27 @@ class LdapUserBackend implements UserBackend ) === $credential->getUsername(); } + /** + * Removes the '*' characted from $string + * + * @param String $string + * + * @return String + **/ protected function stripAsterisks($string) { return str_replace('*', '', $string); } + /** + * Tries to fetch the username given in $username from + * the ldap connection, using the configuration parameters + * given in the Authentication configuration + * + * @param String $username The username to select + * + * @return object $result + **/ protected function selectUsername($username) { return $this->connection->select() @@ -46,6 +82,9 @@ class LdapUserBackend implements UserBackend ); } + /** + * @see Icinga\Authentication\UserBackend::authenticate + **/ public function authenticate(Credentials $credentials) { if (!$this->connection->testCredentials( diff --git a/library/Icinga/Authentication/Credentials.php b/library/Icinga/Authentication/Credentials.php index 6df333f12..fb0581ad7 100644 --- a/library/Icinga/Authentication/Credentials.php +++ b/library/Icinga/Authentication/Credentials.php @@ -4,13 +4,26 @@ namespace Icinga\Authentication; +/** +* Data holder object for authentication information +* +* This object should be used instead of passing names and +* passwords as primitives in order to allow additional information +* to be provided (like the domain) when needed +**/ class Credentials { protected $username; protected $password; protected $domain; - - + + /** + * Create a new credential object + * + * @param String $username + * @param String $password + * @param String $domain + **/ public function __construct($username = "", $password = null, $domain = null) { $this->username = $username; @@ -18,31 +31,49 @@ class Credentials $this->domain = $domain; } + /** + * @return String + **/ public function getUsername() { return $this->username; } + /** + * @param String $username + **/ public function setUsername($username) { return $this->username = $username; } - + + /** + * @return String + **/ public function getPassword() { return $this->password; } + /** + * @param String $password + **/ public function setPassword($password) { return $this->password = $password; } + /** + * @return String + **/ public function getDomain() { return $this->domain; } + /** + * @param String $domain + **/ public function setDomain($domain) { return $this->domain = $domain; diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index 68d20f264..2bb8f8a12 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -8,19 +8,69 @@ use Icinga\Application\Logger as Logger; use Icinga\Application\Config as Config; use Icinga\Exception\ConfigurationError as ConfigError; +/** +* The authentication manager allows to identify users and +* to persist authentication information in a session. +* +* Direct instanciation is not permitted, the Authencation manager +* must be created using the getInstance method. Subsequent getInstance +* calls return the same object and ignore any additional configuration +* +* When creating the Authentication manager with standard PHP Sessions, +* you have to decide whether you want to modify the session on the first +* initialization and provide the 'writeSession' option if so, otherwise +* session changes won't be written to disk. This is done to prevent PHP +* from blockung concurrent requests +* +* @TODO: Group support is not implemented yet +**/ class Manager { const BACKEND_TYPE_USER = "User"; const BACKEND_TYPE_GROUP = "Group"; - + + /** + * @var Manager + **/ private static $instance = null; + /** + * @var User + **/ private $user = null; private $groups = array(); - private $userBackend = null; - private $groupBackend = null; - private $session = null; + /** + * @var UserBackend + **/ + private $userBackend = null; + + /** + * @var GroupBackend + **/ + private $groupBackend = null; + + /** + * @var Session + **/ + private $session = null; + + /** + * Creates a new authentication manager using the provided config (or the + * configuration provided in the authentication.ini if no config is given) + * and with the given options. + * + * @param Icinga\Config $config The configuration to use for authentication + * instead of the authentication.ini + * @param Array $options Additional options that affect the managers behaviour. + * Supported values: + * * writeSession : Whether the session should be writable + * * userBackendClass : Allows to provide an own user backend class + * (used for testing) + * * groupBackendClass : Allows to provide an own group backend class + * (used for testing) + * * sessionClass : Allows to provide a different session implementation) + **/ private function __construct($config = null, array $options = array()) { if ($config === null) { @@ -50,6 +100,9 @@ class Manager } } + /** + * @see Manager:__construct() + **/ public static function getInstance($config = null, array $options = array()) { if (self::$instance === null) { @@ -58,23 +111,51 @@ class Manager return self::$instance; } + /** + * Clears the instance (this is mostly needed for testing and shouldn't be called otherwise) + **/ public static function clearInstance() { self::$instance = null; } + /** + * Creates a backend for the the given authenticationTarget (User or Group) and the + * Authenticaiton source. + * + * initBackend("User", "Ldap") would create a UserLdapBackend, + * initBackend("Group", "MySource") would create a GroupMySourceBackend, + * + * Supported backends can be found in the Authentication\Backend folder + * + * @param String $authenticationTarget "User" or "Group", depending on what + * authentication information the backend should + * provide + * @param String $authenticationSource The Source, see the above examples + * + * @return (null|UserBackend|GroupBackend) + **/ private function initBackend($authenticationTarget, $authenticationSource) { - $userBackend = ucwords(strtolower($authenticationSource->backend)); + $backend = ucwords(strtolower($authenticationSource->backend)); - if (!$userBackend) { + if (!$backend) { return null; } - $class = '\\Icinga\\Authentication\\Backend\\' . $userBackend . $authenticationTarget. 'Backend'; + $class = '\\Icinga\\Authentication\\Backend\\' . $backend . $authenticationTarget. 'Backend'; return new $class($authenticationSource); } + /** + * Tries to authenticate the current user with the Credentials (@see Credentials). + * + * @param Credentials $credentials The credentials to use for authentication + * @param Boolean $persist Whether to persist the authentication result + * in the current session + * + * @return Boolean true on success, otherwise false + **/ public function authenticate(Credentials $credentials, $persist = true) { if (!$this->userBackend) { @@ -103,16 +184,31 @@ class Manager return true; } + + /** + * Writes the current user to the session (only usable when writeSession = true) + * + **/ public function persistCurrentUser() { $this->session->set("user", $this->user); } - + + /** + * Tries to authenticate the user with the current session + **/ public function authenticateFromSession() { $this->user = $this->session->get("user", null); } + /** + * Returns true when the user is currently authenticated + * + * @param Boolean $ignoreSession Set to true to prevent authentication by session + * + * @param Boolean + **/ public function isAuthenticated($ignoreSession = false) { if ($this->user === null && !$ignoreSession) { @@ -121,17 +217,29 @@ class Manager return is_object($this->user); } + /** + * Purges the current authorisation information and deletes the session + * + **/ public function removeAuthorization() { $this->user = null; $this->session->purge(); } + /** + * Returns the current user or null if no user is authenticated + * + * @return User + **/ public function getUser() { return $this->user; } + /** + * @see User::getGroups + **/ public function getGroups() { return $this->user->getGroups(); diff --git a/library/Icinga/Authentication/PhpSession.php b/library/Icinga/Authentication/PhpSession.php index 5da310d73..66c83e38b 100644 --- a/library/Icinga/Authentication/PhpSession.php +++ b/library/Icinga/Authentication/PhpSession.php @@ -17,7 +17,6 @@ use Icinga\Application\Logger as Logger; * no parameter in order to auto-close it) to persist all values previously * set with the set() method * - * @package Icinga\Authentication */ class PhpSession extends Session { @@ -33,7 +32,13 @@ class PhpSession extends Session 'hash_function' => true, 'hash_bits_per_character' => 5, ); - + + /** + * Creates a new PHPSession object using the provided options (if any) + * + * @param Array $options An optional array of ini options to set, + * @see http://php.net/manual/en/session.configuration.php + **/ public function __construct(array $options = null) { if ($options !== null) { @@ -55,6 +60,11 @@ class PhpSession extends Session } } + /** + * Returns true when the session has not yet been closed + * + * @return Boolean + **/ private function sessionCanBeChanged() { if ($this->isFlushed) { @@ -64,6 +74,11 @@ class PhpSession extends Session return true; } + /** + * Returns true when the session has not yet been opened + * + * @return Boolean + **/ private function sessionCanBeOpened() { if ($this->isOpen) { @@ -73,6 +88,11 @@ class PhpSession extends Session return $this->sessionCanBeChanged(); } + /** + * Opens a PHP session when possible + * + * @return Boolean True on success + **/ public function open() { if (!$this->sessionCanBeOpened()) { @@ -86,6 +106,11 @@ class PhpSession extends Session return true; } + /** + * Ensures that the session is open modifyable + * + * @return Boolean True on success + **/ private function ensureOpen() { // try to open first @@ -97,6 +122,15 @@ class PhpSession extends Session return true; } + /** + * Reads all values written to the underyling session and + * makes them accessible. if keepOpen is not set, the session + * is immediately closed again + * + * @param Boolean $keepOpen Set to true when modifying the session + * + * @return Boolean True on success + **/ public function read($keepOpen = false) { if (!$this->ensureOpen()) { @@ -109,6 +143,14 @@ class PhpSession extends Session return true; } + /** + * Writes all values of this session opbject to the underyling session implementation + * If keepOpen is not set, the session is closed + * + * @param Boolean $keepOpen Set to true when modifying the session further + * + * @return Boolean True on success + **/ public function write($keepOpen = false) { if (!$this->ensureOpen()) { @@ -125,6 +167,11 @@ class PhpSession extends Session return null; } + /** + * Closes and writes the session. Call @see PHPSession::write in order to persist changes + * and only call this if you want the session to be closed without any changes + * + **/ public function close() { if (!$this->isFlushed) { @@ -132,7 +179,11 @@ class PhpSession extends Session } $this->isFlushed = true; } - + + /** + * Deletes the current session, causing all session information to be lost + * + **/ public function purge() { if ($this->ensureOpen()) { @@ -143,6 +194,10 @@ class PhpSession extends Session } } + /** + * Removes session cookies + * + **/ private function clearCookies() { if (ini_get("session.use_cookies")) { diff --git a/library/Icinga/Authentication/Session.php b/library/Icinga/Authentication/Session.php index 734c3b968..2f1743fb2 100644 --- a/library/Icinga/Authentication/Session.php +++ b/library/Icinga/Authentication/Session.php @@ -4,32 +4,82 @@ namespace Icinga\Authentication; +/** +* Base class for session, providing getter, setters and required +* interface methods +* +**/ abstract class Session { private $sessionValues = array(); + /** + * Opens a session or creates a new one if not exists + * + **/ abstract public function open(); + + /** + * Reads all values from the underyling session implementation + * + * @param Boolean $keepOpen True to keep the session open (depends on implementaiton) + **/ abstract public function read($keepOpen = false); + + /** + * Persists changes to the underlying session implementation + * + * @param Boolean $keepOpen True to keep the session open (depends on implementaiton) + **/ abstract public function write($keepOpen = false); abstract public function close(); abstract public function purge(); + /** + * Sets a $value under the provided key in the internal session data array + * Does not persist those changes, use @see Session::write in order to persist the changes + * made here. + * + * @param String $key + * @param mixed $value + **/ public function set($key, $value) { $this->sessionValues[$key] = $value; } + /** + * Returns the session value stored under $key or $defaultValue if not found. + * call @see Session:read in order to populate this array with the underyling session implementation + * + * @param String $key + * @param mixed $defaultValue + * + * @return mixed + **/ public function get($key, $defaultValue = null) { return isset($this->sessionValues[$key]) ? $this->sessionValues[$key] : $defaultValue; } + /** + * Returns the current session value state (also dirty changes not yet written to the session) + * + * @return Array + **/ public function getAll() { return $this->sessionValues; } + /** + * Writes all values provided in the key=>value array to the internal session value state. + * In order to persist these chages, call @see Session:write + * + * @param Array $values + * @param Boolean $overwrite Whether to overwrite already set values + **/ public function setAll(array $values, $overwrite = false) { if ($overwrite) { @@ -42,7 +92,11 @@ abstract class Session $this->sessionValues[$key] = $value; } } - + + /** + * Clears all values from the session cache + * + **/ public function clear() { $this->sessionValues = array(); diff --git a/library/Icinga/Authentication/User.php b/library/Icinga/Authentication/User.php index e4de7c241..6571af33e 100644 --- a/library/Icinga/Authentication/User.php +++ b/library/Icinga/Authentication/User.php @@ -2,21 +2,13 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -/** - * Icinga Authentication User class - * - * @package Icinga\Authentication - */ namespace Icinga\Authentication; /** - * This class represents a user object + * This class represents an authorized user and can be used + * to retrieve authorization information (@TODO: Not implemented yet) or + * to retrieve user information * - * - * @copyright Copyright (c) 2013 Icinga-Web Team - * @author Icinga-Web Team - * @package Icinga\Application - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License */ class User { @@ -29,7 +21,15 @@ class User public $permissions = array(); public $groups = array(); - + + /** + * Creates a user object given the provided information + * + * @param String $username + * @param String $firstname + * @param String $lastname + * @param String $email + **/ public function __construct($username, $firstname = null, $lastname = null, $email = null) { $this->setUsername($username); @@ -47,85 +47,144 @@ class User } } + /** + * Returns all groups this user belongs to + * + * @return Array + **/ public function getGroups() { return $this->groups; } + /** + * Sets the groups this user belongs to + * + * @return Array + **/ public function setGroups(array $groups) { $this->groups = $groups; } - + + /** + * Returns true if the user is a member of this group + * + * @return Boolean + **/ public function isMemberOf(Group $group) { return in_array($group, $this->groups); } + /** + * Returns permission information for this user + * + * @return Array + **/ public function getPermissions() { return $this->permissions; } + /** + * @return String + **/ public function getUsername() { return $this->username; } + /** + * @param String $name + **/ public function setUsername($name) { $this->username = $name; } + /** + * @return String + **/ public function getFirstname() { return $this->firstname; } + /*+ + * @param String $name + **/ public function setFirstname($name) { $this->firstname = $name; } + /** + * @return String + **/ public function getLastname() { return $this->lastname; } + /** + * @param String $name + **/ public function setLastname($name) { $this->lastname = $name; } + /** + * @return String + **/ public function getEmail() { return $this->email; } - + + /** + * @param String $mail + * + * @throws \InvalidArgumentException When an invalid mail is provided + **/ public function setEmail($mail) { if (filter_var($mail, FILTER_VALIDATE_EMAIL)) { $this->mail = $mail; } else { - throw new InvalidArgumentException("Invalid mail given for user $this->username: $mail"); + throw new \InvalidArgumentException("Invalid mail given for user $this->username: $mail"); } } - + + /** + * @param String $domain + **/ public function setDomain($domain) { $this->domain = $domain; } + /** + * @return String + **/ public function getDomain() { return $this->domain; } + /** + * @param String $key + * @param String $value + **/ public function setAdditional($key, $value) { $this->additionalInformation[$key] = $value; } + /** + * @return mixed + **/ public function getAdditional($key) { if (isset($this->additionalInformation[$key])) {