| // Copyright (C) 2017 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.ratelimiter; |
| |
| import com.google.common.collect.ArrayTable; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Table; |
| import com.google.gerrit.common.data.GroupDescription.Basic; |
| import com.google.gerrit.entities.AccountGroup; |
| import com.google.gerrit.extensions.annotations.PluginName; |
| import com.google.gerrit.server.config.PluginConfigFactory; |
| import com.google.gerrit.server.group.GroupResolver; |
| import com.google.inject.Inject; |
| import com.google.inject.ProvisionException; |
| import com.google.inject.Singleton; |
| import java.util.Arrays; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import org.eclipse.jgit.lib.Config; |
| |
| @Singleton |
| class Configuration { |
| |
| static final String RATE_LIMIT_TOKEN = "${rateLimit}"; |
| private static final String GROUP_SECTION = "group"; |
| private static final String DEFAULT_UPLOADPACK_LIMIT_EXCEEDED_MSG = |
| "Exceeded rate limit of " + RATE_LIMIT_TOKEN + " fetch requests/hour"; |
| |
| private Table<RateLimitType, AccountGroup.UUID, RateLimit> rateLimits; |
| private final String rateLimitExceededMsg; |
| |
| @Inject |
| Configuration( |
| PluginConfigFactory pluginConfigFactory, |
| @PluginName String pluginName, |
| GroupResolver groupsCollection) { |
| Config config = pluginConfigFactory.getGlobalPluginConfig(pluginName); |
| parseAllGroupsRateLimits(config, groupsCollection); |
| rateLimitExceededMsg = parseLimitExceededMsg(config); |
| } |
| |
| private void parseAllGroupsRateLimits(Config config, GroupResolver groupsCollection) { |
| Map<String, AccountGroup.UUID> groups = getResolvedGroups(config, groupsCollection); |
| if (groups.size() == 0) { |
| return; |
| } |
| rateLimits = ArrayTable.create(Arrays.asList(RateLimitType.values()), groups.values()); |
| for (Entry<String, AccountGroup.UUID> group : groups.entrySet()) { |
| parseGroupRateLimits(config, group.getKey(), group.getValue()); |
| } |
| } |
| |
| private Map<String, AccountGroup.UUID> getResolvedGroups( |
| Config config, GroupResolver groupsCollection) { |
| LinkedHashMap<String, AccountGroup.UUID> groups = new LinkedHashMap<>(); |
| for (String groupName : config.getSubsections(GROUP_SECTION)) { |
| Basic basic = groupsCollection.parseId(groupName); |
| if (basic != null) { |
| groups.put(groupName, basic.getGroupUUID()); |
| } |
| } |
| return groups; |
| } |
| |
| private void parseGroupRateLimits(Config config, String groupName, AccountGroup.UUID groupUUID) |
| throws ProvisionException { |
| for (String typeName : config.getNames(GROUP_SECTION, groupName, true)) { |
| RateLimitType rateLimitType = RateLimitType.from(typeName); |
| if (rateLimitType != null) { |
| rateLimits.put(rateLimitType, groupUUID, parseRateLimit(config, groupName, rateLimitType)); |
| } else { |
| throw new ProvisionException( |
| String.format("Invalid configuration, unsupported rate limit type: %s", typeName)); |
| } |
| } |
| } |
| |
| private static RateLimit parseRateLimit(Config c, String groupName, RateLimitType rateLimitType) { |
| String value = c.getString(GROUP_SECTION, groupName, rateLimitType.toString()); |
| try { |
| return new RateLimit(rateLimitType, Integer.parseInt(value)); |
| } catch (NumberFormatException e) { |
| throw new ProvisionException( |
| String.format( |
| "Invalid configuration, 'rate limit value '%s' for '%s.%s.%s' is not a valid number", |
| value, GROUP_SECTION, groupName, rateLimitType.toString())); |
| } |
| } |
| |
| private static String parseLimitExceededMsg(Config config) { |
| String msg = config.getString("configuration", null, "uploadpackLimitExceededMsg"); |
| return (msg != null) ? msg : DEFAULT_UPLOADPACK_LIMIT_EXCEEDED_MSG; |
| } |
| |
| String getRateLimitExceededMsg() { |
| return rateLimitExceededMsg; |
| } |
| |
| /** |
| * @param rateLimitType type of rate limit |
| * @return map of rate limits per group uuid |
| */ |
| Map<AccountGroup.UUID, RateLimit> getRatelimits(RateLimitType rateLimitType) { |
| return rateLimits != null ? rateLimits.row(rateLimitType) : ImmutableMap.of(); |
| } |
| } |