Skip to content

Commit

Permalink
fix: sign ProblemReport when TO is unspecified (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
FabioPinheiro authored Sep 4, 2023
1 parent 7d35edc commit 586dd9f
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 49 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ inThisBuild(

/** Versions */
lazy val V = new {
val scalaDID = "0.1.0-M9"
val scalaDID = "0.1.0-M10"
// val scalajsJavaSecureRandom = "1.0.0"

// FIXME another bug in the test framework https://github.com/scalameta/munit/issues/554
Expand Down
2 changes: 1 addition & 1 deletion mediator/src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mediator = {
endpoint = ${?SERVICE_ENDPOINT}
}
server.http.port = 8080
# server.http.port = ${?PORT}
server.http.port = ${?PORT}
database = {
protocol = mongodb
protocol = ${?MONGODB_PROTOCOL}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,37 @@ object ActionUtils {
): ZIO[
Operations & Agent & Resolver & MessageDispatcher & OutboxMessageRepo,
MediatorError,
Option[EncryptedMessage]
Option[SignedMessage | EncryptedMessage]
] =
action match {
case _: NoReply.type => ZIO.succeed(None)
case action: AnyReply =>
val reply = action.msg
for {
msg <- {
reply.from match
case Some(value) => authEncrypt(reply)
case None => anonEncrypt(reply)
}.mapError(fail => MediatorDidError(fail))

outboxRepo <- ZIO.service[OutboxMessageRepo]
// TODO forward message
maybeSyncReplyMsg <- reply.to.map(_.toSeq) match // TODO improve
case None => ZIO.logWarning("Have a reply but the field 'to' is missing") *> ZIO.none
case Some(Seq()) => ZIO.logWarning("Have a reply but the field 'to' is empty") *> ZIO.none
maybeSyncReplyMsg: Option[SignedMessage | EncryptedMessage] <- reply.to.map(_.toSeq) match // TODO improve
case None =>
ZIO.logWarning("Have a reply but the field 'to' is missing") *>
sign(reply)
.mapError(fail => MediatorDidError(fail))
.map(Some(_))
case Some(Seq()) =>
ZIO.logWarning("Have a reply but the field 'to' is empty") *>
sign(reply)
.mapError(fail => MediatorDidError(fail))
.map(Some(_))
case Some(send2DIDs) =>
ZIO
.foreach(send2DIDs)(to =>
val job: ZIO[MessageDispatcher & (Resolver & Any), MediatorError, Matchable] = for {
for {
msg <- {
reply.from match
case Some(value) => authEncrypt(reply)
case None => anonEncrypt(reply)
}.mapError(fail => MediatorDidError(fail))

replyViaDIDCommMessagingProgramme = ZIO.foreach(send2DIDs) { to =>
for {
messageDispatcher <- ZIO.service[MessageDispatcher]
resolver <- ZIO.service[Resolver]

Expand Down Expand Up @@ -94,19 +104,23 @@ object ActionUtils {
.catchAll { case error => ZIO.logError(s"Store Outbox Error: $error") }
} yield ()
}

} yield (jobToRun)
action match
case Reply(_) =>
job
.when( // this is +- the opposite condition as below
originalMessage
.map { oMsg => oMsg.return_route.isEmpty || oMsg.return_route.contains(ReturnRoute.none) }
.getOrElse(true) // If originalMessage is None
)
case SyncReplyOnly(_) => ZIO.unit
case AsyncReplyOnly(_) => job
) *> ZIO
} yield ()
}
returnTmp <- action match
case Reply(_) =>
if (
originalMessage // this condition is +- the opposite condition as below
.map { oMsg => oMsg.return_route.isEmpty || oMsg.return_route.contains(ReturnRoute.none) }
.getOrElse(true) // If originalMessage is None
) (replyViaDIDCommMessagingProgramme *> ZIO.none)
else ZIO.some(msg)
case SyncReplyOnly(_) => ZIO.some(msg)
case AsyncReplyOnly(_) => replyViaDIDCommMessagingProgramme *> ZIO.none
} yield (returnTmp)
_ <- maybeSyncReplyMsg match {
case None => ZIO.unit
case Some(msg) =>
ZIO // Store send message INLINE_REPLY
.succeed(msg)
.tap(msg =>
outboxRepo
Expand All @@ -127,11 +141,18 @@ object ActionUtils {
.map { oMsg =>
{ // Should replies use the same transport channel?
oMsg.return_route.contains(ReturnRoute.all) || oMsg.return_route.contains(ReturnRoute.thread)
} &&
oMsg.from.map(_.asTO).exists(send2DIDs.contains) // Is the reply back to the original sender?
} && {
msg match
case sMsg: SignedMessage => true // TODO If the Message is only sign shoud we reply back?
case eMsg: EncryptedMessage => // Is the reply back to the original sender/caller?
val recipients = eMsg.recipientsSubject.toSeq.map(subject => TO(subject.did))
oMsg.from.map(_.asTO).exists(recipients.contains)
}
}
.getOrElse(false) // If originalMessage is None
)
}

} yield maybeSyncReplyMsg
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ trait ProtocolExecuter[-R, +E] { // <: MediatorError | StorageError] {
/** @return can return a Sync Reply Msg */
def execute[R1 <: R](
plaintextMessage: PlaintextMessage
): ZIO[R1, E, Option[EncryptedMessage]] =
): ZIO[R1, E, Option[SignedMessage | EncryptedMessage]] =
program(plaintextMessage) *> ZIO.none

def program[R1 <: R](plaintextMessage: PlaintextMessage): ZIO[R1, E, Action]
Expand All @@ -43,7 +43,7 @@ case class ProtocolExecuterCollection[-R <: Agent, +E](

override def execute[R1 <: R](
plaintextMessage: PlaintextMessage,
): ZIO[R1, E, Option[EncryptedMessage]] =
): ZIO[R1, E, Option[SignedMessage | EncryptedMessage]] =
selectExecutersFor(plaintextMessage.`type`) match
// case None => NullProtocolExecuter.execute(plaintextMessage)
case None => MissingProtocolExecuter.execute(plaintextMessage)
Expand All @@ -66,7 +66,7 @@ trait ProtocolExecuterWithServices[
override def execute[R1 <: R](
plaintextMessage: PlaintextMessage,
// context: Context
): ZIO[R1, E, Option[EncryptedMessage]] =
): ZIO[R1, E, Option[SignedMessage | EncryptedMessage]] =
program(plaintextMessage)
.tap(v => ZIO.logDebug(v.toString)) // DEBUG
.flatMap(action => ActionUtils.packResponse(Some(plaintextMessage), action))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ case class MediatorAgent(
): ZIO[
Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & UserAccountRepo & OutboxMessageRepo,
MediatorError | StorageError,
Option[EncryptedMessage]
Option[SignedMessage | EncryptedMessage]
] =
for {
msg <- data.fromJson[EncryptedMessage] match
Expand All @@ -114,7 +114,7 @@ case class MediatorAgent(
): ZIO[
Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & UserAccountRepo & OutboxMessageRepo,
MediatorError | StorageError,
Option[EncryptedMessage]
Option[SignedMessage | EncryptedMessage]
] =
ZIO
.logAnnotate("msgHash", msg.sha1) {
Expand Down Expand Up @@ -338,8 +338,9 @@ object MediatorAgent {
ret <- agent
.receiveMessage(data, None)
.map {
case None => Response.ok
case Some(value) => Response.json(value.toJson)
case None => Response.ok
case Some(value: SignedMessage) => Response.json(value.toJson)
case Some(value: EncryptedMessage) => Response.json(value.toJson)
}
.catchAll {
case MediatorDidError(error) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,82 @@ given BSONDocumentReader[EncryptedMessage] with {
aux.readDocument(doc)
}

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

given BSONWriter[Payload] with {
import Payload.*
def writeTry(obj: Payload): Try[BSONValue] = Try(BSONString(obj.base64url))
}
given BSONReader[Payload] with {
def readTry(bson: BSONValue): Try[Payload] = bson.asTry[String].map(v => Payload.fromBase64url(v))
}

given BSONWriter[SigningAlgorithm] with {
def writeTry(obj: SigningAlgorithm): Try[BSONValue] = Try(BSONString(obj.toString()))
}
given BSONReader[SigningAlgorithm] with {
def readTry(bson: BSONValue): Try[SigningAlgorithm] = bson.asTry[String].map(v => SigningAlgorithm.valueOf(v))
}

given BSONDocumentWriter[SignProtectedHeader] = Macros.writer[SignProtectedHeader]
given BSONDocumentReader[SignProtectedHeader] = Macros.reader[SignProtectedHeader]

given given_BSONWriter_Base64Obj_SignProtectedHeader: BSONWriter[Base64Obj[SignProtectedHeader]] with {
import Base64Obj.*
def writeTry(obj: Base64Obj[SignProtectedHeader]): Try[BSONValue] = {
val protectedHeader: String = (obj.obj, obj.original) match {
case (_, Some(op)) => op.urlBase64
case (p, None) => obj.base64url
}
Try(BSONString(protectedHeader))
}
}
given given_BSONReader_Base64Obj_SignProtectedHeader: BSONReader[Base64Obj[SignProtectedHeader]] with {
def readTry(bson: BSONValue): Try[Base64Obj[SignProtectedHeader]] =
bson
.asTry[String]
.flatMap { v =>
s""""$v"""".fromJson[Base64Obj[SignProtectedHeader]] match // TODO with a new methods from ScalaDid
case Left(value) => Failure(RuntimeException(value))
case Right(value) => Try(value)
}
}

given BSONWriter[SignatureJWM] with {
import SignatureJWM.*
def writeTry(obj: SignatureJWM): Try[BSONValue] = Try(BSONString(obj.value))
}
given BSONReader[SignatureJWM] with {
def readTry(bson: BSONValue): Try[SignatureJWM] = bson.asTry[String].map(v => SignatureJWM(v))
}

given BSONDocumentWriter[JWMHeader] = Macros.writer[JWMHeader]
given BSONDocumentReader[JWMHeader] = Macros.reader[JWMHeader]

given BSONDocumentWriter[JWMSignatureObj] = Macros.writer[JWMSignatureObj]
given BSONDocumentReader[JWMSignatureObj] = Macros.reader[JWMSignatureObj]

given BSONDocumentWriter[SignedMessage] = Macros.writer[SignedMessage]
given BSONDocumentReader[SignedMessage] = Macros.reader[SignedMessage]

given BSONDocumentWriter[SignedMessage | EncryptedMessage] with {
override def writeTry(obj: SignedMessage | EncryptedMessage): Try[BSONDocument] =
obj match {
case msg: EncryptedMessage => given_BSONDocumentWriter_EncryptedMessage.writeTry(msg)
case msg: SignedMessage => given_BSONDocumentWriter_SignedMessage.writeTry(msg)
}
}
given BSONDocumentReader[SignedMessage | EncryptedMessage] with {
override def readDocument(doc: BSONDocument): Try[SignedMessage | EncryptedMessage] =
given_BSONDocumentReader_EncryptedMessage
.readDocument(doc)
.orElse(
given_BSONDocumentReader_SignedMessage
.readDocument(doc)
)
}

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

given BSONWriter[MsgID] with {
import MsgID.*
Expand Down
38 changes: 26 additions & 12 deletions mediator/src/main/scala/io/iohk/atala/mediator/db/DataModels.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fmgp.did.comm.*
import reactivemongo.api.bson.*
import java.time.Instant
import scala.util.Try
import zio.json._

type HASH = String
// messages
Expand Down Expand Up @@ -43,32 +44,45 @@ object DidAccount {
// messages outbox
case class SentMessageItem(
_id: BSONObjectID = BSONObjectID.generate(),
encrypt: EncryptedMessage,
encrypt: SignedMessage | EncryptedMessage,
hash: HASH,
headers: ProtectedHeader,
headers: ast.Json, // ProtectedHeader | SignProtectedHeader,
plaintext: PlaintextMessage,
transport: Seq[SentMessageItem.TransportInfo],
)

object SentMessageItem {

def apply(
msg: EncryptedMessage,
msg: SignedMessage | EncryptedMessage,
plaintext: PlaintextMessage,
recipient: Set[TO],
distination: Option[String],
sendMethod: MessageSendMethod,
result: Option[String]
): SentMessageItem = {
new SentMessageItem(
encrypt = msg,
hash = msg.sha1,
headers = msg.`protected`.obj,
plaintext = plaintext,
transport = Seq(
TransportInfo(recipient = recipient, distination = distination, sendMethod = sendMethod, result = result)
)
)
msg match
case sMsg: SignedMessage =>
new SentMessageItem(
encrypt = msg,
hash = sMsg.sha1, // FIXME
headers = sMsg.signatures.headOption.flatMap(_.`protected`.obj.toJsonAST.toOption).getOrElse(ast.Json.Null),
plaintext = plaintext,
transport = Seq(
TransportInfo(recipient = recipient, distination = distination, sendMethod = sendMethod, result = result)
)
)
case eMsg: EncryptedMessage =>
new SentMessageItem(
encrypt = msg,
hash = eMsg.sha1,
headers = eMsg.`protected`.obj.toJsonAST.getOrElse(ast.Json.Null),
plaintext = plaintext,
transport = Seq(
TransportInfo(recipient = recipient, distination = distination, sendMethod = sendMethod, result = result)
)
)

}

given BSONDocumentWriter[SentMessageItem] = {
Expand Down

0 comments on commit 586dd9f

Please sign in to comment.