Add support for external actions

This commit is contained in:
Bernd Schoolmann 2025-12-09 18:46:35 +01:00
parent 9726f4994f
commit b4681ccf99
No known key found for this signature in database
5 changed files with 153 additions and 133 deletions

View File

@ -48,7 +48,14 @@ public interface IUserRepository : IRepository<User, Guid>
/// <summary>
/// Sets the account cryptographic state to a user in a single transaction. The provided
/// MUST be a V2 encryption state. Passing in a V1 encryption state will throw.
/// Extra actions can be passed in case other user data needs to be updated in the same transaction.
/// </summary>
Task SetV2AccountCryptographicStateAsync(Guid userId, UserAccountKeysData accountKeysData);
Task SetV2AccountCryptographicStateAsync(
Guid userId,
UserAccountKeysData accountKeysData,
IEnumerable<UpdateUserData>? updateUserDataActions = null);
Task DeleteManyAsync(IEnumerable<User> users);
}
public delegate Task UpdateUserData(Microsoft.Data.SqlClient.SqlConnection? connection = null,
Microsoft.Data.SqlClient.SqlTransaction? transaction = null);

View File

@ -288,7 +288,10 @@ public class UserRepository : Repository<User, Guid>, IUserRepository
UnprotectData(user);
}
public async Task SetV2AccountCryptographicStateAsync(Guid userId, UserAccountKeysData accountKeysData)
public async Task SetV2AccountCryptographicStateAsync(
Guid userId,
UserAccountKeysData accountKeysData,
IEnumerable<UpdateUserData>? updateUserDataActions = null)
{
if (!accountKeysData.IsV2Encryption())
{
@ -299,27 +302,47 @@ public class UserRepository : Repository<User, Guid>, IUserRepository
var signatureKeyPairId = CoreHelpers.GenerateComb();
await using var connection = new SqlConnection(ConnectionString);
await using var cmd = new SqlCommand("[dbo].[User_UpdateAccountCryptographicState]", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Id", SqlDbType.UniqueIdentifier).Value = userId;
cmd.Parameters.Add("@PublicKey", SqlDbType.NVarChar).Value = accountKeysData.PublicKeyEncryptionKeyPairData.PublicKey;
cmd.Parameters.Add("@PrivateKey", SqlDbType.NVarChar).Value = accountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey;
cmd.Parameters.Add("@SignedPublicKey", SqlDbType.NVarChar).Value =
accountKeysData.PublicKeyEncryptionKeyPairData.SignedPublicKey;
cmd.Parameters.Add("@SecurityState", SqlDbType.NVarChar).Value =
accountKeysData.SecurityStateData!.SecurityState;
cmd.Parameters.Add("@SecurityVersion", SqlDbType.Int).Value =
accountKeysData.SecurityStateData!.SecurityVersion;
cmd.Parameters.Add("@SignatureKeyPairId", SqlDbType.UniqueIdentifier).Value = signatureKeyPairId;
cmd.Parameters.Add("@SignatureAlgorithm", SqlDbType.TinyInt).Value = accountKeysData.SignatureKeyPairData!.SignatureAlgorithm;
cmd.Parameters.Add("@SigningKey", SqlDbType.VarChar).Value =
accountKeysData.SignatureKeyPairData!.WrappedSigningKey;
cmd.Parameters.Add("@VerifyingKey", SqlDbType.VarChar).Value =
accountKeysData.SignatureKeyPairData!.VerifyingKey;
cmd.Parameters.Add("@RevisionDate", SqlDbType.DateTime2).Value = timestamp;
cmd.Parameters.Add("@AccountRevisionDate", SqlDbType.DateTime2).Value = timestamp;
await connection.OpenAsync();
await cmd.ExecuteNonQueryAsync();
await using var transaction = connection.BeginTransaction();
try
{
await connection.ExecuteAsync(
"[dbo].[User_UpdateAccountCryptographicState]",
new
{
Id = userId,
PublicKey = accountKeysData.PublicKeyEncryptionKeyPairData.PublicKey,
PrivateKey = accountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey,
SignedPublicKey = accountKeysData.PublicKeyEncryptionKeyPairData.SignedPublicKey,
SecurityState = accountKeysData.SecurityStateData!.SecurityState,
SecurityVersion = accountKeysData.SecurityStateData!.SecurityVersion,
SignatureKeyPairId = signatureKeyPairId,
SignatureAlgorithm = accountKeysData.SignatureKeyPairData!.SignatureAlgorithm,
SigningKey = accountKeysData.SignatureKeyPairData!.WrappedSigningKey,
VerifyingKey = accountKeysData.SignatureKeyPairData!.VerifyingKey,
RevisionDate = timestamp,
AccountRevisionDate = timestamp
},
transaction: transaction,
commandType: CommandType.StoredProcedure);
// Update user data that depends on cryptographic state
if (updateUserDataActions != null)
{
foreach (var action in updateUserDataActions)
{
await action(connection, transaction);
}
}
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
public async Task<IEnumerable<User>> GetManyAsync(IEnumerable<Guid> ids)

View File

@ -242,7 +242,10 @@ public class UserRepository : Repository<Core.Entities.User, User, Guid>, IUserR
await transaction.CommitAsync();
}
public async Task SetV2AccountCryptographicStateAsync(Guid userId, UserAccountKeysData accountKeysData)
public async Task SetV2AccountCryptographicStateAsync(
Guid userId,
UserAccountKeysData accountKeysData,
IEnumerable<UpdateUserData>? updateUserDataActions = null)
{
if (!accountKeysData.IsV2Encryption())
{
@ -301,6 +304,15 @@ public class UserRepository : Repository<Core.Entities.User, User, Guid>, IUserR
}
await dbContext.SaveChangesAsync();
// Update additional user data within the same transaction
if (updateUserDataActions != null)
{
foreach (var action in updateUserDataActions)
{
await action();
}
}
await transaction.CommitAsync();
}

View File

@ -15,62 +15,51 @@ AS
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION
UPDATE
[dbo].[User]
SET
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[SignedPublicKey] = @SignedPublicKey,
[SecurityState] = @SecurityState,
[SecurityVersion] = @SecurityVersion,
[RevisionDate] = @RevisionDate,
[AccountRevisionDate] = @AccountRevisionDate
WHERE
[Id] = @Id
BEGIN TRY
UPDATE
[dbo].[User]
IF EXISTS (SELECT 1 FROM [dbo].[UserSignatureKeyPair] WHERE [UserId] = @Id)
BEGIN
UPDATE [dbo].[UserSignatureKeyPair]
SET
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[SignedPublicKey] = @SignedPublicKey,
[SecurityState] = @SecurityState,
[SecurityVersion] = @SecurityVersion,
[RevisionDate] = @RevisionDate,
[AccountRevisionDate] = @AccountRevisionDate
[SignatureAlgorithm] = @SignatureAlgorithm,
[SigningKey] = @SigningKey,
[VerifyingKey] = @VerifyingKey,
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
IF EXISTS (SELECT 1 FROM [dbo].[UserSignatureKeyPair] WHERE [UserId] = @Id)
BEGIN
UPDATE [dbo].[UserSignatureKeyPair]
SET
[SignatureAlgorithm] = @SignatureAlgorithm,
[SigningKey] = @SigningKey,
[VerifyingKey] = @VerifyingKey,
[RevisionDate] = @RevisionDate
WHERE
[UserId] = @Id
END
ELSE
BEGIN
INSERT INTO [dbo].[UserSignatureKeyPair]
(
[Id],
[UserId],
[SignatureAlgorithm],
[SigningKey],
[VerifyingKey],
[CreationDate],
[RevisionDate]
)
VALUES
(
@SignatureKeyPairId,
@Id,
@SignatureAlgorithm,
@SigningKey,
@VerifyingKey,
@RevisionDate,
@RevisionDate
)
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
THROW
END CATCH
[UserId] = @Id
END
ELSE
BEGIN
INSERT INTO [dbo].[UserSignatureKeyPair]
(
[Id],
[UserId],
[SignatureAlgorithm],
[SigningKey],
[VerifyingKey],
[CreationDate],
[RevisionDate]
)
VALUES
(
@SignatureKeyPairId,
@Id,
@SignatureAlgorithm,
@SigningKey,
@VerifyingKey,
@RevisionDate,
@RevisionDate
)
END
END

View File

@ -21,63 +21,52 @@ AS
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION
UPDATE
[dbo].[User]
SET
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[SignedPublicKey] = @SignedPublicKey,
[SecurityState] = @SecurityState,
[SecurityVersion] = @SecurityVersion,
[RevisionDate] = @RevisionDate,
[AccountRevisionDate] = @AccountRevisionDate
WHERE
[Id] = @Id
BEGIN TRY
UPDATE
[dbo].[User]
IF EXISTS (SELECT 1 FROM [dbo].[UserSignatureKeyPair] WHERE [UserId] = @Id)
BEGIN
UPDATE [dbo].[UserSignatureKeyPair]
SET
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[SignedPublicKey] = @SignedPublicKey,
[SecurityState] = @SecurityState,
[SecurityVersion] = @SecurityVersion,
[RevisionDate] = @RevisionDate,
[AccountRevisionDate] = @AccountRevisionDate
[SignatureAlgorithm] = @SignatureAlgorithm,
[SigningKey] = @SigningKey,
[VerifyingKey] = @VerifyingKey,
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
IF EXISTS (SELECT 1 FROM [dbo].[UserSignatureKeyPair] WHERE [UserId] = @Id)
BEGIN
UPDATE [dbo].[UserSignatureKeyPair]
SET
[SignatureAlgorithm] = @SignatureAlgorithm,
[SigningKey] = @SigningKey,
[VerifyingKey] = @VerifyingKey,
[RevisionDate] = @RevisionDate
WHERE
[UserId] = @Id
END
ELSE
BEGIN
INSERT INTO [dbo].[UserSignatureKeyPair]
(
[Id],
[UserId],
[SignatureAlgorithm],
[SigningKey],
[VerifyingKey],
[CreationDate],
[RevisionDate]
)
VALUES
(
@SignatureKeyPairId,
@Id,
@SignatureAlgorithm,
@SigningKey,
@VerifyingKey,
@RevisionDate,
@RevisionDate
)
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
THROW
END CATCH
[UserId] = @Id
END
ELSE
BEGIN
INSERT INTO [dbo].[UserSignatureKeyPair]
(
[Id],
[UserId],
[SignatureAlgorithm],
[SigningKey],
[VerifyingKey],
[CreationDate],
[RevisionDate]
)
VALUES
(
@SignatureKeyPairId,
@Id,
@SignatureAlgorithm,
@SigningKey,
@VerifyingKey,
@RevisionDate,
@RevisionDate
)
END
END
GO