@@ -1,4 +1,4 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>netcoreapp2.2</TargetFramework> | <TargetFramework>netcoreapp2.2</TargetFramework> | ||||
@@ -18,7 +18,12 @@ namespace MQTTnet.Server.Web | |||||
{ | { | ||||
private readonly ILogger<AuthenticationHandler> _logger; | private readonly ILogger<AuthenticationHandler> _logger; | ||||
public AuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder, ISystemClock clock, ILogger<AuthenticationHandler> logger) | |||||
public AuthenticationHandler( | |||||
IOptionsMonitor<AuthenticationSchemeOptions> options, | |||||
ILoggerFactory loggerFactory, | |||||
UrlEncoder encoder, | |||||
ISystemClock clock, | |||||
ILogger<AuthenticationHandler> logger) | |||||
: base(options, loggerFactory, encoder, clock) | : base(options, loggerFactory, encoder, clock) | ||||
{ | { | ||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
@@ -26,11 +31,9 @@ namespace MQTTnet.Server.Web | |||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() | protected override async Task<AuthenticateResult> HandleAuthenticateAsync() | ||||
{ | { | ||||
await Task.CompletedTask; | |||||
if (!Request.Headers.ContainsKey(HeaderNames.Authorization)) | if (!Request.Headers.ContainsKey(HeaderNames.Authorization)) | ||||
{ | { | ||||
return AuthenticateResult.Fail("Missing Authorization Header"); | |||||
return AuthenticateResult.NoResult(); | |||||
} | } | ||||
try | try | ||||
@@ -83,6 +86,10 @@ namespace MQTTnet.Server.Web | |||||
return AuthenticateResult.Fail(exception); | return AuthenticateResult.Fail(exception); | ||||
} | } | ||||
finally | |||||
{ | |||||
await Task.CompletedTask; | |||||
} | |||||
} | } | ||||
private bool ValidateUser(PythonDictionary context) | private bool ValidateUser(PythonDictionary context) | ||||
@@ -1,7 +1,9 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | |||||
using Microsoft.AspNetCore.Authentication; | using Microsoft.AspNetCore.Authentication; | ||||
using Microsoft.AspNetCore.Authentication.Cookies; | |||||
using Microsoft.AspNetCore.Builder; | using Microsoft.AspNetCore.Builder; | ||||
using Microsoft.AspNetCore.Hosting; | using Microsoft.AspNetCore.Hosting; | ||||
using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||
@@ -15,6 +17,7 @@ using MQTTnet.Server.Logging; | |||||
using MQTTnet.Server.Mqtt; | using MQTTnet.Server.Mqtt; | ||||
using MQTTnet.Server.Scripting; | using MQTTnet.Server.Scripting; | ||||
using MQTTnet.Server.Scripting.DataSharing; | using MQTTnet.Server.Scripting.DataSharing; | ||||
using Swashbuckle.AspNetCore.SwaggerGen; | |||||
using Swashbuckle.AspNetCore.SwaggerUI; | using Swashbuckle.AspNetCore.SwaggerUI; | ||||
namespace MQTTnet.Server.Web | namespace MQTTnet.Server.Web | ||||
@@ -115,17 +118,22 @@ namespace MQTTnet.Server.Web | |||||
services.AddSwaggerGen(c => | services.AddSwaggerGen(c => | ||||
{ | { | ||||
c.DescribeAllEnumsAsStrings(); | c.DescribeAllEnumsAsStrings(); | ||||
c.AddSecurityDefinition("Basic", new OpenApiSecurityScheme | |||||
var securityScheme = new OpenApiSecurityScheme | |||||
{ | { | ||||
Scheme = "Basic", | Scheme = "Basic", | ||||
Name = HeaderNames.Authorization, | Name = HeaderNames.Authorization, | ||||
Type = SecuritySchemeType.Http, | Type = SecuritySchemeType.Http, | ||||
In = ParameterLocation.Header | In = ParameterLocation.Header | ||||
}); | |||||
}; | |||||
c.AddSecurityDefinition("Swagger", securityScheme); | |||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement | c.AddSecurityRequirement(new OpenApiSecurityRequirement | ||||
{ | { | ||||
[new OpenApiSecurityScheme { Name = "Basic" }] = new List<string>() | |||||
[securityScheme] = new List<string>() | |||||
}); | }); | ||||
c.SwaggerDoc("v1", new OpenApiInfo | c.SwaggerDoc("v1", new OpenApiInfo | ||||
{ | { | ||||
Title = "MQTTnet.Server API", | Title = "MQTTnet.Server API", | ||||
@@ -145,7 +153,9 @@ namespace MQTTnet.Server.Web | |||||
}); | }); | ||||
}); | }); | ||||
services.AddAuthentication("Basic").AddScheme<AuthenticationSchemeOptions, AuthenticationHandler>("Basic", null); | |||||
services.AddAuthentication("Basic") | |||||
.AddScheme<AuthenticationSchemeOptions, AuthenticationHandler>("Basic", null) | |||||
.AddCookie(); | |||||
} | } | ||||
private void ReadMqttSettings(IServiceCollection services) | private void ReadMqttSettings(IServiceCollection services) | ||||
@@ -200,7 +210,7 @@ namespace MQTTnet.Server.Web | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
context.Response.StatusCode = 400; | |||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest; | |||||
} | } | ||||
} | } | ||||
else | else | ||||
@@ -2,9 +2,19 @@ | |||||
""" | """ | ||||
This function is invoked whenever a user tries to access protected HTTP resources. | This function is invoked whenever a user tries to access protected HTTP resources. | ||||
This function must exist and return a proper value. Otherwise the request is denied. | This function must exist and return a proper value. Otherwise the request is denied. | ||||
All authentication data must be sent via using the "Authorization" header. This header | |||||
will be parsed and all values will be exposed to the context. | |||||
""" | """ | ||||
header_value = context["header_value"] # The untouched header value. | |||||
username = context["username"] | |||||
password = context["password"] | |||||
scheme = context["scheme"] | |||||
parameter = context["parameter"] | |||||
context["is_authenticated"] = True # Change this to _False_ in case of invalid credentials. | |||||
username = context["username"] # Only set if proper "Basic" authorization is used. | |||||
password = context["password"] # Only set if proper "Basic" authorization is used. | |||||
context["is_authenticated"] = True # Change this to _False_ in case of invalid credentials. | |||||
# Example for an API key with proper format. | |||||
if header_value == "APIKey 123456": | |||||
context["is_authenticated"] = True |
@@ -38,10 +38,10 @@ | |||||
}, | }, | ||||
"CommunicationTimeout": 15, // In seconds. | "CommunicationTimeout": 15, // In seconds. | ||||
"ConnectionBacklog": 10, // Set 0 to disable | "ConnectionBacklog": 10, // Set 0 to disable | ||||
"EnablePersistentSessions": false, | |||||
"EnablePersistentSessions": true, | |||||
"MaxPendingMessagesPerClient": 250, | "MaxPendingMessagesPerClient": 250, | ||||
"RetainedApplicationMessages": { | "RetainedApplicationMessages": { | ||||
"Persist": false, | |||||
"Persist": true, | |||||
"Path": "RetainedApplicationMessages.json", | "Path": "RetainedApplicationMessages.json", | ||||
"WriteInterval": 10 // In seconds. | "WriteInterval": 10 // In seconds. | ||||
}, | }, | ||||