array( 'order' => 'asc' ) ); /** * Set the user backend to be associated with this user group backend * * @param LdapUserBackend $backend * * @return $this */ public function setUserBackend(LdapUserBackend $backend) { $this->userBackend = $backend; return $this; } /** * Return the user backend being associated with this user group backend * * @return LdapUserBackend */ public function getUserBackend() { return $this->userBackend; } /** * Set the base DN to use for a user query * * @param string $baseDn * * @return $this */ public function setUserBaseDn($baseDn) { if (($baseDn = trim($baseDn))) { $this->userBaseDn = $baseDn; } return $this; } /** * Return the base DN to use for a user query * * @return string */ public function getUserBaseDn() { return $this->userBaseDn; } /** * Set the base DN to use for a group query * * @param string $baseDn * * @return $this */ public function setGroupBaseDn($baseDn) { if (($baseDn = trim($baseDn))) { $this->groupBaseDn = $baseDn; } return $this; } /** * Return the base DN to use for a group query * * @return string */ public function getGroupBaseDn() { return $this->groupBaseDn; } /** * Set the objectClass where to look for users * * @param string $userClass * * @return $this */ public function setUserClass($userClass) { $this->userClass = $this->getNormedAttribute($userClass); return $this; } /** * Return the objectClass where to look for users * * @return string */ public function getUserClass() { return $this->userClass; } /** * 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); return $this; } /** * Return the objectClass where to look for groups * * @return string */ public function getGroupClass() { return $this->groupClass; } /** * Set the attribute name where to find a user's name * * @param string $userNameAttribute * * @return $this */ public function setUserNameAttribute($userNameAttribute) { $this->userNameAttribute = $this->getNormedAttribute($userNameAttribute); return $this; } /** * Return the attribute name where to find a user's name * * @return string */ public function getUserNameAttribute() { return $this->userNameAttribute; } /** * Set the attribute name where to find a group's name * * @param string $groupNameAttribute * * @return $this */ public function setGroupNameAttribute($groupNameAttribute) { $this->groupNameAttribute = $this->getNormedAttribute($groupNameAttribute); return $this; } /** * Return the attribute name where to find a group's name * * @return string */ public function getGroupNameAttribute() { return $this->groupNameAttribute; } /** * Set the attribute name where to find a group's member * * @param string $groupMemberAttribute * * @return $this */ public function setGroupMemberAttribute($groupMemberAttribute) { $this->groupMemberAttribute = $this->getNormedAttribute($groupMemberAttribute); return $this; } /** * Return the attribute name where to find a group's member * * @return string */ public function getGroupMemberAttribute() { return $this->groupMemberAttribute; } /** * Set the custom LDAP filter to apply on a user query * * @param string $filter * * @return $this */ public function setUserFilter($filter) { if (($filter = trim($filter))) { if ($filter[0] === '(') { $filter = substr($filter, 1, -1); } $this->userFilter = $filter; } return $this; } /** * Return the custom LDAP filter to apply on a user query * * @return string */ public function getUserFilter() { return $this->userFilter; } /** * Set the custom LDAP filter to apply on a group query * * @param string $filter * * @return $this */ public function setGroupFilter($filter) { if (($filter = trim($filter))) { $this->groupFilter = $filter; } return $this; } /** * Return the custom LDAP filter to apply on a group query * * @return string */ public function getGroupFilter() { return $this->groupFilter; } /** * Return a new query for the given columns * * @param array $columns The desired columns, if null all columns will be queried * * @return RepositoryQuery */ public function select(array $columns = null) { $query = parent::select($columns); $query->getQuery()->setBase($this->groupBaseDn); if ($this->groupFilter) { // TODO(jom): This should differentiate between groups and their memberships $query->getQuery()->where(new Expression($this->groupFilter)); } return $query; } /** * Initialize this repository's query columns * * @return array * * @throws ProgrammingError In case either $this->groupNameAttribute or $this->groupClass 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->ds->getCapabilities()->isActiveDirectory()) { $createdAtAttribute = 'whenCreated'; $lastModifiedAttribute = 'whenChanged'; } else { $createdAtAttribute = 'createTimestamp'; $lastModifiedAttribute = 'modifyTimestamp'; } $columns = array( 'group' => $this->groupNameAttribute, 'group_name' => $this->groupNameAttribute, 'user' => $this->groupMemberAttribute, 'user_name' => $this->groupMemberAttribute, 'created_at' => $createdAtAttribute, 'last_modified' => $lastModifiedAttribute ); return array($this->groupClass => $columns, $this->groupClass => $columns); } /** * Initialize this repository's filter columns * * @return array */ protected function initializeFilterColumns() { return array( t('Username') => 'user', t('User Group') => 'group_name', t('Created At') => 'created_at', t('Last Modified') => 'last_modified' ); } /** * Initialize this repository's conversion rules * * @return array * * @throws ProgrammingError In case $this->groupClass has not been set yet */ protected function initializeConversionRules() { if ($this->groupClass === null) { throw new ProgrammingError('It is required to set the objectClass where to look for groups first'); } if ($this->groupMemberAttribute === null) { throw new ProgrammingError('It is required to set a attribute name where to find a group\'s members first'); } $rules = array( $this->groupClass => array( 'created_at' => 'generalized_time', 'last_modified' => 'generalized_time' ) ); if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) { $rules[$this->groupClass][] = 'user_name'; } return $rules; } /** * Return the uid for the given distinguished name * * @param string $username * * @param string */ protected function retrieveUserName($dn) { return $this->ds ->select() ->from('*', array($this->userNameAttribute)) ->setBase($dn) ->fetchOne(); } /** * Validate that the requested table exists * * This will return $this->groupClass in case $table equals "group" or "group_membership". * * @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 requireTable($table, RepositoryQuery $query = null) { $table = parent::requireTable($table, $query); if ($table === 'group' || $table === 'group_membership') { $table = $this->groupClass; } return $table; } /** * Validate that the given column is a valid query target and return it or the actual name if it's an alias * * @param string $table The table where to look for the column or alias * @param string $name The name or alias of the column to validate * @param RepositoryQuery $query An optional query to pass as context * * @return string The given column's name * * @throws QueryException In case the given column is not a valid query column */ public function requireQueryColumn($table, $name, RepositoryQuery $query = null) { $column = parent::requireQueryColumn($table, $name, $query); if ($name === 'user_name' && $query !== null) { $query->getQuery()->setUnfoldAttribute('user_name'); } return $column; } /** * Return the groups the given user is a member of * * @param User $user * * @return array */ public function getMemberships(User $user) { if ($this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) { $queryValue = $user->getUsername(); } elseif (($queryValue = $user->getAdditional('ldap_dn')) === null) { $userQuery = $this->ds ->select() ->from($this->userClass) ->where($this->userNameAttribute, $user->getUsername()) ->setBase($this->userBaseDn) ->setUsePagedResults(false); if ($this->userFilter) { $userQuery->where(new Expression($this->userFilter)); } if (($queryValue = $userQuery->fetchDn()) === null) { return array(); } } $groupQuery = $this->ds ->select() ->from($this->groupClass, array($this->groupNameAttribute)) ->where($this->groupMemberAttribute, $queryValue) ->setBase($this->groupBaseDn); if ($this->groupFilter) { $groupQuery->where(new Expression($this->groupFilter)); } $groups = array(); foreach ($groupQuery as $row) { $groups[] = $row->{$this->groupNameAttribute}; } return $groups; } /** * Apply the given configuration on this backend * * @param ConfigObject $config * * @return $this * * @throws ConfigurationError In case a linked user backend does not exist or is invalid */ public function setConfig(ConfigObject $config) { if ($config->backend === 'ldap') { $defaults = $this->getOpenLdapDefaults(); } elseif ($config->backend === 'msldap') { $defaults = $this->getActiveDirectoryDefaults(); } else { $defaults = new ConfigObject(); } if ($config->user_backend && $config->user_backend !== 'none') { $userBackend = UserBackend::create($config->user_backend); if (! $userBackend instanceof LdapUserBackend) { throw new ConfigurationError('User backend "%s" is not of type LDAP', $config->user_backend); } if ( $this->ds->getHostname() !== $userBackend->getDataSource()->getHostname() || $this->ds->getPort() !== $userBackend->getDataSource()->getPort() ) { // TODO(jom): Elaborate whether it makes sense to link directories on different hosts throw new ConfigurationError( 'It is required that a linked user backend refers to the ' . 'same directory as it\'s user group backend counterpart' ); } $this->setUserBackend($userBackend); $defaults->merge(array( 'user_base_dn' => $userBackend->getBaseDn(), 'user_class' => $userBackend->getUserClass(), 'user_name_attribute' => $userBackend->getUserNameAttribute(), 'user_filter' => $userBackend->getFilter() )); } return $this ->setGroupBaseDn($config->base_dn) ->setUserBaseDn($config->get('user_base_dn', $defaults->user_base_dn)) ->setGroupClass($config->get('group_class', $defaults->group_class)) ->setUserClass($config->get('user_class', $defaults->user_class)) ->setGroupNameAttribute($config->get('group_name_attribute', $defaults->group_name_attribute)) ->setUserNameAttribute($config->get('user_name_attribute', $defaults->user_name_attribute)) ->setGroupMemberAttribute($config->get('group_member_attribute', $defaults->group_member_attribute)) ->setGroupFilter($config->filter) ->setUserFilter($config->user_filter); } /** * Return the configuration defaults for an OpenLDAP environment * * @return ConfigObject */ public function getOpenLdapDefaults() { return new ConfigObject(array( 'group_class' => 'group', 'user_class' => 'inetOrgPerson', 'group_name_attribute' => 'gid', 'user_name_attribute' => 'uid', 'group_member_attribute' => 'member' )); } /** * Return the configuration defaults for an ActiveDirectory environment * * @return ConfigObject */ public function getActiveDirectoryDefaults() { return new ConfigObject(array( 'group_class' => 'group', 'user_class' => 'user', 'group_name_attribute' => 'sAMAccountName', 'user_name_attribute' => 'sAMAccountName', 'group_member_attribute' => 'member' )); } }