using ScadaLink.Commons.Types.InboundApi;
namespace ScadaLink.Commons.Entities.InboundApi;
///
/// An inbound-API bearer credential. Per ConfigurationDatabase-012 the plaintext key
/// is never persisted: the entity stores only , a deterministic
/// keyed hash of the key (HMAC-SHA256 with a server-side pepper). The plaintext is
/// generated at creation, shown to the operator exactly once, and then discarded.
///
public class ApiKey
{
/// Database primary key.
public int Id { get; set; }
/// Display name for the API key.
public string Name { get; set; }
///
/// Deterministic keyed hash of the API key value. This is the only form of the
/// credential persisted; the plaintext key is never stored. Authentication hashes
/// the presented candidate with the same scheme and compares against this value.
///
public string KeyHash { get; set; }
/// When false, the key is rejected even if the hash matches.
public bool IsEnabled { get; set; }
///
/// Creates an API key from a plaintext value, immediately hashing it with the
/// unpeppered default hasher () so the entity
/// never holds the plaintext. Production code paths that have a configured pepper
/// should use with a peppered hash instead.
///
/// Display name for the API key.
/// Plaintext key value; hashed immediately and never stored.
public ApiKey(string name, string keyValue)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
if (keyValue is null) throw new ArgumentNullException(nameof(keyValue));
KeyHash = ApiKeyHasher.Default.Hash(keyValue);
}
///
/// Parameterless constructor for the EF Core materializer. Application code uses
/// or .
///
private ApiKey()
{
Name = string.Empty;
KeyHash = string.Empty;
}
///
/// Creates an API key from an already-computed key hash. Used by the creation
/// path, which generates a random key, hashes it with the configured (peppered)
/// , and stores only the resulting hash.
///
/// Display name for the API key.
/// Pre-computed keyed hash of the API key value.
public static ApiKey FromHash(string name, string keyHash)
{
return new ApiKey
{
Name = name ?? throw new ArgumentNullException(nameof(name)),
KeyHash = keyHash ?? throw new ArgumentNullException(nameof(keyHash)),
};
}
}