Repository: Ensure that we'll internally only work with virtual table names

refs #10367
This commit is contained in:
Johannes Meyer 2015-10-16 14:46:44 +02:00
parent f88bd525f1
commit 36340aafa6
4 changed files with 74 additions and 123 deletions

View File

@ -102,15 +102,13 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
/** /**
* Set the objectClass where to look for users * Set the objectClass where to look for users
* *
* Sets also the base table name for the underlying repository.
*
* @param string $userClass * @param string $userClass
* *
* @return $this * @return $this
*/ */
public function setUserClass($userClass) public function setUserClass($userClass)
{ {
$this->baseTable = $this->userClass = $this->getNormedAttribute($userClass); $this->userClass = $this->getNormedAttribute($userClass);
return $this; return $this;
} }
@ -216,13 +214,10 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
* *
* @return array * @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() 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) { if ($this->userNameAttribute === null) {
throw new ProgrammingError('It is required to set a attribute name where to find a user\'s name first'); 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( return array(
$this->userClass => array( 'user' => array(
'user' => $this->userNameAttribute, 'user' => $this->userNameAttribute,
'user_name' => $this->userNameAttribute, 'user_name' => $this->userNameAttribute,
'is_active' => $isActiveAttribute, 'is_active' => $isActiveAttribute,
@ -269,15 +264,9 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
* Initialize this repository's conversion rules * Initialize this repository's conversion rules
* *
* @return array * @return array
*
* @throws ProgrammingError In case $this->userClass has not been set yet
*/ */
protected function initializeConversionRules() 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()) { if ($this->ds->getCapabilities()->isActiveDirectory()) {
$stateConverter = 'user_account_control'; $stateConverter = 'user_account_control';
} else { } else {
@ -285,7 +274,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
} }
return array( return array(
$this->userClass => array( 'user' => array(
'is_active' => $stateConverter, 'is_active' => $stateConverter,
'created_at' => 'generalized_time', 'created_at' => 'generalized_time',
'last_modified' => 'generalized_time' 'last_modified' => 'generalized_time'
@ -330,14 +319,26 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
} }
/** /**
* Validate that the requested table exists
* @param Inspection $info Optional inspection to fill with diagnostic info
* *
* @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() ->getQuery()
->setUsePagedResults(false) ->setUsePagedResults(false)
->fetchDn(); ->fetchDn();
if ($userDn === null) { if ($userDn === null) {
return false; return false;
} }
$testCredentialsResult = $this->ds->testCredentials($userDn, $password); $validCredentials = $this->ds->testCredentials($userDn, $password);
if ($testCredentialsResult) { if ($validCredentials) {
$user->setAdditional('ldap_dn', $userDn); $user->setAdditional('ldap_dn', $userDn);
} }
return $testCredentialsResult; return $validCredentials;
} catch (LdapException $e) { } catch (LdapException $e) {
throw new AuthenticationException( throw new AuthenticationException(
'Failed to authenticate user "%s" against backend "%s". An exception was thrown:', 'Failed to authenticate user "%s" against backend "%s". An exception was thrown:',

View File

@ -211,15 +211,13 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
/** /**
* Set the objectClass where to look for groups * Set the objectClass where to look for groups
* *
* Sets also the base table name for the underlying repository.
*
* @param string $groupClass * @param string $groupClass
* *
* @return $this * @return $this
*/ */
public function setGroupClass($groupClass) public function setGroupClass($groupClass)
{ {
$this->baseTable = $this->groupClass = $this->getNormedAttribute($groupClass); $this->groupClass = $this->getNormedAttribute($groupClass);
return $this; return $this;
} }
@ -382,16 +380,17 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
* *
* @return array * @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() 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) { if ($this->groupNameAttribute === null) {
throw new ProgrammingError('It is required to set a attribute name where to find a group\'s name first'); 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()) { if ($this->ds->getCapabilities()->isActiveDirectory()) {
$createdAtAttribute = 'whenCreated'; $createdAtAttribute = 'whenCreated';
@ -409,7 +408,7 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
'created_at' => $createdAtAttribute, 'created_at' => $createdAtAttribute,
'last_modified' => $lastModifiedAttribute '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 * @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() protected function initializeConversionRules()
{ {
@ -444,13 +444,17 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
} }
$rules = array( $rules = array(
$this->groupClass => array( 'group' => array(
'created_at' => 'generalized_time',
'last_modified' => 'generalized_time'
),
'group_membership' => array(
'created_at' => 'generalized_time', 'created_at' => 'generalized_time',
'last_modified' => 'generalized_time' 'last_modified' => 'generalized_time'
) )
); );
if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) { if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) {
$rules[$this->groupClass][] = 'user_name'; $rules['group']['user_name'] = 'user_name';
} }
return $rules; return $rules;

View File

@ -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 * 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. * may be possible that columns from another table where joined in which would otherwise not being converted.
* *
* @param array|string $table * @param string $table
* @param string $column * @param string $column
* *
* @return bool * @return bool
*/ */
@ -477,10 +477,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
{ {
if ($column !== null) { if ($column !== null) {
if ($this->validateQueryColumnAssociation($table, $column)) { if ($this->validateQueryColumnAssociation($table, $column)) {
return parent::providesValueConversion( return parent::providesValueConversion($table, $column);
$this->removeTablePrefix($this->clearTableAlias($table)),
$column
);
} }
if (($tableName = $this->findTableName($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) protected function getConverter($table, $name, $context, RepositoryQuery $query = null)
{ {
if ( if (
($query !== null && $this->validateQueryColumnAssociation($table, $name)) ! ($query !== null && $this->validateQueryColumnAssociation($table, $name))
|| ($query === null && $this->validateStatementColumnAssociation($table, $name)) && !($query === null && $this->validateStatementColumnAssociation($table, $name))
) { ) {
$table = $this->removeTablePrefix($this->clearTableAlias($table));
} else {
$table = $this->findTableName($name); $table = $this->findTableName($name);
if (! $table) { if (! $table) {
throw new ProgrammingError('Column name validation seems to have failed. Did you require the column?'); 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 $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 * 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 $table
* @param string $column * @param string $column
* *
* @return string|null * @return string|null
*/ */
public function reassembleQueryColumnAlias($table, $column) public function reassembleQueryColumnAlias($table, $column)
{ {
$alias = parent::reassembleQueryColumnAlias($this->removeTablePrefix($this->clearTableAlias($table)), $column); $alias = parent::reassembleQueryColumnAlias($table, $column);
if ( if (
$alias === null $alias === null
&& !$this->validateQueryColumnAssociation($table, $column) && !$this->validateQueryColumnAssociation($table, $column)
@ -633,29 +601,13 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
return $alias; 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 * 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 * Attempts to join the given column from a different table if its association to the given table cannot be
* verified. * 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 string $name The name or alias of the column to validate
* @param RepositoryQuery $query An optional query to pass as context, * @param RepositoryQuery $query An optional query to pass as context,
* if not given no join will be attempted * 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) public function requireQueryColumn($table, $name, RepositoryQuery $query = null)
{ {
if ($query === null || $this->validateQueryColumnAssociation($table, $name)) { 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); 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 * Attempts to join the given column from a different table if its association to the given table cannot be
* verified. * 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 string $name The name or alias of the column to validate
* @param RepositoryQuery $query An optional query to pass as context, * @param RepositoryQuery $query An optional query to pass as context,
* if not given the column is considered being used for a statement filter * 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)) { 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); 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 * Return the statement column name for the given alias or null in case the alias does not exist
* *
* @param array|string $table * @param string $table
* @param string $alias * @param string $alias
* *
* @return string|null * @return string|null
*/ */
@ -716,7 +668,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
return $statementAliasColumnMap[$alias]; return $statementAliasColumnMap[$alias];
} }
$prefixedAlias = $this->removeTablePrefix($this->clearTableAlias($table)) . '.' . $alias; $prefixedAlias = $table . '.' . $alias;
if (isset($statementAliasColumnMap[$prefixedAlias])) { if (isset($statementAliasColumnMap[$prefixedAlias])) {
return $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 * 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 $table
* @param string $column * @param string $column
* *
* @return string|null * @return string|null
*/ */
@ -737,7 +689,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
return $statementColumnAliasMap[$column]; return $statementColumnAliasMap[$column];
} }
$prefixedColumn = $this->removeTablePrefix($this->clearTableAlias($table)) . '.' . $column; $prefixedColumn = $table . '.' . $column;
if (isset($statementColumnAliasMap[$prefixedColumn])) { if (isset($statementColumnAliasMap[$prefixedColumn])) {
return $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 * Return whether the given alias or statement column name is available in the given table
* *
* @param array|string $table * @param string $table
* @param string $alias * @param string $alias
* *
* @return bool * @return bool
*/ */
public function validateStatementColumnAssociation($table, $alias) public function validateStatementColumnAssociation($table, $alias)
{ {
$tableName = $this->removeTablePrefix($this->clearTableAlias($table));
$statementAliasTableMap = $this->getStatementAliasTableMap(); $statementAliasTableMap = $this->getStatementAliasTableMap();
if (isset($statementAliasTableMap[$alias])) { if (isset($statementAliasTableMap[$alias])) {
return $statementAliasTableMap[$alias] === $tableName; return $statementAliasTableMap[$alias] === $table;
} }
$statementColumnTableMap = $this->getStatementColumnTableMap(); $statementColumnTableMap = $this->getStatementColumnTableMap();
if (isset($statementColumnTableMap[$alias])) { 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 isset($statementAliasTableMap[$prefixedAlias]) || isset($statementColumnTableMap[$prefixedAlias]);
} }
/** /**
* Return whether the given column name or alias of the given table is a valid statement column * 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 $table The table where to look for the column or alias
* @param string $name The column name or alias to check * @param string $name The column name or alias to check
* *
* @return bool * @return bool
*/ */
@ -784,7 +734,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
&& $this->reassembleStatementColumnAlias($table, $name) === null) && $this->reassembleStatementColumnAlias($table, $name) === null)
|| !$this->validateStatementColumnAssociation($table, $name) || !$this->validateStatementColumnAssociation($table, $name)
) { ) {
return parent::hasStatementColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name); return parent::hasStatementColumn($table, $name);
} }
return true; 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 * 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 $table The table for which to require the column
* @param string $name The name or alias of the column to validate * @param string $name The name or alias of the column to validate
* *
* @return string The given column's name * @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) { } elseif (($alias = $this->reassembleStatementColumnAlias($table, $name)) !== null) {
$column = $name; $column = $name;
} else { } else {
return parent::requireStatementColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name); return parent::requireStatementColumn($table, $name);
} }
if (! $this->validateStatementColumnAssociation($table, $alias)) { if (! $this->validateStatementColumnAssociation($table, $alias)) {
throw new StatementException( throw new StatementException('Statement column "%s" not found in table "%s"', $name, $table);
'Statement column "%s" not found in table "%s"',
$name,
$this->removeTablePrefix($this->clearTableAlias($table))
);
} }
return $column; 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. * 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 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 * @param RepositoryQUery $query The query to apply the JOIN-clause on
* *
* @return string The resolved alias or $name * @return string The resolved alias or $name
@ -843,7 +789,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
throw new ProgrammingError( throw new ProgrammingError(
'Unable to find a valid table for column "%s" to join into "%s"', 'Unable to find a valid table for column "%s" to join into "%s"',
$name, $name,
$this->removeTablePrefix($this->clearTableAlias($target)) $target
); );
} }
@ -861,7 +807,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
throw new ProgrammingError( throw new ProgrammingError(
'Unable to join table "%s" into "%s". Method "%s" not found', 'Unable to join table "%s" into "%s". Method "%s" not found',
$tableName, $tableName,
$this->removeTablePrefix($this->clearTableAlias($target)), $target,
$joinMethod $joinMethod
); );
} }

View File

@ -79,8 +79,9 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
*/ */
public function from($target, array $columns = null) public function from($target, array $columns = null)
{ {
$target = $this->repository->requireTable($target, $this); $this->query = $this->repository->getDataSource()->select()->from(
$this->query = $this->repository->getDataSource()->select()->from($target); $this->repository->requireTable($target, $this)
);
$this->query->columns($this->prepareQueryColumns($target, $columns)); $this->query->columns($this->prepareQueryColumns($target, $columns));
$this->target = $target; $this->target = $target;
return $this; return $this;