Skip to content

Anonymous: Add configurable device limit - #762

Open
malinosqui wants to merge 36 commits into
enhance-anonymous-accessfrom
implement-device-limits
Open

malinosqui wants to merge 36 commits into
enhance-anonymous-accessfrom
implement-device-limits

Conversation

@malinosqui

@malinosqui malinosqui commented Jun 1, 2026

Copy link
Copy Markdown

This pull request introduces a configurable limit on the number of anonymous devices that can access the system.

Key Changes:

  • Configuration: Added a new device_limit setting under the [auth.anonymous] section in the configuration.
  • Limit Enforcement:
    • Implemented logic in the database store to count active anonymous devices within a 30-day expiration window.
    • If the configured device_limit is reached (and is greater than 0), the system will reject the creation of new anonymous devices while still allowing existing active devices to update their sessions.
  • Authentication Flow: Modified the anonymous authentication process to tag devices synchronously (previously done asynchronously in a goroutine). If the device limit is reached, the authentication request is now blocked and returns an error.
  • Frontend Settings: Exposed the anonymousDeviceLimit to the frontend via GrafanaConfig and FrontendSettingsDTO.

Jguer and others added 30 commits December 12, 2023 12:57
* Anonymous: Add device limiter

* break auth if limit reached

* fix typo

* refactored const to make it clearer with expiration

* anon device limit for config

---------

Co-authored-by: Eric Leijonmarck <eric.leijonmarck@gmail.com>
@malinosqui

malinosqui commented Jun 1, 2026

Copy link
Copy Markdown
Author

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

Comment on lines +108 to +118
// if device limit is reached, only update devices
if s.deviceLimit > 0 {
count, err := s.CountDevices(ctx, time.Now().UTC().Add(-anonymousDeviceExpiration), time.Now().UTC().Add(time.Minute))
if err != nil {
return err
}

if count >= s.deviceLimit {
return s.updateDevice(ctx, device)
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

Time-of-Check to Time-of-Use (TOCTOU) race condition between CountDevices and the device insert allows concurrent requests to bypass limit checks and exceed the configured device count. Enforce the limit atomically using a database constraint or lock.

Prompt for LLM

File pkg/services/anonymous/anonimpl/anonstore/database.go:

Line 108 to 118:

WHAT: A Time-of-Check to Time-of-Use (TOCTOU) race condition exists between checking the `CountDevices` result and inserting a new device.
WHY: Multiple concurrent requests from new devices can simultaneously read a device count below the limit, bypass the `updateDevice` branch, and execute inserts that cause the total count to exceed the configured device limit.
HOW: Enforce the device limit atomically using a database constraint or lock, or document that concurrent bursts may slightly exceed the configured limit.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +44 to 51
if err := a.anonDeviceService.TagDevice(ctx, httpReqCopy, anonymous.AnonDeviceUI); err != nil {
if errors.Is(err, anonstore.ErrDeviceLimitReached) {
return nil, err
}
}()

a.log.Warn("Failed to tag anonymous session", "error", err)
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Security critical

Cache inconsistency in TagDevice persists the device ID before database verification, allowing rejected devices to bypass limits on subsequent retries. Clear the cache entry using a.localCache.Delete(key) in the tagDeviceUI error path.

Prompt for LLM

File pkg/services/anonymous/anonimpl/client.go:

Line 44 to 51:

WHAT: The authentication block relies on `TagDevice`, which incorrectly caches the device ID before verifying if the database insert succeeds and fails to remove it on error.
WHY: An attacker whose new device is correctly rejected due to the device limit can simply retry their request, which will hit the incorrectly preserved cache, return no error, and grant them access.
HOW: Clear the cache entry if the database operation fails (e.g., by adding `a.localCache.Delete(key)` to the error path in `tagDeviceUI`).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants