# Auth.Indexable Usage Guide

## Secure Reordering with Permission Checking

The `auth.Indexable` implementation adds **permission checking** to the generic reordering functionality using `EnforceBatch`.

- **Core Implementation**: `api/pkg/auth/indexable.go` - generic implementation with permission checking
- **Project Factory**: `api/pkg/auth/project_indexable.go` - convenient factory for projects
- **Key Feature**: Uses `EnforceBatch` to check permissions for all affected objects

## How It Works

### Permission Checking Flow
1. **Get current object** to find its index
2. **Determine affected objects** that will be shifted during reordering
3. **Check permissions** using `EnforceBatch` for all affected objects + target object
4. **Verify all permissions** - if any object lacks update permission, return error
5. **Proceed with reordering** only if all permissions are granted

### Key Differences from Basic Indexable
- **Additional parameter**: `accountRef` for permission checking
- **Permission validation**: All affected objects must have `ActionUpdate` permission
- **Security**: Prevents unauthorized reordering that could affect other users' data

## Usage

### 1. Using the Generic Auth.Indexable Implementation

```go
import "github.com/tech/sendico/pkg/auth"

// For any type that embeds model.Indexable, define helper functions:
createEmpty := func() *YourType {
    return &YourType{}
}

getIndexable := func(obj *YourType) *model.Indexable {
    return &obj.Indexable
}

// Create auth.IndexableDB with enforcer
indexableDB := auth.NewIndexableDB(repo, logger, enforcer, createEmpty, getIndexable)

// Use with account reference for permission checking
err := indexableDB.Reorder(ctx, accountRef, objectID, newIndex, filter)
```

### 2. Using the Project Factory (Recommended for Projects)

```go
import "github.com/tech/sendico/pkg/auth"

// Create auth.ProjectIndexableDB (automatically applies org filter)
projectDB := auth.NewProjectIndexableDB(repo, logger, enforcer, organizationRef)

// Reorder project with permission checking
err := projectDB.Reorder(ctx, accountRef, projectID, newIndex, repository.Query())

// Reorder with additional filters (combined with org filter)
additionalFilter := repository.Query().Comparison(repository.Field("state"), builder.Eq, "active")
err := projectDB.Reorder(ctx, accountRef, projectID, newIndex, additionalFilter)
```

## Examples for Different Types

### Project Auth.IndexableDB
```go
createEmpty := func() *model.Project {
    return &model.Project{}
}

getIndexable := func(p *model.Project) *model.Indexable {
    return &p.Indexable
}

projectDB := auth.NewIndexableDB(repo, logger, enforcer, createEmpty, getIndexable)
orgFilter := repository.OrgFilter(organizationRef)
projectDB.Reorder(ctx, accountRef, projectID, 2, orgFilter)
```

### Status Auth.IndexableDB
```go
createEmpty := func() *model.Status {
    return &model.Status{}
}

getIndexable := func(s *model.Status) *model.Indexable {
    return &s.Indexable
}

statusDB := auth.NewIndexableDB(repo, logger, enforcer, createEmpty, getIndexable)
projectFilter := repository.Query().Comparison(repository.Field("projectRef"), builder.Eq, projectRef)
statusDB.Reorder(ctx, accountRef, statusID, 1, projectFilter)
```

### Task Auth.IndexableDB
```go
createEmpty := func() *model.Task {
    return &model.Task{}
}

getIndexable := func(t *model.Task) *model.Indexable {
    return &t.Indexable
}

taskDB := auth.NewIndexableDB(repo, logger, enforcer, createEmpty, getIndexable)
statusFilter := repository.Query().Comparison(repository.Field("statusRef"), builder.Eq, statusRef)
taskDB.Reorder(ctx, accountRef, taskID, 3, statusFilter)
```

## Permission Checking Details

### What Gets Checked
When reordering an object from index `A` to index `B`:

1. **Target object** - the object being moved
2. **Affected objects** - all objects whose indices will be shifted:
   - Moving down: objects between `A+1` and `B` (shifted up by -1)
   - Moving up: objects between `B` and `A-1` (shifted down by +1)

### Permission Requirements
- **Action**: `model.ActionUpdate` 
- **Scope**: All affected objects must be `PermissionBoundStorable`
- **Result**: If any object lacks permission, the entire operation fails

### Error Handling
```go
// Permission denied error
if err != nil {
    if strings.Contains(err.Error(), "accessDenied") {
        // Handle permission denied
    }
}
```

## Security Benefits

### ✅ **Comprehensive Permission Checking**
- Checks permissions for **all affected objects**, not just the target
- Prevents unauthorized reordering that could affect other users' data
- Uses efficient `EnforceBatch` for bulk permission checking

### ✅ **Type Safety**
- Generic implementation works with any `Indexable` struct
- Compile-time type checking
- No runtime type assertions

### ✅ **Flexible Filtering**
- Single `builder.Query` parameter for scoping
- Can combine organization filters with additional criteria
- Project factory automatically applies organization filtering

### ✅ **Clean Architecture**
- Separates permission logic from reordering logic
- Easy to test with mock enforcers
- Follows existing auth patterns

## Testing

### Mock Enforcer Setup
```go
mockEnforcer := &MockEnforcer{}

// Grant all permissions
permissions := map[bson.ObjectID]bool{
    objectID1: true,
    objectID2: true,
}
mockEnforcer.On("EnforceBatch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
    Return(permissions, nil)

// Deny specific permission
permissions[objectID2] = false
mockEnforcer.On("EnforceBatch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
    Return(permissions, nil)
```

### Test Scenarios
- ✅ **Permission granted** - reordering succeeds
- ❌ **Permission denied** - reordering fails with access denied error
- 🔄 **No change needed** - early return, minimal permission checking
- 🏢 **Organization filtering** - automatic org scope for projects

## Comparison: Basic vs Auth.Indexable

| Feature | Basic Indexable | Auth.Indexable |
|---------|----------------|----------------|
| Permission checking | ❌ No | ✅ Yes |
| Account parameter | ❌ No | ✅ Required |
| Security | ❌ None | ✅ Comprehensive |
| Performance | ✅ Fast | ⚠️ Slower (permission checks) |
| Use case | Internal operations | User-facing operations |

## Best Practices

1. **Use Auth.Indexable** for user-facing reordering operations
2. **Use Basic Indexable** for internal/system operations
3. **Always provide account reference** for proper permission checking
4. **Test permission scenarios** thoroughly with mock enforcers
5. **Handle permission errors** gracefully in user interfaces

That's it! **Secure, type-safe reordering** with comprehensive permission checking using `EnforceBatch`. 