Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/props/Base.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<StudioVersion>0.21.23.0</StudioVersion>
<StudioVersion>0.21.24.0</StudioVersion>
<CoreVersion>0.31.17.0</CoreVersion>
<UniversalFpgaProjectSystemVersion>0.41.17.0</UniversalFpgaProjectSystemVersion>
<EssentialsVersion>0.11.17.0</EssentialsVersion>
Expand Down
12 changes: 12 additions & 0 deletions src/OneWare.CloudIntegration/Dto/CurrentUserDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace OneWare.CloudIntegration.Dto;

public class CurrentUserDto
{
public string? Email { get; set; }

public string? AvatarUrl { get; set; }

public required bool IsProfileComplete { get; set; }

public required UserPlanDto UserPlan { get; set; }
}
8 changes: 8 additions & 0 deletions src/OneWare.CloudIntegration/Dto/UserBalanceDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace OneWare.CloudIntegration.Dto;

public class UserBalanceDto
{
public required int Balance { get; init; }

public required int IncludedMonthlyCreditsUsed { get; init; }
}
34 changes: 34 additions & 0 deletions src/OneWare.CloudIntegration/Dto/UserPlanDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace OneWare.CloudIntegration.Dto;

public class UserPlanDto
{
public required string Id { get; set; }

public required string Name { get; set; }

public decimal DiscountRate { get; set; }

public long MaxProjectSizeBytes { get; set; }

public int MaxProjectCount { get; set; }

public int MaxModelCount { get; set; }

public int MaxModelExports { get; set; }

public int MaxModelTests { get; set; }

public long MaxProjectSizeMegaBytes
{
get => MaxProjectSizeBytes / (1000 * 1000);
set => MaxProjectSizeBytes = value * 1000 * 1000;
}

public int? DefaultDurationDays { get; set; }

public DateTime? ExpiresAt { get; set; }

public DateTime? AssignedAt { get; set; }

public int IncludedMonthlyCredits { get; set; }
}
4 changes: 2 additions & 2 deletions src/OneWare.CloudIntegration/OneWareCloudIntegrationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace OneWare.CloudIntegration;
public class OneWareCloudIntegrationModule : IModule
{
public const string OneWareCloudHostKey = "General_OneWareCloud_Host";
public const string OneWareAccountEmailKey = "General_OneWareCloud_AccountEmail";
public const string OneWareAccountUserIdKey = "General_OneWareCloud_AccountUserId";
public const string CredentialStore = "https://cloud.one-ware.com";

public void RegisterTypes(IContainerRegistry containerRegistry)
Expand All @@ -34,7 +34,7 @@ public void OnInitialized(IContainerProvider containerProvider)
Environment.SetEnvironmentVariable("GCM_CREDENTIAL_STORE", "secretservice");

containerProvider.Resolve<ISettingsService>().RegisterSetting("General", "OneWare Cloud", OneWareCloudHostKey, new TextBoxSetting("Host", "https://cloud.one-ware.com", "https://cloud.one-ware.com"));
containerProvider.Resolve<ISettingsService>().RegisterCustom("General", "OneWare Cloud", OneWareAccountEmailKey, containerProvider.Resolve<OneWareCloudAccountSetting>());
containerProvider.Resolve<ISettingsService>().RegisterCustom("General", "OneWare Cloud", OneWareAccountUserIdKey, containerProvider.Resolve<OneWareCloudAccountSetting>());
containerProvider.Resolve<IWindowService>().RegisterUiExtension("MainWindow_BottomRightExtension", new UiExtension(x =>
new CloudIntegrationMainWindowBottomRightExtension()
{
Expand Down
47 changes: 24 additions & 23 deletions src/OneWare.CloudIntegration/Services/OneWareCloudLoginService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Net;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
Expand Down Expand Up @@ -44,7 +45,7 @@ public OneWareCloudLoginService(ILogger logger, ISettingsService settingService,
.Subscribe(x =>
{
Logout(settingService.GetSettingValue<string>(OneWareCloudIntegrationModule
.OneWareAccountEmailKey));
.OneWareAccountUserIdKey));
});

OneWareCloudIsUsed =
Expand All @@ -60,35 +61,35 @@ public RestClient GetRestClient()

public Task<(string? token, HttpStatusCode status)> GetLoggedInJwtTokenAsync()
{
var email = _settingService.GetSettingValue<string>(OneWareCloudIntegrationModule.OneWareAccountEmailKey);
var userId = _settingService.GetSettingValue<string>(OneWareCloudIntegrationModule.OneWareAccountUserIdKey);

return GetJwtTokenAsync(email);
return GetJwtTokenAsync(userId);
}

/// <summary>
/// Gives a JWT Token that has at least 5 minutes left before expiration
/// TODO Change this to return the full token implementation
/// </summary>
public async Task<(string? token, HttpStatusCode status)> GetJwtTokenAsync(string email)
public async Task<(string? token, HttpStatusCode status)> GetJwtTokenAsync(string userId)
{
await _semaphoreSlim.WaitAsync();

try
{
if (string.IsNullOrWhiteSpace(email)) return (null, HttpStatusCode.Unauthorized);
if (string.IsNullOrWhiteSpace(userId)) return (null, HttpStatusCode.Unauthorized);

_jwtTokenCache.TryGetValue(email, out var existingToken);
_jwtTokenCache.TryGetValue(userId, out var existingToken);

if (existingToken?.ValidTo > DateTime.UtcNow.AddMinutes(5))
{
return (existingToken.RawData, HttpStatusCode.NoContent);
}

var (result, status) = await RefreshFromEmailAsync(email);
var (result, status) = await RefreshFromUserIdAsync(userId);

if (!result) return (null, status);

if (!_jwtTokenCache.TryGetValue(email, out var regeneratedToken)) return (null, status);
if (!_jwtTokenCache.TryGetValue(userId, out var regeneratedToken)) return (null, status);

return (regeneratedToken.RawData, status);
}
Expand All @@ -98,14 +99,14 @@ public RestClient GetRestClient()
}
}

public async Task<(bool success, HttpStatusCode status)> RefreshFromEmailAsync(string email)
public async Task<(bool success, HttpStatusCode status)> RefreshFromUserIdAsync(string userId)
{
try
{
string? refreshToken = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var tokenPath = Path.Combine(_tokenPath, $"{email}.bin");
var tokenPath = Path.Combine(_tokenPath, $"{userId}.bin");
if (File.Exists(tokenPath))
{
var encrypted = await File.ReadAllBytesAsync(tokenPath);
Expand All @@ -117,7 +118,7 @@ public RestClient GetRestClient()
{
var store = CredentialManager.Create("oneware");

var cred = store.Get(OneWareCloudIntegrationModule.CredentialStore, email);
var cred = store.Get(OneWareCloudIntegrationModule.CredentialStore, userId);
refreshToken = cred?.Password;
}

Expand Down Expand Up @@ -204,26 +205,26 @@ public RestClient GetRestClient()
return (false, HttpStatusCode.NoContent);
}

public void Logout(string email)
public void Logout(string userId)
{
_settingService.SetSettingValue(OneWareCloudIntegrationModule.OneWareAccountEmailKey, "");
_settingService.SetSettingValue(OneWareCloudIntegrationModule.OneWareAccountUserIdKey, "");
_ = ContainerLocator.Container.Resolve<OneWareCloudNotificationService>().DisconnectAsync();

try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var tokenPath = Path.Combine(_tokenPath, $"{email}.bin");
var tokenPath = Path.Combine(_tokenPath, $"{userId}.bin");
if (File.Exists(tokenPath))
File.Delete(tokenPath);
}
else
{
var store = CredentialManager.Create("oneware");
store.Remove(OneWareCloudIntegrationModule.CredentialStore, email);
store.Remove(OneWareCloudIntegrationModule.CredentialStore, userId);
}

_jwtTokenCache.Remove(email);
_jwtTokenCache.Remove(userId);
}
catch (Exception e)
{
Expand Down Expand Up @@ -276,20 +277,20 @@ public async Task<bool> SendFeedbackAsync(string category, string message)
private void SaveCredentials(string jwt, string refreshToken)
{
var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(jwt);
var email = jwtToken.Claims.FirstOrDefault(x => x.Type == "unique_name")?.Value ?? null;
if (email == null)
var userId = jwtToken.Claims.FirstOrDefault(x => x.Type == "nameid")?.Value ?? null;
if (userId == null)
{
throw new Exception("Email not found");
throw new Exception("User ID not found in token");
}

_jwtTokenCache[email] = jwtToken;
_jwtTokenCache[userId] = jwtToken;

try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Directory.CreateDirectory(_tokenPath);
var tokenPath = Path.Combine(_tokenPath, $"{email}.bin");
var tokenPath = Path.Combine(_tokenPath, $"{userId}.bin");

var plaintext = Encoding.UTF8.GetBytes(refreshToken);
var encrypted = ProtectedData.Protect(plaintext, null, DataProtectionScope.CurrentUser);
Expand All @@ -298,15 +299,15 @@ private void SaveCredentials(string jwt, string refreshToken)
else
{
var store = CredentialManager.Create("oneware");
store.AddOrUpdate(OneWareCloudIntegrationModule.CredentialStore, email, refreshToken);
store.AddOrUpdate(OneWareCloudIntegrationModule.CredentialStore, userId, refreshToken);
}
}
catch (Exception e)
{
_logger.Error(e.Message, e);
}

_settingService.SetSettingValue(OneWareCloudIntegrationModule.OneWareAccountEmailKey, email);
_settingService.SetSettingValue(OneWareCloudIntegrationModule.OneWareAccountUserIdKey, userId);
_settingService.Save(_paths.SettingsPath);
}

Expand Down
60 changes: 0 additions & 60 deletions src/OneWare.CloudIntegration/Settings/CreditBalanceSetting.cs

This file was deleted.

Loading
Loading