7da303d7bb
Inbound-API bearer credentials are no longer persisted in plaintext. ApiKey now holds a KeyHash (peppered HMAC-SHA256); the key is shown once at creation and only its hash is stored. Lookup and validation hash the presented candidate. Cross-module: Commons (ApiKey, ApiKeyHasher), ConfigurationDatabase (mapping + HashApiKeyValue migration), InboundAPI (ApiKeyValidator), ManagementService (key creation), CentralUI (ApiKeys.razor). Existing keys must be re-issued.
78 lines
2.7 KiB
C#
78 lines
2.7 KiB
C#
using Microsoft.EntityFrameworkCore.Migrations;
|
|
|
|
#nullable disable
|
|
|
|
namespace ScadaLink.ConfigurationDatabase.Migrations
|
|
{
|
|
/// <summary>
|
|
/// ConfigurationDatabase-012: replaces the plaintext <c>KeyValue</c> column with a
|
|
/// <c>KeyHash</c> column holding a deterministic HMAC-SHA256 hash of the key.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// A hash is one-way: existing plaintext keys cannot be converted to hashes
|
|
/// without the originals. This migration therefore deletes all existing API-key
|
|
/// rows. <strong>Every existing API key must be re-issued</strong> after this
|
|
/// migration is applied — create new keys via the CLI / Management API / Central
|
|
/// UI, distribute the one-time plaintext to callers, and approve them on methods.
|
|
/// </remarks>
|
|
public partial class HashApiKeyValue : Migration
|
|
{
|
|
/// <inheritdoc />
|
|
protected override void Up(MigrationBuilder migrationBuilder)
|
|
{
|
|
// Existing keys hold only plaintext, which cannot be hashed back. They
|
|
// must be re-issued, so remove them before the column change to keep the
|
|
// new unique KeyHash index satisfiable.
|
|
migrationBuilder.Sql("DELETE FROM ApiKeys;");
|
|
|
|
migrationBuilder.DropIndex(
|
|
name: "IX_ApiKeys_KeyValue",
|
|
table: "ApiKeys");
|
|
|
|
migrationBuilder.DropColumn(
|
|
name: "KeyValue",
|
|
table: "ApiKeys");
|
|
|
|
migrationBuilder.AddColumn<string>(
|
|
name: "KeyHash",
|
|
table: "ApiKeys",
|
|
type: "nvarchar(256)",
|
|
maxLength: 256,
|
|
nullable: false,
|
|
defaultValue: "");
|
|
|
|
migrationBuilder.CreateIndex(
|
|
name: "IX_ApiKeys_KeyHash",
|
|
table: "ApiKeys",
|
|
column: "KeyHash",
|
|
unique: true);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void Down(MigrationBuilder migrationBuilder)
|
|
{
|
|
migrationBuilder.DropIndex(
|
|
name: "IX_ApiKeys_KeyHash",
|
|
table: "ApiKeys");
|
|
|
|
migrationBuilder.DropColumn(
|
|
name: "KeyHash",
|
|
table: "ApiKeys");
|
|
|
|
migrationBuilder.AddColumn<string>(
|
|
name: "KeyValue",
|
|
table: "ApiKeys",
|
|
type: "nvarchar(500)",
|
|
maxLength: 500,
|
|
nullable: false,
|
|
defaultValue: "");
|
|
|
|
migrationBuilder.CreateIndex(
|
|
name: "IX_ApiKeys_KeyValue",
|
|
table: "ApiKeys",
|
|
column: "KeyValue",
|
|
unique: true);
|
|
}
|
|
}
|
|
}
|