feat(studio): optional pinned-key field in the store-add form
Some checks are pending
Security / Security check (push) Waiting to run
Some checks are pending
Security / Security check (push) Waiting to run
The module-store manager's 'Add a store' form gains an optional PEM public-key field; when set it pins that publisher key to the store (per-store signing trust). Localized DE/EN. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
762788ea9e
commit
7625509237
7 changed files with 68 additions and 5 deletions
|
|
@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
|
|||
import '../data/error_presentation.dart';
|
||||
import '../data/hub.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class ChainStoresDialog extends StatefulWidget {
|
||||
|
|
@ -30,6 +31,7 @@ class _ChainStoresDialogState extends State<ChainStoresDialog> {
|
|||
late Future<List<StoreSource>> _future;
|
||||
final _name = TextEditingController();
|
||||
final _url = TextEditingController();
|
||||
final _pubkey = TextEditingController();
|
||||
bool _busy = false;
|
||||
|
||||
/// Reachability of each suggested store's index URL. `null` while the
|
||||
|
|
@ -81,24 +83,31 @@ class _ChainStoresDialogState extends State<ChainStoresDialog> {
|
|||
void dispose() {
|
||||
_name.dispose();
|
||||
_url.dispose();
|
||||
_pubkey.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _add() =>
|
||||
_addStore(_name.text.trim(), _url.text.trim(), fromFields: true);
|
||||
Future<void> _add() => _addStore(
|
||||
_name.text.trim(),
|
||||
_url.text.trim(),
|
||||
pinnedPubkeyPem: _pubkey.text.trim(),
|
||||
fromFields: true,
|
||||
);
|
||||
|
||||
/// Shared add path — used by both the manual name/URL form and the
|
||||
/// one-click suggested-store buttons.
|
||||
Future<void> _addStore(String name, String url,
|
||||
{bool fromFields = false}) async {
|
||||
{String pinnedPubkeyPem = '', bool fromFields = false}) async {
|
||||
if (name.isEmpty || url.isEmpty) return;
|
||||
setState(() => _busy = true);
|
||||
try {
|
||||
final r = await HubService.instance.addStore(name: name, url: url);
|
||||
final r = await HubService.instance
|
||||
.addStore(name: name, url: url, pinnedPubkeyPem: pinnedPubkeyPem);
|
||||
if (!mounted) return;
|
||||
if (fromFields) {
|
||||
_name.clear();
|
||||
_url.clear();
|
||||
_pubkey.clear();
|
||||
}
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text(r.message)));
|
||||
|
|
@ -248,6 +257,22 @@ class _ChainStoresDialogState extends State<ChainStoresDialog> {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
// Optional per-store pinned publisher key (PEM). When set,
|
||||
// signed modules from this store verify against this key
|
||||
// instead of the built-in vendor anchor.
|
||||
TextField(
|
||||
controller: _pubkey,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
style: ChainTheme.mono(size: 11),
|
||||
decoration: InputDecoration(
|
||||
labelText: l.storesManagerPinnedKeyLabel,
|
||||
helperText: l.storesManagerPinnedKeyHint,
|
||||
helperMaxLines: 3,
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue