From 36340aafa68f66f7d2709dd3f63125209410c947 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 16 Oct 2015 14:46:44 +0200 Subject: [PATCH] Repository: Ensure that we'll internally only work with virtual table names refs #10367 --- .../Authentication/User/LdapUserBackend.php | 46 +++---- .../UserGroup/LdapUserGroupBackend.php | 26 ++-- library/Icinga/Repository/DbRepository.php | 120 +++++------------- library/Icinga/Repository/RepositoryQuery.php | 5 +- 4 files changed, 74 insertions(+), 123 deletions(-) diff --git a/library/Icinga/Authentication/User/LdapUserBackend.php b/library/Icinga/Authentication/User/LdapUserBackend.php index d8ea19ca2..f3a852115 100644 --- a/library/Icinga/Authentication/User/LdapUserBackend.php +++ b/library/Icinga/Authentication/User/LdapUserBackend.php @@ -102,15 +102,13 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In /** * Set the objectClass where to look for users * - * Sets also the base table name for the underlying repository. - * * @param string $userClass * * @return $this */ public function setUserClass($userClass) { - $this->baseTable = $this->userClass = $this->getNormedAttribute($userClass); + $this->userClass = $this->getNormedAttribute($userClass); return $this; } @@ -216,13 +214,10 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In * * @return array * - * @throws ProgrammingError In case either $this->userNameAttribute or $this->userClass has not been set yet + * @throws ProgrammingError In case $this->userNameAttribute has not been set yet */ protected function initializeQueryColumns() { - if ($this->userClass === null) { - throw new ProgrammingError('It is required to set the objectClass where to look for users first'); - } if ($this->userNameAttribute === null) { throw new ProgrammingError('It is required to set a attribute name where to find a user\'s name first'); } @@ -240,7 +235,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In } return array( - $this->userClass => array( + 'user' => array( 'user' => $this->userNameAttribute, 'user_name' => $this->userNameAttribute, 'is_active' => $isActiveAttribute, @@ -269,15 +264,9 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In * Initialize this repository's conversion rules * * @return array - * - * @throws ProgrammingError In case $this->userClass has not been set yet */ protected function initializeConversionRules() { - if ($this->userClass === null) { - throw new ProgrammingError('It is required to set the objectClass where to look for users first'); - } - if ($this->ds->getCapabilities()->isActiveDirectory()) { $stateConverter = 'user_account_control'; } else { @@ -285,7 +274,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In } return array( - $this->userClass => array( + 'user' => array( 'is_active' => $stateConverter, 'created_at' => 'generalized_time', 'last_modified' => 'generalized_time' @@ -330,14 +319,26 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In } /** - - * @param Inspection $info Optional inspection to fill with diagnostic info + * Validate that the requested table exists * - * @throws AuthenticationException When authentication is not possible + * This will return $this->userClass in case $table equals "user". + * + * @param string $table The table to validate + * @param RepositoryQuery $query An optional query to pass as context + * (unused by the base implementation) + * + * @return string + * + * @throws ProgrammingError In case the given table does not exist */ - public function assertAuthenticationPossible(Inspection $insp = null) + public function requireTable($table, RepositoryQuery $query = null) { + $table = parent::requireTable($table, $query); + if ($table === 'user') { + $table = $this->userClass; + } + return $table; } /** @@ -359,17 +360,16 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In ->getQuery() ->setUsePagedResults(false) ->fetchDn(); - if ($userDn === null) { return false; } - $testCredentialsResult = $this->ds->testCredentials($userDn, $password); - if ($testCredentialsResult) { + $validCredentials = $this->ds->testCredentials($userDn, $password); + if ($validCredentials) { $user->setAdditional('ldap_dn', $userDn); } - return $testCredentialsResult; + return $validCredentials; } catch (LdapException $e) { throw new AuthenticationException( 'Failed to authenticate user "%s" against backend "%s". An exception was thrown:', diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index a25f8ad96..2aa615a84 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -211,15 +211,13 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt /** * Set the objectClass where to look for groups * - * Sets also the base table name for the underlying repository. - * * @param string $groupClass * * @return $this */ public function setGroupClass($groupClass) { - $this->baseTable = $this->groupClass = $this->getNormedAttribute($groupClass); + $this->groupClass = $this->getNormedAttribute($groupClass); return $this; } @@ -382,16 +380,17 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt * * @return array * - * @throws ProgrammingError In case either $this->groupNameAttribute or $this->groupClass has not been set yet + * @throws ProgrammingError In case either $this->groupNameAttribute or + * $this->groupMemberAttribute has not been set yet */ protected function initializeQueryColumns() { - if ($this->groupClass === null) { - throw new ProgrammingError('It is required to set the objectClass where to look for groups first'); - } if ($this->groupNameAttribute === null) { throw new ProgrammingError('It is required to set a attribute name where to find a group\'s name first'); } + if ($this->groupMemberAttribute === null) { + throw new ProgrammingError('It is required to set a attribute name where to find a group\'s members first'); + } if ($this->ds->getCapabilities()->isActiveDirectory()) { $createdAtAttribute = 'whenCreated'; @@ -409,7 +408,7 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt 'created_at' => $createdAtAttribute, 'last_modified' => $lastModifiedAttribute ); - return array($this->groupClass => $columns, $this->groupClass => $columns); + return array('group' => $columns, 'group_membership' => $columns); } /** @@ -432,7 +431,8 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt * * @return array * - * @throws ProgrammingError In case $this->groupClass has not been set yet + * @throws ProgrammingError In case either $this->groupClass or $this->groupMemberAttribute + * has not been set yet */ protected function initializeConversionRules() { @@ -444,13 +444,17 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt } $rules = array( - $this->groupClass => array( + 'group' => array( + 'created_at' => 'generalized_time', + 'last_modified' => 'generalized_time' + ), + 'group_membership' => array( 'created_at' => 'generalized_time', 'last_modified' => 'generalized_time' ) ); if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) { - $rules[$this->groupClass][] = 'user_name'; + $rules['group']['user_name'] = 'user_name'; } return $rules; diff --git a/library/Icinga/Repository/DbRepository.php b/library/Icinga/Repository/DbRepository.php index a187d76c0..89701cb9d 100644 --- a/library/Icinga/Repository/DbRepository.php +++ b/library/Icinga/Repository/DbRepository.php @@ -468,8 +468,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, * This does not check whether any conversion for the given table is available if $column is not given, as it * may be possible that columns from another table where joined in which would otherwise not being converted. * - * @param array|string $table - * @param string $column + * @param string $table + * @param string $column * * @return bool */ @@ -477,10 +477,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, { if ($column !== null) { if ($this->validateQueryColumnAssociation($table, $column)) { - return parent::providesValueConversion( - $this->removeTablePrefix($this->clearTableAlias($table)), - $column - ); + return parent::providesValueConversion($table, $column); } if (($tableName = $this->findTableName($column))) { @@ -513,11 +510,9 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, protected function getConverter($table, $name, $context, RepositoryQuery $query = null) { if ( - ($query !== null && $this->validateQueryColumnAssociation($table, $name)) - || ($query === null && $this->validateStatementColumnAssociation($table, $name)) + ! ($query !== null && $this->validateQueryColumnAssociation($table, $name)) + && !($query === null && $this->validateStatementColumnAssociation($table, $name)) ) { - $table = $this->removeTablePrefix($this->clearTableAlias($table)); - } else { $table = $this->findTableName($name); if (! $table) { throw new ProgrammingError('Column name validation seems to have failed. Did you require the column?'); @@ -584,44 +579,17 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, return $filter; } - /** - * Return this repository's query columns of the given table mapped to their respective aliases - * - * @param array|string $table - * - * @return array - * - * @throws ProgrammingError In case $table does not exist - */ - public function requireAllQueryColumns($table) - { - return parent::requireAllQueryColumns($this->removeTablePrefix($this->clearTableAlias($table))); - } - - /** - * Return the query column name for the given alias or null in case the alias does not exist - * - * @param array|string $table - * @param string $alias - * - * @return string|null - */ - public function resolveQueryColumnAlias($table, $alias) - { - return parent::resolveQueryColumnAlias($this->removeTablePrefix($this->clearTableAlias($table)), $alias); - } - /** * Return the alias for the given query column name or null in case the query column name does not exist * - * @param array|string $table - * @param string $column + * @param string $table + * @param string $column * * @return string|null */ public function reassembleQueryColumnAlias($table, $column) { - $alias = parent::reassembleQueryColumnAlias($this->removeTablePrefix($this->clearTableAlias($table)), $column); + $alias = parent::reassembleQueryColumnAlias($table, $column); if ( $alias === null && !$this->validateQueryColumnAssociation($table, $column) @@ -633,29 +601,13 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, return $alias; } - /** - * Return whether the given query column name or alias is available in the given table - * - * @param array|string $table - * @param string $column - * - * @return bool - */ - public function validateQueryColumnAssociation($table, $column) - { - return parent::validateQueryColumnAssociation( - $this->removeTablePrefix($this->clearTableAlias($table)), - $column - ); - } - /** * Validate that the given column is a valid query target and return it or the actual name if it's an alias * * Attempts to join the given column from a different table if its association to the given table cannot be * verified. * - * @param array|string $table The table where to look for the column or alias + * @param string $table The table where to look for the column or alias * @param string $name The name or alias of the column to validate * @param RepositoryQuery $query An optional query to pass as context, * if not given no join will be attempted @@ -667,7 +619,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, public function requireQueryColumn($table, $name, RepositoryQuery $query = null) { if ($query === null || $this->validateQueryColumnAssociation($table, $name)) { - return parent::requireQueryColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name, $query); + return parent::requireQueryColumn($table, $name, $query); } return $this->joinColumn($name, $table, $query); @@ -679,7 +631,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, * Attempts to join the given column from a different table if its association to the given table cannot be * verified. * - * @param array|string $table The table where to look for the column or alias + * @param string $table The table where to look for the column or alias * @param string $name The name or alias of the column to validate * @param RepositoryQuery $query An optional query to pass as context, * if not given the column is considered being used for a statement filter @@ -695,7 +647,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, } if ($this->validateQueryColumnAssociation($table, $name)) { - return parent::requireFilterColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name, $query); + return parent::requireFilterColumn($table, $name, $query); } return $this->joinColumn($name, $table, $query); @@ -704,8 +656,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, /** * Return the statement column name for the given alias or null in case the alias does not exist * - * @param array|string $table - * @param string $alias + * @param string $table + * @param string $alias * * @return string|null */ @@ -716,7 +668,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, return $statementAliasColumnMap[$alias]; } - $prefixedAlias = $this->removeTablePrefix($this->clearTableAlias($table)) . '.' . $alias; + $prefixedAlias = $table . '.' . $alias; if (isset($statementAliasColumnMap[$prefixedAlias])) { return $statementAliasColumnMap[$prefixedAlias]; } @@ -725,8 +677,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, /** * Return the alias for the given statement column name or null in case the statement column does not exist * - * @param array|string $table - * @param string $column + * @param string $table + * @param string $column * * @return string|null */ @@ -737,7 +689,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, return $statementColumnAliasMap[$column]; } - $prefixedColumn = $this->removeTablePrefix($this->clearTableAlias($table)) . '.' . $column; + $prefixedColumn = $table . '.' . $column; if (isset($statementColumnAliasMap[$prefixedColumn])) { return $statementColumnAliasMap[$prefixedColumn]; } @@ -746,34 +698,32 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, /** * Return whether the given alias or statement column name is available in the given table * - * @param array|string $table - * @param string $alias + * @param string $table + * @param string $alias * * @return bool */ public function validateStatementColumnAssociation($table, $alias) { - $tableName = $this->removeTablePrefix($this->clearTableAlias($table)); - $statementAliasTableMap = $this->getStatementAliasTableMap(); if (isset($statementAliasTableMap[$alias])) { - return $statementAliasTableMap[$alias] === $tableName; + return $statementAliasTableMap[$alias] === $table; } $statementColumnTableMap = $this->getStatementColumnTableMap(); if (isset($statementColumnTableMap[$alias])) { - return $statementColumnTableMap[$alias] === $tableName; + return $statementColumnTableMap[$alias] === $table; } - $prefixedAlias = $tableName . '.' . $alias; + $prefixedAlias = $table . '.' . $alias; return isset($statementAliasTableMap[$prefixedAlias]) || isset($statementColumnTableMap[$prefixedAlias]); } /** * Return whether the given column name or alias of the given table is a valid statement column * - * @param array|string $table The table where to look for the column or alias - * @param string $name The column name or alias to check + * @param string $table The table where to look for the column or alias + * @param string $name The column name or alias to check * * @return bool */ @@ -784,7 +734,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, && $this->reassembleStatementColumnAlias($table, $name) === null) || !$this->validateStatementColumnAssociation($table, $name) ) { - return parent::hasStatementColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name); + return parent::hasStatementColumn($table, $name); } return true; @@ -793,8 +743,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, /** * Validate that the given column is a valid statement column and return it or the actual name if it's an alias * - * @param array|string $table The table for which to require the column - * @param string $name The name or alias of the column to validate + * @param string $table The table for which to require the column + * @param string $name The name or alias of the column to validate * * @return string The given column's name * @@ -807,15 +757,11 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, } elseif (($alias = $this->reassembleStatementColumnAlias($table, $name)) !== null) { $column = $name; } else { - return parent::requireStatementColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name); + return parent::requireStatementColumn($table, $name); } if (! $this->validateStatementColumnAssociation($table, $alias)) { - throw new StatementException( - 'Statement column "%s" not found in table "%s"', - $name, - $this->removeTablePrefix($this->clearTableAlias($table)) - ); + throw new StatementException('Statement column "%s" not found in table "%s"', $name, $table); } return $column; @@ -829,7 +775,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, * The method is called with the same parameters but in reversed order. * * @param string $name The alias or column name to join into $target - * @param array|string $target The table to join $name into + * @param string $target The table to join $name into * @param RepositoryQUery $query The query to apply the JOIN-clause on * * @return string The resolved alias or $name @@ -843,7 +789,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, throw new ProgrammingError( 'Unable to find a valid table for column "%s" to join into "%s"', $name, - $this->removeTablePrefix($this->clearTableAlias($target)) + $target ); } @@ -861,7 +807,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable, throw new ProgrammingError( 'Unable to join table "%s" into "%s". Method "%s" not found', $tableName, - $this->removeTablePrefix($this->clearTableAlias($target)), + $target, $joinMethod ); } diff --git a/library/Icinga/Repository/RepositoryQuery.php b/library/Icinga/Repository/RepositoryQuery.php index 927f4f02b..8beba988b 100644 --- a/library/Icinga/Repository/RepositoryQuery.php +++ b/library/Icinga/Repository/RepositoryQuery.php @@ -79,8 +79,9 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera */ public function from($target, array $columns = null) { - $target = $this->repository->requireTable($target, $this); - $this->query = $this->repository->getDataSource()->select()->from($target); + $this->query = $this->repository->getDataSource()->select()->from( + $this->repository->requireTable($target, $this) + ); $this->query->columns($this->prepareQueryColumns($target, $columns)); $this->target = $target; return $this;