diff --git a/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml b/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml index e252c1c8ac5b..e3491cc9cace 100644 --- a/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml +++ b/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml @@ -48,12 +48,10 @@ - - diff --git a/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinator.Core.cs b/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinator.Core.cs index 7df6633754ff..8383184bfa0f 100644 --- a/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinator.Core.cs +++ b/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinator.Core.cs @@ -16,7 +16,6 @@ using Datadog.Trace.Util.Http; using Datadog.Trace.Vendors.Serilog.Events; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; diff --git a/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinatorHelpers.Core.cs b/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinatorHelpers.Core.cs index f0f7c2659912..523af9c3ddaf 100644 --- a/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinatorHelpers.Core.cs +++ b/tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinatorHelpers.Core.cs @@ -7,10 +7,11 @@ #if !NETFRAMEWORK using System; using System.Collections.Generic; +using System.Reflection; using Datadog.Trace.AppSec.Waf; +using Datadog.Trace.DuckTyping; using Datadog.Trace.Logging; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; @@ -20,6 +21,8 @@ internal static class SecurityCoordinatorHelpers { private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(SecurityCoordinatorHelpers)); + internal static readonly Type? SessionFeature = Assembly.GetAssembly(typeof(IHeaderDictionary))?.GetType("Microsoft.AspNetCore.Http.Features.ISessionFeature", throwOnError: false); + internal static void CheckAndBlock(this Security security, HttpContext context, Span span) { if (security.AppsecEnabled) @@ -82,9 +85,16 @@ internal static void CheckPathParamsAndSessionId(this Security security, HttpCon var args = new Dictionary { { AddressesConstants.RequestPathParams, pathParams } }; IResult? result; // we need to check context.Features.Get as accessing the Session item if session has not been configured for the application is throwing InvalidOperationException - if (context.Features.Get() is { Session.IsAvailable: true } feature) + var sessionFeature = context.Features[SessionFeature]; + Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNetCore.UserEvents.ISessionFeature? sessionFeatureProxy = null; + if (sessionFeature is not null) + { + sessionFeatureProxy = sessionFeature.DuckCast(); + } + + if (sessionFeatureProxy?.Session?.IsAvailable == true) { - result = securityCoordinator.RunWaf(args, sessionId: feature.Session.Id); + result = securityCoordinator.RunWaf(args, sessionId: sessionFeatureProxy.Session.Id); } else { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/HttpContextSetUser.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/HttpContextSetUser.cs index b2d69da601ed..3195217bf403 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/HttpContextSetUser.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/HttpContextSetUser.cs @@ -13,6 +13,7 @@ using Datadog.Trace.AppSec.Coordinator; using Datadog.Trace.ClrProfiler.CallTarget; using Datadog.Trace.Configuration; +using Datadog.Trace.DuckTyping; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -90,8 +91,15 @@ internal static CallTargetState OnMethodBegin(TTarget instance, ref Cla } } - var sessionId = httpContext.Features.Get()?.Session?.Id; - var result = secCoord.RunWafForUser(userSessionId: sessionId, userId: userId); + ISessionFeature? sessionFeatureProxy = null; + var sessionFeature = httpContext.Features[SecurityCoordinatorHelpers.SessionFeature]; + + if (sessionFeature is not null) + { + sessionFeatureProxy = sessionFeature.DuckCast(); + } + + var result = secCoord.RunWafForUser(userSessionId: sessionFeatureProxy?.Session?.Id, userId: userId); secCoord.BlockAndReport(result); UserEventsCommon.RecordMetricsLoginSuccessIfNotFound(foundUserId, true); diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/ISession.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/ISession.cs new file mode 100644 index 000000000000..c501379b47d5 --- /dev/null +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/ISession.cs @@ -0,0 +1,29 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#if !NETFRAMEWORK +#nullable enable +using System.ComponentModel; + +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNetCore.UserEvents; + +/// +/// Duck type of the SignInManager aspnet core type +/// +[Browsable(false)] +[EditorBrowsable(EditorBrowsableState.Never)] +public interface ISession +{ + /// + /// Gets a value indicating whether the session is available + /// + bool IsAvailable { get; } + + /// + /// Gets the session id + /// + string Id { get; } +} +#endif diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/ISessionFeature.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/ISessionFeature.cs new file mode 100644 index 000000000000..afb8ddc8d880 --- /dev/null +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNetCore/UserEvents/ISessionFeature.cs @@ -0,0 +1,24 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#if !NETFRAMEWORK +#nullable enable +using System.ComponentModel; + +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNetCore.UserEvents; + +/// +/// Duck type of the ISessionFeature aspnet core type in Microsoft.AspNetCore.Http.Features assembly +/// +[Browsable(false)] +[EditorBrowsable(EditorBrowsableState.Never)] +public interface ISessionFeature +{ + /// + /// Gets the Session object, can be null + /// + public ISession Session { get; } +} +#endif diff --git a/tracer/test/test-applications/security/Samples.Security.AspNetCore5/Controllers/SessionController.cs b/tracer/test/test-applications/security/Samples.Security.AspNetCore5/Controllers/SessionController.cs new file mode 100644 index 000000000000..096e17f75ae1 --- /dev/null +++ b/tracer/test/test-applications/security/Samples.Security.AspNetCore5/Controllers/SessionController.cs @@ -0,0 +1,34 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; +using Microsoft.AspNetCore.Routing; +using System.Collections.Generic; +using System.Threading.Tasks; +using Datadog.Trace; + +namespace weblog +{ + [ApiController] + [Route("session")] + public class SessionController : Controller + { + [HttpGet("new")] + public IActionResult New() + { + return Content(HttpContext.Session.Id); + } + + [HttpGet("user")] + public IActionResult User(string sdk_user) + { + if (sdk_user != null) + { + Samples.SampleHelpers.TrackUserLoginSuccessEvent(sdk_user, null); + } + + return Content($"Hello, set the user to {sdk_user}"); + } + } +}