monorepo/dialer/lib/features/settings/cryptography/manage_keys_page.dart

250 lines
7.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
import 'package:dialer/services/cryptography/asymmetric_crypto_service.dart';
class ManageKeysPage extends StatefulWidget {
@override
_ManageKeysPageState createState() => _ManageKeysPageState();
}
class _ManageKeysPageState extends State<ManageKeysPage> {
List<Map<String, dynamic>> _keys = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_fetchKeys();
}
Future<void> _fetchKeys() async {
final AsymmetricCryptoService cryptoService = Provider.of<AsymmetricCryptoService>(context, listen: false);
try {
final keys = await cryptoService.getAllKeys();
setState(() {
_keys = keys;
_isLoading = false;
});
} catch (e) {
// Handle error, e.g., show a snackbar
setState(() {
_isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error fetching keys: $e')),
);
}
}
Future<void> _createKey() async {
final AsymmetricCryptoService cryptoService = Provider.of<AsymmetricCryptoService>(context, listen: false);
String? label = await _showCreateKeyDialog();
if (label == null) return; // User cancelled
try {
await cryptoService.generateKeyPair(label: label);
await _fetchKeys();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Key "$label" created successfully.')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error creating key: $e')),
);
}
}
Future<String?> _showCreateKeyDialog() async {
String label = '';
return showDialog<String>(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Create New Key'),
content: TextField(
onChanged: (value) {
label = value;
},
decoration: InputDecoration(hintText: "Enter key label"),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(null),
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(label.trim().isEmpty ? 'Key ${Uuid().v4()}' : label.trim()),
child: Text('Create'),
),
],
);
},
);
}
Future<void> _deleteKey(String alias, String label) async {
final AsymmetricCryptoService cryptoService = Provider.of<AsymmetricCryptoService>(context, listen: false);
bool confirm = await _showDeleteConfirmationDialog(label);
if (!confirm) return;
try {
await cryptoService.deleteKeyPair(alias);
await _fetchKeys();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Key "$label" deleted successfully.')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error deleting key: $e')),
);
}
}
Future<bool> _showDeleteConfirmationDialog(String label) async {
return await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete Key'),
content: Text('Are you sure you want to delete the key "$label"? This action cannot be undone.'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
style: ElevatedButton.styleFrom(foregroundColor: Colors.red),
child: Text('Delete'),
),
],
),
) ??
false;
}
Future<void> _viewPublicKey(String alias, String label) async {
final AsymmetricCryptoService cryptoService = Provider.of<AsymmetricCryptoService>(context, listen: false);
try {
final String publicKey = await cryptoService.getPublicKey(alias);
await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Public Key for "$label"'),
content: SelectableText(publicKey),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Close'),
),
// Optionally, add a button to copy the key to clipboard
],
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error retrieving public key: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Manage Keys'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: _createKey,
tooltip: 'Create New Key',
),
],
),
body: _isLoading
? Center(child: CircularProgressIndicator())
: _keys.isEmpty
? Center(child: Text('No keys found.'))
: ListView.builder(
itemCount: _keys.length,
itemBuilder: (context, index) {
final key = _keys[index];
return ListTile(
title: Text(key['label']),
subtitle: Text('Created at: ${key['created_at']}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _editKeyLabel(key['alias'], key['label']),
tooltip: 'Edit Label',
),
IconButton(
icon: Icon(Icons.visibility),
onPressed: () => _viewPublicKey(key['alias'], key['label']),
tooltip: 'View Public Key',
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _deleteKey(key['alias'], key['label']),
tooltip: 'Delete Key',
),
],
),
);
},
));
}
/// Updates the label of a key.
Future<void> _editKeyLabel(String alias, String currentLabel) async {
final AsymmetricCryptoService cryptoService = Provider.of<AsymmetricCryptoService>(context, listen: false);
String? newLabel = await _showEditLabelDialog(currentLabel);
if (newLabel == null) return; // User cancelled
try {
await cryptoService.updateKeyLabel(alias, newLabel);
await _fetchKeys();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Key label updated to "$newLabel".')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error updating key label: $e')),
);
}
}
Future<String?> _showEditLabelDialog(String currentLabel) async {
String label = currentLabel;
return showDialog<String>(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Edit Key Label'),
content: TextField(
onChanged: (value) {
label = value;
},
decoration: InputDecoration(hintText: "Enter new key label"),
controller: TextEditingController(text: currentLabel),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(null),
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(label.trim().isEmpty ? currentLabel : label.trim()),
child: Text('Save'),
),
],
);
},
);
}
}