• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1959198787

31 Jul 2025 12:04PM UTC coverage: 65.551% (+0.001%) from 65.55%
1959198787

push

gitlab-ci

web-flow
Merge pull request #833 from alfrunes/ratelimits-config-agian

Update ratelimits configuration interface

30 of 46 new or added lines in 2 files covered. (65.22%)

4 existing lines in 2 files now uncovered.

32387 of 49407 relevant lines covered (65.55%)

1.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.91
/backend/pkg/config/ratelimits/config.go
1
// Copyright 2025 Northern.tech AS
2
//
3
//        Licensed under the Apache License, Version 2.0 (the "License");
4
//        you may not use this file except in compliance with the License.
5
//        You may obtain a copy of the License at
6
//
7
//            http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//        Unless required by applicable law or agreed to in writing, software
10
//        distributed under the License is distributed on an "AS IS" BASIS,
11
//        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//        See the License for the specific language governing permissions and
13
//        limitations under the License.
14

15
package ratelimits
16

17
import (
18
        "fmt"
19
        "time"
20

21
        "github.com/mendersoftware/mender-server/pkg/config"
22
        "github.com/mendersoftware/mender-server/pkg/log"
23
        "github.com/mendersoftware/mender-server/pkg/rate"
24
        "github.com/mendersoftware/mender-server/pkg/redis"
25
)
26

27
type ConfigDisabledError struct {
28
        Path string
29
}
30

31
func (err *ConfigDisabledError) Error() string {
×
32
        return `configuration "` + err.Path + `" disabled`
×
33
}
×
34

35
func init() {
3✔
36
        config.Config.SetDefault(SettingRatelimitsAuthGroups, []any{map[string]any{
3✔
37
                paramName:            paramNameDefault,
3✔
38
                paramQuota:           paramQuotaDefault,
3✔
39
                paramInterval:        paramIntervalDefault,
3✔
40
                paramEventExpression: paramEventExpressionDefault,
3✔
41
        }})
3✔
42
        config.Config.SetDefault(SettingRatelimitsAuthMatch, []any{map[string]any{
3✔
43
                paramPattern: "/", // Catch all
3✔
44
                paramGroup:   paramNameDefault,
3✔
45
        }})
3✔
46
}
3✔
47

48
const (
49
        SettingRatelimits                    = "ratelimits"
50
        SettingRatelimitsAuth                = SettingRatelimits + ".auth"
51
        SettingRatelimitsAuthEnable          = SettingRatelimitsAuth + ".enable"
52
        SettingRatelimitsAuthGroups          = SettingRatelimitsAuth + ".groups"
53
        SettingRatelimitsAuthMatch           = SettingRatelimitsAuth + ".match"
54
        SettingRatelimitsAuthRejectUnmatched = SettingRatelimitsAuth + ".reject_unmatched"
55

56
        paramName                   = "name"
57
        paramNameDefault            = "default"
58
        paramQuota                  = "quota"
59
        paramQuotaDefault           = int64(300)
60
        paramInterval               = "interval"
61
        paramIntervalDefault        = time.Minute
62
        paramEventExpression        = "event_expression"
63
        paramEventExpressionDefault = `{{with .Identity}}{{.Subject}}{{end}}`
64

65
        paramPattern = "api_pattern"
66
        paramGroup   = "group_expression"
67
)
68

69
func LoadRatelimits(c config.Reader) (*RatelimitConfig, error) {
×
70
        if !c.GetBool(SettingRatelimitsAuthEnable) {
×
71
                return nil, nil
×
72
        }
×
NEW
73
        ratelimitConfig := &RatelimitConfig{
×
NEW
74
                RejectUnmatched: c.GetBool(SettingRatelimitsAuthRejectUnmatched),
×
75
        }
×
76
        err := config.UnmarshalSliceSetting(c,
×
77
                SettingRatelimitsAuthGroups,
×
78
                &ratelimitConfig.RatelimitGroups,
×
79
        )
×
80
        if err != nil {
×
81
                return nil, fmt.Errorf("error loading rate limit groups: %w", err)
×
82
        }
×
83

84
        err = config.UnmarshalSliceSetting(c,
×
85
                SettingRatelimitsAuthMatch,
×
86
                &ratelimitConfig.MatchExpressions,
×
87
        )
×
88
        if err != nil {
×
89
                return nil, fmt.Errorf("error loading rate limit match expressions: %w", err)
×
90
        }
×
NEW
91
        return ratelimitConfig, nil
×
92
}
93

94
func SetupRedisRateLimits(
95
        redisClient redis.Client,
96
        keyPrefix string,
97
        c config.Reader,
98
) (*rate.HTTPLimiter, error) {
×
99
        if !c.GetBool(SettingRatelimitsAuthEnable) {
×
100
                return nil, &ConfigDisabledError{
×
101
                        Path: SettingRatelimitsAuthEnable,
×
102
                }
×
103
        }
×
104
        lims, err := LoadRatelimits(c)
×
105
        if err != nil {
×
106
                return nil, err
×
107
        }
×
108
        log.NewEmpty().Debugf("loaded rate limit configuration: %v", lims)
×
NEW
109
        mux := rate.NewHTTPLimiter()
×
NEW
110
        if c.GetBool(SettingRatelimitsAuthRejectUnmatched) {
×
NEW
111
                mux.WithRejectUnmatched()
×
112
        }
×
113
        for _, group := range lims.RatelimitGroups {
×
114
                groupPrefix := fmt.Sprintf("%s:rate:g:%s", keyPrefix, group.Name)
×
115
                limiter := redis.NewFixedWindowRateLimiter(
×
116
                        redisClient, groupPrefix, time.Duration(group.Interval), group.Quota,
×
117
                )
×
118
                err = mux.AddRateLimitGroup(limiter, group.Name, group.EventExpression)
×
119
                if err != nil {
×
120
                        return nil, fmt.Errorf("error setting up rate limit group %s: %w", group.Name, err)
×
121
                }
×
122
        }
123
        for _, expr := range lims.MatchExpressions {
×
124
                err = mux.AddMatchExpression(expr.APIPattern, expr.GroupExpression)
×
125
                if err != nil {
×
126
                        return nil, fmt.Errorf("error setting up match patterns: %w", err)
×
127
                }
×
128
        }
129
        return mux, nil
×
130
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc