Merge branch 'stable-3.2' into stable-3.3 * stable-3.2: Fix formatting of documentation Remove unsused variable Allow blocking of usernames using wildcards and regex Add command to register existing user as service user Change-Id: Ib9dfe4cac48acefd85a103a7241c2abbc9c2f675
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/BlockedNameFilter.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/BlockedNameFilter.java new file mode 100644 index 0000000..c365fb7 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/BlockedNameFilter.java
@@ -0,0 +1,81 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.serviceuser; + +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.server.config.PluginConfig; +import com.google.gerrit.server.config.PluginConfigFactory; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +@Singleton +public class BlockedNameFilter { + private final PluginConfig cfg; + private final Set<String> blockedExactNames = new HashSet<>(); + private final List<String> blockedNamePrefixes = new ArrayList<>(); + private final List<Pattern> blockedRegexNames = new ArrayList<>(); + + @Inject + public BlockedNameFilter(PluginConfigFactory cfgFactory, @PluginName String pluginName) { + this.cfg = cfgFactory.getFromGerritConfig(pluginName); + parseConfig(); + } + + public boolean isBlocked(String username) { + username = username.toLowerCase(); + return isBlockedByExactName(username) + || isBlockedByWildcard(username) + || isBlockedByRegex(username); + } + + private void parseConfig() { + for (String s : cfg.getStringList("block")) { + if (s.startsWith("^")) { + blockedRegexNames.add(Pattern.compile(s, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); + } else if (s.endsWith("*")) { + blockedNamePrefixes.add(s.substring(0, s.length() - 1).toLowerCase()); + } else { + blockedExactNames.add(s.toLowerCase()); + } + } + } + + private boolean isBlockedByExactName(String username) { + return blockedExactNames.contains(username); + } + + private boolean isBlockedByWildcard(String username) { + for (String prefix : blockedNamePrefixes) { + if (username.startsWith(prefix)) { + return true; + } + } + return false; + } + + private boolean isBlockedByRegex(String username) { + for (Pattern p : blockedRegexNames) { + if (p.matcher(username).find()) { + return true; + } + } + return false; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java index 0bb2cbf..b63cf8b 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java
@@ -16,9 +16,7 @@ import static com.google.gerrit.server.api.ApiUtil.asRestApiException; -import com.google.common.base.Function; import com.google.common.base.Strings; -import com.google.common.collect.Lists; import com.google.gerrit.entities.Account; import com.google.gerrit.entities.Project; import com.google.gerrit.extensions.annotations.PluginName; @@ -55,7 +53,6 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Date; -import java.util.List; import java.util.Locale; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Config; @@ -80,7 +77,6 @@ private final PluginConfig cfg; private final Provider<ProjectLevelConfig.Bare> configProvider; private final CreateAccount createAccount; - private final List<String> blockedNames; private final Provider<CurrentUser> userProvider; private final MetaDataUpdate.User metaDataUpdateFactory; private final Project.NameKey allProjects; @@ -88,6 +84,7 @@ private final Provider<GetConfig> getConfig; private final AccountLoader.Factory accountLoader; private final StorageCache storageCache; + private final BlockedNameFilter blockedNameFilter; @Inject CreateServiceUser( @@ -101,19 +98,11 @@ ProjectCache projectCache, Provider<GetConfig> getConfig, AccountLoader.Factory accountLoader, - StorageCache storageCache) { + StorageCache storageCache, + BlockedNameFilter blockedNameFilter) { this.cfg = cfgFactory.getFromGerritConfig(pluginName); this.configProvider = configProvider; this.createAccount = createAccount; - this.blockedNames = - Lists.transform( - Arrays.asList(cfg.getStringList("block")), - new Function<String, String>() { - @Override - public String apply(String blockedName) { - return blockedName.toLowerCase(); - } - }); this.userProvider = userProvider; this.metaDataUpdateFactory = metaDataUpdateFactory; this.allProjects = projectCache.getAllProjects().getProject().getNameKey(); @@ -123,6 +112,7 @@ this.getConfig = getConfig; this.accountLoader = accountLoader; this.storageCache = storageCache; + this.blockedNameFilter = blockedNameFilter; } @Override @@ -150,7 +140,7 @@ throw new BadRequestException("sshKey invalid."); } - if (blockedNames.contains(username.toLowerCase())) { + if (blockedNameFilter.isBlocked(username)) { throw new BadRequestException( "The username '" + username + "' is not allowed as name for service users."); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java index fef0bc7..421b326 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java
@@ -27,7 +27,6 @@ import com.google.inject.Singleton; import java.io.IOException; import java.util.concurrent.Future; - import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.slf4j.Logger;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java new file mode 100644 index 0000000..f0319f1 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java
@@ -0,0 +1,193 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.serviceuser; + +import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER; +import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_CREATED_AT; +import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_CREATED_BY; +import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_CREATOR_ID; +import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_OWNER; +import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.USER; + +import com.google.common.base.Strings; +import com.google.gerrit.entities.Account; +import com.google.gerrit.entities.Project; +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.common.AccountInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; +import com.google.gerrit.extensions.restapi.Response; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.RestCollectionCreateView; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.account.AccountLoader; +import com.google.gerrit.server.account.AccountResolver; +import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException; +import com.google.gerrit.server.config.ConfigResource; +import com.google.gerrit.server.git.meta.MetaDataUpdate; +import com.google.gerrit.server.group.GroupResolver; +import com.google.gerrit.server.permissions.PermissionBackend; +import com.google.gerrit.server.permissions.PermissionBackendException; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.project.ProjectLevelConfig; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.googlesource.gerrit.plugins.serviceuser.GetServiceUser.ServiceUserInfo; +import com.googlesource.gerrit.plugins.serviceuser.RegisterServiceUser.Input; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.PersonIdent; + +@RequiresCapability(CreateServiceUserCapability.ID) +@Singleton +class RegisterServiceUser + implements RestCollectionCreateView<ConfigResource, ServiceUserResource, Input> { + + static class Input { + String username; + String creator; + String owner; + } + + private final Provider<ProjectLevelConfig.Bare> configProvider; + private final AccountResolver accountResolver; + private final GroupResolver groupResolver; + private final Provider<CurrentUser> userProvider; + private final MetaDataUpdate.User metaDataUpdateFactory; + private final Project.NameKey allProjects; + private final DateFormat rfc2822DateFormatter; + private final AccountLoader.Factory accountLoader; + private final StorageCache storageCache; + private final PermissionBackend permissionBackend; + private final BlockedNameFilter blockedNameFilter; + + @Inject + RegisterServiceUser( + Provider<ProjectLevelConfig.Bare> configProvider, + AccountResolver accountResolver, + GroupResolver groupResolver, + Provider<CurrentUser> userProvider, + @GerritPersonIdent PersonIdent gerritIdent, + MetaDataUpdate.User metaDataUpdateFactory, + ProjectCache projectCache, + AccountLoader.Factory accountLoader, + StorageCache storageCache, + PermissionBackend permissionBackend, + BlockedNameFilter blockedNameFilter) { + this.configProvider = configProvider; + this.accountResolver = accountResolver; + this.groupResolver = groupResolver; + this.userProvider = userProvider; + this.metaDataUpdateFactory = metaDataUpdateFactory; + this.allProjects = projectCache.getAllProjects().getProject().getNameKey(); + this.rfc2822DateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); + this.rfc2822DateFormatter.setCalendar( + Calendar.getInstance(gerritIdent.getTimeZone(), Locale.US)); + this.accountLoader = accountLoader; + this.storageCache = storageCache; + this.permissionBackend = permissionBackend; + this.blockedNameFilter = blockedNameFilter; + } + + @Override + public Response<ServiceUserInfo> apply(ConfigResource parentResource, IdString id, Input input) + throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException { + CurrentUser requestingUser = userProvider.get(); + if (requestingUser == null || !requestingUser.isIdentifiedUser()) { + throw new AuthException("authentication required"); + } + + if (input == null) { + input = new Input(); + } + + IdentifiedUser user; + try { + user = accountResolver.resolve(input.username).asUniqueUser(); + } catch (UnresolvableAccountException e) { + throw new BadRequestException("Username does not exist"); + } + + if (!requestingUser.getAccountId().equals(user.getAccountId()) + && !permissionBackend.user(requestingUser).testOrFalse(ADMINISTRATE_SERVER)) { + throw new MethodNotAllowedException("Forbidden"); + } + + if (blockedNameFilter.isBlocked(input.username)) { + throw new BadRequestException( + "The username '" + input.username + "' is not allowed as name for service users."); + } + + String creator; + Account.Id creatorId; + if (Strings.isNullOrEmpty(input.creator)) { + creator = requestingUser.getUserName().orElse(null); + creatorId = requestingUser.asIdentifiedUser().getAccountId(); + } else { + creator = input.creator; + creatorId = accountResolver.resolve(input.creator).asUniqueUser().getAccountId(); + } + String creationDate = rfc2822DateFormatter.format(new Date()); + + String owner = null; + if (!Strings.isNullOrEmpty(input.owner)) { + try { + owner = groupResolver.parse(input.owner).getGroupUUID().toString(); + } catch (UnresolvableAccountException e) { + throw new BadRequestException("The group '" + input.owner + "' does not exist"); + } + } + + try (MetaDataUpdate md = metaDataUpdateFactory.create(allProjects)) { + ProjectLevelConfig.Bare update = configProvider.get(); + update.load(md); + + Config db = update.getConfig(); + if (db.getSubsections(USER).contains(input.username)) { + return Response.none(); + } + db.setInt(USER, input.username, KEY_CREATOR_ID, creatorId.get()); + if (creator != null) { + db.setString(USER, input.username, KEY_CREATED_BY, creator); + } + if (owner != null) { + db.setString(USER, input.username, KEY_OWNER, owner); + } + db.setString(USER, input.username, KEY_CREATED_AT, creationDate); + + md.setMessage("Create service user '" + input.username + "'\n"); + update.commit(md); + storageCache.invalidate(); + } + + ServiceUserInfo info = new ServiceUserInfo(new AccountInfo(user.getAccountId().get())); + AccountLoader al = accountLoader.create(true); + info.createdBy = al.get(creatorId); + al.fill(); + info.createdAt = creationDate; + return Response.created(info); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUserCommand.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUserCommand.java new file mode 100644 index 0000000..fb8503c --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUserCommand.java
@@ -0,0 +1,67 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.serviceuser; + +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.server.config.ConfigResource; +import com.google.gerrit.server.permissions.PermissionBackendException; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.gerrit.sshd.SshCommand; +import com.google.inject.Inject; +import java.io.IOException; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@RequiresCapability(CreateServiceUserCapability.ID) +@CommandMetaData(name = "register", description = "Register Service User") +class RegisterServiceUserCommand extends SshCommand { + + @Argument(index = 0, required = true, metaVar = "USERNAME", usage = "name of the service user") + private String username; + + @Option( + name = "--creator", + required = false, + metaVar = "CREATOR", + usage = "name of the creator of the service user") + private String creator; + + @Option( + name = "--owner", + required = false, + metaVar = "OWNER", + usage = "group that owns the service user") + private String owner; + + @Inject private RegisterServiceUser registerServiceUser; + + @Override + protected void run() + throws IOException, UnloggedFailure, ConfigInvalidException, PermissionBackendException { + RegisterServiceUser.Input input = new RegisterServiceUser.Input(); + input.username = username; + input.creator = creator; + input.owner = owner; + + try { + registerServiceUser.apply(new ConfigResource(), IdString.fromDecoded(username), input); + } catch (RestApiException e) { + throw die(e.getMessage()); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/SshModule.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/SshModule.java index f775e4c..40a1509 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/SshModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/SshModule.java
@@ -21,5 +21,6 @@ @Override protected void configureCommands() { command(CreateServiceUserCommand.class); + command(RegisterServiceUserCommand.class); } }
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md index 17a4e29..e19d856 100644 --- a/src/main/resources/Documentation/about.md +++ b/src/main/resources/Documentation/about.md
@@ -36,11 +36,11 @@ createdAt = Wed, 13 Nov 2013 14:45:00 +0100 ``` -<a id="createdBy"> +<a id="createdBy"></a> `user.<service-user-name>.createdBy` : The username of the user who created the service user. -<a id="createdAt"> +<a id="createdAt"></a> `user.<service-user-name>.createdAt` : The date when the service user was created.
diff --git a/src/main/resources/Documentation/cmd-register.md b/src/main/resources/Documentation/cmd-register.md new file mode 100644 index 0000000..3378a2c --- /dev/null +++ b/src/main/resources/Documentation/cmd-register.md
@@ -0,0 +1,50 @@ +@PLUGIN@ register +================= + +NAME +---- +@PLUGIN@ register - Registers an existing user as a service user + +SYNOPSIS +-------- +``` +ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ register + --creator <CREATOR> + --owner <OWNER> + <USERNAME> +``` + +DESCRIPTION +----------- +Registers an existing user as a service user. + +ACCESS +------ +Caller must be a member of a group that is granted the +'Create Service User' capability (provided by this plugin) or the +'Administrate Server' capability. If not possessing the 'Administrate +Server' capability, the user to be registered as a service user must +also be the caller. + +SCRIPTING +--------- +This command is intended to be used in scripts. + +OPTIONS +------- + +`--creator` +: Username of the user that will be set as the creator of the + serviceuser. Defaults to the caller. + +`--owner` +: ID or name of the group that will own the service user. Defaults + to no owner group being set. + +EXAMPLES +-------- +Register a service user: + +``` + $ ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ register --creator admin --owner Administrators username +```
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index f52421e..aa2e4b5 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -9,30 +9,41 @@ group = Service Users ``` -<a id="block"> +<a id="block"></a> `plugin.@PLUGIN@.block` : A username which is forbidden to be used as name for a service - user. The blocked username is case insensitive. Multiple - usernames can be blocked by specifying multiple + user. The blocked username is case insensitive. The match can + either be exact, have a wildcard ('*') at the end or use regular + expressions, which have to start with '^'. If the regex pattern is not + ending with '$', every username starting with a matching prefix will be + blocked. Multiple usernames can be blocked by specifying multiple `plugin.@PLUGIN@.block` entries. + Examples: -<a id="group"> +``` + [plugin "serviceuser"] + block = johndoe + block = jane* + block = ^gerrit[0-9]* +``` + +<a id="group"></a> `plugin.@PLUGIN@.group` : The name of an internal group to which newly created service users should be automatically added. Multiple groups can be specified by having multiple `plugin.@PLUGIN@.group` entries. -<a id="infoMessage"> +<a id="infoMessage"></a> `plugin.@PLUGIN@.infoMessage` : HTML formatted message that should be displayed on the service user creation screen. -<a id="onSuccessMessage"> +<a id="onSuccessMessage"></a> `plugin.@PLUGIN@.onSuccessMessage` : Message that should be displayed after a service user was successfully created. -<a id="allowEmail"> +<a id="allowEmail"></a> `plugin.@PLUGIN@.allowEmail` : Whether it is allowed for service user owners to set email addresses for their service users. Independent of this setting @@ -40,7 +51,7 @@ any service user. By default false. -<a id="allowHttpPassword"> +<a id="allowHttpPassword"></a> `plugin.@PLUGIN@.allowHttpPassword` : Whether it is allowed for service user owners to generate HTTP passwords for their service users. Independent of this setting @@ -48,12 +59,12 @@ passwords for any service user. By default false. -<a id="allowOwner"> +<a id="allowOwner"></a> `plugin.@PLUGIN@.allowOwner` : Whether it is allowed to set an owner group for a service user. By default false. -<a id="createNotes"> +<a id="createNotes"></a> `plugin.@PLUGIN@.createNotes` : Whether commits of a service user should be annotated by a Git note that contains information about the current owners of the service @@ -62,7 +73,7 @@ user the 'Forge Committer' access right must be blocked for service users. By default true. -<a id="createNotes"> +<a id="createNotes"></a> `plugin.@PLUGIN@.createNotesAsync` : Whether the Git notes on commits that are pushed by a service user should be created asynchronously. By default false.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/serviceuser/BlockedNameFilterTest.java b/src/test/java/com/googlesource/gerrit/plugins/serviceuser/BlockedNameFilterTest.java new file mode 100644 index 0000000..997c275 --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/serviceuser/BlockedNameFilterTest.java
@@ -0,0 +1,89 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.serviceuser; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.gerrit.server.config.PluginConfig; +import com.google.gerrit.server.config.PluginConfigFactory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class BlockedNameFilterTest { + + private static String[] BLOCKED_NAMES = + new String[] { + "exact", "ex*act", "wild*", "^regex[0-9]+", "^ABC", "^[0-9]+$", "regex[0-9]+", "^⁋+" + }; + + private BlockedNameFilter blockedNameFilter; + + @Mock private PluginConfigFactory configFactory; + + @Mock private PluginConfig config; + + @Before + public void setup() { + when(configFactory.getFromGerritConfig("serviceuser")).thenReturn(config); + when(config.getStringList("block")).thenReturn(BLOCKED_NAMES); + blockedNameFilter = new BlockedNameFilter(configFactory, "serviceuser"); + } + + @Test + public void exactMatchIsBlocked() { + assertThat(blockedNameFilter.isBlocked("exact")).isTrue(); + assertThat(blockedNameFilter.isBlocked("ExAct")).isTrue(); + assertThat(blockedNameFilter.isBlocked("ex*act")).isTrue(); + assertThat(blockedNameFilter.isBlocked("regex[0-9]+")).isTrue(); + assertThat(blockedNameFilter.isBlocked("notexact")).isFalse(); + assertThat(blockedNameFilter.isBlocked("exxact")).isFalse(); + } + + @Test + public void wildcardMatchIsBlocked() { + assertThat(blockedNameFilter.isBlocked("wild")).isTrue(); + assertThat(blockedNameFilter.isBlocked("wildcard")).isTrue(); + assertThat(blockedNameFilter.isBlocked("Wilde")).isTrue(); + assertThat(blockedNameFilter.isBlocked("wil")).isFalse(); + } + + @Test + public void regexMatchIsBlocked() { + assertThat(blockedNameFilter.isBlocked("regex1")).isTrue(); + assertThat(blockedNameFilter.isBlocked("Regex1")).isTrue(); + + // Pattern matching is done at the beginning of the username + assertThat(blockedNameFilter.isBlocked("foo-regex1")).isFalse(); + + // Names with unicode characters can be blocked + assertThat(blockedNameFilter.isBlocked("⁋")).isTrue(); + + // Regex matches only complete name, when ending with '$'. + assertThat(blockedNameFilter.isBlocked("01234")).isTrue(); + assertThat(blockedNameFilter.isBlocked("01234abcd")).isFalse(); + + // Regex matches prefix without trailing '$' + assertThat(blockedNameFilter.isBlocked("regex1suffix")).isTrue(); + + // Uppercase regex matches case-insenstive + assertThat(blockedNameFilter.isBlocked("abc")).isTrue(); + assertThat(blockedNameFilter.isBlocked("ABC")).isTrue(); + } +}