-
Notifications
You must be signed in to change notification settings - Fork 0
Anonymous: Add configurable device limit #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: enhance-anonymous-access
Are you sure you want to change the base?
Anonymous: Add configurable device limit #10
Conversation
* 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 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
Greptile OverviewConfidence Score: 1/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant Client
participant Anonymous as Anonymous Client
participant AnonDeviceService
participant AnonDBStore
participant Database
Client->>Anonymous: Authenticate(ctx, request)
Anonymous->>Anonymous: Get org by name
Anonymous->>AnonDeviceService: TagDevice(ctx, httpReq, AnonDeviceUI)
AnonDeviceService->>AnonDeviceService: Extract device info from request
AnonDeviceService->>AnonDeviceService: Check local cache
alt Device in cache
AnonDeviceService-->>Anonymous: Return early (nil)
else Device not in cache
AnonDeviceService->>AnonDeviceService: Add to local cache
AnonDeviceService->>AnonDBStore: CreateOrUpdateDevice(ctx, device)
alt Device limit enabled (> 0)
AnonDBStore->>Database: CountDevices (30 days window)
Database-->>AnonDBStore: count
alt count >= deviceLimit
AnonDBStore->>Database: UPDATE device WHERE device_id = ? AND updated_at BETWEEN ? AND ?
Database-->>AnonDBStore: rows affected
alt rows affected = 0
AnonDBStore-->>AnonDeviceService: ErrDeviceLimitReached
AnonDeviceService-->>Anonymous: ErrDeviceLimitReached
Anonymous-->>Client: Return error
end
else count < deviceLimit
AnonDBStore->>Database: INSERT ... ON CONFLICT UPDATE
Database-->>AnonDBStore: Success
end
else No limit
AnonDBStore->>Database: INSERT ... ON CONFLICT UPDATE
Database-->>AnonDBStore: Success
end
AnonDeviceService-->>Anonymous: nil
end
Anonymous->>Anonymous: Create Identity with org info
Anonymous-->>Client: Return Identity
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
11 files reviewed, 3 comments
| 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) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Race condition: concurrent requests can bypass limit. Between CountDevices and updateDevice, multiple goroutines can see count < limit and all proceed. Use database-level locking or atomic operations.
| 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) | |
| } | |
| // if device limit is reached, only update devices | |
| if s.deviceLimit > 0 { | |
| // Try update first - if it succeeds, device exists | |
| err := s.updateDevice(ctx, device) | |
| if err == nil { | |
| return nil | |
| } | |
| if !errors.Is(err, ErrDeviceLimitReached) { | |
| return err | |
| } | |
| // Device doesn't exist - check limit with lock or use constraint | |
| 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 ErrDeviceLimitReached | |
| } | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/services/anonymous/anonimpl/anonstore/database.go
Line: 109:117
Comment:
**logic:** Race condition: concurrent requests can bypass limit. Between `CountDevices` and `updateDevice`, multiple goroutines can see count < limit and all proceed. Use database-level locking or atomic operations.
```suggestion
// if device limit is reached, only update devices
if s.deviceLimit > 0 {
// Try update first - if it succeeds, device exists
err := s.updateDevice(ctx, device)
if err == nil {
return nil
}
if !errors.Is(err, ErrDeviceLimitReached) {
return err
}
// Device doesn't exist - check limit with lock or use constraint
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 ErrDeviceLimitReached
}
}
```
How can I resolve this? If you propose a fix, please make it concise.| func (s *AnonDBStore) updateDevice(ctx context.Context, device *Device) error { | ||
| const query = `UPDATE anon_device SET | ||
| client_ip = ?, | ||
| user_agent = ?, | ||
| updated_at = ? | ||
| WHERE device_id = ? AND updated_at BETWEEN ? AND ?` | ||
|
|
||
| args := []interface{}{device.ClientIP, device.UserAgent, device.UpdatedAt.UTC(), device.DeviceID, | ||
| device.UpdatedAt.UTC().Add(-anonymousDeviceExpiration), device.UpdatedAt.UTC().Add(time.Minute), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Flawed logic: updateDevice uses time-based condition that will fail for most update attempts. When device.UpdatedAt is "now" and you check updated_at BETWEEN (now - 30 days) AND (now + 1 min), it only works if DB already has a recent entry. New devices or old devices won't match.
The WHERE clause should only check device_id:
| func (s *AnonDBStore) updateDevice(ctx context.Context, device *Device) error { | |
| const query = `UPDATE anon_device SET | |
| client_ip = ?, | |
| user_agent = ?, | |
| updated_at = ? | |
| WHERE device_id = ? AND updated_at BETWEEN ? AND ?` | |
| args := []interface{}{device.ClientIP, device.UserAgent, device.UpdatedAt.UTC(), device.DeviceID, | |
| device.UpdatedAt.UTC().Add(-anonymousDeviceExpiration), device.UpdatedAt.UTC().Add(time.Minute), | |
| const query = `UPDATE anon_device SET | |
| client_ip = ?, | |
| user_agent = ?, | |
| updated_at = ? | |
| WHERE device_id = ?` | |
| args := []interface{}{device.ClientIP, device.UserAgent, device.UpdatedAt.UTC(), device.DeviceID} |
Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/services/anonymous/anonimpl/anonstore/database.go
Line: 73:81
Comment:
**logic:** Flawed logic: `updateDevice` uses time-based condition that will fail for most update attempts. When `device.UpdatedAt` is "now" and you check `updated_at BETWEEN (now - 30 days) AND (now + 1 min)`, it only works if DB already has a recent entry. New devices or old devices won't match.
The WHERE clause should only check `device_id`:
```suggestion
const query = `UPDATE anon_device SET
client_ip = ?,
user_agent = ?,
updated_at = ?
WHERE device_id = ?`
args := []interface{}{device.ClientIP, device.UserAgent, device.UpdatedAt.UTC(), device.DeviceID}
```
How can I resolve this? If you propose a fix, please make it concise.| 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) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Changed from async to blocking call without timeout. If TagDevice is slow (DB contention during high traffic), authentication hangs. The old code had a 2-minute timeout.
Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/services/anonymous/anonimpl/client.go
Line: 44:50
Comment:
**style:** Changed from async to blocking call without timeout. If `TagDevice` is slow (DB contention during high traffic), authentication hangs. The old code had a 2-minute timeout.
How can I resolve this? If you propose a fix, please make it concise.
Test 1
Replicated from ai-code-review-evaluation/grafana-greptile#1