Skip to content

Sub-Issue 182.3: Integration Tests + SecretAttachmentPolicy Update #189

@kevalyq

Description

@kevalyq

📌 Parent Issue

#182 (Phase 3: Secret Sharing & Access Control)

🎯 Goal

Complete Phase 3 with integration tests, policy updates for file attachments, and documentation finalization.


📋 Implementation Tasks

1. Update SecretAttachmentPolicy

Location: app/Policies/SecretAttachmentPolicy.php

Current Issue: Policy only checks owner_id, ignoring shares.

Updates Needed:

  • view(User $user, SecretAttachment $attachment) - Check secret shares
  • create(User $user, Secret $secret) - Check write+ permission
  • delete(User $user, SecretAttachment $attachment) - Check write+ permission

Implementation:

public function view(User $user, SecretAttachment $attachment): bool
{
    return $attachment->secret->userHasPermission($user, 'read');
}

public function create(User $user, Secret $secret): bool
{
    return $secret->userHasPermission($user, 'write');
}

public function delete(User $user, SecretAttachment $attachment): bool
{
    return $attachment->secret->userHasPermission($user, 'write');
}

2. Integration Tests

Test Suites:

Secrets + Shares Integration

  • User can view secret shared with read permission
  • User can update secret shared with write permission
  • User can delete secret shared with admin permission
  • User with read permission cannot update secret
  • User with write permission cannot delete secret
  • Expired share does not grant access
  • Revoking share immediately removes access

Secrets + Attachments + Shares Integration

  • User with read share can view attachments
  • User with read share can download attachments
  • User with read share cannot upload attachments
  • User with write share can upload attachments
  • User with write share can delete attachments
  • User without share cannot access attachments

Role-Based Shares Integration

  • User with role receives permissions from role share
  • User assigned to role can access shared secrets
  • User removed from role loses access to role-shared secrets
  • Multiple roles combine permissions correctly

Edge Cases

  • Cascade delete: Deleting secret removes shares
  • Cascade delete: Deleting secret removes attachments
  • Cascade delete: Deleting user removes their shares
  • Owner can always access secret even with no explicit share
  • Sharing with self (user_id = owner_id) is allowed but redundant

Coverage Target: ≥80% for integration scenarios


3. API Documentation

Location: SecPal/contracts repository

Tasks:

  • Update OpenAPI spec with Secret endpoints
  • Update OpenAPI spec with SecretShare endpoints
  • Add example requests/responses
  • Document XOR constraint for shares
  • Document permission hierarchy (admin > write > read)

Files to Update:

contracts/
├── openapi.yaml
├── paths/
│   ├── secrets.yaml (new)
│   └── secret-shares.yaml (new)
└── components/
    └── schemas/
        ├── Secret.yaml (new)
        └── SecretShare.yaml (new)

4. Documentation Updates

api/docs/:

  • Update docs/rbac-architecture.md - Add Secret Sharing section
  • Create docs/guides/secret-sharing.md - User guide
  • Update README.md - Add Secret Management to features list

Content for docs/guides/secret-sharing.md:

# Secret Sharing Guide

## Permission Levels

- **read**: View secret + download attachments
- **write**: read + update secret + upload/delete attachments
- **admin**: write + delete secret + manage shares

## Granting Access

### Share with User
POST /v1/secrets/{id}/shares
{
  "user_id": "uuid",
  "permission": "read",
  "expires_at": "2026-01-01T00:00:00Z"
}

### Share with Role
POST /v1/secrets/{id}/shares
{
  "role_id": 5,
  "permission": "write"
}

## Revoking Access
DELETE /v1/secrets/{id}/shares/{share_id}

5. CHANGELOG Update

Location: CHANGELOG.md

Add to Unreleased > Added:

- **Secret Sharing API (Phase 3 Complete)** (#182, #184, #185, #186)
  - CRUD API for secrets with permission checks
  - Share secrets with users or roles (read/write/admin permissions)
  - Grant/revoke access endpoints
  - Permission hierarchy enforcement (admin > write > read)
  - Expiration support for temporary shares
  - SecretAttachmentPolicy respects shares
  - Integration tests: Secrets + Shares + Attachments
  - OpenAPI documentation for Secret Management

6. Tests (Pest)

Feature Tests - SecretAttachmentPolicy:

  • User with read share can view attachment
  • User with read share cannot upload attachment
  • User with write share can upload attachment
  • User with write share can delete attachment
  • User without share cannot access attachment

Integration Tests:

  • Complete workflow: Create → Share → Access → Revoke
  • Permission escalation: read → write → admin
  • Role-based sharing across multiple users
  • Expiration enforcement across all endpoints

Total New Tests: ~15-20


✅ Acceptance Criteria

  • SecretAttachmentPolicy updated for shares
  • All integration tests passing (~20 tests)
  • OpenAPI spec updated in contracts repo
  • Documentation complete (guides + README)
  • CHANGELOG.md updated with complete Phase 3 summary
  • PHPStan level max passing
  • Laravel Pint passing
  • REUSE 3.3 compliant
  • Issue Phase 3: Secret Sharing & Access Control (RBAC Integration) #182 can be closed (all sub-issues complete)

🔗 Dependencies


📝 Technical Notes

Test Structure

describe('Secrets + Shares + Attachments Integration', function () {
    test('user with read share can view but not upload attachments', function () {
        $owner = User::factory()->create();
        $sharedUser = User::factory()->create();
        
        $secret = createTestSecret(['owner_id' => $owner->id]);
        $attachment = createTestAttachment(['secret_id' => $secret->id]);
        
        // Grant read permission
        SecretShare::create([
            'secret_id' => $secret->id,
            'user_id' => $sharedUser->id,
            'permission' => 'read',
            'granted_by' => $owner->id,
            'granted_at' => now(),
        ]);
        
        // Can view attachment
        $response = $this->actingAs($sharedUser)
            ->getJson("/api/v1/secrets/{$secret->id}/attachments/{$attachment->id}");
        $response->assertOk();
        
        // Cannot upload attachment
        $response = $this->actingAs($sharedUser)
            ->postJson("/api/v1/secrets/{$secret->id}/attachments", [
                'file' => UploadedFile::fake()->create('doc.pdf', 100),
            ]);
        $response->assertForbidden();
    });
});

📝 PR Linking Instructions

When creating the PR:

Fixes #186
Closes #182

⚠️ Important:


Type: Sub-Issue (Backend + Documentation)
Priority: High
Estimated Effort: 2-3 days
Status: ⏸️ Blocked by #184, #185

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions