fix: throttle student answers and attempt starts per user

WR-03: every new or changed open answer in a learning quiz with a
grader is a paid grading call. Add per-user buckets golem15.quizzes.answer
(60/min) and golem15.quizzes.start (10/min), configurable under
golem15.quizzes.throttle.*.
This commit is contained in:
Jakub Zych
2026-11-09 22:48:50 +01:00
parent 34b0966819
commit bb18242af9
5 changed files with 61 additions and 5 deletions

View File

@@ -226,13 +226,19 @@ func (p *Plugin) FrontendPermissions() []pact.FrontendPermission {
}}
}
// Buckets declares the per-user rate limit of the owner manage routes:
// building a quiz question by question is chatty, so 120 calls a minute.
// A request without a user (never after user.api_token) keys by client IP.
// Buckets declares the per-user rate limits: the owner manage routes
// (building a quiz question by question is chatty, so 120 calls a minute),
// and the student's answers and attempt starts, configurable as
// golem15.quizzes.throttle.answers_per_minute and attempts_per_minute. A
// request without a user (never after user.api_token or jwt.auth) keys by
// client IP.
func (p *Plugin) Buckets() map[string]surf.Bucket {
trusted := surf.TrustedProxies(nil)
answers, starts := classes.DefaultAnswersPerMinute, classes.DefaultAttemptsPerMinute
if p != nil && p.app != nil {
trusted = surf.TrustedProxies(p.app.Config)
answers = classes.PerMinuteFrom(p.app.Config, "golem15.quizzes.throttle.answers_per_minute", answers)
starts = classes.PerMinuteFrom(p.app.Config, "golem15.quizzes.throttle.attempts_per_minute", starts)
}
byUser := func(prefix string) func(*http.Request) string {
return func(r *http.Request) string {
@@ -244,6 +250,8 @@ func (p *Plugin) Buckets() map[string]surf.Bucket {
}
return map[string]surf.Bucket{
bucketManage: {Max: 120, Decay: time.Minute, Key: byUser(bucketManage)},
bucketAnswer: {Max: answers, Decay: time.Minute, Key: byUser(bucketAnswer)},
bucketStart: {Max: starts, Decay: time.Minute, Key: byUser(bucketStart)},
}
}