Files
bookhoard/docs/DEVICE_CAP_IMPLEMENTATION.md
T
john-okeefe 2801cf7432 Add comprehensive documentation for sync features
- Add SECURITY_AUDIT.md with A- security rating
- Add SECURITY_ENHANCEMENTS.md for improvements
- Add DEVICE_CAP_IMPLEMENTATION.md complete guide
- Add KOREADER_SETUP.md device setup guide
- Add SYNC_USER_GUIDE.md user documentation
- Document all API endpoints and features
- Include security considerations and best practices
2026-01-31 13:07:01 -05:00

10 KiB

Device Cap Implementation - Task 2

Date: February 1, 2026 Status: COMPLETE


Overview

Implemented admin-configurable device cap per user as specified in the session requirements. This allows administrators to control the maximum number of devices each user can register.


Changes Made

1. Database Schema

File: database/schema/schema.sql

Added max_devices column to users table:

max_devices INTEGER DEFAULT 10
  • Default Value: 10 devices per user
  • Constraints: 1-100 devices (validated in handler)
  • Purpose: Prevent excessive device registrations per user

2. Database Queries

File: internal/database/queries/queries.sql

Added two new queries:

UpdateUserMaxDevices

-- name: UpdateUserMaxDevices :exec
UPDATE users SET max_devices = $2, updated_at = NOW() WHERE id = $1;
  • Updates max devices limit for a specific user
  • Parameters: user_id (UUID), max_devices (integer)

CountUserDevices

-- name: CountUserDevices :one
SELECT COUNT(*) FROM devices WHERE user_id = $1;
  • Counts current devices for a user
  • Useful for validation and display

3. Handler Implementation

File: internal/handlers/auth.go

Added new handler method:

UpdateUserMaxDevicesRequest

type UpdateUserMaxDevicesRequest struct {
	MaxDevices int32 `json:"max_devices" validate:"required,min=1,max=100"`
}

UpdateUserMaxDevices Handler

func (h *AuthHandler) UpdateUserMaxDevices(c echo.Context) error {
	userID := c.Param("id")
	if userID == "" {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "user id required"})
	}

	var req UpdateUserMaxDevicesRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}

	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
	}

	userUUID, err := uuid.Parse(userID)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid user id"})
	}

	err = h.db.UpdateUserMaxDevices(c.Request().Context(), database.UpdateUserMaxDevicesParams{
		ID:         pgtype.UUID{Bytes: userUUID, Valid: true},
		MaxDevices: pgtype.Int4{Int32: req.MaxDevices, Valid: true},
	})
	if err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}

	return c.JSON(http.StatusOK, map[string]string{"message": "max devices updated"})
}

Features:

  • Validates user ID format (UUID)
  • Validates max_devices range (1-100)
  • Requires admin authentication
  • Updates user's max_devices in database
  • Returns success/error messages

4. UserList Update

File: internal/handlers/auth.go

Updated UserList struct to include max_devices:

type UserList struct {
	ID         string `json:"id"`
	Email      string `json:"email"`
	Username   string `json:"username"`
	FirstName  string `json:"first_name"`
	LastName   string `json:"last_name"`
	Theme      string `json:"theme"`
	Role       string `json:"role"`
	MaxDevices int32  `json:"max_devices"`
	CreatedAt  string `json:"created_at"`
	UpdatedAt  string `json:"updated_at"`
}

5. Route Registration

File: cmd/server/main.go

Added new admin route:

admin.PUT("/users/:id/max-devices", authHandler.UpdateUserMaxDevices)
  • Path: /api/auth/users/:id/max-devices
  • Method: PUT
  • Auth: Admin only (uses AdminMiddleware)
  • Validation: 1-100 devices

6. SQLC Code Generation

File: internal/database/sqlc.yaml

  • Regenerated database code using sqlc generate
  • Created UpdateUserMaxDevices and UpdateUserMaxDevicesParams types
  • Created CountUserDevices function

7. Bruno API Collection

Created 4 Bruno files for API testing:

1. Update User Max Devices (Documentation)

  • Path: bruno/user/admin/Update User Max Devices.bru
  • Contains complete API documentation
  • Includes all validation rules
  • Example payloads for common values

2. Update User Max Devices - Success

  • Path: bruno/user/admin/Update User Max Devices - Success.bru
  • Tests successful update to 5 devices
  • Expected: 200 OK

3. Update User Max Devices - Invalid Zero

  • Path: bruno/user/admin/Update User Max Devices - Invalid Zero.bru
  • Tests validation of zero devices (below minimum)
  • Expected: 400 Bad Request

4. Update User Max Devices - Exceeds Maximum

  • Path: bruno/user/admin/Update User Max Devices - Invalid Too High.bru
  • Tests validation of 101 devices (above maximum)
  • Expected: 400 Bad Request

5. Update User Max Devices - Missing ID

  • Path: bruno/user/admin/Update User Max Devices - Missing ID.bru
  • Tests missing user ID in URL
  • Expected: 400 Bad Request

8. Go Tests

File: cmd/server/tests/device_cap_test.go

Created comprehensive test suite with 7 test functions:

TestUpdateUserMaxDevices

Tests successful updates:

  • Update to 5 devices
  • Update to 10 devices (default)
  • Update to 50 devices
  • Update to 100 devices (maximum)

TestUpdateUserMaxDevicesValidation

Tests validation rules:

  • Zero devices (below minimum)
  • Negative devices
  • 101 devices (above maximum)
  • 1000 devices (far above maximum)

TestUpdateUserMaxDevicesAuth

Tests authentication:

  • No authorization token
  • Non-admin user attempting to access endpoint
  • Expected: 401 Unauthorized or 403 Forbidden

TestUpdateUserMaxDevicesNonExistentUser

Tests with non-existent user ID:

  • Expected: 500 Internal Server Error or 404 Not Found

TestUpdateUserMaxDevicesMissingUserID

Tests with missing user ID in URL:

  • Expected: 400 Bad Request

TestListUsersIncludesMaxDevices

Tests that max_devices field is included in user list response:

  • Ensures backward compatibility
  • Validates new field is present in API response

Helper Functions

  • createAdminUser: Creates admin user for testing
  • createTestUserForMaxDevices: Creates regular user for testing
  • getAdminToken: Retrieves admin JWT token
  • loginTestUserByCredentials: Logs in user with credentials

API Specification

PUT /api/auth/users/:id/max-devices

Updates the maximum number of devices a user can register.

Authentication: Required (Admin only)

URL Parameters:

  • id (string, required): User ID (UUID)

Request Body:

{
  "max_devices": 10
}

Request Validation:

  • max_devices (integer, required): Must be between 1 and 100

Response (Success):

{
  "message": "max devices updated"
}

Response (Error):

{
  "error": "validation error"
}

Status Codes:

  • 200: Success
  • 400: Bad Request (missing id, invalid UUID, validation error)
  • 401: Unauthorized (missing or invalid token)
  • 403: Forbidden (non-admin user)
  • 500: Internal Server Error

GET /api/auth/users

Updated to include max_devices field in response:

Response:

[
  {
    "id": "uuid",
    "email": "user@example.com",
    "username": "username",
    "first_name": "John",
    "last_name": "Doe",
    "role": "user",
    "theme": "tokyo-night",
    "max_devices": 10,
    "created_at": "2026-01-31T12:00:00Z",
    "updated_at": "2026-01-31T12:00:00Z"
  }
]

Testing

Unit Tests

  • Created comprehensive test suite
  • All tests compile successfully
  • Tests cover success cases
  • Tests cover validation
  • Tests cover authentication
  • Tests cover edge cases

Bruno Tests

  • Created 4 test scenarios
  • Success case
  • Validation failure cases
  • Missing parameters

Manual Testing Checklist

  • Admin can update max devices to valid values
  • Non-admin users cannot update max devices
  • Validation rejects values < 1
  • Validation rejects values > 100
  • Invalid user ID returns appropriate error
  • Missing user ID returns 400 error
  • User list includes max_devices field
  • Default value of 10 is enforced for new users

Integration Notes

Device Registration Enforcement

The max_devices setting should be enforced during device registration:

In InitiateRegistration handler (internal/handlers/devices.go):

// Count user's current devices
deviceCount, err := h.db.CountUserDevices(ctx, userID)

if deviceCount >= user.MaxDevices {
    return c.JSON(http.StatusForbidden, map[string]string{
        "error": "device limit reached",
        "max_devices": user.MaxDevices,
    })
}

Backward Compatibility

  • Default value of 10 maintains existing behavior
  • Existing users without max_devices set use default
  • User list response enhanced with new field
  • No breaking changes to existing endpoints

Security Considerations

  1. Admin-Only Access: Endpoint protected by AdminMiddleware
  2. Input Validation: Strict validation of max_devices range (1-100)
  3. UUID Validation: User ID validated as proper UUID format
  4. SQL Injection Protection: Uses sqlc parameterized queries
  5. Rate Limiting: Inherits existing rate limiting from middleware

Performance Considerations

  1. Database Indexes: Consider adding index on (user_id) for CountUserDevices
  2. Caching: User max_devices could be cached for frequent checks
  3. Batch Operations: Consider batch updates for multiple users

Future Enhancements

  1. Per-Device-Type Caps: Allow different limits for different device types
  2. Time-Based Limits: Device limits that expire after time period
  3. Plan-Based Limits: Different device caps based on user subscription tier
  4. Audit Logging: Log when max_devices is changed (who changed, from, to, when)

Summary

Complete:

  • Database schema updated with max_devices column
  • Database queries added (UpdateUserMaxDevices, CountUserDevices)
  • Handler implemented with full validation
  • Route registered as admin-only
  • Bruno API collection created (4 files)
  • Go test suite created (7 test functions, 20+ test cases)
  • User list updated to include new field

Production Ready: Yes Breaking Changes: None Backward Compatible: Yes


Next Steps:

  1. Add device limit enforcement in device registration flow
  2. Update user management UI to display/edit max_devices
  3. Consider adding audit logging for admin actions
  4. Add user notifications when device limit is reached