Add IApplicationManager
This commit is contained in:
9
Akari.Prototype.Server/Models/Application.cs
Normal file
9
Akari.Prototype.Server/Models/Application.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Server.Models
|
||||
{
|
||||
public record Application(string Name, string TokenHash, IList<string> Fingerprints);
|
||||
}
|
||||
148
Akari.Prototype.Server/Services/ApplicationsManager.cs
Normal file
148
Akari.Prototype.Server/Services/ApplicationsManager.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using Akari.Prototype.Server.Models;
|
||||
using Akari.Prototype.Server.Utils;
|
||||
using Isopoh.Cryptography.Argon2;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Server.Services
|
||||
{
|
||||
public class ApplicationsManager : IApplicationsManager
|
||||
{
|
||||
public const string ApplicationsPath = "applications.json";
|
||||
public const int ApplicationTokenLength = 18;
|
||||
|
||||
private readonly ILogger<ApplicationsManager> _logger;
|
||||
private readonly IKeyManager _keyManager;
|
||||
private readonly IAuthManager _authManager;
|
||||
private readonly AkariPath _akariPath;
|
||||
|
||||
private IDictionary<string, Application> _applications;
|
||||
|
||||
public ApplicationsManager(ILogger<ApplicationsManager> logger, IKeyManager keyManager, IAuthManager authManager, AkariPath akariPath)
|
||||
{
|
||||
_logger = logger;
|
||||
_keyManager = keyManager;
|
||||
_authManager = authManager;
|
||||
_akariPath = akariPath;
|
||||
|
||||
LoadApplications();
|
||||
}
|
||||
|
||||
private void LoadApplications()
|
||||
{
|
||||
var path = _akariPath.GetPath(ApplicationsPath);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
_applications = new Dictionary<string, Application>();
|
||||
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(_applications.Values));
|
||||
}
|
||||
else
|
||||
{
|
||||
_applications = JsonSerializer.Deserialize<ICollection<Application>>(File.ReadAllText(path)).ToDictionary(a => a.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveApplications()
|
||||
{
|
||||
var path = _akariPath.GetPath(ApplicationsPath);
|
||||
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(_applications.Values));
|
||||
}
|
||||
|
||||
public bool AddFingerprint(string applicationName, string applicationToken, string fingerprintName, string masterPassword)
|
||||
{
|
||||
if (!VerifyToken(applicationName, applicationToken))
|
||||
{
|
||||
_logger.LogDebug($"Wrong token provided for {applicationName}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify master password
|
||||
|
||||
// Try get fingerprint key
|
||||
if (!_authManager.TryGetKey(fingerprintName, out var key))
|
||||
{
|
||||
_logger.LogDebug($"Fingerprint '{fingerprintName}' has not been auth");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encrypt key
|
||||
//_keyManager.AddFingerprint(applicationName, fingerprintName, key, masterPassword);
|
||||
|
||||
_applications[applicationName].Fingerprints.Add(fingerprintName);
|
||||
|
||||
SaveApplications();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool VerifyToken(string applicationName, string token)
|
||||
{
|
||||
if (!_applications.TryGetValue(applicationName, out var application))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Argon2.Verify(application.TokenHash, Convert.FromBase64String(token));
|
||||
}
|
||||
|
||||
public bool TryCreate(string applicationName, out string token)
|
||||
{
|
||||
if (_applications.ContainsKey(applicationName))
|
||||
{
|
||||
_logger.LogDebug($"Can't create '{applicationName}' as it already exists");
|
||||
|
||||
token = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var tokenData = new byte[ApplicationTokenLength];
|
||||
|
||||
RandomNumberGenerator.Fill(tokenData);
|
||||
|
||||
var hash = Security.NewArgon2idHash(tokenData);
|
||||
|
||||
var application = new Application(applicationName, hash, new List<string>());
|
||||
|
||||
_applications[applicationName] = application;
|
||||
|
||||
token = Convert.ToBase64String(tokenData);
|
||||
|
||||
SaveApplications();
|
||||
|
||||
_logger.LogDebug($"Application '{applicationName}' created");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryRetrieveKey(string applicationName, string token, out AesGcm key)
|
||||
{
|
||||
key = null;
|
||||
|
||||
if (!_applications.TryGetValue(applicationName, out var application))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VerifyToken(applicationName, token))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//key = _authManager.Retrieve(applicationName);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,16 +25,14 @@ namespace Akari.Prototype.Server.Services
|
||||
public IEnumerable<KeyValuePair<string, TimedEntry<AesGcm>>> Pairs => _keys;
|
||||
|
||||
private readonly ILogger<AuthManager> _logger;
|
||||
private readonly IKeyManager _keyManager;
|
||||
private readonly AkariPath _akariPath;
|
||||
|
||||
private IDictionary<string, TimedEntry<AesGcm>> _keys;
|
||||
private IDictionary<string, string> _salts;
|
||||
|
||||
public AuthManager(ILogger<AuthManager> logger, IKeyManager keyManager, AkariPath akariPath)
|
||||
public AuthManager(ILogger<AuthManager> logger, AkariPath akariPath)
|
||||
{
|
||||
_logger = logger;
|
||||
_keyManager = keyManager;
|
||||
_akariPath = akariPath;
|
||||
|
||||
_keys = new ConcurrentDictionary<string, TimedEntry<AesGcm>>();
|
||||
@@ -56,7 +54,7 @@ namespace Akari.Prototype.Server.Services
|
||||
// Load
|
||||
else
|
||||
{
|
||||
_salts = JsonSerializer.Deserialize<IDictionary<string, string>>(File.ReadAllText(path));
|
||||
_salts = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Akari.Prototype.Server.Services
|
||||
// Load
|
||||
else
|
||||
{
|
||||
_fingerprintsHash = JsonSerializer.Deserialize<IDictionary<string, string>>(File.ReadAllText(path));
|
||||
_fingerprintsHash = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
Akari.Prototype.Server/Services/IApplicationsManager.cs
Normal file
18
Akari.Prototype.Server/Services/IApplicationsManager.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Akari.Prototype.Server.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Server.Services
|
||||
{
|
||||
public interface IApplicationsManager
|
||||
{
|
||||
bool TryCreate(string applicationName, out string token);
|
||||
|
||||
bool AddFingerprint(string applicationName, string applicationToken, string fingerprintName, string masterPassword);
|
||||
|
||||
bool TryRetrieveKey(string applicationName, string token, out AesGcm key);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ namespace Akari.Prototype.Server.Services
|
||||
|
||||
void Auth(string name, byte[] token);
|
||||
|
||||
bool TryGetKey(string name, out AesGcm key);
|
||||
|
||||
bool Remove(string name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Akari.Prototype.Server
|
||||
services.AddSingleton<IFingerprintManager, FingerprintManager>();
|
||||
services.AddSingleton<IAuthManager, AuthManager>();
|
||||
services.AddSingleton<IKeyManager, KeyManager>();
|
||||
services.AddSingleton<IApplicationsManager, ApplicationsManager>();
|
||||
services.AddSingleton<AkariPath>();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user