add documentation site with markdown rendering (M12)
This commit is contained in:
396
portal/internal/web/docs/guides/permissions.md
Normal file
396
portal/internal/web/docs/guides/permissions.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# Permissions Guide
|
||||
|
||||
Mosis apps run in a secure sandbox with limited access to device features. To access sensitive capabilities, apps must declare permissions in their manifest.
|
||||
|
||||
## Why Permissions?
|
||||
|
||||
Permissions protect user privacy and security by:
|
||||
|
||||
1. **Informing users** what an app can access before installation
|
||||
2. **Limiting damage** if an app misbehaves
|
||||
3. **Maintaining trust** in the Mosis ecosystem
|
||||
|
||||
## Declaring Permissions
|
||||
|
||||
Add permissions to your `manifest.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.example.myapp",
|
||||
"name": "My App",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"network"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Only request permissions your app actually needs. Users are more likely to trust apps with fewer permissions.
|
||||
|
||||
## Available Permissions
|
||||
|
||||
### storage
|
||||
|
||||
**Description:** Persist data locally between app sessions.
|
||||
|
||||
**Use cases:**
|
||||
- Save user preferences
|
||||
- Cache data for offline use
|
||||
- Store app state
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
storage.set("key", value)
|
||||
storage.get("key")
|
||||
storage.remove("key")
|
||||
storage.clear()
|
||||
```
|
||||
|
||||
**Note:** All apps have access to in-memory storage during a session. The `storage` permission enables persistence across sessions.
|
||||
|
||||
---
|
||||
|
||||
### network
|
||||
|
||||
**Description:** Make HTTP/HTTPS requests to external servers.
|
||||
|
||||
**Use cases:**
|
||||
- Fetch data from APIs
|
||||
- Submit form data
|
||||
- Load remote content
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
http.get(url, callback)
|
||||
http.post(url, options, callback)
|
||||
http.request(options, callback)
|
||||
```
|
||||
|
||||
**Restrictions:**
|
||||
- HTTPS only (HTTP blocked for security)
|
||||
- Cannot access localhost or internal IPs
|
||||
- Subject to CORS policies
|
||||
|
||||
---
|
||||
|
||||
### clipboard
|
||||
|
||||
**Description:** Read from and write to the system clipboard.
|
||||
|
||||
**Use cases:**
|
||||
- Copy text or data
|
||||
- Paste user content
|
||||
- Share functionality
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
clipboard.write(text)
|
||||
clipboard.read(callback)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### notifications
|
||||
|
||||
**Description:** Display system notifications to the user.
|
||||
|
||||
**Use cases:**
|
||||
- Reminders
|
||||
- Alerts
|
||||
- Background updates
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
notifications.show({
|
||||
title = "Reminder",
|
||||
body = "Your timer is done!",
|
||||
icon = "icons/alarm.png"
|
||||
})
|
||||
```
|
||||
|
||||
**Restrictions:**
|
||||
- Notifications may be rate-limited
|
||||
- Users can disable notifications per-app
|
||||
|
||||
---
|
||||
|
||||
### camera
|
||||
|
||||
**Description:** Capture photos using the device camera.
|
||||
|
||||
**Use cases:**
|
||||
- Photo capture
|
||||
- QR code scanning
|
||||
- Augmented reality
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
camera.capture({
|
||||
quality = "high",
|
||||
facing = "back"
|
||||
}, function(result)
|
||||
if result.success then
|
||||
local imageData = result.data
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
**Restrictions:**
|
||||
- User prompt before first access
|
||||
- Cannot record video (photo only)
|
||||
|
||||
---
|
||||
|
||||
### microphone
|
||||
|
||||
**Description:** Record audio from the device microphone.
|
||||
|
||||
**Use cases:**
|
||||
- Voice notes
|
||||
- Audio messages
|
||||
- Voice commands
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
microphone.start()
|
||||
microphone.stop(function(result)
|
||||
local audioData = result.data
|
||||
end)
|
||||
```
|
||||
|
||||
**Restrictions:**
|
||||
- User prompt before first access
|
||||
- Maximum recording duration enforced
|
||||
|
||||
---
|
||||
|
||||
### location
|
||||
|
||||
**Description:** Access device location information.
|
||||
|
||||
**Use cases:**
|
||||
- Weather apps
|
||||
- Maps
|
||||
- Location-based features
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
location.get(function(result)
|
||||
if result.success then
|
||||
print(result.latitude, result.longitude)
|
||||
end
|
||||
end)
|
||||
|
||||
location.watch(function(result)
|
||||
-- Called on location changes
|
||||
end)
|
||||
```
|
||||
|
||||
**Restrictions:**
|
||||
- User prompt before first access
|
||||
- Approximate location only (no precise GPS)
|
||||
- Battery impact warning
|
||||
|
||||
---
|
||||
|
||||
### contacts
|
||||
|
||||
**Description:** Read device contacts.
|
||||
|
||||
**Use cases:**
|
||||
- Contact picker
|
||||
- Address book integration
|
||||
- Sharing with friends
|
||||
|
||||
**API access:**
|
||||
```lua
|
||||
contacts.pick(function(result)
|
||||
if result.success then
|
||||
print(result.name, result.phone)
|
||||
end
|
||||
end)
|
||||
|
||||
contacts.getAll(function(result)
|
||||
for i, contact in ipairs(result.contacts) do
|
||||
print(contact.name)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
**Restrictions:**
|
||||
- Read-only access
|
||||
- User prompt before first access
|
||||
|
||||
## Permission Levels
|
||||
|
||||
| Level | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| **Normal** | Low risk, minimal review | storage |
|
||||
| **Sensitive** | Requires user prompt | camera, microphone, location |
|
||||
| **Dangerous** | Extensive review required | contacts |
|
||||
|
||||
## Runtime Behavior
|
||||
|
||||
### First-Time Prompts
|
||||
|
||||
Some permissions trigger a user prompt on first use:
|
||||
|
||||
```lua
|
||||
-- First call triggers prompt
|
||||
camera.capture(options, function(result)
|
||||
if result.denied then
|
||||
-- User denied permission
|
||||
showPermissionExplanation()
|
||||
elseif result.success then
|
||||
-- Permission granted
|
||||
handlePhoto(result.data)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### Checking Permission Status
|
||||
|
||||
```lua
|
||||
-- Check if permission is granted
|
||||
if permissions.check("camera") then
|
||||
-- Already have permission
|
||||
showCameraButton()
|
||||
else
|
||||
-- Need to request
|
||||
showRequestButton()
|
||||
end
|
||||
```
|
||||
|
||||
### Requesting at Runtime
|
||||
|
||||
```lua
|
||||
permissions.request("camera", function(granted)
|
||||
if granted then
|
||||
startCamera()
|
||||
else
|
||||
showAlternative()
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Minimize Permissions
|
||||
|
||||
Only request what you need. An app with fewer permissions:
|
||||
- Builds more user trust
|
||||
- Passes review faster
|
||||
- Has smaller attack surface
|
||||
|
||||
### 2. Request at the Right Time
|
||||
|
||||
Don't request all permissions at startup. Request when the user takes an action that needs it:
|
||||
|
||||
```lua
|
||||
-- Bad: Request on app start
|
||||
function onAppStart()
|
||||
permissions.request("camera") -- Why?
|
||||
end
|
||||
|
||||
-- Good: Request when needed
|
||||
function onTakePhotoClicked()
|
||||
permissions.request("camera", function(granted)
|
||||
if granted then
|
||||
camera.capture(options, handlePhoto)
|
||||
end
|
||||
end)
|
||||
end
|
||||
```
|
||||
|
||||
### 3. Explain Why
|
||||
|
||||
Tell users why you need a permission before requesting:
|
||||
|
||||
```xml
|
||||
<div id="permission-explanation" style="display: none;">
|
||||
<p>This app needs camera access to scan QR codes.</p>
|
||||
<button onclick="requestCamera()">Enable Camera</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4. Handle Denial Gracefully
|
||||
|
||||
Apps should work (with reduced functionality) even if permissions are denied:
|
||||
|
||||
```lua
|
||||
function capturePhoto()
|
||||
if not permissions.check("camera") then
|
||||
-- Offer alternative
|
||||
showManualEntryOption()
|
||||
return
|
||||
end
|
||||
-- Proceed with camera
|
||||
end
|
||||
```
|
||||
|
||||
### 5. Don't Ask Again Immediately
|
||||
|
||||
If a user denies a permission, don't immediately ask again:
|
||||
|
||||
```lua
|
||||
local lastDenied = storage.get("camera_denied_time")
|
||||
if lastDenied and os.time() - lastDenied < 86400 then
|
||||
-- Wait at least 24 hours before asking again
|
||||
return
|
||||
end
|
||||
```
|
||||
|
||||
## Review Impact
|
||||
|
||||
Permission requests affect app review:
|
||||
|
||||
| Permission | Review Impact |
|
||||
|------------|---------------|
|
||||
| storage, network | Automatic approval |
|
||||
| clipboard | Quick review |
|
||||
| notifications | Standard review |
|
||||
| camera, microphone | Extended review |
|
||||
| location | Extended review |
|
||||
| contacts | Manual review required |
|
||||
|
||||
Apps requesting sensitive permissions must:
|
||||
1. Justify the need in submission notes
|
||||
2. Use the permission appropriately
|
||||
3. Respect user privacy
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Permission not declared"
|
||||
|
||||
```
|
||||
Error: Cannot use camera without 'camera' permission
|
||||
```
|
||||
|
||||
Add the permission to your manifest:
|
||||
```json
|
||||
"permissions": ["camera"]
|
||||
```
|
||||
|
||||
### "Permission denied by user"
|
||||
|
||||
Handle this gracefully in your code:
|
||||
```lua
|
||||
if result.denied then
|
||||
showAlternativeUI()
|
||||
end
|
||||
```
|
||||
|
||||
### "Permission blocked"
|
||||
|
||||
The user permanently blocked the permission. Direct them to settings:
|
||||
```lua
|
||||
if result.blocked then
|
||||
showMessage("Please enable camera in system settings")
|
||||
end
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Manifest Reference](../api/manifest.md) - Full manifest documentation
|
||||
- [Security Guide](security.md) - App security best practices
|
||||
- [Publishing Guide](publishing.md) - App review process
|
||||
Reference in New Issue
Block a user