Skip to content

Commit

Permalink
Merge pull request #47 from mdsol/feature/MCC-529263
Browse files Browse the repository at this point in the history
[MCC-529263] Adding logging support
  • Loading branch information
Herry Kurniawan authored Sep 18, 2019
2 parents 437265f + 452d41b commit 7cd6db0
Show file tree
Hide file tree
Showing 15 changed files with 179 additions and 30 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changes in Medidata.MAuth

## v4.0.0
- **[All]** Added implementation for MWSV2 signinig and authentication.
- **[All]** Added implementation for MWSV2 signinig and authentication. Added logging support during MAuthentication.

## v3.1.3
- **[Core]** Refactored `MAuthCoreExtensions.cs` and moved Signing and Verification method into `IMAuthCore.cs`.
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ public class Startup
}
}
```
For enabling the logging from Owin application, the following configuration will need to be added or updated:
```C#
<configuration>
<system.diagnostics>
<switches>
<add name="Microsoft.Owin" value="Information, Error, Warning" />
</switches>
</system.diagnostics>
</configuration>
```

A similar way can be implemented for ASP.NET Core (also in the `Startup` class):

Expand Down Expand Up @@ -262,6 +272,9 @@ public static class WebApiConfig

// when ready to disable authentication of V1 protococl
options.DisableV1 = true

// if loggerfactory is present
options.loggerfactory = new Microsoft.Extensions.Logging.LoggerFactory(); // or provide the existing loggerfactory
};

config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options));
Expand Down
1 change: 0 additions & 1 deletion src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public static IApplicationBuilder UseMAuthAuthentication(this IApplicationBuilde
if (options == null)
throw new ArgumentNullException(nameof(options));


return app.UseMiddleware<MAuthMiddleware>(options);
}

Expand Down
1 change: 1 addition & 0 deletions src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Medidata.MAuth.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;

namespace Medidata.MAuth.AspNetCore
{
Expand Down
22 changes: 20 additions & 2 deletions src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,40 @@
using Medidata.MAuth.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace Medidata.MAuth.AspNetCore
{
/// <summary>
/// Enables the middleware for the aspnet core applications.
/// </summary>
internal class MAuthMiddleware
{
private readonly MAuthMiddlewareOptions options;
private readonly MAuthAuthenticator authenticator;
private readonly RequestDelegate next;

public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options)
/// <summary>
/// Creates a new <see cref="MAuthMiddleware"/>
/// </summary>
/// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
/// <param name="options">The <see cref="MAuthMiddlewareOptions"/> representing the options for the middleware.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> representing the factory that used to create logger instances.</param>
public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory)
{
this.next = next;
this.options = options;
this.authenticator = new MAuthAuthenticator(options);
loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
ILogger logger = loggerFactory.CreateLogger<MAuthMiddleware>();
this.authenticator = new MAuthAuthenticator(options, logger);
}

/// <summary>
/// Invokes the logic of the middleware.
/// </summary>
/// <param name="context"> The <see cref="HttpContext"/>.</param>
/// <returns>A <see cref="Task"/> that completes when the middleware has completed processing.</returns>
public async Task Invoke(HttpContext context)
{
context.Request.EnableRewind();
Expand Down
19 changes: 15 additions & 4 deletions src/Medidata.MAuth.Core/MAuthAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
using Microsoft.Extensions.Caching.Memory;
using Org.BouncyCastle.Crypto;
using Medidata.MAuth.Core.Models;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace Medidata.MAuth.Core
{
internal class MAuthAuthenticator
{
private readonly MAuthOptionsBase options;
private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions());
private readonly ILogger logger;

public Guid ApplicationUuid => options.ApplicationUuid;

public MAuthAuthenticator(MAuthOptionsBase options)
public MAuthAuthenticator(MAuthOptionsBase options, ILogger logger)
{
if (options.ApplicationUuid == default(Guid))
throw new ArgumentException(nameof(options.ApplicationUuid));
Expand All @@ -27,19 +30,23 @@ public MAuthAuthenticator(MAuthOptionsBase options)
throw new ArgumentNullException(nameof(options.PrivateKey));

this.options = options;
this.logger = logger;
}

/// <summary>
/// Verifies if the <see cref="HttpRequestMessage"/> request is authenticated or not.
/// </summary>
/// <param name="request">The <see cref="HttpRequestMessage"/> request. </param>
/// <param name="request">The <see cref="HttpRequestMessage"/> request.</param>
/// <returns>A task object of the boolean value that verifies if the request is authenticated or not.</returns>
public async Task<bool> AuthenticateRequest(HttpRequestMessage request)
{
try
{
logger.LogInformation("Initiating Authentication of the request.");
var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader();

logger.LogInformation("Authentication is for the {version}.",version);

if (options.DisableV1 && version == MAuthVersion.MWS)
throw new InvalidVersionException($"Authentication with {version} version is disabled.");

Expand All @@ -52,25 +59,29 @@ public async Task<bool> AuthenticateRequest(HttpRequestMessage request)
}
catch (ArgumentException ex)
{
logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers.");
throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex);
}
catch (RetriedRequestException ex)
{
logger.LogError(ex, "Unable to query the application information from MAuth server.");
throw new AuthenticationException(
"Could not query the application information for the application from the MAuth server.", ex);
}
catch (InvalidCipherTextException ex)
{

logger.LogWarning(ex, "Unable to authenticate due to invalid payload information.");
throw new AuthenticationException(
"The request verification failed due to an invalid payload information.", ex);
}
catch (InvalidVersionException ex)
{
logger.LogError(ex, "Unable to authenticate due to invalid version.");
throw new InvalidVersionException(ex.Message, ex);
}
catch (Exception ex)
{
logger.LogError(ex, "Unable to authenticate due to unexpected error.");
throw new AuthenticationException(
"An unexpected error occured during authentication. Please see the inner exception for details.",
ex
Expand Down Expand Up @@ -108,7 +119,7 @@ private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenReque
/// Extracts the authentication information from a <see cref="HttpRequestMessage"/>.
/// </summary>
/// <param name="request">The request that has the authentication information.</param>
/// /// <param name="version">Enum value of the MAuthVersion.</param>
/// <param name="version">Enum value of the MAuthVersion.</param>
/// <returns>The authentication information with the payload from the request.</returns>
internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request, MAuthVersion version)
{
Expand Down
7 changes: 5 additions & 2 deletions src/Medidata.MAuth.Core/UtilityExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Net.Http;
using System.Threading.Tasks;
using Medidata.MAuth.Core.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace Medidata.MAuth.Core
{
Expand Down Expand Up @@ -65,10 +67,11 @@ public static bool TryParseAuthenticationHeader(this string headerValue,
/// </summary>
/// <param name="request">The request message to authenticate.</param>
/// <param name="options">The MAuth options to use for the authentication.</param>
/// <param name="logger">The logger interface used for logging.</param>
/// <returns>The task for the operation that is when completes will result in <see langword="true"/> if
/// the authentication is successful; otherwise <see langword="false"/>.</returns>
public static Task<bool> Authenticate(this HttpRequestMessage request, MAuthOptionsBase options) =>
new MAuthAuthenticator(options).AuthenticateRequest(request);
public static Task<bool> Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILogger logger) =>
new MAuthAuthenticator(options, logger).AuthenticateRequest(request);

/// <summary>
/// Determines the MAuth version enumerator reading authHeader.
Expand Down
4 changes: 3 additions & 1 deletion src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Microsoft.Owin.Logging;
using Owin;

namespace Medidata.MAuth.Owin
Expand All @@ -25,7 +26,8 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd
if (options == null)
throw new ArgumentNullException(nameof(options));

return app.Use<MAuthMiddleware>(options);
var owinLogger = app.CreateLogger<MAuthMiddleware>();
return app.Use<MAuthMiddleware>(options, owinLogger);
}

/// <summary>
Expand Down
7 changes: 4 additions & 3 deletions src/Medidata.MAuth.Owin/MAuthMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Medidata.MAuth.Core;
using Microsoft.Owin;
using ILogger = Microsoft.Owin.Logging.ILogger;

namespace Medidata.MAuth.Owin
{
Expand All @@ -10,16 +11,16 @@ internal class MAuthMiddleware: OwinMiddleware
private readonly MAuthMiddlewareOptions options;
private readonly MAuthAuthenticator authenticator;

public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options): base(next)
public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, ILogger owinLogger) : base(next)
{
this.options = options;
authenticator = new MAuthAuthenticator(options);
Microsoft.Extensions.Logging.ILogger logger = new OwinLoggerWrapper(owinLogger);
authenticator = new MAuthAuthenticator(options, logger);
}

public override async Task Invoke(IOwinContext context)
{
await context.EnsureRequestBodyStreamSeekable();

if (!options.Bypass(context.Request) &&
!await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized))
{
Expand Down
84 changes: 84 additions & 0 deletions src/Medidata.MAuth.Owin/OwinLoggerWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Microsoft.Owin.Logging;
using ILogger = Microsoft.Owin.Logging.ILogger;

namespace Medidata.MAuth.Owin
{
internal class OwinLoggerWrapper : Microsoft.Extensions.Logging.ILogger
{
private readonly ILogger _owinLogger;

public OwinLoggerWrapper(ILogger owinLogger)
{
_owinLogger = owinLogger;
}

public IDisposable BeginScope<TState>(TState state)
{
throw new NotImplementedException();
}

public bool IsEnabled(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.Critical:
return _owinLogger.IsEnabled(TraceEventType.Critical);
case LogLevel.Debug:
case LogLevel.Trace:
case LogLevel.Information:
return _owinLogger.IsEnabled(TraceEventType.Information);
case LogLevel.Error:
return _owinLogger.IsEnabled(TraceEventType.Error);
case LogLevel.Warning:
return _owinLogger.IsEnabled(TraceEventType.Warning);
default:
throw new ArgumentOutOfRangeException(nameof(logLevel));
}
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}

if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
string message = null;
if (null != formatter)
{
message = formatter(state, exception);
}
if (!string.IsNullOrEmpty(message) || exception != null)
{
switch (logLevel)
{
case LogLevel.Critical:
_owinLogger.WriteCritical(message, exception);
break;
case LogLevel.Debug:
case LogLevel.Trace:
case LogLevel.Error:
_owinLogger.WriteError(message, exception);
break;
case LogLevel.Information:
_owinLogger.WriteInformation(message);
break;
case LogLevel.Warning:
_owinLogger.WriteWarning(message, exception);
break;
default:
_owinLogger.WriteWarning($"Encountered unknown log level {logLevel}, writing out as Info.");
_owinLogger.WriteInformation(message);
break;
}
}
}
}
}
14 changes: 11 additions & 3 deletions src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Threading;
using System.Threading.Tasks;
using Medidata.MAuth.Core;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace Medidata.MAuth.WebApi
{
Expand All @@ -27,8 +29,7 @@ public class MAuthAuthenticatingHandler : DelegatingHandler
public MAuthAuthenticatingHandler(MAuthWebApiOptions options)
{
this.options = options;

authenticator = new MAuthAuthenticator(options);
this.authenticator = this.SetupMAuthAuthenticator(options);
}

/// <summary>
Expand All @@ -42,7 +43,7 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options)
public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler)
{
this.options = options;
authenticator = new MAuthAuthenticator(options);
this.authenticator = this.SetupMAuthAuthenticator(options);
}

/// <summary>
Expand All @@ -66,5 +67,12 @@ protected override async Task<HttpResponseMessage> SendAsync(
.SendAsync(request, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
}

private MAuthAuthenticator SetupMAuthAuthenticator(MAuthWebApiOptions opt)
{
var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance;
var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticatingHandler));
return new MAuthAuthenticator(options, logger);
}
}
}
6 changes: 6 additions & 0 deletions src/Medidata.MAuth.WebApi/MAuthWebApiOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Medidata.MAuth.Core;
using Microsoft.Extensions.Logging;

namespace Medidata.MAuth.WebApi
{
Expand All @@ -13,5 +14,10 @@ public class MAuthWebApiOptions: MAuthOptionsBase
/// The default is <see langword="true"/>.
/// </summary>
public bool HideExceptionsAndReturnUnauthorized { get; set; } = true;

/// <summary>
///
/// </summary>
public ILoggerFactory LoggerFactory { get; set; }
}
}
Loading

0 comments on commit 7cd6db0

Please sign in to comment.