From ed279a1f1c48f7eac3aaff1064bc8847f5b5383d Mon Sep 17 00:00:00 2001 From: yanniboi Date: Fri, 5 Feb 2016 17:28:37 +0000 Subject: [PATCH 1/4] Issue #2663432 by yanniboi: Copied UserSelection to DecoupledUserSelection and created crm_organisation field. --- modules/crm/decoupled_auth_crm.info.yml | 1 + modules/crm/decoupled_auth_crm.install | 44 +++ .../DecoupledUserSelection.php | 257 ++++++++++++++++++ travis-ci/travis-before-script.sh | 1 + 4 files changed, 303 insertions(+) create mode 100644 modules/crm/decoupled_auth_crm.install create mode 100644 src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php diff --git a/modules/crm/decoupled_auth_crm.info.yml b/modules/crm/decoupled_auth_crm.info.yml index 799abcd..c5be3bf 100644 --- a/modules/crm/decoupled_auth_crm.info.yml +++ b/modules/crm/decoupled_auth_crm.info.yml @@ -7,6 +7,7 @@ core: 8.x dependencies: - decoupled_auth + - og - user - profile - address diff --git a/modules/crm/decoupled_auth_crm.install b/modules/crm/decoupled_auth_crm.install new file mode 100644 index 0000000..9da11ef --- /dev/null +++ b/modules/crm/decoupled_auth_crm.install @@ -0,0 +1,44 @@ +addGroup('user', 'user'); + + // Add reference field to users. + $settings = ['field_name' => 'crm_organisation']; + $field = Drupal\og\Og::createField('og_group_ref', 'user', 'user', $settings); + + + $settings = $field->getSettings(); + $settings['handler'] = 'decoupled_auth_user'; + $settings['handler_settings'] = [ + 'include_anonymous' => 0, + 'include_decoupled' => 1, + 'filter' => [ + 'type' => 'role', + 'role' => [ + 'crm_org' => 'crm_org', + ] + ] + ]; + $field->setSettings($settings); + $field->save(); +} + +/** + * Implements hook_uninstall(). + */ +function decoupled_auth_crm_uninstall() { + // Remove og organisation field. + $field = Drupal\field\Entity\FieldConfig::load('user.user.crm_organisation'); + $field->delete(); +} \ No newline at end of file diff --git a/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php b/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php new file mode 100644 index 0000000..a757348 --- /dev/null +++ b/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php @@ -0,0 +1,257 @@ +connection = $connection; + $this->userStorage = $entity_manager->getStorage('user'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity.manager'), + $container->get('module_handler'), + $container->get('current_user'), + $container->get('database') + ); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $selection_handler_settings = $this->configuration['handler_settings']; + + // Merge in default values. + $selection_handler_settings += array( + 'filter' => array( + 'type' => '_none', + ), + 'include_anonymous' => TRUE, + 'include_decoupled' => TRUE, + ); + + $form['include_anonymous'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Include the anonymous user.'), + '#default_value' => $selection_handler_settings['include_anonymous'], + ); + + $form['include_decoupled'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Include the decoupled users.'), + '#default_value' => $selection_handler_settings['include_decoupled'], + ); + + // Add user specific filter options. + $form['filter']['type'] = array( + '#type' => 'select', + '#title' => $this->t('Filter by'), + '#options' => array( + '_none' => $this->t('- None -'), + 'role' => $this->t('User role'), + ), + '#ajax' => TRUE, + '#limit_validation_errors' => array(), + '#default_value' => $selection_handler_settings['filter']['type'], + ); + + $form['filter']['settings'] = array( + '#type' => 'container', + '#attributes' => array('class' => array('entity_reference-settings')), + '#process' => array(array('\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', 'formProcessMergeParent')), + ); + + if ($selection_handler_settings['filter']['type'] == 'role') { + // Merge in default values. + $selection_handler_settings['filter'] += array( + 'role' => NULL, + ); + + $form['filter']['settings']['role'] = array( + '#type' => 'checkboxes', + '#title' => $this->t('Restrict to the selected roles'), + '#required' => TRUE, + '#options' => array_diff_key(user_role_names(TRUE), array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID)), + '#default_value' => $selection_handler_settings['filter']['role'], + ); + } + + $form += parent::buildConfigurationForm($form, $form_state); + + return $form; + } + + /** + * {@inheritdoc} + */ + protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') { + $query = parent::buildEntityQuery($match, $match_operator); + + // The user entity doesn't have a label column. + if (isset($match)) { + $query->condition('name', $match, $match_operator); + } + + // Filter by role. + $handler_settings = $this->configuration['handler_settings']; + if (!empty($handler_settings['filter']['role'])) { + $query->condition('roles', $handler_settings['filter']['role'], 'IN'); + } + + // Adding the permission check is sadly insufficient for users: core + // requires us to also know about the concept of 'blocked' and 'active'. + if (!$this->currentUser->hasPermission('administer users')) { + $query->condition('status', 1); + } + return $query; + } + + /** + * {@inheritdoc} + */ + public function createNewEntity($entity_type_id, $bundle, $label, $uid) { + $user = parent::createNewEntity($entity_type_id, $bundle, $label, $uid); + + // In order to create a referenceable user, it needs to be active. + if (!$this->currentUser->hasPermission('administer users')) { + /** @var \Drupal\user\UserInterface $user */ + $user->activate(); + } + + return $user; + } + + /** + * {@inheritdoc} + */ + public function validateReferenceableNewEntities(array $entities) { + $entities = parent::validateReferenceableNewEntities($entities); + // Mirror the conditions checked in buildEntityQuery(). + if (!empty($this->configuration['handler_settings']['filter']['role'])) { + $entities = array_filter($entities, function ($user) { + /** @var \Drupal\user\UserInterface $user */ + return !empty(array_intersect($user->getRoles(), $this->configuration['handler_settings']['filter']['role'])); + }); + } + if (!$this->currentUser->hasPermission('administer users')) { + $entities = array_filter($entities, function ($user) { + /** @var \Drupal\user\UserInterface $user */ + return $user->isActive(); + }); + } + return $entities; + } + + /** + * {@inheritdoc} + */ + public function entityQueryAlter(SelectInterface $query) { + // Bail out early if we do not need to match the Anonymous user. + $handler_settings = $this->configuration['handler_settings']; + if (isset($handler_settings['include_anonymous']) && !$handler_settings['include_anonymous']) { + return; + } + + if ($this->currentUser->hasPermission('administer users')) { + // In addition, if the user is administrator, we need to make sure to + // match the anonymous user, that doesn't actually have a name in the + // database. + $conditions = &$query->conditions(); + foreach ($conditions as $key => $condition) { + if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users_field_data.name') { + // Remove the condition. + unset($conditions[$key]); + + // Re-add the condition and a condition on uid = 0 so that we end up + // with a query in the form: + // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0) + $or = db_or(); + $or->condition($condition['field'], $condition['value'], $condition['operator']); + // Sadly, the Database layer doesn't allow us to build a condition + // in the form ':placeholder = :placeholder2', because the 'field' + // part of a condition is always escaped. + // As a (cheap) workaround, we separately build a condition with no + // field, and concatenate the field and the condition separately. + $value_part = db_and(); + $value_part->condition('anonymous_name', $condition['value'], $condition['operator']); + $value_part->compile($this->connection, $query); + $or->condition(db_and() + ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => \Drupal::config('user.settings')->get('anonymous'))) + ->condition('base_table.uid', 0) + ); + $query->condition($or); + } + } + } + } + +} diff --git a/travis-ci/travis-before-script.sh b/travis-ci/travis-before-script.sh index edd9584..ccfed1f 100644 --- a/travis-ci/travis-before-script.sh +++ b/travis-ci/travis-before-script.sh @@ -10,6 +10,7 @@ drupal_ti_ensure_drupal mkdir -p "$DRUPAL_TI_DRUPAL_DIR/$DRUPAL_TI_MODULES_PATH" cd "$DRUPAL_TI_DRUPAL_DIR/$DRUPAL_TI_MODULES_PATH" git clone --branch 8.x-1.x http://git.drupal.org/project/composer_manager.git --depth 1 +git clone --branch 8.x-1.x https://github.com/amitaibu/og.git --depth 1 php composer_manager/scripts/init.php # Ensure the module is linked into the codebase. From 1b17962686e857e50b4ebd219fdc87e0fec8ed31 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Tue, 9 Feb 2016 16:23:21 +0000 Subject: [PATCH 2/4] Issue #2663432 by yanniboi: Replace the default user selection handler with our decoupled user selection handler. --- modules/crm/decoupled_auth_crm.install | 8 +-- .../DecoupledUserSelection.php | 61 +++++-------------- 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/modules/crm/decoupled_auth_crm.install b/modules/crm/decoupled_auth_crm.install index 9da11ef..027064a 100644 --- a/modules/crm/decoupled_auth_crm.install +++ b/modules/crm/decoupled_auth_crm.install @@ -17,11 +17,7 @@ function decoupled_auth_crm_install() { $settings = ['field_name' => 'crm_organisation']; $field = Drupal\og\Og::createField('og_group_ref', 'user', 'user', $settings); - - $settings = $field->getSettings(); - $settings['handler'] = 'decoupled_auth_user'; - $settings['handler_settings'] = [ - 'include_anonymous' => 0, + $handler_settings = [ 'include_decoupled' => 1, 'filter' => [ 'type' => 'role', @@ -30,7 +26,7 @@ function decoupled_auth_crm_install() { ] ] ]; - $field->setSettings($settings); + $field->setSetting('handler_settings', $handler_settings); $field->save(); } diff --git a/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php b/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php index a757348..2c71598 100644 --- a/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php +++ b/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php @@ -21,11 +21,11 @@ * Provides specific access control for the user entity type. * * @EntityReferenceSelection( - * id = "decoupled_auth_user", - * label = @Translation("Decoupled User selection"), + * id = "default:user", + * label = @Translation("Decoupled user selection"), * entity_types = {"user"}, - * group = "decoupled_auth_user", - * weight = 10 + * group = "default", + * weight = 1 * ) */ class DecoupledUserSelection extends DefaultSelection { @@ -99,12 +99,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta 'include_decoupled' => TRUE, ); - $form['include_anonymous'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Include the anonymous user.'), - '#default_value' => $selection_handler_settings['include_anonymous'], - ); - $form['include_decoupled'] = array( '#type' => 'checkbox', '#title' => $this->t('Include the decoupled users.'), @@ -158,7 +152,10 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') // The user entity doesn't have a label column. if (isset($match)) { - $query->condition('name', $match, $match_operator); + $or = $query->orConditionGroup(); + $or->condition('name', $match, $match_operator); + $or->condition('mail', $match, $match_operator); + $query->condition($or); } // Filter by role. @@ -215,43 +212,17 @@ public function validateReferenceableNewEntities(array $entities) { * {@inheritdoc} */ public function entityQueryAlter(SelectInterface $query) { - // Bail out early if we do not need to match the Anonymous user. + // Only show decoupled users if requested and user has permission. $handler_settings = $this->configuration['handler_settings']; - if (isset($handler_settings['include_anonymous']) && !$handler_settings['include_anonymous']) { - return; - } - - if ($this->currentUser->hasPermission('administer users')) { - // In addition, if the user is administrator, we need to make sure to - // match the anonymous user, that doesn't actually have a name in the - // database. - $conditions = &$query->conditions(); - foreach ($conditions as $key => $condition) { - if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users_field_data.name') { - // Remove the condition. - unset($conditions[$key]); - - // Re-add the condition and a condition on uid = 0 so that we end up - // with a query in the form: - // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0) - $or = db_or(); - $or->condition($condition['field'], $condition['value'], $condition['operator']); - // Sadly, the Database layer doesn't allow us to build a condition - // in the form ':placeholder = :placeholder2', because the 'field' - // part of a condition is always escaped. - // As a (cheap) workaround, we separately build a condition with no - // field, and concatenate the field and the condition separately. - $value_part = db_and(); - $value_part->condition('anonymous_name', $condition['value'], $condition['operator']); - $value_part->compile($this->connection, $query); - $or->condition(db_and() - ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => \Drupal::config('user.settings')->get('anonymous'))) - ->condition('base_table.uid', 0) - ); - $query->condition($or); - } + if (isset($handler_settings['include_decoupled']) && $handler_settings['include_decoupled']) { + // @TODO Use a better permission that administer users when available. + if (!$this->currentUser->hasPermission('administer users')) { + $query->isNotNull('name'); } } + else { + $query->isNotNull('name'); + } } } From ec9e053f60ce5ae40c349da957064be1dc2ca292 Mon Sep 17 00:00:00 2001 From: yanniboi Date: Wed, 10 Feb 2016 12:37:00 +0000 Subject: [PATCH 3/4] Issue #2663432 by yanniboi: Test the DecoupleUserSelection plugin. --- .../src/Kernel/DecoupledUserSelectionTest.php | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 tests/src/Kernel/DecoupledUserSelectionTest.php diff --git a/tests/src/Kernel/DecoupledUserSelectionTest.php b/tests/src/Kernel/DecoupledUserSelectionTest.php new file mode 100644 index 0000000..b82a39f --- /dev/null +++ b/tests/src/Kernel/DecoupledUserSelectionTest.php @@ -0,0 +1,271 @@ +installConfig(['decoupled_auth']); + $this->installEntitySchema('user'); + $this->installSchema('system', 'sequences'); + + $this->userRole = $this->createRole([]); + $this->adminUser = $this->createUser([], NULL, TRUE); + $this->setSelectionHandler(); + } + + /** + * Updates the selection handler. + * + * @param bool|FALSE $filter + * Whether or not the handler should filter by user role. + */ + protected function setSelectionHandler($filter = FALSE) { + $options = [ + 'target_type' => 'user', + 'handler' => 'default:user', + 'handler_settings' => [ + 'include_decoupled' => 1, + ], + ]; + + if ($filter) { + $options['handler_settings']['filter'] = [ + 'type' => 'role', + 'role' => [ + $this->userRole => $this->userRole, + ] + ]; + } + + $this->selectionHandler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($options); + } + + /** + * Creates a range of users to test against. + */ + protected function createTestUsers() { + // Decoupled users (with and without user role). + $this->decoupledUserNoRole = $this->createDecoupledUser(); + + $this->decoupledUserRole = $this->createDecoupledUser(); + $this->decoupledUserRole->activate(); + $this->decoupledUserRole->addRole($this->userRole); + $this->decoupledUserRole->save(); + + // Coupled users (with and without user role). + $this->coupledUserNoRole = $this->createUser(); + + $this->coupledUserRole = $this->createUser(); + $this->coupledUserRole->addRole($this->userRole); + $this->coupledUserRole->save(); + } + + /** + * Sets the current account. + * + * @param \Drupal\Core\Session\AccountInterface $account + */ + protected function setCurrentAccount(AccountInterface $account) { + $this->container->get('account_switcher')->switchTo($account); + } + + /** + * Testing selection handler class. + */ + public function testSelectionHandlerClass() { + $this->assertEquals(get_class($this->selectionHandler), 'Drupal\decoupled_auth\Plugin\EntityReferenceSelection\DecoupledUserSelection'); + } + + + /** + * Testing selection handler results without role filter. + * + * All users should be referenceable without role filter. + * Decoupled users should only be referenceable for the admin user. + */ + public function testSelectionHandlerResults() { + // Create the demo users. + $this->createTestUsers(); + + // Get selection users. + $groups = $this->selectionHandler->getReferenceableEntities(); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertTrue(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->coupledUserRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserRole->id(), $gids)); + + // Switch to admin user. + $this->setCurrentAccount($this->adminUser); + + // Get selection users. + $groups = $this->selectionHandler->getReferenceableEntities(); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertTrue(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->coupledUserRole->id(), $gids)); + $this->assertTrue(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->decoupledUserRole->id(), $gids)); + } + + /** + * Testing selection handler results with role filter. + * + * Only user with the correct role should be referenceable. + * Decoupled users should only be referenceable for the admin user. + */ + public function testSelectionHandlerRolesResults() { + // Create the demo users and use the handler with role filter. + $this->setSelectionHandler(TRUE); + $this->createTestUsers(); + + // Get selection users. + $groups = $this->selectionHandler->getReferenceableEntities(); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertFalse(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->coupledUserRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserRole->id(), $gids)); + + // Switch to admin user. + $this->setCurrentAccount($this->adminUser); + + // Get selection users. + $groups = $this->selectionHandler->getReferenceableEntities(); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertFalse(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->coupledUserRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->decoupledUserRole->id(), $gids)); + } + + /** + * Testing selection handler results with matches. + * + * Check matching works on username and email for decoupled and coupled users. + */ + public function testSelectionHandlerMatchResults() { + // Create the demo users and use the handler with role filter. + $this->createTestUsers(); + $this->setCurrentAccount($this->adminUser); + + // Get selection users with email match. + $groups = $this->selectionHandler->getReferenceableEntities('@'); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertTrue(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->coupledUserRole->id(), $gids)); + $this->assertTrue(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertTrue(in_array($this->decoupledUserRole->id(), $gids)); + + + // Get selection users with username match. + $groups = $this->selectionHandler->getReferenceableEntities($this->coupledUserNoRole->getAccountName()); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertTrue(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertFalse(in_array($this->coupledUserRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserRole->id(), $gids)); + + // Get selection users with email prefix. + $groups = $this->selectionHandler->getReferenceableEntities($this->decoupledUserNoRole->email_prefix); + $gids = array_keys($groups['user']); + + // Check results. + $this->assertFalse(in_array($this->coupledUserNoRole->id(), $gids)); + $this->assertFalse(in_array($this->coupledUserRole->id(), $gids)); + $this->assertTrue(in_array($this->decoupledUserNoRole->id(), $gids)); + $this->assertFalse(in_array($this->decoupledUserRole->id(), $gids)); + } + +} From 0ba4206272157fd3457a51b5ff9b606399bddd7d Mon Sep 17 00:00:00 2001 From: yanniboi Date: Wed, 10 Feb 2016 14:39:03 +0000 Subject: [PATCH 4/4] Issue #2663432 by yanniboi: Separated UserDecoupledSelection from UserSelection again and added entity_reference_selection schema. --- config/schema/decoupled_auth.schema.yml | 20 +++++++++++++++++++ modules/crm/decoupled_auth_crm.install | 1 + .../DecoupledUserSelection.php | 6 +++--- .../src/Kernel/DecoupledUserSelectionTest.php | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/config/schema/decoupled_auth.schema.yml b/config/schema/decoupled_auth.schema.yml index 1b56b21..56d9444 100644 --- a/config/schema/decoupled_auth.schema.yml +++ b/config/schema/decoupled_auth.schema.yml @@ -25,3 +25,23 @@ decoupled_auth.settings: label: 'Selected roles' sequence: type: string + +entity_reference_selection.decoupled_auth_user: + type: entity_reference_selection + mapping: + filter: + type: mapping + label: 'Filter settings' + mapping: + type: + type: string + label: 'Filter by' + role: + type: sequence + label: 'Restrict to the selected roles' + sequence: + type: string + label: 'Role' + include_decoupled: + type: boolean + label: 'Include the decoupled users.' \ No newline at end of file diff --git a/modules/crm/decoupled_auth_crm.install b/modules/crm/decoupled_auth_crm.install index 027064a..f800621 100644 --- a/modules/crm/decoupled_auth_crm.install +++ b/modules/crm/decoupled_auth_crm.install @@ -17,6 +17,7 @@ function decoupled_auth_crm_install() { $settings = ['field_name' => 'crm_organisation']; $field = Drupal\og\Og::createField('og_group_ref', 'user', 'user', $settings); + $field->setSetting('handler', 'decoupled_auth_user'); $handler_settings = [ 'include_decoupled' => 1, 'filter' => [ diff --git a/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php b/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php index 2c71598..c4e14f7 100644 --- a/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php +++ b/src/Plugin/EntityReferenceSelection/DecoupledUserSelection.php @@ -21,11 +21,11 @@ * Provides specific access control for the user entity type. * * @EntityReferenceSelection( - * id = "default:user", + * id = "decoupled_auth_user", * label = @Translation("Decoupled user selection"), * entity_types = {"user"}, - * group = "default", - * weight = 1 + * group = "decoupled_auth_user", + * weight = 10 * ) */ class DecoupledUserSelection extends DefaultSelection { diff --git a/tests/src/Kernel/DecoupledUserSelectionTest.php b/tests/src/Kernel/DecoupledUserSelectionTest.php index b82a39f..c9d8a11 100644 --- a/tests/src/Kernel/DecoupledUserSelectionTest.php +++ b/tests/src/Kernel/DecoupledUserSelectionTest.php @@ -102,7 +102,7 @@ protected function setUp() { protected function setSelectionHandler($filter = FALSE) { $options = [ 'target_type' => 'user', - 'handler' => 'default:user', + 'handler' => 'decoupled_auth_user', 'handler_settings' => [ 'include_decoupled' => 1, ],