You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2025/01/03 04:09:14 UTC
(james-project) 01/02: [ENHANCEMENT] ip and x-forwarded-for im JMAP logging context
This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch x-forwarded-for
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 2a56444c68fe5a6608490c0f8c9748801f3dff65
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Thu Dec 5 08:36:16 2024 +0100
[ENHANCEMENT] ip and x-forwarded-for im JMAP logging context
---
.../java/org/apache/james/util/ReactorUtils.java | 2 +-
.../jmap/method/DelegateSetCreatePerformer.scala | 19 +++---
.../jmap/method/DelegateSetDeletePerformer.scala | 18 +++---
.../method/DelegatedAccountDeletePerformer.scala | 17 +++---
.../jmap/method/EmailSetDeletePerformer.scala | 11 ++--
.../jmap/method/EmailSubmissionSetMethod.scala | 70 ++++++++++++----------
.../jmap/method/MailboxSetUpdatePerformer.scala | 23 +++----
.../apache/james/jmap/routes/JMAPApiRoutes.scala | 4 ++
8 files changed, 95 insertions(+), 69 deletions(-)
diff --git a/server/container/util/src/main/java/org/apache/james/util/ReactorUtils.java b/server/container/util/src/main/java/org/apache/james/util/ReactorUtils.java
index 8c26be6382..beb2c0dc5e 100644
--- a/server/container/util/src/main/java/org/apache/james/util/ReactorUtils.java
+++ b/server/container/util/src/main/java/org/apache/james/util/ReactorUtils.java
@@ -194,7 +194,7 @@ public class ReactorUtils {
return signal -> logWithContext(logStatement, signal.getContextView());
}
- private static void logWithContext(Runnable logStatement, ContextView contextView) {
+ public static void logWithContext(Runnable logStatement, ContextView contextView) {
try (Closeable mdc = retrieveMDCBuilder(contextView).build()) {
logStatement.run();
} catch (IOException e) {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetCreatePerformer.scala
index dd72143d0c..da3ff86305 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetCreatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetCreatePerformer.scala
@@ -30,7 +30,7 @@ import org.apache.james.jmap.method.DelegateSetCreatePerformer.{CreationFailure,
import org.apache.james.mailbox.MailboxSession
import org.apache.james.mailbox.exception.UserDoesNotExistException
import org.apache.james.user.api.{DelegationStore, UsersRepository}
-import org.apache.james.util.AuditTrail
+import org.apache.james.util.{AuditTrail, ReactorUtils}
import org.slf4j.LoggerFactory
import play.api.libs.json.JsObject
import reactor.core.scala.publisher.{SFlux, SMono}
@@ -98,13 +98,16 @@ class DelegateSetCreatePerformer @Inject()(delegationStore: DelegationStore,
SMono.fromPublisher(usersRepository.containsReactive(request.username))
.filter(bool => bool)
.flatMap(_ => SMono.fromPublisher(delegationStore.addAuthorizedUser(mailboxSession.getUser, request.username))
- .doOnSuccess(_ => AuditTrail.entry
- .username(() => mailboxSession.getUser.asString())
- .protocol("JMAP")
- .action("DelegateSet/create")
- .parameters(() => ImmutableMap.of("delegator", mailboxSession.getUser.asString(),
- "delegatee", request.username.asString()))
- .log("Delegation added."))
+ .subscriberContext(context => {
+ ReactorUtils.logWithContext(() => AuditTrail.entry
+ .username(() => mailboxSession.getUser.asString())
+ .protocol("JMAP")
+ .action("DelegateSet/create")
+ .parameters(() => ImmutableMap.of("delegator", mailboxSession.getUser.asString(),
+ "delegatee", request.username.asString()))
+ .log("Delegation added."), context)
+ context
+ })
.`then`(SMono.just[CreationResult](CreationSuccess(delegateCreationId, evaluateCreationResponse(request, mailboxSession))))
.onErrorResume(e => SMono.just[CreationResult](CreationFailure(delegateCreationId, e))))
.switchIfEmpty(SMono.just[CreationResult](CreationFailure(delegateCreationId, new UserDoesNotExistException(request.username))))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetDeletePerformer.scala
index 2e7f88e4dc..cf8487366c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetDeletePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegateSetDeletePerformer.scala
@@ -80,12 +80,16 @@ class DelegateSetDeletePerformer @Inject()(delegationStore: DelegationStore) {
.filter(authorizedUser => DelegationId.from(baseUser, authorizedUser).equals(id))
.next()
.flatMap(authorizedUser => SMono(delegationStore.removeAuthorizedUser(baseUser, authorizedUser))
- .doOnSuccess(_ => AuditTrail.entry
- .username(() => baseUser.asString())
- .protocol("JMAP")
- .action("DelegateSet/destroy")
- .parameters(() => ImmutableMap.of("delegator", baseUser.asString(),
- "delegatee", authorizedUser.asString()))
- .log("Delegation removed.")))
+
+ .subscriberContext(context => {
+ ReactorUtils.logWithContext(() => AuditTrail.entry
+ .username(() => baseUser.asString())
+ .protocol("JMAP")
+ .action("DelegateSet/destroy")
+ .parameters(() => ImmutableMap.of("delegator", baseUser.asString(),
+ "delegatee", authorizedUser.asString()))
+ .log("Delegation removed."), context)
+ context
+ }))
.`then`(SMono.just[DelegateDeletionResult](DelegateDeletionSuccess(id))))
}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountDeletePerformer.scala
index 677b46c492..149fceaabf 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountDeletePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/DelegatedAccountDeletePerformer.scala
@@ -80,12 +80,15 @@ class DelegatedAccountDeletePerformer @Inject()(delegationStore: DelegationStore
.filter(delegatedUser => DelegationId.from(delegatedUser, baseUser).equals(id))
.next()
.flatMap(delegatedUser => SMono(delegationStore.removeDelegatedUser(baseUser, delegatedUser))
- .doOnSuccess(_ => AuditTrail.entry
- .username(() => baseUser.asString())
- .protocol("JMAP")
- .action("DelegatedAccountSet/destroy")
- .parameters(() => ImmutableMap.of("delegator", delegatedUser.asString(),
- "delegatee", baseUser.asString()))
- .log("Delegation removed.")))
+ .subscriberContext(context => {
+ ReactorUtils.logWithContext(() => AuditTrail.entry
+ .username(() => baseUser.asString())
+ .protocol("JMAP")
+ .action("DelegatedAccountSet/destroy")
+ .parameters(() => ImmutableMap.of("delegator", delegatedUser.asString(),
+ "delegatee", baseUser.asString()))
+ .log("Delegation removed."), context)
+ context
+ }))
.`then`(SMono.just[DelegatedAccountDeletionResult](DelegatedAccountDeletionSuccess(id))))
}
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
index b75764f300..72e8d56d18 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
@@ -97,7 +97,10 @@ class EmailSetDeletePerformer @Inject()(messageIdManager: MessageIdManager,
}
SMono(messageIdManager.delete(messageIds.toSet.asJava, mailboxSession))
- .doOnSuccess(auditTrail(_, mailboxSession))
+ .subscriberContext(context => {
+ auditTrail(messageIds, mailboxSession)
+ context
+ })
.map(DestroyResult.from)
.onErrorResume(e => SMono.just(messageIds.map(id => DestroyFailure(EmailSet.asUnparsed(id), e))))
.map(_ ++ parsingErrors)
@@ -107,13 +110,13 @@ class EmailSetDeletePerformer @Inject()(messageIdManager: MessageIdManager,
}
}
- private def auditTrail(deleteResult: DeleteResult, mailboxSession: MailboxSession): Unit =
- if (!deleteResult.getDestroyed.isEmpty) {
+ private def auditTrail(deleteResult: Seq[MessageId], mailboxSession: MailboxSession): Unit =
+ if (deleteResult.nonEmpty) {
AuditTrail.entry
.username(() => mailboxSession.getUser.asString())
.protocol("JMAP")
.action("Email/set destroy")
- .parameters(() => ImmutableMap.of("messageIds", StringUtils.join(deleteResult.getDestroyed),
+ .parameters(() => ImmutableMap.of("messageIds", StringUtils.join(deleteResult),
"loggedInUser", mailboxSession.getLoggedInUser.toScala
.map(_.asString())
.getOrElse("")))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
index 95fa095a44..d5c5939e58 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
@@ -51,7 +51,7 @@ import org.apache.james.queue.api.MailQueueFactory.SPOOL
import org.apache.james.queue.api.{MailQueue, MailQueueFactory}
import org.apache.james.rrt.api.CanSendFrom
import org.apache.james.server.core.{MailImpl, MimeMessageSource, MimeMessageWrapper}
-import org.apache.james.util.AuditTrail
+import org.apache.james.util.{AuditTrail, ReactorUtils}
import org.apache.mailet.{Attribute, AttributeName, AttributeValue, Mail}
import org.slf4j.{Logger, LoggerFactory}
import play.api.libs.json._
@@ -298,38 +298,44 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
private def enqueue(mail: Mail, delay: Duration, mailboxSession: MailboxSession): SMono[Unit] =
(delay match {
case d if d.isNegative || d.isZero => SMono(queue.enqueueReactive(mail))
- .doOnSuccess(_ => AuditTrail.entry
- .username(() => mailboxSession.getUser.asString())
- .protocol("JMAP")
- .action("EmailSubmission")
- .parameters(() => ImmutableMap.of("mailId", mail.getName,
- "mimeMessageId", Option(mail.getMessage)
- .flatMap(message => Option(message.getMessageID))
- .getOrElse(""),
- "sender", mail.getMaybeSender.asString,
- "recipients", StringUtils.join(mail.getRecipients),
- "size", mail.getMessageSize.toString,
- "loggedInUser", mailboxSession.getLoggedInUser.toScala
- .map(_.asString())
- .getOrElse("")))
- .log("JMAP mail spooled."))
+ .subscriberContext(context => {
+ ReactorUtils.logWithContext(() => AuditTrail.entry
+ .username(() => mailboxSession.getUser.asString())
+ .protocol("JMAP")
+ .action("EmailSubmission")
+ .parameters(() => ImmutableMap.of("mailId", mail.getName,
+ "mimeMessageId", Option(mail.getMessage)
+ .flatMap(message => Option(message.getMessageID))
+ .getOrElse(""),
+ "sender", mail.getMaybeSender.asString,
+ "recipients", StringUtils.join(mail.getRecipients),
+ "size", mail.getMessageSize.toString,
+ "loggedInUser", mailboxSession.getLoggedInUser.toScala
+ .map(_.asString())
+ .getOrElse("")))
+ .log("JMAP mail spooled."), context)
+ context
+ })
case _ => SMono(queue.enqueueReactive(mail, delay))
- .doOnSuccess(_ => AuditTrail.entry
- .username(() => mailboxSession.getUser.asString())
- .protocol("JMAP")
- .action("EmailSubmission")
- .parameters(() => ImmutableMap.of("mailId", mail.getName,
- "mimeMessageId", Option(mail.getMessage)
- .flatMap(message => Option(message.getMessageID))
- .getOrElse(""),
- "size", mail.getMessageSize.toString,
- "sender", mail.getMaybeSender.asString,
- "recipients", StringUtils.join(mail.getRecipients),
- "holdFor", delay.toString,
- "loggedInUser", mailboxSession.getLoggedInUser.toScala
- .map(_.asString())
- .getOrElse("")))
- .log("JMAP mail spooled."))
+ .subscriberContext(context => {
+ ReactorUtils.logWithContext(() => AuditTrail.entry
+ .username(() => mailboxSession.getUser.asString())
+ .protocol("JMAP")
+ .action("EmailSubmission")
+ .parameters(() => ImmutableMap.of("mailId", mail.getName,
+ "mimeMessageId", Option(mail.getMessage)
+ .flatMap(message => Option(message.getMessageID))
+ .getOrElse(""),
+ "size", mail.getMessageSize.toString,
+ "sender", mail.getMaybeSender.asString,
+ "recipients", StringUtils.join(mail.getRecipients),
+ "holdFor", delay.toString,
+ "loggedInUser", mailboxSession.getLoggedInUser.toScala
+ .map(_.asString())
+ .getOrElse("")))
+ .log("JMAP mail spooled."), context)
+ context
+ })
}).`then`(SMono.fromCallable(() => LifecycleUtil.dispose(mail)).subscribeOn(Schedulers.boundedElastic()))
private def retrieveDelay(mailParameters: Option[Map[ParameterName, Option[ParameterValue]]]): Try[Duration] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
index 7824aff7d2..3a4c8b0697 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
@@ -251,16 +251,19 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer,
val partialUpdatesOperation: SMono[Unit] = SFlux.fromIterable(validatedPatch.rightsPartialUpdates)
.flatMap(partialUpdate => SMono.fromCallable(() => mailboxManager.applyRightsCommand(mailboxId, partialUpdate.asACLCommand(), mailboxSession))
- .doOnSuccess(_ => AuditTrail.entry
- .username(() => mailboxSession.getUser.asString())
- .protocol("JMAP")
- .action("Mailbox/set update")
- .parameters(() => ImmutableMap.of("loggedInUser", mailboxSession.getLoggedInUser.toScala.map(_.asString()).getOrElse(""),
- "delegator", mailboxSession.getUser.asString(),
- "delegatee", partialUpdate.entryKey.getName,
- "mailboxId", mailboxId.serialize(),
- "rights", partialUpdate.rights.asJava.serialize()))
- .log("JMAP mailbox shared.")),
+ .subscriberContext(context => {
+ ReactorUtils.logWithContext(() => AuditTrail.entry
+ .username(() => mailboxSession.getUser.asString())
+ .protocol("JMAP")
+ .action("Mailbox/set update")
+ .parameters(() => ImmutableMap.of("loggedInUser", mailboxSession.getLoggedInUser.toScala.map(_.asString()).getOrElse(""),
+ "delegator", mailboxSession.getUser.asString(),
+ "delegatee", partialUpdate.entryKey.getName,
+ "mailboxId", mailboxId.serialize(),
+ "rights", partialUpdate.rights.asJava.serialize()))
+ .log("JMAP mailbox shared."), context)
+ context
+ }),
maxConcurrency = 5)
.`then`()
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
index 5873ab3fb0..377c1e0cdc 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
@@ -37,6 +37,7 @@ import org.apache.james.jmap.http.{Authenticator, UserProvisioning}
import org.apache.james.jmap.json.ResponseSerializer
import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
import org.apache.james.mailbox.MailboxSession
+import org.apache.james.util.{MDCBuilder, ReactorUtils}
import org.slf4j.{Logger, LoggerFactory}
import play.api.libs.json.{JsError, JsSuccess, Json}
import reactor.core.publisher.{Mono, SynchronousSink}
@@ -75,6 +76,9 @@ class JMAPApiRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator:
.onErrorResume(throwable => handleError(throwable, httpServerResponse))
.asJava()
.`then`()
+ .contextWrite(ReactorUtils.context("MDCBuilder.IP", MDCBuilder.create()
+ .addToContext(MDCBuilder.IP, Option(httpServerRequest.hostAddress()).map(_.toString()).getOrElse(""))
+ .addToContext("x-forwarded-for", Option(httpServerRequest.requestHeaders().get("X-Forwarded-For")).getOrElse(""))))
private def requestAsJsonStream(httpServerRequest: HttpServerRequest): SMono[RequestObject] =
SMono.fromPublisher(httpServerRequest
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org