mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 18:08:26 +03:00
stub out beginnings of apiservice
This commit is contained in:
parent
0d417b3eee
commit
579a7e0398
10 changed files with 279 additions and 1 deletions
|
@ -1,5 +1,6 @@
|
|||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -8,7 +9,7 @@ using Xamarin.Forms;
|
|||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
public class MobilePlatformUtilsService
|
||||
public class MobilePlatformUtilsService : IPlatformUtilsService
|
||||
{
|
||||
private static readonly Random _random = new Random();
|
||||
|
||||
|
|
28
src/Core/Abstractions/IPlatformUtilsService.cs
Normal file
28
src/Core/Abstractions/IPlatformUtilsService.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IPlatformUtilsService
|
||||
{
|
||||
string IdentityClientId { get; }
|
||||
|
||||
Task CopyToClipboardAsync(string text, Dictionary<string, object> options = null);
|
||||
string GetApplicationVersion();
|
||||
DeviceType GetDevice();
|
||||
string GetDeviceString();
|
||||
bool IsDev();
|
||||
bool IsSelfHost();
|
||||
bool IsViewOpen();
|
||||
void LaunchUri(string uri, Dictionary<string, object> options = null);
|
||||
int? LockTimeout();
|
||||
Task<string> ReadFromClipboardAsync(Dictionary<string, object> options = null);
|
||||
void SaveFile();
|
||||
Task<bool> ShowDialogAsync(string text, string title = null, string confirmText = null,
|
||||
string cancelText = null, string type = null);
|
||||
void ShowToast(string type, string title, string text, Dictionary<string, object> options = null);
|
||||
void ShowToast(string type, string title, string[] text, Dictionary<string, object> options = null);
|
||||
bool SupportsU2f();
|
||||
}
|
||||
}
|
14
src/Core/Exceptions/ApiException.cs
Normal file
14
src/Core/Exceptions/ApiException.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Bit.Core.Models.Response;
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Exceptions
|
||||
{
|
||||
public class ApiException : Exception
|
||||
{
|
||||
public ApiException(ErrorResponse error)
|
||||
: base("An API error has occurred.")
|
||||
{ }
|
||||
|
||||
public ErrorResponse Error { get; set; }
|
||||
}
|
||||
}
|
9
src/Core/Models/Domain/EnvironmentUrls.cs
Normal file
9
src/Core/Models/Domain/EnvironmentUrls.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class EnvironmentUrls
|
||||
{
|
||||
public string Base { get; set; }
|
||||
public string Api { get; set; }
|
||||
public string Identity { get; set; }
|
||||
}
|
||||
}
|
21
src/Core/Models/Request/DeviceRequestModels.cs
Normal file
21
src/Core/Models/Request/DeviceRequestModels.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Request
|
||||
{
|
||||
public class DeviceRequest
|
||||
{
|
||||
public DeviceRequest(string appId, IPlatformUtilsService platformUtilsService)
|
||||
{
|
||||
Type = platformUtilsService.GetDevice();
|
||||
Name = platformUtilsService.GetDeviceString();
|
||||
Identifier = appId;
|
||||
PushToken = null; // TODO?
|
||||
}
|
||||
|
||||
public DeviceType? Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public string PushToken { get; set; }
|
||||
}
|
||||
}
|
17
src/Core/Models/Request/TokenRequest.cs
Normal file
17
src/Core/Models/Request/TokenRequest.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using Bit.Core.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Bit.Core.Models.Request
|
||||
{
|
||||
public class TokenRequest
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string MasterPasswordHash { get; set; }
|
||||
public string Token { get; set; }
|
||||
public TwoFactorProviderType Provider { get; set; }
|
||||
public bool Remember { get; set; }
|
||||
public DeviceRequest Device { get; set; }
|
||||
}
|
||||
}
|
62
src/Core/Models/Response/ErrorResponse.cs
Normal file
62
src/Core/Models/Response/ErrorResponse.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace Bit.Core.Models.Response
|
||||
{
|
||||
public class ErrorResponse
|
||||
{
|
||||
public ErrorResponse(JObject response, HttpStatusCode status, bool identityResponse = false)
|
||||
{
|
||||
JObject errorModel = null;
|
||||
if(response != null)
|
||||
{
|
||||
var responseErrorModel = response.GetValue("ErrorModel", StringComparison.OrdinalIgnoreCase);
|
||||
if(responseErrorModel != null && identityResponse)
|
||||
{
|
||||
errorModel = responseErrorModel.Value<JObject>(); ;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorModel = response;
|
||||
}
|
||||
}
|
||||
if(errorModel != null)
|
||||
{
|
||||
Message = errorModel.GetValue("Message", StringComparison.OrdinalIgnoreCase)?.Value<string>();
|
||||
ValidationErrors = errorModel.GetValue("ValidationErrors", StringComparison.OrdinalIgnoreCase)
|
||||
?.Value<Dictionary<string, List<string>>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if((int)status == 429)
|
||||
{
|
||||
Message = "Rate limit exceeded. Try again later.";
|
||||
}
|
||||
}
|
||||
StatusCode = status;
|
||||
}
|
||||
|
||||
public string Message { get; set; }
|
||||
public Dictionary<string, List<string>> ValidationErrors { get; set; }
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
public string GetSingleMessage()
|
||||
{
|
||||
if(ValidationErrors == null)
|
||||
{
|
||||
return Message;
|
||||
}
|
||||
foreach(var error in ValidationErrors)
|
||||
{
|
||||
if(error.Value?.Any() ?? false)
|
||||
{
|
||||
return error.Value[0];
|
||||
}
|
||||
}
|
||||
return Message;
|
||||
}
|
||||
}
|
||||
}
|
13
src/Core/Models/Response/IdentityTokenResponse.cs
Normal file
13
src/Core/Models/Response/IdentityTokenResponse.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Bit.Core.Models.Response
|
||||
{
|
||||
public class IdentityTokenResponse
|
||||
{
|
||||
public string AccessToken { get; set; }
|
||||
public string ExpiresIn { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
public string TokenType { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string TwoFactorToken { get; set; }
|
||||
}
|
||||
}
|
11
src/Core/Models/Response/IdentityTwoFactorResponse.cs
Normal file
11
src/Core/Models/Response/IdentityTwoFactorResponse.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Bit.Core.Enums;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.Core.Models.Response
|
||||
{
|
||||
public class IdentityTwoFactorResponse
|
||||
{
|
||||
public List<TwoFactorProviderType> TwoFactorProviders { get; set; }
|
||||
public Dictionary<TwoFactorProviderType, Dictionary<string, object>> TwoFactorProviders2 { get; set; }
|
||||
}
|
||||
}
|
102
src/Core/Services/ApiService.cs
Normal file
102
src/Core/Services/ApiService.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Response;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public class ApiService
|
||||
{
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
|
||||
private string _deviceType;
|
||||
private bool _usingBaseUrl = false;
|
||||
|
||||
public ApiService(
|
||||
ITokenService tokenService,
|
||||
IPlatformUtilsService platformUtilsService)
|
||||
{
|
||||
_tokenService = tokenService;
|
||||
_platformUtilsService = platformUtilsService;
|
||||
}
|
||||
|
||||
public bool UrlsSet { get; private set; }
|
||||
public string ApiBaseUrl { get; set; }
|
||||
public string IdentityBaseUrl { get; set; }
|
||||
|
||||
public void SetUrls(EnvironmentUrls urls)
|
||||
{
|
||||
UrlsSet = true;
|
||||
if(!string.IsNullOrWhiteSpace(urls.Base))
|
||||
{
|
||||
_usingBaseUrl = true;
|
||||
ApiBaseUrl = urls.Base + "/api";
|
||||
IdentityBaseUrl = urls.Base + "/identity";
|
||||
return;
|
||||
}
|
||||
if(!string.IsNullOrWhiteSpace(urls.Api) && !string.IsNullOrWhiteSpace(urls.Identity))
|
||||
{
|
||||
ApiBaseUrl = urls.Api;
|
||||
IdentityBaseUrl = urls.Identity;
|
||||
return;
|
||||
}
|
||||
// Local Dev
|
||||
//ApiBaseUrl = "http://localhost:4000";
|
||||
//IdentityBaseUrl = "http://localhost:33656";
|
||||
// Production
|
||||
ApiBaseUrl = "https://api.bitwarden.com";
|
||||
IdentityBaseUrl = "https://identity.bitwarden.com";
|
||||
}
|
||||
|
||||
#region Auth APIs
|
||||
|
||||
public async Task<Tuple<IdentityTokenResponse, IdentityTwoFactorResponse>> PostIdentityTokenAsync()
|
||||
{
|
||||
var request = new HttpRequestMessage
|
||||
{
|
||||
RequestUri = new Uri(string.Concat(IdentityBaseUrl, "/connect/token")),
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
request.Headers.Add("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
||||
request.Headers.Add("Accept", "application/json");
|
||||
request.Headers.Add("Device-Type", _deviceType);
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
JObject responseJObject = null;
|
||||
if(response.Headers.Contains("content-type") &&
|
||||
response.Headers.GetValues("content-type").Any(h => h.Contains("application/json")))
|
||||
{
|
||||
var responseJsonString = await response.Content.ReadAsStringAsync();
|
||||
responseJObject = JObject.Parse(responseJsonString);
|
||||
}
|
||||
|
||||
if(responseJObject != null)
|
||||
{
|
||||
if(response.IsSuccessStatusCode)
|
||||
{
|
||||
return new Tuple<IdentityTokenResponse, IdentityTwoFactorResponse>(
|
||||
responseJObject.ToObject<IdentityTokenResponse>(), null);
|
||||
}
|
||||
else if(response.StatusCode == HttpStatusCode.BadRequest &&
|
||||
responseJObject.ContainsKey("TwoFactorProviders2") &&
|
||||
responseJObject["TwoFactorProviders2"] != null &&
|
||||
responseJObject["TwoFactorProviders2"].HasValues)
|
||||
{
|
||||
return new Tuple<IdentityTokenResponse, IdentityTwoFactorResponse>(
|
||||
null, responseJObject.ToObject<IdentityTwoFactorResponse>());
|
||||
}
|
||||
}
|
||||
throw new ApiException(new ErrorResponse(responseJObject, response.StatusCode, true));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue