mirror of
https://github.com/bitwarden/server.git
synced 2024-11-22 12:15:36 +01:00
This reverts commit f11c58e396
.
This commit is contained in:
parent
03bbc7195b
commit
15954fb679
@ -11,6 +11,7 @@ using Bit.Core.Repositories;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.Core.Utilities.Duo;
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@ -152,7 +153,7 @@ public class TwoFactorController : Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||||
await duoApi.JSONApiCall("GET", "/auth/v2/check");
|
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||||
}
|
}
|
||||||
catch (DuoException)
|
catch (DuoException)
|
||||||
{
|
{
|
||||||
@ -209,7 +210,7 @@ public class TwoFactorController : Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||||
await duoApi.JSONApiCall("GET", "/auth/v2/check");
|
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||||
}
|
}
|
||||||
catch (DuoException)
|
catch (DuoException)
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,6 @@ using Bit.Api.Models.Request.Accounts;
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
|
|
||||||
namespace Bit.Api.Models.Request;
|
namespace Bit.Api.Models.Request;
|
||||||
@ -105,7 +104,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
|
|||||||
|
|
||||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if (!DuoApi.ValidHost(Host))
|
if (!Core.Utilities.Duo.DuoApi.ValidHost(Host))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Host is invalid.", new string[] { nameof(Host) });
|
yield return new ValidationResult("Host is invalid.", new string[] { nameof(Host) });
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api.Response.Duo;
|
|
||||||
|
|
||||||
public class DuoResponseModel
|
|
||||||
{
|
|
||||||
[JsonPropertyName("stat")]
|
|
||||||
public string Stat { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("code")]
|
|
||||||
public int? Code { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("message")]
|
|
||||||
public string Message { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("message_detail")]
|
|
||||||
public string MessageDetail { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("response")]
|
|
||||||
public Response Response { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Response
|
|
||||||
{
|
|
||||||
[JsonPropertyName("time")]
|
|
||||||
public int Time { get; set; }
|
|
||||||
}
|
|
@ -15,9 +15,8 @@ using System.Text;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using Bit.Core.Models.Api.Response.Duo;
|
|
||||||
|
|
||||||
namespace Bit.Core.Utilities;
|
namespace Bit.Core.Utilities.Duo;
|
||||||
|
|
||||||
public class DuoApi
|
public class DuoApi
|
||||||
{
|
{
|
||||||
@ -28,8 +27,6 @@ public class DuoApi
|
|||||||
private readonly string _ikey;
|
private readonly string _ikey;
|
||||||
private readonly string _skey;
|
private readonly string _skey;
|
||||||
|
|
||||||
private readonly HttpClient _httpClient = new();
|
|
||||||
|
|
||||||
public DuoApi(string ikey, string skey, string host)
|
public DuoApi(string ikey, string skey, string host)
|
||||||
{
|
{
|
||||||
_ikey = ikey;
|
_ikey = ikey;
|
||||||
@ -95,6 +92,11 @@ public class DuoApi
|
|||||||
return string.Concat("Basic ", Encode64(auth));
|
return string.Concat("Basic ", Encode64(auth));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ApiCall(string method, string path, Dictionary<string, string> parameters = null)
|
||||||
|
{
|
||||||
|
return ApiCall(method, path, parameters, 0, out var statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
/// <param name="timeout">The request timeout, in milliseconds.
|
/// <param name="timeout">The request timeout, in milliseconds.
|
||||||
/// Specify 0 to use the system-default timeout. Use caution if
|
/// Specify 0 to use the system-default timeout. Use caution if
|
||||||
/// you choose to specify a custom timeout - some API
|
/// you choose to specify a custom timeout - some API
|
||||||
@ -102,7 +104,8 @@ public class DuoApi
|
|||||||
/// return a response until an out-of-band authentication process
|
/// return a response until an out-of-band authentication process
|
||||||
/// has completed. In some cases, this may take as much as a
|
/// has completed. In some cases, this may take as much as a
|
||||||
/// small number of minutes.</param>
|
/// small number of minutes.</param>
|
||||||
private async Task<(string result, HttpStatusCode statusCode)> ApiCall(string method, string path, Dictionary<string, string> parameters, int timeout)
|
public string ApiCall(string method, string path, Dictionary<string, string> parameters, int timeout,
|
||||||
|
out HttpStatusCode statusCode)
|
||||||
{
|
{
|
||||||
if (parameters == null)
|
if (parameters == null)
|
||||||
{
|
{
|
||||||
@ -118,39 +121,58 @@ public class DuoApi
|
|||||||
query = "?" + canonParams;
|
query = "?" + canonParams;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var url = $"{UrlScheme}://{_host}{path}{query}";
|
var url = string.Format("{0}://{1}{2}{3}", UrlScheme, _host, path, query);
|
||||||
|
|
||||||
var dateString = RFC822UtcNow();
|
var dateString = RFC822UtcNow();
|
||||||
var auth = Sign(method, path, canonParams, dateString);
|
var auth = Sign(method, path, canonParams, dateString);
|
||||||
|
|
||||||
var request = new HttpRequestMessage
|
var request = (HttpWebRequest)WebRequest.Create(url);
|
||||||
{
|
request.Method = method;
|
||||||
Method = new HttpMethod(method),
|
request.Accept = "application/json";
|
||||||
RequestUri = new Uri(url),
|
|
||||||
};
|
|
||||||
request.Headers.Add("Authorization", auth);
|
request.Headers.Add("Authorization", auth);
|
||||||
request.Headers.Add("X-Duo-Date", dateString);
|
request.Headers.Add("X-Duo-Date", dateString);
|
||||||
request.Headers.UserAgent.ParseAdd(UserAgent);
|
request.UserAgent = UserAgent;
|
||||||
|
|
||||||
if (timeout > 0)
|
|
||||||
{
|
|
||||||
_httpClient.Timeout = TimeSpan.FromMilliseconds(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method.Equals("POST") || method.Equals("PUT"))
|
if (method.Equals("POST") || method.Equals("PUT"))
|
||||||
{
|
{
|
||||||
request.Content = new StringContent(canonParams, Encoding.UTF8, "application/x-www-form-urlencoded");
|
var data = Encoding.UTF8.GetBytes(canonParams);
|
||||||
}
|
request.ContentType = "application/x-www-form-urlencoded";
|
||||||
|
request.ContentLength = data.Length;
|
||||||
var response = await _httpClient.SendAsync(request);
|
using (var requestStream = request.GetRequestStream())
|
||||||
var result = await response.Content.ReadAsStringAsync();
|
|
||||||
var statusCode = response.StatusCode;
|
|
||||||
return (result, statusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Response> JSONApiCall(string method, string path, Dictionary<string, string> parameters = null)
|
|
||||||
{
|
{
|
||||||
return await JSONApiCall(method, path, parameters, 0);
|
requestStream.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timeout > 0)
|
||||||
|
{
|
||||||
|
request.Timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the request and process the result.
|
||||||
|
HttpWebResponse response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = (HttpWebResponse)request.GetResponse();
|
||||||
|
}
|
||||||
|
catch (WebException ex)
|
||||||
|
{
|
||||||
|
response = (HttpWebResponse)ex.Response;
|
||||||
|
if (response == null)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using (var reader = new StreamReader(response.GetResponseStream()))
|
||||||
|
{
|
||||||
|
statusCode = response.StatusCode;
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T JSONApiCall<T>(string method, string path, Dictionary<string, string> parameters = null)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
return JSONApiCall<T>(method, path, parameters, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <param name="timeout">The request timeout, in milliseconds.
|
/// <param name="timeout">The request timeout, in milliseconds.
|
||||||
@ -160,18 +182,27 @@ public class DuoApi
|
|||||||
/// return a response until an out-of-band authentication process
|
/// return a response until an out-of-band authentication process
|
||||||
/// has completed. In some cases, this may take as much as a
|
/// has completed. In some cases, this may take as much as a
|
||||||
/// small number of minutes.</param>
|
/// small number of minutes.</param>
|
||||||
private async Task<Response> JSONApiCall(string method, string path, Dictionary<string, string> parameters, int timeout)
|
public T JSONApiCall<T>(string method, string path, Dictionary<string, string> parameters, int timeout)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
var (res, statusCode) = await ApiCall(method, path, parameters, timeout);
|
var res = ApiCall(method, path, parameters, timeout, out var statusCode);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var obj = JsonSerializer.Deserialize<DuoResponseModel>(res);
|
// TODO: We should deserialize this into our own DTO and not work on dictionaries.
|
||||||
if (obj.Stat == "OK")
|
var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(res);
|
||||||
|
if (dict["stat"].ToString() == "OK")
|
||||||
{
|
{
|
||||||
return obj.Response;
|
return JsonSerializer.Deserialize<T>(dict["response"].ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ApiException(obj.Code ?? 0, (int)statusCode, obj.Message, obj.MessageDetail);
|
var check = ToNullableInt(dict["code"].ToString());
|
||||||
|
var code = check.GetValueOrDefault(0);
|
||||||
|
var messageDetail = string.Empty;
|
||||||
|
if (dict.ContainsKey("message_detail"))
|
||||||
|
{
|
||||||
|
messageDetail = dict["message_detail"].ToString();
|
||||||
|
}
|
||||||
|
throw new ApiException(code, (int)statusCode, dict["message"].ToString(), messageDetail);
|
||||||
}
|
}
|
||||||
catch (ApiException)
|
catch (ApiException)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user