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
|
|
@ -220,8 +220,14 @@ class HubService {
|
||||||
required String name,
|
required String name,
|
||||||
required String url,
|
required String url,
|
||||||
String bearerEnv = '',
|
String bearerEnv = '',
|
||||||
|
String pinnedPubkeyPem = '',
|
||||||
}) =>
|
}) =>
|
||||||
_client.addStore(name: name, url: url, bearerEnv: bearerEnv);
|
_client.addStore(
|
||||||
|
name: name,
|
||||||
|
url: url,
|
||||||
|
bearerEnv: bearerEnv,
|
||||||
|
pinnedPubkeyPem: pinnedPubkeyPem,
|
||||||
|
);
|
||||||
|
|
||||||
/// Drop a configured store by name (the bundled seed cannot be removed).
|
/// Drop a configured store by name (the bundled seed cannot be removed).
|
||||||
Future<RemoveStoreResponse> removeStore(String name) =>
|
Future<RemoveStoreResponse> removeStore(String name) =>
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,8 @@
|
||||||
"storesManagerRemoveFailed": "Store konnte nicht entfernt werden",
|
"storesManagerRemoveFailed": "Store konnte nicht entfernt werden",
|
||||||
"storesManagerSuggested": "Vorgeschlagene Stores",
|
"storesManagerSuggested": "Vorgeschlagene Stores",
|
||||||
"storesManagerAddShort": "Hinzufügen",
|
"storesManagerAddShort": "Hinzufügen",
|
||||||
|
"storesManagerPinnedKeyLabel": "Angepinnter Public-Key — PEM (optional)",
|
||||||
|
"storesManagerPinnedKeyHint": "Signierte Module dieses Stores werden gegen diesen Key geprüft statt gegen den eingebauten Vendor-Key. Füge einen PEM-Public-Key ein.",
|
||||||
"storesManagerNotAvailable": "Noch nicht verfügbar",
|
"storesManagerNotAvailable": "Noch nicht verfügbar",
|
||||||
"storesManagerNotAvailableHint": "Der Anbieter hat diesen Store-Index noch nicht veröffentlicht. Sobald er das tut, ist er installierbar.",
|
"storesManagerNotAvailableHint": "Der Anbieter hat diesen Store-Index noch nicht veröffentlicht. Sobald er das tut, ist er installierbar.",
|
||||||
"storesSuggestedReclaimDesc": "Recl∆Im — Module für Rechts- und Pflichten-Reform-Mapping",
|
"storesSuggestedReclaimDesc": "Recl∆Im — Module für Rechts- und Pflichten-Reform-Mapping",
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,8 @@
|
||||||
"storesManagerRemoveFailed": "Could not remove store",
|
"storesManagerRemoveFailed": "Could not remove store",
|
||||||
"storesManagerSuggested": "Suggested stores",
|
"storesManagerSuggested": "Suggested stores",
|
||||||
"storesManagerAddShort": "Add",
|
"storesManagerAddShort": "Add",
|
||||||
|
"storesManagerPinnedKeyLabel": "Pinned public key — PEM (optional)",
|
||||||
|
"storesManagerPinnedKeyHint": "Signed modules from this store verify against this key instead of the built-in vendor key. Paste a PEM public key.",
|
||||||
"storesManagerNotAvailable": "Not available yet",
|
"storesManagerNotAvailable": "Not available yet",
|
||||||
"storesManagerNotAvailableHint": "The provider hasn't published this store index yet. It will become installable once they do.",
|
"storesManagerNotAvailableHint": "The provider hasn't published this store index yet. It will become installable once they do.",
|
||||||
"storesSuggestedReclaimDesc": "Recl∆Im — modules for law & duty reform-mapping",
|
"storesSuggestedReclaimDesc": "Recl∆Im — modules for law & duty reform-mapping",
|
||||||
|
|
|
||||||
|
|
@ -1514,6 +1514,18 @@ abstract class AppLocalizations {
|
||||||
/// **'Add'**
|
/// **'Add'**
|
||||||
String get storesManagerAddShort;
|
String get storesManagerAddShort;
|
||||||
|
|
||||||
|
/// No description provided for @storesManagerPinnedKeyLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Pinned public key — PEM (optional)'**
|
||||||
|
String get storesManagerPinnedKeyLabel;
|
||||||
|
|
||||||
|
/// No description provided for @storesManagerPinnedKeyHint.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Signed modules from this store verify against this key instead of the built-in vendor key. Paste a PEM public key.'**
|
||||||
|
String get storesManagerPinnedKeyHint;
|
||||||
|
|
||||||
/// No description provided for @storesManagerNotAvailable.
|
/// No description provided for @storesManagerNotAvailable.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
|
||||||
|
|
@ -815,6 +815,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get storesManagerAddShort => 'Hinzufügen';
|
String get storesManagerAddShort => 'Hinzufügen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get storesManagerPinnedKeyLabel =>
|
||||||
|
'Angepinnter Public-Key — PEM (optional)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get storesManagerPinnedKeyHint =>
|
||||||
|
'Signierte Module dieses Stores werden gegen diesen Key geprüft statt gegen den eingebauten Vendor-Key. Füge einen PEM-Public-Key ein.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storesManagerNotAvailable => 'Noch nicht verfügbar';
|
String get storesManagerNotAvailable => 'Noch nicht verfügbar';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -833,6 +833,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get storesManagerAddShort => 'Add';
|
String get storesManagerAddShort => 'Add';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get storesManagerPinnedKeyLabel =>
|
||||||
|
'Pinned public key — PEM (optional)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get storesManagerPinnedKeyHint =>
|
||||||
|
'Signed modules from this store verify against this key instead of the built-in vendor key. Paste a PEM public key.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storesManagerNotAvailable => 'Not available yet';
|
String get storesManagerNotAvailable => 'Not available yet';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
|
||||||
import '../data/error_presentation.dart';
|
import '../data/error_presentation.dart';
|
||||||
import '../data/hub.dart';
|
import '../data/hub.dart';
|
||||||
import '../l10n/app_localizations.dart';
|
import '../l10n/app_localizations.dart';
|
||||||
|
import '../theme/theme.dart';
|
||||||
import '../theme/tokens.dart';
|
import '../theme/tokens.dart';
|
||||||
|
|
||||||
class ChainStoresDialog extends StatefulWidget {
|
class ChainStoresDialog extends StatefulWidget {
|
||||||
|
|
@ -30,6 +31,7 @@ class _ChainStoresDialogState extends State<ChainStoresDialog> {
|
||||||
late Future<List<StoreSource>> _future;
|
late Future<List<StoreSource>> _future;
|
||||||
final _name = TextEditingController();
|
final _name = TextEditingController();
|
||||||
final _url = TextEditingController();
|
final _url = TextEditingController();
|
||||||
|
final _pubkey = TextEditingController();
|
||||||
bool _busy = false;
|
bool _busy = false;
|
||||||
|
|
||||||
/// Reachability of each suggested store's index URL. `null` while the
|
/// Reachability of each suggested store's index URL. `null` while the
|
||||||
|
|
@ -81,24 +83,31 @@ class _ChainStoresDialogState extends State<ChainStoresDialog> {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_name.dispose();
|
_name.dispose();
|
||||||
_url.dispose();
|
_url.dispose();
|
||||||
|
_pubkey.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _add() =>
|
Future<void> _add() => _addStore(
|
||||||
_addStore(_name.text.trim(), _url.text.trim(), fromFields: true);
|
_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
|
/// Shared add path — used by both the manual name/URL form and the
|
||||||
/// one-click suggested-store buttons.
|
/// one-click suggested-store buttons.
|
||||||
Future<void> _addStore(String name, String url,
|
Future<void> _addStore(String name, String url,
|
||||||
{bool fromFields = false}) async {
|
{String pinnedPubkeyPem = '', bool fromFields = false}) async {
|
||||||
if (name.isEmpty || url.isEmpty) return;
|
if (name.isEmpty || url.isEmpty) return;
|
||||||
setState(() => _busy = true);
|
setState(() => _busy = true);
|
||||||
try {
|
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 (!mounted) return;
|
||||||
if (fromFields) {
|
if (fromFields) {
|
||||||
_name.clear();
|
_name.clear();
|
||||||
_url.clear();
|
_url.clear();
|
||||||
|
_pubkey.clear();
|
||||||
}
|
}
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(SnackBar(content: Text(r.message)));
|
.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