diff --git a/examples/WireMock.Net.ConsoleApplication/Program.cs b/examples/WireMock.Net.ConsoleApplication/Program.cs index b4c83630..cf0be467 100644 --- a/examples/WireMock.Net.ConsoleApplication/Program.cs +++ b/examples/WireMock.Net.ConsoleApplication/Program.cs @@ -19,6 +19,8 @@ namespace WireMock.Net.ConsoleApplication var server = FluentMockServer.StartWithAdminInterface(url1, url2, url3); Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls)); + server.SetBasicAuthentication("a", "b"); + server.AllowPartialMapping(); server diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index 5f34c262..ef24c85c 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -73,5 +73,13 @@ namespace WireMock return result; } + + /// + /// Gets a value indicating whether this mapping is an Admin Interface. + /// + /// + /// true if this mapping is an Admin Interface; otherwise, false. + /// + public bool IsAdminInterface => Provider is DynamicResponseProvider; } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs index 24deb393..a45ba100 100644 --- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs +++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs @@ -142,7 +142,7 @@ namespace WireMock.Server return new ResponseMessage { StatusCode = 500, Body = e.ToString() }; } - return new ResponseMessage { Body = "Mapping added" }; + return new ResponseMessage { StatusCode = 201, Body = "Mapping added" }; } private void DeserializeAndAddMapping(string json, Guid? guid = null) diff --git a/src/WireMock.Net/Server/FluentMockServer.cs b/src/WireMock.Net/Server/FluentMockServer.cs index 64d2c3c1..6abb300b 100644 --- a/src/WireMock.Net/Server/FluentMockServer.cs +++ b/src/WireMock.Net/Server/FluentMockServer.cs @@ -4,10 +4,12 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Net; +using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; using WireMock.Http; using WireMock.Logging; +using WireMock.Matchers; using WireMock.Matchers.Request; using WireMock.Validation; @@ -34,22 +36,27 @@ namespace WireMock.Server private bool _allowPartialMapping; + private IMatcher _authorizationMatcher; + /// /// Gets the ports. /// /// /// The ports. /// + [PublicAPI] public List Ports { get; } /// /// Gets the urls. /// + [PublicAPI] public string[] Urls { get; } /// /// Gets the request logs. /// + [PublicAPI] public IEnumerable LogEntries { get @@ -64,6 +71,7 @@ namespace WireMock.Server /// /// Gets the mappings. /// + [PublicAPI] public IEnumerable Mappings { get @@ -159,6 +167,7 @@ namespace WireMock.Server /// /// Stop this server. /// + [PublicAPI] public void Stop() { _httpServer.Stop(); @@ -167,6 +176,7 @@ namespace WireMock.Server /// /// Resets LogEntries and Mappings. /// + [PublicAPI] public void Reset() { ResetLogEntries(); @@ -177,6 +187,7 @@ namespace WireMock.Server /// /// Resets the LogEntries. /// + [PublicAPI] public void ResetLogEntries() { lock (((ICollection)_logEntries).SyncRoot) @@ -209,6 +220,7 @@ namespace WireMock.Server /// /// Resets the Mappings. /// + [PublicAPI] public void ResetMappings() { lock (((ICollection)_mappings).SyncRoot) @@ -243,6 +255,7 @@ namespace WireMock.Server /// /// The matcher. /// The . + [PublicAPI] public IEnumerable SearchLogsFor(IRequestMatcher matcher) { lock (((ICollection)_logEntries).SyncRoot) @@ -258,6 +271,7 @@ namespace WireMock.Server /// /// The delay. /// + [PublicAPI] public void AddRequestProcessingDelay(TimeSpan delay) { lock (_syncRoot) @@ -269,6 +283,7 @@ namespace WireMock.Server /// /// Allows the partial mapping. /// + [PublicAPI] public void AllowPartialMapping() { lock (_syncRoot) @@ -277,11 +292,27 @@ namespace WireMock.Server } } + /// + /// Sets the basic authentication. + /// + /// The username. + /// The password. + [PublicAPI] + public void SetBasicAuthentication([NotNull] string username, [NotNull] string password) + { + Check.NotNull(username, nameof(username)); + Check.NotNull(password, nameof(password)); + + string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); + _authorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$"); + } + /// /// The given. /// /// The request matcher. /// The . + [PublicAPI] public IRespondWithAProvider Given(IRequestMatcher requestMatcher) { return new RespondWithAProvider(RegisterMapping, requestMatcher); @@ -344,10 +375,7 @@ namespace WireMock.Server if (_allowPartialMapping) { var partialMappings = mappings - .Where(pm => - (pm.Mapping.Provider is DynamicResponseProvider && pm.MatchResult.IsPerfectMatch) || - !(pm.Mapping.Provider is DynamicResponseProvider) - ) + .Where(pm => pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch || !pm.Mapping.IsAdminInterface) .OrderBy(m => m.MatchResult) .ThenBy(m => m.Mapping.Priority) .ToList(); @@ -369,24 +397,26 @@ namespace WireMock.Server if (targetMapping == null) { - response = new ResponseMessage - { - StatusCode = 404, - Body = "No mapping found" - }; + response = new ResponseMessage { StatusCode = 404, Body = "No mapping found" }; + return; } - else + + if (targetMapping.IsAdminInterface && _authorizationMatcher != null) { - response = await targetMapping.ResponseTo(request); + string authorization; + bool present = request.Headers.TryGetValue("Authorization", out authorization); + if (!present || _authorizationMatcher.IsMatch(authorization) < 1.0) + { + response = new ResponseMessage { StatusCode = 401 }; + return; + } } + + response = await targetMapping.ResponseTo(request); } catch (Exception ex) { - response = new ResponseMessage - { - StatusCode = 500, - Body = ex.ToString() - }; + response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() }; } finally { @@ -406,4 +436,4 @@ namespace WireMock.Server } } } -} +} \ No newline at end of file