mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 12:56:30 +00:00
add ramdisk, load AI model to ramdisk and start recognition from it
rewrite zmq to DEALER and ROUTER add GET_USER command to get CurrentUser from Python all auth is on the python side inference run and validate annotations on python
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
public class ApiConfig
|
||||
{
|
||||
public string Url { get; set; } = null!;
|
||||
public int RetryCount {get;set;}
|
||||
public double TimeoutSeconds { get; set; }
|
||||
public string ResourcesFolder { get; set; } = "";
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using MessagePack;
|
||||
|
||||
namespace Azaion.CommonSecurity.DTO.Commands;
|
||||
|
||||
[MessagePackObject]
|
||||
public class FileCommand
|
||||
{
|
||||
[Key("CommandType")]
|
||||
public CommandType CommandType { get; set; }
|
||||
|
||||
[Key("Filename")]
|
||||
public string Filename { get; set; }
|
||||
|
||||
[Key("Data")]
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public enum CommandType
|
||||
{
|
||||
None = 0,
|
||||
Inference = 1,
|
||||
Load = 2
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using MessagePack;
|
||||
|
||||
namespace Azaion.CommonSecurity.DTO.Commands;
|
||||
|
||||
[MessagePackObject]
|
||||
public class RemoteCommand(CommandType commandType, string? filename = null, byte[]? data = null)
|
||||
{
|
||||
[Key("CommandType")]
|
||||
public CommandType CommandType { get; set; } = commandType;
|
||||
|
||||
[Key("Filename")]
|
||||
public string? Filename { get; set; } = filename;
|
||||
|
||||
[Key("Data")]
|
||||
public byte[]? Data { get; set; } = data;
|
||||
}
|
||||
|
||||
public enum CommandType
|
||||
{
|
||||
None = 0,
|
||||
Inference = 1,
|
||||
Load = 2,
|
||||
GetUser = 3
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
public class SecureAppConfig
|
||||
{
|
||||
public ApiConfig ApiConfig { get; set; } = null!;
|
||||
}
|
||||
@@ -1,21 +1,11 @@
|
||||
using System.Security.Claims;
|
||||
using MessagePack;
|
||||
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
[MessagePackObject]
|
||||
public class User
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; }
|
||||
public RoleEnum Role { get; set; }
|
||||
|
||||
public User(IEnumerable<Claim> claims)
|
||||
{
|
||||
var claimDict = claims.ToDictionary(x => x.Type, x => x.Value);
|
||||
|
||||
Id = Guid.Parse(claimDict[SecurityConstants.CLAIM_NAME_ID]);
|
||||
Email = claimDict[SecurityConstants.CLAIM_EMAIL];
|
||||
if (!Enum.TryParse(claimDict[SecurityConstants.CLAIM_ROLE], out RoleEnum role))
|
||||
role = RoleEnum.None;
|
||||
Role = role;
|
||||
}
|
||||
[Key("i")]public string Id { get; set; }
|
||||
[Key("e")]public string Email { get; set; }
|
||||
[Key("r")]public RoleEnum Role { get; set; }
|
||||
}
|
||||
@@ -19,9 +19,8 @@ public class SecurityConstants
|
||||
#endregion ApiConfig
|
||||
|
||||
#region SocketClient
|
||||
public const string SOCKET_HOST = "127.0.0.1";
|
||||
public const int SOCKET_SEND_PORT = 5127;
|
||||
public const int SOCKET_RECEIVE_PORT = 5128;
|
||||
public const string ZMQ_HOST = "127.0.0.1";
|
||||
public const int ZMQ_PORT = 5127;
|
||||
|
||||
#endregion SocketClient
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public class AzaionApiClient(HttpClient httpClient) : IDisposable
|
||||
{
|
||||
const string JSON_MEDIA = "application/json";
|
||||
|
||||
private static ApiConfig _apiConfig = null!;
|
||||
|
||||
private string Email { get; set; } = null!;
|
||||
private SecureString Password { get; set; } = new();
|
||||
private string JwtToken { get; set; } = null!;
|
||||
public User User { get; set; } = null!;
|
||||
|
||||
public static AzaionApiClient Create(ApiCredentials credentials)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(SecurityConstants.CONFIG_PATH))
|
||||
throw new FileNotFoundException(SecurityConstants.CONFIG_PATH);
|
||||
var configStr = File.ReadAllText(SecurityConstants.CONFIG_PATH);
|
||||
_apiConfig = JsonConvert.DeserializeObject<SecureAppConfig>(configStr)!.ApiConfig;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
_apiConfig = new ApiConfig
|
||||
{
|
||||
Url = SecurityConstants.DEFAULT_API_URL,
|
||||
RetryCount = SecurityConstants.DEFAULT_API_RETRY_COUNT ,
|
||||
TimeoutSeconds = SecurityConstants.DEFAULT_API_TIMEOUT_SECONDS
|
||||
};
|
||||
}
|
||||
|
||||
var api = new AzaionApiClient(new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(_apiConfig.Url),
|
||||
Timeout = TimeSpan.FromSeconds(_apiConfig.TimeoutSeconds)
|
||||
});
|
||||
|
||||
api.EnterCredentials(credentials);
|
||||
return api;
|
||||
}
|
||||
|
||||
public void EnterCredentials(ApiCredentials credentials)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(credentials.Email) || string.IsNullOrWhiteSpace(credentials.Password))
|
||||
throw new Exception("Email or password is empty!");
|
||||
|
||||
Email = credentials.Email;
|
||||
Password = credentials.Password.ToSecureString();
|
||||
}
|
||||
|
||||
public async Task<Stream> GetResource(string fileName, string password, HardwareInfo hardware)
|
||||
{
|
||||
var response = await Send(httpClient, new HttpRequestMessage(HttpMethod.Post, $"/resources/get/{_apiConfig.ResourcesFolder}")
|
||||
{
|
||||
Content = new StringContent(JsonConvert.SerializeObject(new { fileName, password, hardware }), Encoding.UTF8, JSON_MEDIA)
|
||||
});
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
}
|
||||
|
||||
private async Task Authorize()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Email) || Password.Length == 0)
|
||||
throw new Exception("Email or password is empty! Please do EnterCredentials first!");
|
||||
|
||||
var payload = new
|
||||
{
|
||||
email = Email,
|
||||
password = Password.ToRealString()
|
||||
};
|
||||
var response = await httpClient.PostAsync(
|
||||
"login",
|
||||
new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, JSON_MEDIA));
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception($"EnterCredentials failed: {response.StatusCode}");
|
||||
|
||||
var responseData = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var result = JsonConvert.DeserializeObject<LoginResponse>(responseData);
|
||||
|
||||
if (string.IsNullOrEmpty(result?.Token))
|
||||
throw new Exception("JWT Token not found in response");
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var token = handler.ReadJwtToken(result.Token);
|
||||
|
||||
User = new User(token.Claims);
|
||||
JwtToken = result.Token;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> Send(HttpClient client, HttpRequestMessage request)
|
||||
{
|
||||
if (string.IsNullOrEmpty(JwtToken))
|
||||
await Authorize();
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", JwtToken);
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
await Authorize();
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", JwtToken);
|
||||
response = await client.SendAsync(request);
|
||||
}
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
return response;
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Failed: {response.StatusCode}! Result: {result}");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
httpClient.Dispose();
|
||||
Password.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Text;
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using MessagePack;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IResourceLoader
|
||||
{
|
||||
Task<MemoryStream> LoadFile(string fileName, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
public interface IAuthProvider
|
||||
{
|
||||
User CurrentUser { get; }
|
||||
}
|
||||
|
||||
|
||||
public class PythonResourceLoader : IResourceLoader, IAuthProvider
|
||||
{
|
||||
private readonly DealerSocket _dealer = new();
|
||||
private readonly Guid _clientId = Guid.NewGuid();
|
||||
|
||||
public User CurrentUser { get; }
|
||||
|
||||
public PythonResourceLoader(ApiCredentials credentials)
|
||||
{
|
||||
//Run python by credentials
|
||||
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
||||
_dealer.Connect($"tcp://{SecurityConstants.ZMQ_HOST}:{SecurityConstants.ZMQ_PORT}");
|
||||
|
||||
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.GetUser)));
|
||||
var user = _dealer.Get<User>(out _);
|
||||
if (user == null)
|
||||
throw new Exception("Can't get user from Auth provider");
|
||||
|
||||
CurrentUser = user;
|
||||
}
|
||||
|
||||
|
||||
public async Task<MemoryStream> LoadFile(string fileName, CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Load, fileName)));
|
||||
|
||||
if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(1000), out var bytes))
|
||||
throw new Exception($"Unable to receive {fileName}");
|
||||
|
||||
return await Task.FromResult(new MemoryStream(bytes));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to load fil0e '{fileName}': {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using MessagePack;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IResourceLoader
|
||||
{
|
||||
Task<MemoryStream> Load(string fileName, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public class PythonResourceLoader : IResourceLoader
|
||||
{
|
||||
private readonly PushSocket _pushSocket = new();
|
||||
private readonly PullSocket _pullSocket = new();
|
||||
|
||||
public PythonResourceLoader(ApiCredentials credentials)
|
||||
{
|
||||
//Run python by credentials
|
||||
_pushSocket.Connect($"tcp://{SecurityConstants.SOCKET_HOST}:{SecurityConstants.SOCKET_SEND_PORT}");
|
||||
_pullSocket.Connect($"tcp://{SecurityConstants.SOCKET_HOST}:{SecurityConstants.SOCKET_RECEIVE_PORT}");
|
||||
}
|
||||
|
||||
public async Task<MemoryStream> Load(string fileName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var b = MessagePackSerializer.Serialize(new FileCommand
|
||||
{
|
||||
CommandType = CommandType.Load,
|
||||
Filename = fileName
|
||||
});
|
||||
_pushSocket.SendFrame(b);
|
||||
|
||||
var bytes = _pullSocket.ReceiveFrameBytes(out bool more);
|
||||
return new MemoryStream(bytes);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to load fil0e '{fileName}': {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ResourceLoader(AzaionApiClient api, ApiCredentials credentials) : IResourceLoader
|
||||
{
|
||||
public async Task<MemoryStream> Load(string fileName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var hardwareService = new HardwareService();
|
||||
var hardwareInfo = hardwareService.GetHardware();
|
||||
|
||||
var encryptedStream = Task.Run(() => api.GetResource(fileName, credentials.Password, hardwareInfo), cancellationToken).Result;
|
||||
|
||||
var key = Security.MakeEncryptionKey(credentials.Email, credentials.Password, hardwareInfo.Hash);
|
||||
var stream = new MemoryStream();
|
||||
await encryptedStream.DecryptTo(stream, key, cancellationToken);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using MessagePack;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.CommonSecurity;
|
||||
|
||||
public static class ZeroMqExtensions
|
||||
{
|
||||
public static T? Get<T>(this DealerSocket dealer, out byte[] message)
|
||||
{
|
||||
if (!dealer.TryReceiveFrameBytes(TimeSpan.FromMinutes(2), out var bytes))
|
||||
throw new Exception($"Unable to get {typeof(T).Name}");
|
||||
message = bytes;
|
||||
return MessagePackSerializer.Deserialize<T>(bytes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user