Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bcpsdems 1174 hold non matching vc accounts #195

Merged
merged 10 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions backend/ApprovalFlow/ApprovalFlow.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>1.0.0</Version>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\common\Common.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Data\Migrations\" />
</ItemGroup>


</Project>
93 changes: 93 additions & 0 deletions backend/ApprovalFlow/ApprovalFlowConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace ApprovalFlow;
using common.Constants.Auth;

public class ApprovalFlowConfiguration
{
public static bool IsProduction() => EnvironmentName == Environments.Production;
public static bool IsDevelopment() => EnvironmentName == Environments.Development;
private static readonly string? EnvironmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

public ConnectionStringConfiguration ConnectionStrings { get; set; } = new();
public KafkaClusterConfiguration KafkaCluster { get; set; } = new();
public ApprovalConfiguration ApprovalConfig { get; set; } = new();
public KeycloakConfiguration Keycloak { get; set; } = new();

public SchemaRegistryConfiguration SchemaRegistry { get; set; } = new();
public TelemeteryConfiguration Telemetry { get; set; } = new TelemeteryConfiguration();
public SplunkConfiguration SplunkConfig { get; set; } = new SplunkConfiguration();


public class SplunkConfiguration
{
public string Host { get; set; } = string.Empty;
public string CollectorToken { get; set; } = string.Empty;
}

public class ApprovalConfiguration
{
public string NotifyEmail { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
}

// ------- Configuration Objects -------

public class TelemeteryConfiguration
{
public string CollectorUrl { get; set; } = string.Empty;
public string AzureConnectionString { get; set; } = string.Empty;
public bool LogToConsole { get; set; }

}


public class ConnectionStringConfiguration
{
public string ApprovalFlowDataStore { get; set; } = string.Empty;
}

public class KeycloakConfiguration
{
public string RealmUrl { get; set; } = string.Empty;
public string WellKnownConfig => KeycloakUrls.WellKnownConfig(this.RealmUrl);
public string TokenUrl => KeycloakUrls.Token(this.RealmUrl);
public string AdministrationUrl { get; set; } = string.Empty;
public string AdministrationClientId { get; set; } = string.Empty;
public string AdministrationClientSecret { get; set; } = string.Empty;
public string HcimClientId { get; set; } = string.Empty;
}



public class SchemaRegistryConfiguration
{
public string Url { get; set; } = string.Empty;
public string ClientId { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty;

}


public class KafkaClusterConfiguration
{
public string Url { get; set; } = string.Empty;
public string BootstrapServers { get; set; } = string.Empty;
public string SaslOauthbearerTokenEndpointUrl { get; set; } = string.Empty;
public string IncomingApprovalCreationTopic { get; set; } = string.Empty;
public string ApprovalResponseTopic { get; set; } = string.Empty;
public string NotificationTopic { get;set; } = string.Empty;

public string SaslOauthbearerProducerClientId { get; set; } = string.Empty;
public string SaslOauthbearerProducerClientSecret { get; set; } = string.Empty;
public string SaslOauthbearerConsumerClientId { get; set; } = string.Empty;
public string SaslOauthbearerConsumerClientSecret { get; set; } = string.Empty;
public string SslCaLocation { get; set; } = string.Empty;
public string SslCertificateLocation { get; set; } = string.Empty;
public string SslKeyLocation { get; set; } = string.Empty;
public string Scope { get; set; } = "openid";
public string ConsumerGroupId { get; set; } = "approval-consumer-group";


}

}

21 changes: 21 additions & 0 deletions backend/ApprovalFlow/Data/Approval/ApprovalHistory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace ApprovalFlow.Data.Approval;

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Common.Models.Approval;
using DIAM.Common.Models;
using NodaTime;

[Table(nameof(ApprovalHistory))]
public class ApprovalHistory : BaseAuditable
{
[Key]
public int Id { get; set; }
public string DecisionNote { get; set; } = string.Empty;
public string Approver { get; set; } = string.Empty;
public int RequestId { get; set; }
public Request AccessRequest { get; set; }
public Instant? Deleted { get; set; }
public ApprovalStatus Status { get; set; } = ApprovalStatus.PENDING;

}
24 changes: 24 additions & 0 deletions backend/ApprovalFlow/Data/Approval/ApprovalRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace ApprovalFlow.Data.Approval;

using NodaTime;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using DIAM.Common.Models;

[Table(nameof(ApprovalRequest))]
public class ApprovalRequest : BaseAuditable
{
[Key]
public int Id { get; set; }
public string Reason { get; set; } = string.Empty;
[Required]
public string MessageKey { get; set; } = string.Empty;
public string UserId { get; set; } = string.Empty;
public string IdentityProvider { get; set; } = string.Empty;
public int NoOfApprovalsRequired { get; set; }
public string RequiredAccess { get; set; } = string.Empty;
public Instant? Approved { get; set; }
public Instant? Completed { get; set; }
public ICollection<Request> Requests { get; set; } = new List<Request>();

}
15 changes: 15 additions & 0 deletions backend/ApprovalFlow/Data/Approval/MappingProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace ApprovalFlow.Data.Approval;

using AutoMapper;
using Common.Models.Approval;

public class MappingProfile : Profile
{
public MappingProfile()
{
this.CreateMap<ApprovalRequest, ApprovalModel>();
this.CreateMap<Request, RequestModel>();
this.CreateMap<ApprovalHistory, ApprovalHistoryModel>();

}
}
32 changes: 32 additions & 0 deletions backend/ApprovalFlow/Data/Approval/Request.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace ApprovalFlow.Data.Approval;

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using DIAM.Common.Models;

/// <summary>
/// An ApprovalRequest can contain one or more requests, this allows us to group requests for a user
/// together. E.g. a lawyer might be requesting to be a participant in core and user in disclosure.
/// Or in future a user may request to have access to something and also change their email address
/// </summary>
[Table(nameof(Request))]
public class Request : BaseAuditable
{
[Key]
public int Id { get; set; }
[Required]
public int ApprovalRequestId { get; set; }
public ApprovalRequest? ApprovalRequest { get; set; } // navigation property
public int RequestId { get; set; }

public ApprovalType ApprovalType { get; set; } = ApprovalType.AccessRequest;
public string RequestType { get; set; } = string.Empty;
public ICollection<ApprovalHistory> History { get; set; }

}

public enum ApprovalType
{
AccessRequest,
AccountChange
}
87 changes: 87 additions & 0 deletions backend/ApprovalFlow/Data/ApprovalFlowDataStoreDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
namespace ApprovalFlow.Data;

using ApprovalFlow.Data.Approval;
using ApprovalFlow.Models;
using DIAM.Common.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using NodaTime;

public class ApprovalFlowDataStoreDbContext : DbContext
{
private readonly IClock clock;

public ApprovalFlowDataStoreDbContext(DbContextOptions<ApprovalFlowDataStoreDbContext> options, IClock clock) : base(options) => this.clock = clock;
public DbSet<IdempotentConsumer> IdempotentConsumers { get; set; } = default!;
public DbSet<ApprovalRequest> ApprovalRequests { get; set; } = default!;
public DbSet<Request> Requests { get; set; } = default!;
public DbSet<ApprovalHistory> ApprovalHistories { get; set; } = default!;



public override int SaveChanges()
{
this.ApplyAudits();

return base.SaveChanges();
}

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
this.ApplyAudits();

return await base.SaveChangesAsync(cancellationToken);
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("approvalflow");
base.OnModelCreating(modelBuilder);

modelBuilder
.Entity<Request>()
.Property(d => d.ApprovalType)
.HasConversion(new EnumToStringConverter<ApprovalType>());

modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApprovalFlowDataStoreDbContext).Assembly);
}

private void ApplyAudits()
{
this.ChangeTracker.DetectChanges();
var updated = this.ChangeTracker.Entries()
.Where(x => x.Entity is BaseAuditable
&& (x.State == EntityState.Added || x.State == EntityState.Modified));

var currentInstant = this.clock.GetCurrentInstant();

foreach (var entry in updated)
{
entry.CurrentValues[nameof(BaseAuditable.Modified)] = currentInstant;

if (entry.State == EntityState.Added)
{
entry.CurrentValues[nameof(BaseAuditable.Created)] = currentInstant;
}
else
{
entry.Property(nameof(BaseAuditable.Created)).IsModified = false;
}
}
}

public async Task IdempotentConsumer(string messageId, string consumer, Instant consumeDate)
{
await this.IdempotentConsumers.AddAsync(new IdempotentConsumer
{
MessageId = messageId,
Consumer = consumer,
ConsumeDate = consumeDate
});
await this.SaveChangesAsync();
}

public async Task<bool> HasBeenProcessed(string messageId, string consumer) => await this.IdempotentConsumers.AnyAsync(x => x.MessageId == messageId && x.Consumer == consumer);


}
Loading