feat: add ERP agent pet bridge and startup guide
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Lskj.AgentPet.Host;
|
||||
|
||||
internal static class WindowsCredentialStore
|
||||
{
|
||||
internal const string DefaultAstrBotTarget = "Langsu.Lserp.AstrBot.ApiKey";
|
||||
private const uint CredentialTypeGeneric = 1;
|
||||
private const int MaximumSecretBytes = 2048;
|
||||
|
||||
public static void PopulateAstrBotApiKey(IDictionary<string, string?> environment)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(environment);
|
||||
if (environment.TryGetValue("LSERP_ASTRBOT_API_KEY", out string? configured)
|
||||
&& !string.IsNullOrWhiteSpace(configured)) return;
|
||||
|
||||
string target = environment.TryGetValue("LSERP_ASTRBOT_CREDENTIAL_TARGET", out string? custom)
|
||||
&& !string.IsNullOrWhiteSpace(custom)
|
||||
? custom.Trim()
|
||||
: DefaultAstrBotTarget;
|
||||
if (target.Length is < 8 or > 128) return;
|
||||
|
||||
string? secret = ReadGenericSecret(target);
|
||||
if (!string.IsNullOrWhiteSpace(secret)) environment["LSERP_ASTRBOT_API_KEY"] = secret;
|
||||
}
|
||||
|
||||
private static string? ReadGenericSecret(string target)
|
||||
{
|
||||
if (!OperatingSystem.IsWindows()) return null;
|
||||
if (!CredRead(target, CredentialTypeGeneric, 0, out nint address) || address == 0)
|
||||
return null;
|
||||
try
|
||||
{
|
||||
NativeCredential credential = Marshal.PtrToStructure<NativeCredential>(address);
|
||||
if (credential.CredentialBlob == 0
|
||||
|| credential.CredentialBlobSize is 0 or > MaximumSecretBytes)
|
||||
return null;
|
||||
byte[] bytes = new byte[credential.CredentialBlobSize];
|
||||
Marshal.Copy(credential.CredentialBlob, bytes, 0, bytes.Length);
|
||||
return Encoding.Unicode.GetString(bytes).TrimEnd('\0').Trim();
|
||||
}
|
||||
finally
|
||||
{
|
||||
CredFree(address);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CredRead(
|
||||
string target,
|
||||
uint type,
|
||||
int reservedFlag,
|
||||
out nint credential);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = false)]
|
||||
private static extern void CredFree(nint buffer);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct NativeCredential
|
||||
{
|
||||
public uint Flags;
|
||||
public uint Type;
|
||||
public nint TargetName;
|
||||
public nint Comment;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
||||
public uint CredentialBlobSize;
|
||||
public nint CredentialBlob;
|
||||
public uint Persist;
|
||||
public uint AttributeCount;
|
||||
public nint Attributes;
|
||||
public nint TargetAlias;
|
||||
public nint UserName;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user