# Milestone 2: Permission System

**Status**: ✅ Complete
**Goal**: Gate API access based on app permissions.

---

## Overview

This milestone implements the permission system that controls which APIs an app can access. It defines permission categories (Normal, Dangerous, Signature) and provides mechanisms for checking permissions at runtime.

### Key Deliverables

1. **PermissionGate class** - Permission checking and enforcement
2. **Permission categories** - Normal/Dangerous/Signature classification
3. **User gesture tracking** - Detect recent user interactions
4. **Manifest parsing** - Read app permissions from manifest.json

---

## File Structure

```
src/main/cpp/sandbox/
├── lua_sandbox.h           # (existing)
├── lua_sandbox.cpp         # (existing)
├── permission_gate.h       # NEW - Permission checking
└── permission_gate.cpp     # NEW - Implementation

sandbox-test/
├── scripts/
│   ├── test_permission_normal.lua      # NEW
│   ├── test_permission_dangerous.lua   # NEW
│   └── test_permission_signature.lua   # NEW
└── src/
    └── main.cpp            # Add new tests
```

---

## Implementation Details

### 1. Permission Categories

```cpp
// permission_gate.h
enum class PermissionCategory {
    Normal,      // Auto-granted (e.g., vibrate, internet)
    Dangerous,   // Requires user consent (e.g., camera, location)
    Signature    // System apps only (e.g., system settings, install apps)
};
```

### 2. Permission Definitions

```cpp
// permission_gate.cpp
static const std::unordered_map<std::string, PermissionInfo> PERMISSIONS = {
    // Normal permissions (auto-granted)
    {"internet",           {PermissionCategory::Normal, "Access the internet"}},
    {"vibrate",            {PermissionCategory::Normal, "Vibrate the device"}},
    {"wake_lock",          {PermissionCategory::Normal, "Keep device awake"}},
    {"notifications",      {PermissionCategory::Normal, "Show notifications"}},

    // Dangerous permissions (require user consent)
    {"camera",             {PermissionCategory::Dangerous, "Access the camera"}},
    {"microphone",         {PermissionCategory::Dangerous, "Record audio"}},
    {"location.fine",      {PermissionCategory::Dangerous, "Access precise location"}},
    {"location.coarse",    {PermissionCategory::Dangerous, "Access approximate location"}},
    {"contacts.read",      {PermissionCategory::Dangerous, "Read contacts"}},
    {"contacts.write",     {PermissionCategory::Dangerous, "Modify contacts"}},
    {"storage.external",   {PermissionCategory::Dangerous, "Access external storage"}},
    {"sensors.motion",     {PermissionCategory::Dangerous, "Access motion sensors"}},
    {"bluetooth",          {PermissionCategory::Dangerous, "Use Bluetooth"}},
    {"calendar.read",      {PermissionCategory::Dangerous, "Read calendar"}},
    {"calendar.write",     {PermissionCategory::Dangerous, "Modify calendar"}},

    // Signature permissions (system apps only)
    {"system.settings",    {PermissionCategory::Signature, "Modify system settings"}},
    {"system.install",     {PermissionCategory::Signature, "Install apps"}},
    {"system.uninstall",   {PermissionCategory::Signature, "Uninstall apps"}},
    {"system.admin",       {PermissionCategory::Signature, "Device administrator"}},
};
```

### 3. PermissionGate Class

```cpp
// permission_gate.h
#pragma once

#include <string>
#include <vector>
#include <unordered_set>
#include <chrono>

struct lua_State;

namespace mosis {

struct SandboxContext;  // Forward declaration

enum class PermissionCategory {
    Normal,
    Dangerous,
    Signature
};

struct PermissionInfo {
    PermissionCategory category;
    std::string description;
};

class PermissionGate {
public:
    explicit PermissionGate(const SandboxContext& context);

    // Check if app has permission (throws Lua error if not)
    bool Check(lua_State* L, const std::string& permission);

    // Check without throwing (returns false if denied)
    bool HasPermission(const std::string& permission) const;

    // Get permission category
    static PermissionCategory GetCategory(const std::string& permission);

    // User gesture tracking
    void RecordUserGesture();
    bool HasRecentUserGesture(int ms = 5000) const;

    // Runtime permission grant (called after user consent)
    void GrantPermission(const std::string& permission);
    void RevokePermission(const std::string& permission);

    // Get all declared permissions
    const std::vector<std::string>& GetDeclaredPermissions() const;

    // Get all granted permissions
    std::vector<std::string> GetGrantedPermissions() const;

private:
    const SandboxContext& m_context;
    std::unordered_set<std::string> m_runtime_grants;  // Runtime-granted dangerous perms
    std::chrono::steady_clock::time_point m_last_gesture;

    bool CheckNormalPermission(const std::string& permission) const;
    bool CheckDangerousPermission(const std::string& permission) const;
    bool CheckSignaturePermission(const std::string& permission) const;
};

// Lua helper - throws error if permission denied
int RequirePermission(lua_State* L, const char* permission);

} // namespace mosis
```

### 4. Implementation

```cpp
// permission_gate.cpp
#include "permission_gate.h"
#include "lua_sandbox.h"
#include <lua.hpp>
#include <algorithm>

namespace mosis {

// Permission database
static const std::unordered_map<std::string, PermissionInfo> PERMISSIONS = {
    // Normal
    {"internet",         {PermissionCategory::Normal, "Access the internet"}},
    {"vibrate",          {PermissionCategory::Normal, "Vibrate the device"}},
    {"wake_lock",        {PermissionCategory::Normal, "Keep device awake"}},
    {"notifications",    {PermissionCategory::Normal, "Show notifications"}},

    // Dangerous
    {"camera",           {PermissionCategory::Dangerous, "Access the camera"}},
    {"microphone",       {PermissionCategory::Dangerous, "Record audio"}},
    {"location.fine",    {PermissionCategory::Dangerous, "Access precise location"}},
    {"location.coarse",  {PermissionCategory::Dangerous, "Access approximate location"}},
    {"contacts.read",    {PermissionCategory::Dangerous, "Read contacts"}},
    {"contacts.write",   {PermissionCategory::Dangerous, "Modify contacts"}},
    {"storage.external", {PermissionCategory::Dangerous, "Access external storage"}},
    {"sensors.motion",   {PermissionCategory::Dangerous, "Access motion sensors"}},
    {"bluetooth",        {PermissionCategory::Dangerous, "Use Bluetooth"}},

    // Signature
    {"system.settings",  {PermissionCategory::Signature, "Modify system settings"}},
    {"system.install",   {PermissionCategory::Signature, "Install apps"}},
    {"system.admin",     {PermissionCategory::Signature, "Device administrator"}},
};

PermissionGate::PermissionGate(const SandboxContext& context)
    : m_context(context)
    , m_last_gesture(std::chrono::steady_clock::time_point::min())
{
}

PermissionCategory PermissionGate::GetCategory(const std::string& permission) {
    auto it = PERMISSIONS.find(permission);
    if (it != PERMISSIONS.end()) {
        return it->second.category;
    }
    // Unknown permissions default to Dangerous
    return PermissionCategory::Dangerous;
}

bool PermissionGate::HasPermission(const std::string& permission) const {
    auto category = GetCategory(permission);

    switch (category) {
        case PermissionCategory::Normal:
            return CheckNormalPermission(permission);
        case PermissionCategory::Dangerous:
            return CheckDangerousPermission(permission);
        case PermissionCategory::Signature:
            return CheckSignaturePermission(permission);
    }
    return false;
}

bool PermissionGate::Check(lua_State* L, const std::string& permission) {
    if (!HasPermission(permission)) {
        luaL_error(L, "permission denied: %s", permission.c_str());
        return false;
    }
    return true;
}

bool PermissionGate::CheckNormalPermission(const std::string& permission) const {
    // Normal permissions are auto-granted if declared in manifest
    const auto& declared = m_context.permissions;
    return std::find(declared.begin(), declared.end(), permission) != declared.end();
}

bool PermissionGate::CheckDangerousPermission(const std::string& permission) const {
    // Must be declared in manifest
    const auto& declared = m_context.permissions;
    if (std::find(declared.begin(), declared.end(), permission) == declared.end()) {
        return false;
    }

    // Must be granted at runtime (or be a system app)
    if (m_context.is_system_app) {
        return true;
    }

    return m_runtime_grants.count(permission) > 0;
}

bool PermissionGate::CheckSignaturePermission(const std::string& permission) const {
    // Only system apps get signature permissions
    if (!m_context.is_system_app) {
        return false;
    }

    // Must still be declared
    const auto& declared = m_context.permissions;
    return std::find(declared.begin(), declared.end(), permission) != declared.end();
}

void PermissionGate::RecordUserGesture() {
    m_last_gesture = std::chrono::steady_clock::now();
}

bool PermissionGate::HasRecentUserGesture(int ms) const {
    auto now = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_gesture);
    return elapsed.count() < ms;
}

void PermissionGate::GrantPermission(const std::string& permission) {
    m_runtime_grants.insert(permission);
}

void PermissionGate::RevokePermission(const std::string& permission) {
    m_runtime_grants.erase(permission);
}

const std::vector<std::string>& PermissionGate::GetDeclaredPermissions() const {
    return m_context.permissions;
}

std::vector<std::string> PermissionGate::GetGrantedPermissions() const {
    std::vector<std::string> granted;

    for (const auto& perm : m_context.permissions) {
        if (HasPermission(perm)) {
            granted.push_back(perm);
        }
    }

    return granted;
}

int RequirePermission(lua_State* L, const char* permission) {
    // Get sandbox from registry
    lua_getfield(L, LUA_REGISTRYINDEX, "__mosis_sandbox");
    auto* sandbox = static_cast<LuaSandbox*>(lua_touserdata(L, -1));
    lua_pop(L, 1);

    if (!sandbox) {
        return luaL_error(L, "sandbox not initialized");
    }

    // TODO: Get PermissionGate from sandbox once integrated
    // For now, this is a placeholder
    return 0;
}

} // namespace mosis
```

---

## Test Cases

### Test 1: Normal Permission Auto-Granted

**Script**: `scripts/test_permission_normal.lua`
```lua
-- Test that normal permissions are auto-granted when declared
-- This is called from C++ which sets up context with "internet" permission

-- If we get here, permission check passed
print("PASS: Normal permission granted")
```

**C++ Test**:
```cpp
bool Test_NormalPermissionAutoGranted(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {"internet"};  // Declare normal permission

    LuaSandbox sandbox(ctx);
    PermissionGate gate(ctx);

    // Normal permissions should be auto-granted
    EXPECT_TRUE(gate.HasPermission("internet"));

    return true;
}
```

### Test 2: Dangerous Permission Requires Grant

**C++ Test**:
```cpp
bool Test_DangerousPermissionRequiresGrant(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {"camera"};  // Declare dangerous permission

    PermissionGate gate(ctx);

    // Not granted yet
    EXPECT_FALSE(gate.HasPermission("camera"));

    // Grant at runtime
    gate.GrantPermission("camera");

    // Now should have it
    EXPECT_TRUE(gate.HasPermission("camera"));

    // Revoke
    gate.RevokePermission("camera");
    EXPECT_FALSE(gate.HasPermission("camera"));

    return true;
}
```

### Test 3: Signature Permission System Only

**C++ Test**:
```cpp
bool Test_SignaturePermissionSystemOnly(std::string& error_msg) {
    // Non-system app
    SandboxContext ctx = TestContext();
    ctx.permissions = {"system.settings"};
    ctx.is_system_app = false;

    PermissionGate gate(ctx);
    EXPECT_FALSE(gate.HasPermission("system.settings"));

    // System app
    SandboxContext sys_ctx = TestContext();
    sys_ctx.permissions = {"system.settings"};
    sys_ctx.is_system_app = true;

    PermissionGate sys_gate(sys_ctx);
    EXPECT_TRUE(sys_gate.HasPermission("system.settings"));

    return true;
}
```

### Test 4: User Gesture Tracking

**C++ Test**:
```cpp
bool Test_UserGestureRequired(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    PermissionGate gate(ctx);

    // No recent gesture
    EXPECT_FALSE(gate.HasRecentUserGesture(5000));

    // Record gesture
    gate.RecordUserGesture();

    // Should have recent gesture
    EXPECT_TRUE(gate.HasRecentUserGesture(5000));

    return true;
}
```

### Test 5: Undeclared Permission Denied

**C++ Test**:
```cpp
bool Test_UndeclaredPermissionDenied(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {};  // No permissions declared

    PermissionGate gate(ctx);

    // Even normal permissions need to be declared
    EXPECT_FALSE(gate.HasPermission("internet"));

    return true;
}
```

---

## Build & Test Commands

### Build

```bash
cd sandbox-test
cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
cmake --build build --config Debug
```

### Run All Tests

```bash
./build/Debug/sandbox-test.exe
```

### Run Permission Tests Only

```bash
./build/Debug/sandbox-test.exe --test Permission
```

---

## Acceptance Criteria

All tests must pass:

- [x] `Test_NormalPermissionAutoGranted` - Normal perms auto-granted when declared
- [x] `Test_DangerousPermissionRequiresGrant` - Dangerous perms need runtime grant
- [x] `Test_SignaturePermissionSystemOnly` - Signature perms only for system apps
- [x] `Test_UserGestureTracking` - User gesture tracking works
- [x] `Test_UndeclaredPermissionDenied` - Undeclared perms always denied
- [x] `Test_SystemAppGetsDangerousAuto` - System apps get dangerous perms auto
- [x] `Test_PermissionCategoryCheck` - Permission categories are correct

---

## Integration Notes

After Milestone 2:
1. Add `PermissionGate` to `LuaSandbox` class
2. Call `RequirePermission()` before sensitive operations
3. Wire up user gesture recording from touch events

---

## Next Steps

After Milestone 2 passes:
1. Milestone 3: Audit Logging & Rate Limiting
2. Use permission gate in all subsequent API implementations
