giveAccessToPotentiallyEncrypted method

  1. @override
Future<PotentiallyEncryptedPatient> giveAccessToPotentiallyEncrypted(
  1. PotentiallyEncryptedPatient patient,
  2. String delegatedTo
)
override

Implementation

@override
Future<PotentiallyEncryptedPatient> giveAccessToPotentiallyEncrypted(PotentiallyEncryptedPatient patient, String delegatedTo) async {
  if (!(patient is Patient) && !(patient is EncryptedPatient))  {
    throw ArgumentError("Unexpected type for patient $patient");
  }
  final patientId = patient.id!;
  final existingDelegations = patient.systemMetaData?.delegations ?? {};
  final existingEncryptionKeys = patient.systemMetaData?.encryptionKeys ?? {};
  final localCrypto = _api.crypto;
  final currentUser = (await _api.baseUserApi.getCurrentUser())
      ?? (throw StateError("There is no user currently logged in. You must call this method from an authenticated MedTechApi"));

  if (currentUser.dataOwnerId() == null) {
    throw StateError("The current user is not a data owner. You must been either a patient, a device or a healthcare professional to call this method");
  }

  // Check if delegatedBy has access
  if (!existingDelegations.entries.any((element) => element.key == currentUser.dataOwnerId())) {
    throw StateError("DataOwner ${currentUser.dataOwnerId()} does not have the right to access patient ${patientId}");
  }

  final myId = currentUser.dataOwnerId()!;
  final newSecretIds = await localCrypto.findAndDecryptPotentiallyUnknownKeysForDelegate(
      myId,
      delegatedTo,
      existingDelegations.map((key, value) => MapEntry(key, value.map((e) => e.toDelegationDto()).toSet()))
  );
  final newEncryptionKeys = await localCrypto.findAndDecryptPotentiallyUnknownKeysForDelegate(
      myId,
      delegatedTo,
      existingEncryptionKeys.map((key, value) => MapEntry(key, value.map((e) => e.toDelegationDto()).toSet()))
  );

  if (newSecretIds.isEmpty && newSecretIds.isEmpty) {
    return patient;
  }

  DataOwnerDto? dataOwner = null;
  final Map<String, String> encryptedKeys = {};
  for (var clearKey in { ...newSecretIds, ...newEncryptionKeys }) {
    final encryptedKeyAndOwner = await localCrypto.encryptAESKeyForHcp(
        myId,
        delegatedTo,
        patientId,
        clearKey.formatAsKey()
    );
    encryptedKeys[clearKey] = encryptedKeyAndOwner.item1;
    dataOwner = encryptedKeyAndOwner.item2 ?? dataOwner;
  }
  final newSecretIdsDelegations = newSecretIds.map((clearKey) =>
      Delegation(owner: myId, delegatedTo: delegatedTo, key: encryptedKeys[clearKey]!)
  ).toList();
  final newEncryptionKeysDelegations = newEncryptionKeys.map((clearKey) =>
      Delegation(owner: myId, delegatedTo: delegatedTo, key: encryptedKeys[clearKey]!)
  ).toList();

  final Map<String, List<Delegation>> updatedDelegations = {
    ...existingDelegations,
    delegatedTo: [...(existingDelegations[delegatedTo] ?? []), ...newSecretIdsDelegations]
  };
  final Map<String, List<Delegation>> updatedEncryptionKeys = {
    ...existingEncryptionKeys,
    delegatedTo: [...(existingEncryptionKeys[delegatedTo] ?? []), ...newEncryptionKeysDelegations]
  };

  if (patient.systemMetaData == null) {
    patient.systemMetaData = SystemMetaDataOwnerEncrypted();
  }
  if (dataOwner != null && dataOwner.dataOwnerId == patientId) {
    patient.rev = dataOwner.rev;
    patient.systemMetaData!.hcPartyKeys = dataOwner.hcPartyKeys;
    patient.systemMetaData!.aesExchangeKeys = dataOwner.aesExchangeKeys;
  }
  patient.systemMetaData!.encryptionKeys = updatedEncryptionKeys;
  patient.systemMetaData!.delegations = updatedDelegations;
  return (await modifyPotentiallyEncryptedPatient(patient)) ?? (throw StateError("Couldn't give access to $delegatedTo to patient ${patient.id}"));
}