diff --git a/config.py b/config.py index 005ea4880..2a4d1cceb 100644 --- a/config.py +++ b/config.py @@ -273,6 +273,9 @@ class DefaultConfig(ImmutableConfig): # rather than only write access or admin access. FEATURE_READER_BUILD_LOGS = False + # Feature Flag: If set to true, autocompletion will apply to partial usernames. + FEATURE_PARTIAL_USER_AUTOCOMPLETE = True + # If a namespace is defined in the public namespace list, then it will appear on *all* # user's repository list pages, regardless of whether that user is a member of the namespace. # Typically, this is used by an enterprise customer in configuring a set of "well-known" diff --git a/data/model/user.py b/data/model/user.py index 758491ef1..6e8265f69 100644 --- a/data/model/user.py +++ b/data/model/user.py @@ -605,8 +605,12 @@ def get_matching_user_namespaces(namespace_prefix, username, limit=10): return _basequery.filter_to_repos_for_user(base_query, username).limit(limit) -def get_matching_users(username_prefix, robot_namespace=None, organization=None, limit=20): +def get_matching_users(username_prefix, robot_namespace=None, organization=None, limit=20, + exact_matches_only=False): user_search = prefix_search(User.username, username_prefix) + if exact_matches_only: + user_search = (User.username == username_prefix) + direct_user_query = (user_search & (User.organization == False) & (User.robot == False)) if robot_namespace: diff --git a/endpoints/api/search.py b/endpoints/api/search.py index 8fe3de3ba..204b055c5 100644 --- a/endpoints/api/search.py +++ b/endpoints/api/search.py @@ -1,5 +1,7 @@ """ Conduct searches against all registry context. """ +import features + from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, nickname, resource, require_scope, path_param, internal_only, Unauthorized, InvalidRequest, show_if) @@ -107,7 +109,8 @@ class EntitySearch(ApiResource): robot_namespace = namespace_name # Lookup users in the database for the prefix query. - users = model.user.get_matching_users(prefix, robot_namespace, organization, limit=10) + users = model.user.get_matching_users(prefix, robot_namespace, organization, limit=10, + exact_matches_only=not features.PARTIAL_USER_AUTOCOMPLETE) # Lookup users via the user system for the prefix query. We'll filter out any users that # already exist in the database. diff --git a/test/test_api_usage.py b/test/test_api_usage.py index 06f533612..49259e7a3 100644 --- a/test/test_api_usage.py +++ b/test/test_api_usage.py @@ -1017,6 +1017,23 @@ class TestGetMatchingEntities(ApiTestCase): assert 'outsideorg' in names assert not 'owners' in names + def test_prefix_disabled(self): + with patch('features.PARTIAL_USER_AUTOCOMPLETE', False): + self.login(NO_ACCESS_USER) + + json = self.getJsonResponse(EntitySearch, params=dict(prefix='o', namespace=ORGANIZATION, + includeTeams='true')) + + names = set([r['name'] for r in json['results']]) + assert not 'outsideorg' in names + assert not 'owners' in names + + json = self.getJsonResponse(EntitySearch, params=dict(prefix='outsideorg', namespace=ORGANIZATION, + includeTeams='true')) + names = set([r['name'] for r in json['results']]) + assert 'outsideorg' in names + assert not 'owners' in names + def test_inorg(self): self.login(ADMIN_ACCESS_USER)