142 lines
No EOL
4.9 KiB
C#
142 lines
No EOL
4.9 KiB
C#
using System;
|
|
using System.IO;
|
|
using JD.Oasis.Service.API;
|
|
using System.Net;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace JD.Oasis.Service.Implementation
|
|
{
|
|
/// <summary>
|
|
/// Will store/retrieve alternative credentials for Oasis Web Service from a local file
|
|
/// or in case the file is not yer created, it will ask the caller to provide a set of credentials
|
|
/// using the getCredentials function
|
|
/// </summary>
|
|
public class CredentialsManager : ICredentialsManager
|
|
{
|
|
private readonly Func<Tuple<string, string>> _getCredentialsFunc;
|
|
|
|
// Random generated entropy to secure the storage of the credentials on Windows
|
|
private static readonly byte[] Entropy =
|
|
{165, 55, 145, 89, 198, 64, 173, 97, 65, 167, 166, 17, 61, 186, 161, 51, 84, 219, 177, 211};
|
|
|
|
private const string LOGIN_FILE_NAME = @"oasiswebservicelogin.json";
|
|
|
|
private string _loginFilePath;
|
|
public string LoginFilePath
|
|
{
|
|
get
|
|
{
|
|
if (_loginFilePath == null)
|
|
{
|
|
// Default is under Ubisoft App Data folder
|
|
_loginFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Ubisoft", LOGIN_FILE_NAME);
|
|
}
|
|
|
|
return _loginFilePath;
|
|
}
|
|
set => _loginFilePath = value;
|
|
}
|
|
|
|
public CredentialsManager(Func<Tuple<string, string>> getCredentialsFunc)
|
|
{
|
|
_getCredentialsFunc = getCredentialsFunc ?? throw new ArgumentNullException(nameof(getCredentialsFunc));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool UseAltCredentials => GetLocalLogin() != null;
|
|
|
|
/// <inheritdoc />
|
|
public bool TryGetStoredAltCredentials(out NetworkCredential networkCredential)
|
|
{
|
|
networkCredential = null;
|
|
|
|
// First we try to see if there is any local alternative login already set
|
|
ServiceLogin localLogin = GetLocalLogin();
|
|
|
|
if (localLogin != null)
|
|
{
|
|
// A local login is present, we decrypt the password and use those credentials
|
|
|
|
byte[] pwBytes = ProtectedData.Unprotect(localLogin.Password, Entropy, DataProtectionScope.CurrentUser);
|
|
|
|
networkCredential = new NetworkCredential(localLogin.Username, Encoding.Unicode.GetString(pwBytes));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryAskAltCredentials(out NetworkCredential networkCredential)
|
|
{
|
|
networkCredential = null;
|
|
|
|
// no local login, we run the function to fetch the credentials
|
|
Tuple<string, string> credentials = _getCredentialsFunc();
|
|
|
|
if (credentials != null &&
|
|
credentials.Item1 != null && credentials.Item2 != null)
|
|
{
|
|
networkCredential = new NetworkCredential(credentials.Item1, credentials.Item2);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void StoreAltCredentials(string username, string password)
|
|
{
|
|
if (string.IsNullOrEmpty(password))
|
|
throw new ArgumentException("Value cannot be null or empty.", nameof(password));
|
|
if (string.IsNullOrEmpty(username))
|
|
throw new ArgumentException("Value cannot be null or empty.", nameof(username));
|
|
|
|
// Data to protect. Convert a string to a byte[] using Unicode encoding.
|
|
byte[] plaintext = Encoding.Unicode.GetBytes(password);
|
|
|
|
// Encrypt the password using Windows private keys
|
|
byte[] ciphertext = ProtectedData.Protect(plaintext, Entropy, DataProtectionScope.CurrentUser);
|
|
|
|
// serialize ciphertext
|
|
string serializedLogin = JsonConvert.SerializeObject(new ServiceLogin {Username = username, Password = ciphertext},
|
|
Formatting.Indented);
|
|
|
|
File.WriteAllText(LoginFilePath, serializedLogin);
|
|
}
|
|
|
|
private ServiceLogin GetLocalLogin()
|
|
{
|
|
// if file does not exist, there is no local login
|
|
if (!File.Exists(LoginFilePath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
ServiceLogin login = JsonConvert.DeserializeObject<ServiceLogin>(File.ReadAllText(LoginFilePath));
|
|
|
|
return login;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// file is unreadable
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ServiceLogin
|
|
{
|
|
[JsonProperty("username", Required = Required.Always)]
|
|
public string Username { get; set; }
|
|
|
|
[JsonProperty("password", Required = Required.Always)]
|
|
public byte[] Password { get; set; }
|
|
}
|
|
} |