mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-07-26 07:55:33 -04:00
reject
This commit is contained in:
parent
c047ae05c5
commit
a5a45e2492
5 changed files with 133 additions and 18 deletions
|
@ -86,7 +86,8 @@
|
||||||
"paste_invite_dialog": {
|
"paste_invite_dialog": {
|
||||||
"paste_invite_here": "Paste your contact invite here:",
|
"paste_invite_here": "Paste your contact invite here:",
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"message_from_contact": "Message from contact"
|
"message_from_contact": "Message from contact",
|
||||||
|
"validating": "Validating..."
|
||||||
},
|
},
|
||||||
"enter_pin_dialog": {
|
"enter_pin_dialog": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "Enter PIN",
|
||||||
|
|
|
@ -23,13 +23,13 @@
|
||||||
|
|
||||||
## Accepting an invitation
|
## Accepting an invitation
|
||||||
1. Create a Local Chat DHT record (no content yet, will be encrypted with DH of contact identity key)
|
1. Create a Local Chat DHT record (no content yet, will be encrypted with DH of contact identity key)
|
||||||
2. Create ContactAccept with chat dht record and account master
|
2. Create ContactResponse with chat dht record and account master
|
||||||
3. Create SignedContactResponse with accept=true signed with identity
|
3. Create SignedContactResponse with accept=true signed with identity
|
||||||
4. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
4. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
||||||
5. Add a local contact with the remote chat dht record, updating from the remote profile in it
|
5. Add a local contact with the remote chat dht record, updating from the remote profile in it
|
||||||
|
|
||||||
## Rejecting an invitation
|
## Rejecting an invitation
|
||||||
1. Create ContactReject with account master
|
1. Create ContactResponse with account master
|
||||||
2. Create SignedContactResponse with accept=false signed with identity
|
2. Create SignedContactResponse with accept=false signed with identity
|
||||||
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
||||||
Timestamp? _expiration;
|
Timestamp? _expiration;
|
||||||
ValidContactInvitation? _validInvitation;
|
ValidContactInvitation? _validInvitation;
|
||||||
bool _validatingPaste = false;
|
bool _validatingPaste = false;
|
||||||
|
bool _isAccepting = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -95,17 +96,51 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<void> _onAccept() async {
|
Future<void> _onAccept() async {
|
||||||
Navigator.of(context).pop();
|
final navigator = Navigator.of(context);
|
||||||
if (_validInvitation != null) {
|
|
||||||
return acceptContactInvitation(_validInvitation);
|
setState(() {
|
||||||
|
_isAccepting = true;
|
||||||
|
});
|
||||||
|
final activeAccountInfo = await ref.read(fetchActiveAccountProvider.future);
|
||||||
|
if (activeAccountInfo == null) {
|
||||||
|
setState(() {
|
||||||
|
_isAccepting = false;
|
||||||
|
});
|
||||||
|
navigator.pop();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
final validInvitation = _validInvitation;
|
||||||
|
if (validInvitation != null) {
|
||||||
|
await acceptContactInvitation(activeAccountInfo, validInvitation);
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isAccepting = false;
|
||||||
|
});
|
||||||
|
navigator.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onReject() async {
|
Future<void> _onReject() async {
|
||||||
Navigator.of(context).pop();
|
final navigator = Navigator.of(context);
|
||||||
if (_validInvitation != null) {
|
|
||||||
return rejectContactInvitation(_validInvitation);
|
setState(() {
|
||||||
|
_isAccepting = true;
|
||||||
|
});
|
||||||
|
final activeAccountInfo = await ref.read(fetchActiveAccountProvider.future);
|
||||||
|
if (activeAccountInfo == null) {
|
||||||
|
setState(() {
|
||||||
|
_isAccepting = false;
|
||||||
|
});
|
||||||
|
navigator.pop();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
final validInvitation = _validInvitation;
|
||||||
|
if (validInvitation != null) {
|
||||||
|
await rejectContactInvitation(activeAccountInfo, validInvitation);
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isAccepting = false;
|
||||||
|
});
|
||||||
|
navigator.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onPasteChanged(String text) async {
|
Future<void> _onPasteChanged(String text) async {
|
||||||
|
@ -178,6 +213,9 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
||||||
final textTheme = theme.textTheme;
|
final textTheme = theme.textTheme;
|
||||||
//final height = MediaQuery.of(context).size.height;
|
//final height = MediaQuery.of(context).size.height;
|
||||||
|
|
||||||
|
if (_isAccepting) {
|
||||||
|
return SizedBox(height: 400, child: waitingPage(context));
|
||||||
|
}
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 400,
|
height: 400,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
@ -207,6 +245,11 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
||||||
//labelText: translate('paste_invite_dialog.paste')
|
//labelText: translate('paste_invite_dialog.paste')
|
||||||
),
|
),
|
||||||
).paddingAll(8)),
|
).paddingAll(8)),
|
||||||
|
if (_validatingPaste)
|
||||||
|
Column(children: [
|
||||||
|
Text(translate('paste_invite_dialog.validating')),
|
||||||
|
buildProgressIndicator(context),
|
||||||
|
]),
|
||||||
if (_validInvitation != null && !_validatingPaste)
|
if (_validInvitation != null && !_validatingPaste)
|
||||||
Column(children: [
|
Column(children: [
|
||||||
ProfileWidget(
|
ProfileWidget(
|
||||||
|
@ -232,7 +275,7 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
).withModalHUD(context, _isAccepting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -321,7 +321,7 @@ message ContactResponse {
|
||||||
bool accept = 1;
|
bool accept = 1;
|
||||||
// Account master record key
|
// Account master record key
|
||||||
TypedKey account_master_record_key = 2;
|
TypedKey account_master_record_key = 2;
|
||||||
// Local chat DHT record key if accepted
|
// Remote chat DHT record key if accepted
|
||||||
TypedKey remote_conversation_key = 3;
|
TypedKey remote_conversation_key = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ import '../entities/proto.dart'
|
||||||
ContactInvitationRecord,
|
ContactInvitationRecord,
|
||||||
ContactRequest,
|
ContactRequest,
|
||||||
ContactRequestPrivate,
|
ContactRequestPrivate,
|
||||||
SignedContactInvitation;
|
SignedContactInvitation,
|
||||||
|
ContactResponse,
|
||||||
|
SignedContactResponse;
|
||||||
|
import '../log/loggy.dart';
|
||||||
import '../tools/tools.dart';
|
import '../tools/tools.dart';
|
||||||
import '../veilid_support/veilid_support.dart';
|
import '../veilid_support/veilid_support.dart';
|
||||||
import 'account.dart';
|
import 'account.dart';
|
||||||
|
@ -163,7 +166,8 @@ class ValidContactInvitation {
|
||||||
required this.contactRequestInboxKey,
|
required this.contactRequestInboxKey,
|
||||||
required this.contactRequest,
|
required this.contactRequest,
|
||||||
required this.contactRequestPrivate,
|
required this.contactRequestPrivate,
|
||||||
required this.contactIdentityMaster});
|
required this.contactIdentityMaster,
|
||||||
|
required this.writer});
|
||||||
|
|
||||||
SignedContactInvitation signedContactInvitation;
|
SignedContactInvitation signedContactInvitation;
|
||||||
ContactInvitation contactInvitation;
|
ContactInvitation contactInvitation;
|
||||||
|
@ -171,6 +175,7 @@ class ValidContactInvitation {
|
||||||
ContactRequest contactRequest;
|
ContactRequest contactRequest;
|
||||||
ContactRequestPrivate contactRequestPrivate;
|
ContactRequestPrivate contactRequestPrivate;
|
||||||
IdentityMaster contactIdentityMaster;
|
IdentityMaster contactIdentityMaster;
|
||||||
|
KeyPair writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef GetEncryptionKeyCallback = Future<SecretKey> Function(
|
typedef GetEncryptionKeyCallback = Future<SecretKey> Function(
|
||||||
|
@ -221,26 +226,92 @@ Future<ValidContactInvitation> validateContactInvitation(Uint8List inviteData,
|
||||||
await cs.verify(contactIdentityMaster.identityPublicKey,
|
await cs.verify(contactIdentityMaster.identityPublicKey,
|
||||||
contactInvitationBytes, signature);
|
contactInvitationBytes, signature);
|
||||||
|
|
||||||
|
final writer = KeyPair(
|
||||||
|
key: proto.CryptoKeyProto.fromProto(contactRequestPrivate.writerKey),
|
||||||
|
secret: writerSecret);
|
||||||
|
|
||||||
out = ValidContactInvitation(
|
out = ValidContactInvitation(
|
||||||
signedContactInvitation: signedContactInvitation,
|
signedContactInvitation: signedContactInvitation,
|
||||||
contactInvitation: contactInvitation,
|
contactInvitation: contactInvitation,
|
||||||
contactRequestInboxKey: contactRequestInboxKey,
|
contactRequestInboxKey: contactRequestInboxKey,
|
||||||
contactRequest: contactRequest,
|
contactRequest: contactRequest,
|
||||||
contactRequestPrivate: contactRequestPrivate,
|
contactRequestPrivate: contactRequestPrivate,
|
||||||
contactIdentityMaster: contactIdentityMaster);
|
contactIdentityMaster: contactIdentityMaster,
|
||||||
|
writer: writer);
|
||||||
});
|
});
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> acceptContactInvitation(
|
Future<void> acceptContactInvitation(ActiveAccountInfo activeAccountInfo,
|
||||||
ValidContactInvitation validContactInvitation) async {
|
ValidContactInvitation validContactInvitation) async {
|
||||||
//
|
final pool = await DHTRecordPool.instance();
|
||||||
|
await (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
|
||||||
|
validContactInvitation.writer))
|
||||||
|
.deleteScope((contactRequestInbox) async {
|
||||||
|
final cs = await pool.veilid
|
||||||
|
.getCryptoSystem(validContactInvitation.contactRequestInboxKey.kind);
|
||||||
|
|
||||||
|
// xxx
|
||||||
|
final contactResponse = ContactResponse()
|
||||||
|
..accept = false
|
||||||
|
..accountMasterRecordKey = activeAccountInfo
|
||||||
|
.localAccount.identityMaster.masterRecordKey
|
||||||
|
.toProto();
|
||||||
|
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||||
|
|
||||||
|
final identitySignature = await cs.sign(
|
||||||
|
activeAccountInfo.localAccount.identityMaster.identityPublicKey,
|
||||||
|
activeAccountInfo.userLogin.identitySecret.value,
|
||||||
|
contactResponseBytes);
|
||||||
|
|
||||||
|
final signedContactResponse = SignedContactResponse()
|
||||||
|
..contactResponse = contactResponseBytes
|
||||||
|
..identitySignature = identitySignature.toProto();
|
||||||
|
|
||||||
|
// Write the rejection to the invox
|
||||||
|
if (await contactRequestInbox.tryWriteProtobuf(
|
||||||
|
SignedContactResponse.fromBuffer, signedContactResponse,
|
||||||
|
subkey: 1) !=
|
||||||
|
null) {
|
||||||
|
log.error('failed to accept contact invitation');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> rejectContactInvitation(
|
Future<void> rejectContactInvitation(ActiveAccountInfo activeAccountInfo,
|
||||||
ValidContactInvitation validContactInvitation) async {
|
ValidContactInvitation validContactInvitation) async {
|
||||||
//
|
final pool = await DHTRecordPool.instance();
|
||||||
|
await (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
|
||||||
|
validContactInvitation.writer))
|
||||||
|
.deleteScope((contactRequestInbox) async {
|
||||||
|
final cs = await pool.veilid
|
||||||
|
.getCryptoSystem(validContactInvitation.contactRequestInboxKey.kind);
|
||||||
|
|
||||||
|
final contactResponse = ContactResponse()
|
||||||
|
..accept = false
|
||||||
|
..accountMasterRecordKey = activeAccountInfo
|
||||||
|
.localAccount.identityMaster.masterRecordKey
|
||||||
|
.toProto();
|
||||||
|
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||||
|
|
||||||
|
final identitySignature = await cs.sign(
|
||||||
|
activeAccountInfo.localAccount.identityMaster.identityPublicKey,
|
||||||
|
activeAccountInfo.userLogin.identitySecret.value,
|
||||||
|
contactResponseBytes);
|
||||||
|
|
||||||
|
final signedContactResponse = SignedContactResponse()
|
||||||
|
..contactResponse = contactResponseBytes
|
||||||
|
..identitySignature = identitySignature.toProto();
|
||||||
|
|
||||||
|
// Write the rejection to the invox
|
||||||
|
if (await contactRequestInbox.tryWriteProtobuf(
|
||||||
|
SignedContactResponse.fromBuffer, signedContactResponse,
|
||||||
|
subkey: 1) !=
|
||||||
|
null) {
|
||||||
|
log.error('failed to reject contact invitation');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the active account contact invitation list
|
/// Get the active account contact invitation list
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue