Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,234 @@
# Code Signing Guide for MapleFile Desktop
This document outlines the code signing requirements and procedures for MapleFile desktop application releases.
## Why Code Signing is Important
Code signing provides:
1. **Integrity Verification**: Ensures the binary hasn't been tampered with since signing
2. **Publisher Authentication**: Confirms the software comes from MapleFile/Maple Open Technologies
3. **User Trust**: Operating systems trust signed applications more readily
4. **Malware Protection**: Unsigned apps trigger security warnings that users may ignore
## Platform Requirements
### macOS
**Certificate Types:**
- **Developer ID Application**: Required for distribution outside the Mac App Store
- **Developer ID Installer**: Required for signed `.pkg` installers
**Requirements:**
1. Apple Developer Program membership ($99/year)
2. Developer ID certificates from Apple Developer portal
3. Notarization through Apple's notary service
**Signing Process:**
```bash
# Sign the application
codesign --force --options runtime --sign "Developer ID Application: Your Name (TEAM_ID)" \
--timestamp MapleFile.app
# Create a signed DMG
hdiutil create -volname "MapleFile" -srcfolder MapleFile.app -ov -format UDZO MapleFile.dmg
codesign --sign "Developer ID Application: Your Name (TEAM_ID)" MapleFile.dmg
# Notarize (required for macOS 10.15+)
xcrun notarytool submit MapleFile.dmg --apple-id your@email.com --team-id TEAM_ID --wait
xcrun stapler staple MapleFile.dmg
```
**Wails Build Integration:**
```bash
# Wails supports code signing via environment variables
export MACOS_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAM_ID)"
export MACOS_NOTARIZATION_TEAM_ID="TEAM_ID"
export MACOS_NOTARIZATION_APPLE_ID="your@email.com"
export MACOS_NOTARIZATION_PASSWORD="@keychain:AC_PASSWORD"
wails build -platform darwin/universal
```
### Windows
**Certificate Types:**
- **EV Code Signing Certificate**: Extended Validation - highest trust, required for SmartScreen reputation
- **Standard Code Signing Certificate**: Basic signing, builds reputation over time
**Requirements:**
1. Code signing certificate from a trusted CA (DigiCert, Sectigo, GlobalSign, etc.)
2. Hardware token (required for EV certificates)
3. SignTool from Windows SDK
**Signing Process:**
```powershell
# Sign with timestamp (important for validity after certificate expiry)
signtool sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 ^
/a /n "Maple Open Technologies" MapleFile.exe
# Verify signature
signtool verify /pa /v MapleFile.exe
```
**Wails Build Integration:**
```powershell
# Set environment variables before build
$env:WINDOWS_SIGNING_CERTIFICATE = "path/to/certificate.pfx"
$env:WINDOWS_SIGNING_PASSWORD = "certificate_password"
wails build -platform windows/amd64
```
### Linux
Linux doesn't have a universal code signing requirement, but you can:
1. **GPG Signing**: Sign release artifacts with GPG
```bash
gpg --armor --detach-sign MapleFile.tar.gz
```
2. **AppImage Signing**: Sign AppImage files
```bash
# Import your signing key
./appimagetool --sign MapleFile.AppImage
```
3. **Package Signatures**: Use distribution-specific signing
- `.deb`: `dpkg-sig --sign builder package.deb`
- `.rpm`: `rpm --addsign package.rpm`
## Secure Update Mechanism
### Current State
MapleFile currently does not include automatic updates.
### Recommended Implementation
1. **Update Server**: Host update manifests with signed checksums
2. **Version Checking**: Application checks for updates on startup (optional)
3. **Download Verification**: Verify signature before applying update
4. **Rollback Support**: Keep previous version for rollback on failure
**Update Manifest Format:**
```json
{
"version": "1.2.3",
"release_date": "2025-01-15",
"platforms": {
"darwin-arm64": {
"url": "https://releases.maplefile.com/v1.2.3/MapleFile-darwin-arm64.dmg",
"sha256": "abc123...",
"signature": "base64-encoded-signature"
},
"darwin-amd64": {
"url": "https://releases.maplefile.com/v1.2.3/MapleFile-darwin-amd64.dmg",
"sha256": "def456...",
"signature": "base64-encoded-signature"
},
"windows-amd64": {
"url": "https://releases.maplefile.com/v1.2.3/MapleFile-windows-amd64.exe",
"sha256": "ghi789...",
"signature": "base64-encoded-signature"
}
}
}
```
**Verification Process:**
```go
// Pseudocode for update verification
func verifyUpdate(downloadPath, expectedSHA256, signature string) error {
// 1. Verify SHA256 hash
actualHash := sha256sum(downloadPath)
if actualHash != expectedSHA256 {
return errors.New("hash mismatch")
}
// 2. Verify signature (using embedded public key)
if !verifySignature(downloadPath, signature, publicKey) {
return errors.New("signature verification failed")
}
return nil
}
```
## Certificate Management
### Storage
- **Never** commit private keys to version control
- Store certificates in secure vault (e.g., HashiCorp Vault, AWS Secrets Manager)
- Use CI/CD secrets for automated builds
### Rotation
- Set calendar reminders for certificate expiry (typically 1-3 years)
- Plan for certificate rotation before expiry
- Test signing process after certificate renewal
### Revocation
- Maintain list of compromised certificates
- Have incident response plan for key compromise
- Document process for certificate revocation
## Build Pipeline Integration
### GitHub Actions Example
```yaml
name: Release Build
on:
push:
tags:
- 'v*'
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Import Code Signing Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE }}
CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
echo $CERTIFICATE_BASE64 | base64 --decode > certificate.p12
security create-keychain -p "" build.keychain
security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain
- name: Build and Sign
env:
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
run: |
wails build -platform darwin/universal
# Notarize here...
```
## Verification Commands
### macOS
```bash
# Check signature
codesign -dvv MapleFile.app
# Verify notarization
spctl -a -vv MapleFile.app
```
### Windows
```powershell
# Check signature
signtool verify /pa /v MapleFile.exe
# PowerShell alternative
Get-AuthenticodeSignature MapleFile.exe
```
## References
- [Apple Developer Code Signing](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution)
- [Microsoft Authenticode](https://docs.microsoft.com/en-us/windows/win32/seccrypto/cryptography-tools)
- [Wails Build Documentation](https://wails.io/docs/guides/signing)
- [OWASP Code Signing Guidelines](https://cheatsheetseries.owasp.org/cheatsheets/Code_Signing_Cheat_Sheet.html)

View file

@ -0,0 +1,391 @@
# Collection Icon Customization Plan
## Overview
Add the ability to customize collection (folder) icons with emojis or predefined icons. This feature enhances the user experience by allowing visual differentiation between collections.
## Requirements
1. **Customization Options**:
- Custom emoji (e.g., 📁, 🎵, 📷, 💼, 🏠)
- Predefined cross-browser icons from a curated set
- Default folder icon when no customization is set
2. **Behavior**:
- Default to standard folder icon if no customization
- Persist across browser sessions (stored in database)
- Easy to change or revert to default
- Intuitive, user-friendly UI
3. **Security Consideration**:
- Icon data should be encrypted (E2EE) like collection name
- Only emoji characters or predefined icon identifiers allowed
---
## Data Model Design
### New Field: `custom_icon`
Add a new encrypted field to the Collection model that stores icon customization data.
```go
// CustomIcon stores the collection's custom icon configuration
type CustomIcon struct {
Type string `json:"type"` // "emoji", "icon", or "" (empty = default)
Value string `json:"value"` // Emoji character or icon identifier
}
```
**Field Storage Options**:
| Option | Pros | Cons |
|--------|------|------|
| A) Single encrypted JSON field | Simple, one field | Requires parsing |
| B) Two fields (type + value) | Clear structure | More columns |
| C) Single string field | Simplest | Limited validation |
**Recommended: Option C** - Single `encrypted_custom_icon` field storing either:
- Empty string `""` → Default folder icon
- Emoji character (e.g., `"📷"`) → Display as emoji
- Icon identifier (e.g., `"icon:briefcase"`) → Predefined icon
This keeps the schema simple and the client handles interpretation.
---
## Implementation Plan
### Phase 1: Backend (cloud/maplefile-backend)
#### 1.1 Database Schema Update
**Note:** No new migration files needed - updating existing migration `012_create_collections_by_id.up.cql` directly (assumes full database wipe).
Add to `collections_by_id` table:
```sql
encrypted_custom_icon TEXT,
```
#### 1.2 Update Domain Model
File: `internal/domain/collection/model.go`
```go
type Collection struct {
// ... existing fields ...
// EncryptedCustomIcon stores the custom icon for this collection.
// Empty string means use default folder icon.
// Contains either an emoji character or "icon:<identifier>" for predefined icons.
// Encrypted with the collection key for E2EE.
EncryptedCustomIcon string `bson:"encrypted_custom_icon" json:"encrypted_custom_icon"`
}
```
Also add to `CollectionSyncItem` for sync operations:
```go
type CollectionSyncItem struct {
// ... existing fields ...
EncryptedCustomIcon string `json:"encrypted_custom_icon,omitempty" bson:"encrypted_custom_icon,omitempty"`
}
```
**Note:** The sync query in `collectionsync.go:getCollectionSyncItem()` fetches minimal data from `collections_by_id`. This query will need to include `encrypted_custom_icon` so clients can display the correct icon during sync.
#### 1.3 Update Repository Layer
Files to modify:
- `internal/repo/collection/create.go` - Include new field in INSERT
- `internal/repo/collection/update.go` - Include new field in UPDATE
- `internal/repo/collection/get.go` - Include new field in SELECT
- `internal/repo/collection/sync.go` - Include new field in sync queries
#### 1.4 Update HTTP Handlers (if needed)
The existing create/update endpoints should automatically handle the new field since they accept the full Collection struct.
---
### Phase 2: Frontend (web/maplefile-frontend)
#### 2.1 Create Icon Picker Component
New file: `src/components/IconPicker/IconPicker.jsx`
Features:
- Emoji picker tab with common categories (objects, activities, symbols)
- Predefined icons tab (Heroicons subset)
- "Default" option to revert to folder icon
- Search/filter functionality
- Recently used icons
```jsx
// Example structure
const IconPicker = ({ value, onChange, onClose }) => {
const [activeTab, setActiveTab] = useState('emoji'); // 'emoji' | 'icons'
const predefinedIcons = [
{ id: 'briefcase', icon: BriefcaseIcon, label: 'Work' },
{ id: 'photo', icon: PhotoIcon, label: 'Photos' },
{ id: 'music', icon: MusicalNoteIcon, label: 'Music' },
{ id: 'document', icon: DocumentIcon, label: 'Documents' },
{ id: 'archive', icon: ArchiveBoxIcon, label: 'Archive' },
// ... more icons
];
const popularEmojis = ['📁', '📷', '🎵', '💼', '🏠', '❤️', '⭐', '🎮', '📚', '🎨'];
return (
// ... picker UI
);
};
```
#### 2.2 Update CollectionEdit Page
File: `src/pages/User/FileManager/Collections/CollectionEdit.jsx`
Add icon customization section:
```jsx
{/* Icon Customization Section */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
<SparklesIcon className="h-5 w-5 mr-2 text-gray-500" />
Customize Icon
</h2>
<div className="flex items-center space-x-4">
{/* Current Icon Preview */}
<div className="flex items-center justify-center h-16 w-16 bg-gray-100 rounded-xl border-2 border-dashed border-gray-300">
<CollectionIcon icon={formData.customIcon} size="lg" />
</div>
{/* Change/Reset Buttons */}
<div className="space-y-2">
<button onClick={() => setShowIconPicker(true)} className="...">
Change Icon
</button>
{formData.customIcon && (
<button onClick={() => setFormData({...formData, customIcon: ''})} className="...">
Reset to Default
</button>
)}
</div>
</div>
</div>
```
#### 2.3 Create CollectionIcon Component
New file: `src/components/CollectionIcon/CollectionIcon.jsx`
Renders the appropriate icon based on the customIcon value:
```jsx
const CollectionIcon = ({ icon, collectionType = 'folder', size = 'md', className = '' }) => {
const sizes = {
sm: 'h-4 w-4',
md: 'h-6 w-6',
lg: 'h-10 w-10',
};
// Default folder/album icon
if (!icon || icon === '') {
const Icon = collectionType === 'album' ? PhotoIcon : FolderIcon;
return <Icon className={`${sizes[size]} ${className}`} />;
}
// Predefined icon
if (icon.startsWith('icon:')) {
const iconId = icon.replace('icon:', '');
const IconComponent = predefinedIconMap[iconId];
return IconComponent ? <IconComponent className={`${sizes[size]} ${className}`} /> : <FolderIcon className={`${sizes[size]} ${className}`} />;
}
// Emoji
return <span className={`${emojiSizes[size]} ${className}`}>{icon}</span>;
};
```
#### 2.4 Update Collection List/Grid Views
Update anywhere collections are displayed to use the new `CollectionIcon` component:
- `FileManagerIndex.jsx`
- `CollectionDetails.jsx`
- Sidebar navigation (if applicable)
#### 2.5 Update Encryption/Decryption
Update the collection encryption service to handle the new field:
- Encrypt `customIcon` when saving
- Decrypt `encrypted_custom_icon` when loading
---
### Phase 3: Native Desktop (native/desktop/maplefile)
#### 3.1 Update Domain Model
File: `internal/domain/collection/model.go`
```go
type Collection struct {
// ... existing fields ...
// CustomIcon stores the decrypted custom icon for this collection.
// Empty string means use default folder icon.
// Contains either an emoji character or "icon:<identifier>" for predefined icons.
CustomIcon string `json:"custom_icon,omitempty"`
// EncryptedCustomIcon is the encrypted version from cloud
EncryptedCustomIcon string `json:"encrypted_custom_icon,omitempty"`
}
```
#### 3.2 Update Sync Service
Ensure the sync service handles the new field when syncing collections from the cloud.
#### 3.3 Update Frontend (Wails)
The desktop frontend uses the same React patterns, so the IconPicker and CollectionIcon components can be shared or adapted.
---
## Predefined Icon Set
A curated set of cross-browser compatible icons:
| ID | Icon | Use Case |
|----|------|----------|
| `briefcase` | BriefcaseIcon | Work |
| `photo` | PhotoIcon | Photos |
| `music` | MusicalNoteIcon | Music |
| `video` | VideoCameraIcon | Videos |
| `document` | DocumentTextIcon | Documents |
| `archive` | ArchiveBoxIcon | Archive |
| `star` | StarIcon | Favorites |
| `heart` | HeartIcon | Personal |
| `home` | HomeIcon | Home |
| `academic` | AcademicCapIcon | School |
| `code` | CodeBracketIcon | Code |
| `cloud` | CloudIcon | Cloud |
| `lock` | LockClosedIcon | Private |
| `gift` | GiftIcon | Gifts |
| `calendar` | CalendarIcon | Events |
---
## Migration Strategy
1. **Backward Compatible**: Empty `encrypted_custom_icon` means default icon
2. **No Data Migration Needed**: New collections get the field, old collections have NULL/empty
3. **Clients Handle Missing Field**: Treat NULL/empty as default
---
## Testing Checklist
### Backend
- [ ] Migration runs successfully
- [ ] Create collection with custom icon
- [ ] Update collection custom icon
- [ ] Revert to default icon
- [ ] Sync includes custom icon field
- [ ] E2EE encryption/decryption works
### Frontend (Web)
- [ ] Icon picker opens and closes
- [ ] Emoji selection works
- [ ] Predefined icon selection works
- [ ] Reset to default works
- [ ] Icon persists after page reload
- [ ] Icon displays correctly in list/grid views
- [ ] Works across different browsers
### Native Desktop
- [ ] Sync downloads custom icon
- [ ] Icon displays correctly
- [ ] Edit icon works (if implemented)
---
## Security Considerations
1. **E2EE**: Custom icon is encrypted with collection key
2. **Input Validation**: Only allow valid emoji or predefined icon IDs
3. **XSS Prevention**: Sanitize icon display (emoji rendering, no HTML)
4. **Size Limits**: Max length for custom icon field (e.g., 50 chars)
---
## Future Enhancements
1. **Custom uploaded icons** (requires more complex storage)
2. **Icon color customization**
3. **Icon packs/themes**
4. **Bulk icon changes** (apply to multiple collections)
---
## Files to Modify
### Backend (cloud/maplefile-backend)
**Schema Update (modify existing migration):**
1. `migrations/012_create_collections_by_id.up.cql` - Add `encrypted_custom_icon TEXT` column
**Domain Layer:**
2. `internal/domain/collection/model.go` - Add `EncryptedCustomIcon` field to `Collection` and `CollectionSyncItem` structs
**Repository Layer:**
4. `internal/repo/collection/create.go` - Add `encrypted_custom_icon` to INSERT query (line ~57-66)
5. `internal/repo/collection/update.go` - Add `encrypted_custom_icon` to UPDATE query (line ~64-73)
6. `internal/repo/collection/get.go` - Add `encrypted_custom_icon` to SELECT query and scan (line ~44-52, ~72-90)
7. `internal/repo/collection/collectionsync.go` - Add field to sync queries
**Note:** Secondary tables (013-017) do NOT need modification - they are lookup/index tables that only store keys and minimal fields. The `encrypted_custom_icon` is stored only in `collections_by_id`.
### Frontend (web/maplefile-frontend)
**New Components:**
1. `src/components/IconPicker/IconPicker.jsx` - Modal with emoji grid + predefined icons
2. `src/components/CollectionIcon/CollectionIcon.jsx` - Renders appropriate icon based on value
**Modified Pages:**
3. `src/pages/User/FileManager/Collections/CollectionEdit.jsx` - Add icon customization section
4. `src/pages/User/FileManager/Collections/CollectionDetails.jsx` - Display custom icon
5. `src/pages/User/FileManager/Collections/CollectionCreate.jsx` - Optional icon selection on create
6. `src/pages/User/FileManager/FileManagerIndex.jsx` - Display custom icons in list/grid
**Services:**
7. Collection encryption service - Handle `customIcon` field encryption/decryption
### Native Desktop (native/desktop/maplefile)
**Domain:**
1. `internal/domain/collection/model.go` - Add `CustomIcon` and `EncryptedCustomIcon` fields
**Repository:**
2. `internal/repo/collection/repository.go` - Handle new field in CRUD operations
**Sync:**
3. `internal/service/sync/collection.go` - Include field in sync operations
**Frontend:**
4. `frontend/src/` - Adapt IconPicker and CollectionIcon components (if not shared with web)
---
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Backend (migration + model) | 2-3 hours |
| Frontend components | 4-6 hours |
| Frontend integration | 2-3 hours |
| Native desktop | 2-3 hours |
| Testing | 2-3 hours |
| **Total** | **12-18 hours** |