Files
Masterarbeit/Versuche/Versuch 02/Ergenisse/software/Security_Patterns.md

36 KiB

Security Patterns and Implementation - Centron Enterprise Application

Overview

This document provides comprehensive coverage of security patterns and implementations within the Centron .NET 8 enterprise application. Security patterns encompass authentication, authorization, data protection, audit logging, secure communication, and security boundary enforcement that protect the application and its data from threats.

Security Architecture

Multi-Layered Security Approach

The Centron application implements a defense-in-depth security strategy with multiple security layers:

  1. Network Security: Secure communication protocols and network segmentation
  2. Application Security: Authentication, authorization, and input validation
  3. Data Security: Encryption, data protection, and secure storage
  4. Audit Security: Comprehensive logging and monitoring
  5. Operational Security: Secure deployment and configuration management

Authentication Patterns

1. Multi-Authentication Strategy Pattern

The application supports multiple authentication mechanisms through a unified interface:

public interface IAuthenticator
{
    Task<Result<AuthenticationResult>> AuthenticateAsync(AuthenticationRequest request);
    Task<Result> ValidateTokenAsync(string token);
    Task<Result> RefreshTokenAsync(string refreshToken);
    bool SupportsAuthenticationType(AuthenticationType type);
}

public class AuthenticatorFactory
{
    private readonly Dictionary<AuthenticationType, IAuthenticator> _authenticators = new();

    public AuthenticatorFactory()
    {
        _authenticators[AuthenticationType.Basic] = new BasicAuthenticator();
        _authenticators[AuthenticationType.ActiveDirectory] = new ActiveDirectoryAuthenticator();
        _authenticators[AuthenticationType.OpenIdConnect] = new OpenIdConnectAuthenticator();
        _authenticators[AuthenticationType.WebAccount] = new WebAccountAuthenticator();
    }

    public IAuthenticator GetAuthenticator(AuthenticationType type)
    {
        return _authenticators.ContainsKey(type)
            ? _authenticators[type]
            : throw new NotSupportedException($"Authentication type {type} is not supported");
    }
}

// Basic Authentication Implementation
public class BasicAuthenticator : IAuthenticator
{
    private readonly IUserRepository _userRepository;
    private readonly IPasswordHashingService _passwordService;

    public async Task<Result<AuthenticationResult>> AuthenticateAsync(AuthenticationRequest request)
    {
        try
        {
            Guard.NotNullOrEmpty(request.Username, nameof(request.Username));
            Guard.NotNullOrEmpty(request.Password, nameof(request.Password));

            var userResult = await _userRepository.GetUserByUsernameAsync(request.Username);
            if (userResult.Status != ResultStatus.Success || userResult.Data == null)
                return Result<AuthenticationResult>.AsError("Invalid username or password");

            var user = userResult.Data;

            // Check if account is locked
            if (user.IsLocked)
                return Result<AuthenticationResult>.AsError("Account is locked. Please contact administrator.");

            // Verify password
            var passwordValid = _passwordService.VerifyPassword(request.Password, user.PasswordHash, user.Salt);
            if (!passwordValid)
            {
                await IncrementFailedLoginAttempts(user);
                return Result<AuthenticationResult>.AsError("Invalid username or password");
            }

            // Reset failed login attempts on successful login
            await ResetFailedLoginAttempts(user);

            // Generate authentication token
            var token = GenerateAuthenticationToken(user);

            return Result<AuthenticationResult>.AsSuccess(new AuthenticationResult
            {
                User = user,
                Token = token,
                ExpiresAt = DateTime.UtcNow.AddHours(8),
                AuthenticationType = AuthenticationType.Basic
            });
        }
        catch (Exception ex)
        {
            return Result<AuthenticationResult>.AsError($"Authentication failed: {ex.Message}");
        }
    }

    private async Task IncrementFailedLoginAttempts(User user)
    {
        user.FailedLoginAttempts++;
        user.LastFailedLogin = DateTime.UtcNow;

        // Lock account after 5 failed attempts
        if (user.FailedLoginAttempts >= 5)
        {
            user.IsLocked = true;
            user.LockedAt = DateTime.UtcNow;
        }

        await _userRepository.UpdateUserAsync(user);
    }
}

// Active Directory Authentication
public class ActiveDirectoryAuthenticator : IAuthenticator
{
    private readonly IActiveDirectoryService _adService;
    private readonly IUserMappingService _userMappingService;

    public async Task<Result<AuthenticationResult>> AuthenticateAsync(AuthenticationRequest request)
    {
        try
        {
            // Authenticate against Active Directory
            var adAuthResult = await _adService.AuthenticateUserAsync(request.Username, request.Password);
            if (!adAuthResult.IsSuccess)
                return Result<AuthenticationResult>.AsError("Active Directory authentication failed");

            // Map AD user to local user
            var userMappingResult = await _userMappingService.GetOrCreateUserFromAdAsync(adAuthResult.User);
            if (userMappingResult.Status != ResultStatus.Success)
                return Result<AuthenticationResult>.FromResult(userMappingResult);

            var localUser = userMappingResult.Data;

            // Check user status
            if (!localUser.IsActive)
                return Result<AuthenticationResult>.AsError("User account is disabled");

            // Generate token
            var token = GenerateAuthenticationToken(localUser);

            return Result<AuthenticationResult>.AsSuccess(new AuthenticationResult
            {
                User = localUser,
                Token = token,
                ExpiresAt = DateTime.UtcNow.AddHours(8),
                AuthenticationType = AuthenticationType.ActiveDirectory,
                AdditionalClaims = ExtractAdClaims(adAuthResult.User)
            });
        }
        catch (Exception ex)
        {
            return Result<AuthenticationResult>.AsError($"AD authentication failed: {ex.Message}");
        }
    }
}

// OpenID Connect Authentication
public class OpenIdConnectAuthenticator : IAuthenticator
{
    private readonly IOpenIdConnectService _oidcService;

    public async Task<Result<AuthenticationResult>> AuthenticateAsync(AuthenticationRequest request)
    {
        try
        {
            // Validate OIDC token
            var tokenValidationResult = await _oidcService.ValidateTokenAsync(request.Token);
            if (!tokenValidationResult.IsValid)
                return Result<AuthenticationResult>.AsError("Invalid OpenID Connect token");

            // Extract user information from token claims
            var userInfo = ExtractUserInfoFromClaims(tokenValidationResult.Claims);

            // Get or create local user account
            var localUserResult = await GetOrCreateLocalUser(userInfo);
            if (localUserResult.Status != ResultStatus.Success)
                return Result<AuthenticationResult>.FromResult(localUserResult);

            return Result<AuthenticationResult>.AsSuccess(new AuthenticationResult
            {
                User = localUserResult.Data,
                Token = request.Token,
                ExpiresAt = tokenValidationResult.ExpiresAt,
                AuthenticationType = AuthenticationType.OpenIdConnect
            });
        }
        catch (Exception ex)
        {
            return Result<AuthenticationResult>.AsError($"OIDC authentication failed: {ex.Message}");
        }
    }
}

2. Token-Based Authentication Pattern

public class TokenAuthenticationService
{
    private readonly ITokenGenerator _tokenGenerator;
    private readonly ITokenValidator _tokenValidator;
    private readonly IUserRepository _userRepository;

    public async Task<Result<string>> GenerateTokenAsync(User user, TimeSpan? expiry = null)
    {
        try
        {
            var expiresAt = DateTime.UtcNow.Add(expiry ?? TimeSpan.FromHours(8));

            var tokenClaims = new TokenClaims
            {
                UserId = user.I3D,
                Username = user.Username,
                Email = user.Email,
                Roles = user.UserRoles.Select(ur => ur.Role.Name).ToList(),
                ExpiresAt = expiresAt,
                IssuedAt = DateTime.UtcNow
            };

            var token = _tokenGenerator.GenerateToken(tokenClaims);

            // Store token in database for revocation capability
            await StoreTokenAsync(token, user.I3D, expiresAt);

            return Result<string>.AsSuccess(token);
        }
        catch (Exception ex)
        {
            return Result<string>.AsError($"Token generation failed: {ex.Message}");
        }
    }

    public async Task<Result<TokenValidationResult>> ValidateTokenAsync(string token)
    {
        try
        {
            // Check if token is revoked
            var isRevokedResult = await IsTokenRevokedAsync(token);
            if (isRevokedResult.Status == ResultStatus.Success && isRevokedResult.Data)
                return Result<TokenValidationResult>.AsError("Token has been revoked");

            // Validate token signature and expiry
            var validationResult = _tokenValidator.ValidateToken(token);
            if (!validationResult.IsValid)
                return Result<TokenValidationResult>.AsError(validationResult.Error);

            // Verify user still exists and is active
            var userResult = await _userRepository.GetUserByIdAsync(validationResult.Claims.UserId);
            if (userResult.Status != ResultStatus.Success || !userResult.Data.IsActive)
                return Result<TokenValidationResult>.AsError("User account is no longer active");

            return Result<TokenValidationResult>.AsSuccess(new TokenValidationResult
            {
                IsValid = true,
                Claims = validationResult.Claims,
                User = userResult.Data
            });
        }
        catch (Exception ex)
        {
            return Result<TokenValidationResult>.AsError($"Token validation failed: {ex.Message}");
        }
    }

    public async Task<Result> RevokeTokenAsync(string token)
    {
        return await RevokeTokenInDatabaseAsync(token);
    }
}

Authorization Patterns

1. Role-Based Access Control (RBAC) Pattern

public class AuthorizationService
{
    private readonly IUserRightsRepository _userRightsRepository;
    private readonly IRoleRepository _roleRepository;

    public async Task<Result<bool>> HasRightAsync(int userId, int rightId)
    {
        try
        {
            var userRightsResult = await _userRightsRepository.GetUserRightsAsync(userId);
            if (userRightsResult.Status != ResultStatus.Success)
                return Result<bool>.FromResult(userRightsResult);

            var hasRight = userRightsResult.Data.Any(ur => ur.RightI3D == rightId && ur.HasRight);

            return Result<bool>.AsSuccess(hasRight);
        }
        catch (Exception ex)
        {
            return Result<bool>.AsError($"Authorization check failed: {ex.Message}");
        }
    }

    public async Task<Result<bool>> HasRoleAsync(int userId, string roleName)
    {
        try
        {
            var userRolesResult = await _roleRepository.GetUserRolesAsync(userId);
            if (userRolesResult.Status != ResultStatus.Success)
                return Result<bool>.FromResult(userRolesResult);

            var hasRole = userRolesResult.Data.Any(r => r.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase));

            return Result<bool>.AsSuccess(hasRole);
        }
        catch (Exception ex)
        {
            return Result<bool>.AsError($"Role check failed: {ex.Message}");
        }
    }

    public async Task<Result<List<int>>> GetUserRightsAsync(int userId)
    {
        try
        {
            var userRightsResult = await _userRightsRepository.GetUserRightsAsync(userId);
            if (userRightsResult.Status != ResultStatus.Success)
                return Result<List<int>>.FromResult(userRightsResult);

            var rightIds = userRightsResult.Data
                .Where(ur => ur.HasRight)
                .Select(ur => ur.RightI3D)
                .ToList();

            return Result<List<int>>.AsSuccess(rightIds);
        }
        catch (Exception ex)
        {
            return Result<List<int>>.AsError($"Failed to get user rights: {ex.Message}");
        }
    }
}

// Authorization attribute for web service endpoints
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class AuthenticateAttribute : Attribute
{
    public int[] RequiredRights { get; set; }
    public string[] RequiredRoles { get; set; }
    public bool AllowAnonymous { get; set; } = false;

    public AuthenticateAttribute(params int[] requiredRights)
    {
        RequiredRights = requiredRights ?? new int[0];
    }
}

// Authorization enforcement interceptor
public class AuthorizationInterceptor
{
    private readonly AuthorizationService _authorizationService;

    public async Task<Result> AuthorizeAsync(MethodInvocationContext context)
    {
        var authAttribute = context.Method.GetCustomAttribute<AuthenticateAttribute>();
        if (authAttribute == null || authAttribute.AllowAnonymous)
            return Result.AsSuccess();

        var currentUser = context.GetCurrentUser();
        if (currentUser == null)
            return Result.AsError("Authentication required");

        // Check required rights
        if (authAttribute.RequiredRights != null && authAttribute.RequiredRights.Any())
        {
            foreach (var requiredRight in authAttribute.RequiredRights)
            {
                var hasRightResult = await _authorizationService.HasRightAsync(currentUser.I3D, requiredRight);
                if (hasRightResult.Status != ResultStatus.Success || !hasRightResult.Data)
                    return Result.AsError($"Access denied. Required right: {requiredRight}");
            }
        }

        // Check required roles
        if (authAttribute.RequiredRoles != null && authAttribute.RequiredRoles.Any())
        {
            foreach (var requiredRole in authAttribute.RequiredRoles)
            {
                var hasRoleResult = await _authorizationService.HasRoleAsync(currentUser.I3D, requiredRole);
                if (hasRoleResult.Status != ResultStatus.Success || !hasRoleResult.Data)
                    return Result.AsError($"Access denied. Required role: {requiredRole}");
            }
        }

        return Result.AsSuccess();
    }
}

// Usage in web service methods
public class CentronRestService : ICentronRestService
{
    [Authenticate(UserRightsConst.Sales.Customer.SHOW_CUSTOMER)]
    public async Task<Response<AccountDTO>> GetAccount(Request<AccountFilter> request)
    {
        // Method implementation - authorization is enforced by interceptor
        var result = await _accountsLogic.GetAccountAsync(request.Data.Filter, request.Data.MixMode);
        return Response<AccountDTO>.FromResult(result);
    }

    [Authenticate(UserRightsConst.Administration.Users.MANAGE_USERS)]
    public async Task<Response<List<UserDTO>>> GetUsers(Request<UserFilter> request)
    {
        // Only users with MANAGE_USERS right can access this method
        var result = await _userLogic.GetUsersAsync(request.Data);
        return Response<List<UserDTO>>.FromResult(result);
    }
}

2. Feature-Based Authorization Pattern

public class FeatureAuthorizationService
{
    private readonly IFeatureRepository _featureRepository;

    public async Task<Result<bool>> IsFeatureEnabledForUserAsync(int userId, string featureName)
    {
        try
        {
            var userFeaturesResult = await _featureRepository.GetUserFeaturesAsync(userId);
            if (userFeaturesResult.Status != ResultStatus.Success)
                return Result<bool>.FromResult(userFeaturesResult);

            var isEnabled = userFeaturesResult.Data.Any(f =>
                f.Name.Equals(featureName, StringComparison.OrdinalIgnoreCase) && f.IsEnabled);

            return Result<bool>.AsSuccess(isEnabled);
        }
        catch (Exception ex)
        {
            return Result<bool>.AsError($"Feature authorization check failed: {ex.Message}");
        }
    }
}

// Feature gate attribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class RequireFeatureAttribute : Attribute
{
    public string FeatureName { get; }

    public RequireFeatureAttribute(string featureName)
    {
        FeatureName = featureName ?? throw new ArgumentNullException(nameof(featureName));
    }
}

// Usage in UI modules
[RequireFeature(FeatureConst.ADVANCED_REPORTING)]
public class AdvancedReportModule : ICentronAppModuleController
{
    public bool IsAvailable(User user)
    {
        // Feature authorization is checked by framework
        return true;
    }
}

Data Protection Patterns

1. Encryption and Data Security Pattern

public interface IDataProtectionService
{
    Result<string> EncryptSensitiveData(string plainText);
    Result<string> DecryptSensitiveData(string encryptedText);
    Result<string> HashPassword(string password, out string salt);
    Result<bool> VerifyPassword(string password, string hash, string salt);
}

public class DataProtectionService : IDataProtectionService
{
    private readonly IDataProtector _dataProtector;
    private readonly IPasswordHasher _passwordHasher;

    public DataProtectionService(IDataProtectionProvider dataProtectionProvider)
    {
        _dataProtector = dataProtectionProvider.CreateProtector("Centron.SensitiveData.v1");
        _passwordHasher = new PasswordHasher();
    }

    public Result<string> EncryptSensitiveData(string plainText)
    {
        try
        {
            if (string.IsNullOrEmpty(plainText))
                return Result<string>.AsSuccess(plainText);

            var encrypted = _dataProtector.Protect(plainText);
            return Result<string>.AsSuccess(encrypted);
        }
        catch (Exception ex)
        {
            return Result<string>.AsError($"Encryption failed: {ex.Message}");
        }
    }

    public Result<string> DecryptSensitiveData(string encryptedText)
    {
        try
        {
            if (string.IsNullOrEmpty(encryptedText))
                return Result<string>.AsSuccess(encryptedText);

            var decrypted = _dataProtector.Unprotect(encryptedText);
            return Result<string>.AsSuccess(decrypted);
        }
        catch (Exception ex)
        {
            return Result<string>.AsError($"Decryption failed: {ex.Message}");
        }
    }

    public Result<string> HashPassword(string password, out string salt)
    {
        try
        {
            salt = GenerateSalt();
            var hash = _passwordHasher.HashPassword(password + salt);
            return Result<string>.AsSuccess(hash);
        }
        catch (Exception ex)
        {
            salt = null;
            return Result<string>.AsError($"Password hashing failed: {ex.Message}");
        }
    }

    public Result<bool> VerifyPassword(string password, string hash, string salt)
    {
        try
        {
            var isValid = _passwordHasher.VerifyPassword(password + salt, hash);
            return Result<bool>.AsSuccess(isValid);
        }
        catch (Exception ex)
        {
            return Result<bool>.AsError($"Password verification failed: {ex.Message}");
        }
    }

    private string GenerateSalt()
    {
        using (var rng = RandomNumberGenerator.Create())
        {
            var saltBytes = new byte[32];
            rng.GetBytes(saltBytes);
            return Convert.ToBase64String(saltBytes);
        }
    }
}

// Entity-level data protection
public class CustomerEntity
{
    public int I3D { get; set; }
    public string CompanyName { get; set; }

    [SensitiveData]
    public string Email { get; set; }

    [SensitiveData]
    public string PhoneNumber { get; set; }

    [SensitiveData]
    public string BankAccountNumber { get; set; }

    // Navigation properties
    public virtual List<CustomerAddress> Addresses { get; set; }
}

// Data protection interceptor
public class SensitiveDataInterceptor
{
    private readonly IDataProtectionService _dataProtection;

    public void OnSaving(object entity)
    {
        var properties = entity.GetType().GetProperties()
            .Where(p => p.GetCustomAttribute<SensitiveDataAttribute>() != null);

        foreach (var property in properties)
        {
            var value = property.GetValue(entity) as string;
            if (!string.IsNullOrEmpty(value))
            {
                var encryptedResult = _dataProtection.EncryptSensitiveData(value);
                if (encryptedResult.Status == ResultStatus.Success)
                {
                    property.SetValue(entity, encryptedResult.Data);
                }
            }
        }
    }

    public void OnLoading(object entity)
    {
        var properties = entity.GetType().GetProperties()
            .Where(p => p.GetCustomAttribute<SensitiveDataAttribute>() != null);

        foreach (var property in properties)
        {
            var value = property.GetValue(entity) as string;
            if (!string.IsNullOrEmpty(value))
            {
                var decryptedResult = _dataProtection.DecryptSensitiveData(value);
                if (decryptedResult.Status == ResultStatus.Success)
                {
                    property.SetValue(entity, decryptedResult.Data);
                }
            }
        }
    }
}

Audit Logging and Compliance Patterns

1. Comprehensive Audit Trail Pattern

public class AuditService
{
    private readonly IAuditRepository _auditRepository;
    private readonly ICurrentUserService _currentUserService;

    public async Task<Result> LogActionAsync(AuditLogEntry auditEntry)
    {
        try
        {
            auditEntry.UserId = _currentUserService.GetCurrentUserId();
            auditEntry.Timestamp = DateTime.UtcNow;
            auditEntry.IpAddress = _currentUserService.GetCurrentUserIpAddress();
            auditEntry.UserAgent = _currentUserService.GetCurrentUserAgent();

            var result = await _auditRepository.SaveAuditEntryAsync(auditEntry);
            return result;
        }
        catch (Exception ex)
        {
            // Audit logging should never fail the main operation
            // Log error but return success
            LogAuditError(ex);
            return Result.AsSuccess();
        }
    }

    public async Task<Result> LogDataAccessAsync(string tableName, int recordId, DataAccessType accessType, object oldValues = null, object newValues = null)
    {
        var auditEntry = new AuditLogEntry
        {
            ActionType = "DATA_ACCESS",
            TableName = tableName,
            RecordId = recordId,
            AccessType = accessType.ToString(),
            OldValues = oldValues != null ? JsonSerializer.Serialize(oldValues) : null,
            NewValues = newValues != null ? JsonSerializer.Serialize(newValues) : null
        };

        return await LogActionAsync(auditEntry);
    }

    public async Task<Result> LogBusinessActionAsync(string actionName, string entityType, int? entityId, Dictionary<string, object> parameters = null)
    {
        var auditEntry = new AuditLogEntry
        {
            ActionType = "BUSINESS_ACTION",
            ActionName = actionName,
            EntityType = entityType,
            EntityId = entityId,
            Parameters = parameters != null ? JsonSerializer.Serialize(parameters) : null
        };

        return await LogActionAsync(auditEntry);
    }

    public async Task<Result> LogSecurityEventAsync(SecurityEventType eventType, string description, Dictionary<string, object> details = null)
    {
        var auditEntry = new AuditLogEntry
        {
            ActionType = "SECURITY_EVENT",
            SecurityEventType = eventType.ToString(),
            Description = description,
            Details = details != null ? JsonSerializer.Serialize(details) : null,
            Severity = GetSeverityForSecurityEvent(eventType)
        };

        return await LogActionAsync(auditEntry);
    }
}

// Audit interceptor for automatic logging
public class AuditInterceptor
{
    private readonly AuditService _auditService;

    public async Task LogMethodCallAsync(MethodInvocationContext context)
    {
        var auditAttribute = context.Method.GetCustomAttribute<AuditableAttribute>();
        if (auditAttribute == null) return;

        var parameters = context.Parameters?.ToDictionary(p => p.Key, p => p.Value);

        await _auditService.LogBusinessActionAsync(
            context.Method.Name,
            context.TargetType.Name,
            ExtractEntityId(context.Parameters),
            parameters);
    }

    public async Task LogDataChangeAsync<T>(T entity, DataChangeType changeType, T oldEntity = default) where T : class
    {
        var entityType = typeof(T).Name;
        var entityId = GetEntityId(entity);

        await _auditService.LogDataAccessAsync(
            entityType,
            entityId,
            ConvertChangeTypeToAccessType(changeType),
            oldEntity,
            entity);
    }
}

// Usage in business logic
public class AccountBL : BaseBL
{
    private readonly AuditService _auditService;

    [Auditable]
    public async Task<Result<Account>> SaveAccountAsync(Account account)
    {
        var oldAccount = account.I3D > 0 ? await GetAccountByIdAsync(account.I3D) : null;

        // Save the account
        var saveResult = await _accountRepository.SaveAsync(account);
        if (saveResult.Status != ResultStatus.Success)
            return saveResult;

        // Log the change
        await _auditService.LogDataAccessAsync(
            "Account",
            account.I3D,
            oldAccount == null ? DataAccessType.Create : DataAccessType.Update,
            oldAccount,
            account);

        return saveResult;
    }

    [Auditable]
    public async Task<Result> DeleteAccountAsync(int accountId)
    {
        var account = await GetAccountByIdAsync(accountId);

        var result = await _accountRepository.DeleteAsync(accountId);
        if (result.Status == ResultStatus.Success)
        {
            await _auditService.LogDataAccessAsync("Account", accountId, DataAccessType.Delete, account, null);
        }

        return result;
    }
}

2. GDPR Compliance Pattern

public class DataProtectionComplianceService
{
    private readonly IAuditService _auditService;
    private readonly IPersonalDataService _personalDataService;

    public async Task<Result> ProcessDataDeletionRequestAsync(int customerId, string requestReason)
    {
        try
        {
            // Log the data deletion request
            await _auditService.LogBusinessActionAsync(
                "GDPR_DATA_DELETION_REQUEST",
                "Customer",
                customerId,
                new Dictionary<string, object> { { "Reason", requestReason } });

            // Identify all personal data related to customer
            var personalDataResult = await _personalDataService.GetCustomerPersonalDataAsync(customerId);
            if (personalDataResult.Status != ResultStatus.Success)
                return Result.FromResult(personalDataResult);

            // Anonymize or delete personal data
            foreach (var dataLocation in personalDataResult.Data)
            {
                var anonymizationResult = await AnonymizePersonalDataAsync(dataLocation);
                if (anonymizationResult.Status != ResultStatus.Success)
                {
                    await _auditService.LogBusinessActionAsync(
                        "GDPR_DATA_DELETION_FAILED",
                        dataLocation.EntityType,
                        dataLocation.EntityId,
                        new Dictionary<string, object> { { "Error", anonymizationResult.Error } });
                    return anonymizationResult;
                }
            }

            // Log successful completion
            await _auditService.LogBusinessActionAsync(
                "GDPR_DATA_DELETION_COMPLETED",
                "Customer",
                customerId,
                new Dictionary<string, object> { { "DeletedDataCount", personalDataResult.Data.Count } });

            return Result.AsSuccess();
        }
        catch (Exception ex)
        {
            await _auditService.LogSecurityEventAsync(
                SecurityEventType.GdprComplianceFailure,
                $"GDPR data deletion failed for customer {customerId}",
                new Dictionary<string, object> { { "Exception", ex.Message } });

            return Result.AsError($"Data deletion request processing failed: {ex.Message}");
        }
    }

    public async Task<Result<PersonalDataExport>> ProcessDataExportRequestAsync(int customerId)
    {
        try
        {
            await _auditService.LogBusinessActionAsync(
                "GDPR_DATA_EXPORT_REQUEST",
                "Customer",
                customerId);

            var personalDataResult = await _personalDataService.GetCustomerPersonalDataAsync(customerId);
            if (personalDataResult.Status != ResultStatus.Success)
                return Result<PersonalDataExport>.FromResult(personalDataResult);

            var exportData = new PersonalDataExport
            {
                CustomerId = customerId,
                ExportDate = DateTime.UtcNow,
                Data = personalDataResult.Data.ToDictionary(d => d.DataType, d => d.Value)
            };

            await _auditService.LogBusinessActionAsync(
                "GDPR_DATA_EXPORT_COMPLETED",
                "Customer",
                customerId,
                new Dictionary<string, object> { { "ExportedDataCount", exportData.Data.Count } });

            return Result<PersonalDataExport>.AsSuccess(exportData);
        }
        catch (Exception ex)
        {
            return Result<PersonalDataExport>.AsError($"Data export request processing failed: {ex.Message}");
        }
    }
}

Secure Communication Patterns

1. HTTPS and Certificate Management

public class SecureCommunicationService
{
    public static HttpClient CreateSecureHttpClient(CertificateValidationMode validationMode = CertificateValidationMode.Default)
    {
        var handler = new HttpClientHandler();

        switch (validationMode)
        {
            case CertificateValidationMode.Strict:
                handler.ServerCertificateCustomValidationCallback = ValidateCertificateStrict;
                break;
            case CertificateValidationMode.AllowSelfSigned:
                handler.ServerCertificateCustomValidationCallback = ValidateCertificateAllowSelfSigned;
                break;
            case CertificateValidationMode.Development:
                handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) => true;
                break;
        }

        var client = new HttpClient(handler);
        client.DefaultRequestHeaders.Add("User-Agent", "CentronClient/1.0");

        return client;
    }

    private static bool ValidateCertificateStrict(HttpRequestMessage request, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors errors)
    {
        // Only allow certificates with no errors
        return errors == SslPolicyErrors.None;
    }

    private static bool ValidateCertificateAllowSelfSigned(HttpRequestMessage request, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors errors)
    {
        // Allow self-signed certificates but validate other aspects
        if (errors == SslPolicyErrors.None)
            return true;

        if (errors == SslPolicyErrors.RemoteCertificateChainErrors)
        {
            // Check if the only error is self-signed certificate
            foreach (X509ChainStatus status in chain.ChainStatus)
            {
                if (status.Status != X509ChainStatusFlags.UntrustedRoot)
                    return false;
            }
            return true;
        }

        return false;
    }
}

2. API Security Pattern

public class ApiSecurityMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IApiKeyValidator _apiKeyValidator;
    private readonly IRateLimiter _rateLimiter;

    public async Task InvokeAsync(HttpContext context)
    {
        // API Key validation
        var apiKeyResult = await ValidateApiKeyAsync(context);
        if (apiKeyResult.Status != ResultStatus.Success)
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Unauthorized: Invalid API key");
            return;
        }

        // Rate limiting
        var rateLimitResult = await _rateLimiter.CheckRateLimitAsync(context);
        if (!rateLimitResult.IsAllowed)
        {
            context.Response.StatusCode = 429;
            context.Response.Headers.Add("Retry-After", rateLimitResult.RetryAfter.ToString());
            await context.Response.WriteAsync("Rate limit exceeded");
            return;
        }

        // Request size limiting
        if (context.Request.ContentLength > 10_000_000) // 10MB limit
        {
            context.Response.StatusCode = 413;
            await context.Response.WriteAsync("Request too large");
            return;
        }

        // Add security headers
        AddSecurityHeaders(context);

        await _next(context);
    }

    private void AddSecurityHeaders(HttpContext context)
    {
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
        context.Response.Headers.Add("X-Frame-Options", "DENY");
        context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
        context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
        context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'");
    }
}

Security Best Practices and Guidelines

1. Input Sanitization and Validation

  • All user inputs are validated and sanitized before processing
  • SQL injection prevention through parameterized queries and ORM usage
  • XSS protection through output encoding and Content Security Policy
  • Command injection prevention through input validation and sanitization

2. Authentication Security

  • Multi-factor authentication support for high-privilege accounts
  • Account lockout policies to prevent brute force attacks
  • Password complexity requirements with secure hashing algorithms
  • Session management with secure tokens and proper expiration

3. Authorization Security

  • Principle of least privilege - users only get necessary permissions
  • Role-based access control with granular permission management
  • Feature flags for controlling access to application features
  • Regular permission audits and access reviews

4. Data Protection Security

  • Encryption at rest for sensitive data using industry-standard algorithms
  • Encryption in transit using TLS 1.2+ for all communications
  • Key management with secure key storage and rotation policies
  • Data classification and appropriate protection levels

5. Monitoring and Incident Response

  • Security event logging with centralized log management
  • Real-time threat detection and automated response
  • Incident response procedures with defined escalation paths
  • Regular security assessments and penetration testing

Conclusion

The Centron application implements comprehensive security patterns that provide:

  • Defense in Depth: Multiple security layers protect against various threat vectors
  • Authentication Flexibility: Support for multiple authentication mechanisms
  • Granular Authorization: Fine-grained permission control at feature and data levels
  • Data Protection: Strong encryption and data protection compliance (GDPR)
  • Audit Trail: Complete logging of security events and data access
  • Secure Communication: Encrypted communication channels and API security
  • Compliance: Built-in support for regulatory compliance requirements

These security patterns ensure the application meets enterprise security standards while maintaining usability and performance.