diff --git a/src/CustomerSite/Controllers/WebHook/AzureWebhookController.cs b/src/CustomerSite/Controllers/WebHook/AzureWebhookController.cs index 881bd397..21e05ef8 100644 --- a/src/CustomerSite/Controllers/WebHook/AzureWebhookController.cs +++ b/src/CustomerSite/Controllers/WebHook/AzureWebhookController.cs @@ -9,6 +9,7 @@ using Marketplace.SaaS.Accelerator.Services.Configurations; using Marketplace.SaaS.Accelerator.Services.Exceptions; using Marketplace.SaaS.Accelerator.Services.Services; +using Marketplace.SaaS.Accelerator.Services.Utilities; using Marketplace.SaaS.Accelerator.Services.WebHook; using Microsoft.AspNetCore.Mvc; @@ -63,6 +64,22 @@ public class AzureWebhookController : ControllerBase /// private readonly SubscriptionService subscriptionService; + /// + /// The JWT token validation. + /// + private readonly ValidateJwtToken validateJwtToken; + + /// + /// The ApplicationConfig Repository. + /// + private readonly IApplicationConfigRepository applicationConfigRepository; + + /// + /// The ApplicationConfig service. + /// + private readonly ApplicationConfigService applicationConfigService; + + /// /// Initializes a new instance of the class. /// @@ -72,7 +89,16 @@ public class AzureWebhookController : ControllerBase /// The plan repository. /// The subscriptions repository. /// The SaaSApiClientConfiguration from ENV - public AzureWebhookController(IApplicationLogRepository applicationLogRepository, IWebhookProcessor webhookProcessor, ISubscriptionLogRepository subscriptionsLogRepository, IPlansRepository planRepository, ISubscriptionsRepository subscriptionsRepository, SaaSApiClientConfiguration configuration) + /// The validateJwtToken utility + /// The application config repository + public AzureWebhookController(IApplicationLogRepository applicationLogRepository, + IWebhookProcessor webhookProcessor, + ISubscriptionLogRepository subscriptionsLogRepository, + IPlansRepository planRepository, + ISubscriptionsRepository subscriptionsRepository, + SaaSApiClientConfiguration configuration, + ValidateJwtToken validateJwtToken, + IApplicationConfigRepository applicationConfigRepository) { this.applicationLogRepository = applicationLogRepository; this.subscriptionsRepository = subscriptionsRepository; @@ -82,6 +108,9 @@ public AzureWebhookController(IApplicationLogRepository applicationLogRepository this.webhookProcessor = webhookProcessor; this.applicationLogService = new ApplicationLogService(this.applicationLogRepository); this.subscriptionService = new SubscriptionService(this.subscriptionsRepository, this.planRepository); + this.validateJwtToken = validateJwtToken; + this.applicationConfigRepository = applicationConfigRepository; + this.applicationConfigService = new ApplicationConfigService(this.applicationConfigRepository); } /// @@ -94,6 +123,24 @@ public async Task Post(WebhookPayload request) { await this.applicationLogService.AddApplicationLog("The azure Webhook Triggered.").ConfigureAwait(false); + var appConfigValueConversion = bool.TryParse(this.applicationConfigService.GetValueByName("ValidateWebhookJwtToken"), out bool appConfigValue); + + if (appConfigValueConversion && appConfigValue) + { + try + { + await this.applicationLogService.AddApplicationLog("Validating the JWT token.").ConfigureAwait(false); + var token = this.HttpContext.Request.Headers["Authorization"].ToString().Split(' ')[1]; + await validateJwtToken.ValidateTokenAsync(token); + } + catch (Exception e) + { + await this.applicationLogService.AddApplicationLog($"Jwt token validation failed with error: {e.Message}").ConfigureAwait(false); + + return new UnauthorizedResult(); + } + } + if (request != null) { var json = JsonSerializer.Serialize(request); @@ -106,7 +153,7 @@ public async Task Post(WebhookPayload request) catch (MarketplaceException ex) { await this.applicationLogService.AddApplicationLog( - $"An error occurred while attempting to process a webhook notification: [{ex.Message}].") + $"A Marketplace exception occurred while attempting to process a webhook notification: [{ex.Message}].") .ConfigureAwait(false); return BadRequest(); } diff --git a/src/CustomerSite/Startup.cs b/src/CustomerSite/Startup.cs index c3af61b7..d1b8f6d0 100644 --- a/src/CustomerSite/Startup.cs +++ b/src/CustomerSite/Startup.cs @@ -105,8 +105,7 @@ public void ConfigureServices(IServiceCollection services) services .AddTransient() .AddScoped() - .AddScoped() - ; + .AddScoped(); if (!Uri.TryCreate(config.FulFillmentAPIBaseURL, UriKind.Absolute, out var fulfillmentBaseApi)) { @@ -115,7 +114,8 @@ public void ConfigureServices(IServiceCollection services) services .AddSingleton(new FulfillmentApiService(new MarketplaceSaaSClient(fulfillmentBaseApi, creds), config, new FulfillmentApiClientLogger())) - .AddSingleton(config); + .AddSingleton(config) + .AddSingleton(); services .AddDbContext(options => options.UseSqlServer(this.Configuration.GetConnectionString("DefaultConnection"))); diff --git a/src/DataAccess/Migrations/20240312055030_baseline751.Designer.cs b/src/DataAccess/Migrations/20240312055030_baseline751.Designer.cs new file mode 100644 index 00000000..0f908dbf --- /dev/null +++ b/src/DataAccess/Migrations/20240312055030_baseline751.Designer.cs @@ -0,0 +1,1174 @@ +// +using System; +using Marketplace.SaaS.Accelerator.DataAccess.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Marketplace.SaaS.Accelerator.DataAccess.Migrations +{ + [DbContext(typeof(SaasKitContext))] + [Migration("20240312055030_baseline751")] + partial class baseline751 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.ApplicationLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ActionTime") + .HasColumnType("datetime"); + + b.Property("LogDetail") + .HasMaxLength(4000) + .IsUnicode(false) + .HasColumnType("varchar(4000)"); + + b.HasKey("Id"); + + b.ToTable("ApplicationLog"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.DatabaseVersionHistory", b => + { + b.Property("ChangeLog") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreateBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("VersionNumber") + .HasColumnType("decimal(6,2)"); + + b.ToTable("DatabaseVersionHistory"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Bcc") + .HasMaxLength(1000) + .IsUnicode(false) + .HasColumnType("varchar(1000)") + .HasColumnName("BCC"); + + b.Property("Cc") + .HasMaxLength(1000) + .IsUnicode(false) + .HasColumnType("varchar(1000)") + .HasColumnName("CC"); + + b.Property("Description") + .HasMaxLength(1000) + .IsUnicode(false) + .HasColumnType("varchar(1000)"); + + b.Property("InsertDate") + .HasColumnType("datetime"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Status") + .HasMaxLength(1000) + .IsUnicode(false) + .HasColumnType("varchar(1000)"); + + b.Property("Subject") + .HasMaxLength(1000) + .IsUnicode(false) + .HasColumnType("varchar(1000)"); + + b.Property("TemplateBody") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("ToRecipients") + .HasMaxLength(1000) + .IsUnicode(false) + .HasColumnType("varchar(1000)"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplate"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Events", b => + { + b.Property("EventsId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("EventsId"), 1L, 1); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("EventsName") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.HasKey("EventsId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.KnownUsers", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UserEmail") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("KnownUsers"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredAuditLogs", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreatedBy") + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime"); + + b.Property("RequestJson") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("ResponseJson") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("RunBy") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)"); + + b.Property("StatusCode") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("SubscriptionUsageDate") + .HasColumnType("datetime"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("MeteredAuditLogs"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredDimensions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreatedDate") + .HasColumnType("datetime"); + + b.Property("Description") + .HasMaxLength(250) + .IsUnicode(false) + .HasColumnType("varchar(250)"); + + b.Property("Dimension") + .HasMaxLength(150) + .IsUnicode(false) + .HasColumnType("varchar(150)"); + + b.Property("PlanId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PlanId"); + + b.ToTable("MeteredDimensions"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredPlanSchedulerManagement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("DimensionId") + .IsRequired() + .HasColumnType("int"); + + b.Property("FrequencyId") + .IsRequired() + .HasColumnType("int"); + + b.Property("NextRunTime") + .HasColumnType("datetime2"); + + b.Property("PlanId") + .IsRequired() + .HasColumnType("int"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("float"); + + b.Property("SchedulerName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("StartDate") + .IsRequired() + .HasColumnType("datetime2"); + + b.Property("SubscriptionId") + .IsRequired() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DimensionId"); + + b.HasIndex("FrequencyId"); + + b.HasIndex("PlanId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("MeteredPlanSchedulerManagement"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.OfferAttributes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("Description") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("DisplayName") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("DisplaySequence") + .HasColumnType("int"); + + b.Property("FromList") + .HasColumnType("bit"); + + b.Property("IsDelete") + .HasColumnType("bit"); + + b.Property("IsRequired") + .HasColumnType("bit"); + + b.Property("Isactive") + .HasColumnType("bit"); + + b.Property("Max") + .HasColumnType("int"); + + b.Property("Min") + .HasColumnType("int"); + + b.Property("OfferId") + .HasColumnType("uniqueidentifier"); + + b.Property("ParameterId") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("Type") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("ValueTypeId") + .HasColumnType("int"); + + b.Property("ValuesList") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OfferAttributes"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Offers", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("OfferGuid") + .HasColumnType("uniqueidentifier") + .HasColumnName("OfferGUId"); + + b.Property("OfferId") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("OfferName") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Offers"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.PlanAttributeMapping", b => + { + b.Property("PlanAttributeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("PlanAttributeId"), 1L, 1); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("OfferAttributeId") + .HasColumnType("int") + .HasColumnName("OfferAttributeID"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("PlanAttributeId") + .HasName("PK__PlanAttr__8B476A98C058FAF2"); + + b.ToTable("PlanAttributeMapping"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.PlanAttributeOutput", b => + { + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("OfferAttributeId") + .HasColumnType("int"); + + b.Property("PlanAttributeId") + .HasColumnType("int"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.HasKey("RowNumber") + .HasName("PK__PlanAttr__AAAC09D888FE3E40"); + + b.ToTable("PlanAttributeOutput"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.PlanEventsMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CopyToCustomer") + .HasColumnType("bit"); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("EventId") + .HasColumnType("int"); + + b.Property("FailureStateEmails") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("Isactive") + .HasColumnType("bit"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("SuccessStateEmails") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("PlanEventsMapping"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.PlanEventsOutPut", b => + { + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("CopyToCustomer") + .HasColumnType("bit"); + + b.Property("EventId") + .HasColumnType("int"); + + b.Property("EventsName") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("FailureStateEmails") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("Id") + .HasColumnType("int") + .HasColumnName("ID"); + + b.Property("Isactive") + .HasColumnType("bit"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("SuccessStateEmails") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.HasKey("RowNumber") + .HasName("PK__PlanEven__AAAC09D8C9229532"); + + b.ToTable("PlanEventsOutPut"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Plans", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("DisplayName") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("IsPerUser") + .HasColumnType("bit"); + + b.Property("IsmeteringSupported") + .HasColumnType("bit"); + + b.Property("OfferId") + .HasColumnType("uniqueidentifier") + .HasColumnName("OfferID"); + + b.Property("PlanGuid") + .HasColumnType("uniqueidentifier") + .HasColumnName("PlanGUID"); + + b.Property("PlanId") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.ToTable("Plans"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Roles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Name") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SchedulerFrequency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Frequency") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.ToTable("SchedulerFrequency"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SchedulerManagerView", b => + { + b.Property("AMPSubscriptionId") + .HasColumnType("uniqueidentifier"); + + b.Property("Dimension") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("Frequency") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("Id") + .HasColumnType("int"); + + b.Property("NextRunTime") + .HasColumnType("datetime2"); + + b.Property("PlanId") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("PurchaserEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("Quantity") + .HasColumnType("float"); + + b.Property("SchedulerName") + .HasColumnType("nvarchar(max)"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("SubscriptionName") + .HasColumnType("nvarchar(max)"); + + b.ToView("SchedulerManagerView"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SubscriptionAttributeValues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("OfferId") + .HasColumnType("uniqueidentifier") + .HasColumnName("OfferID"); + + b.Property("PlanAttributeId") + .HasColumnType("int"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier") + .HasColumnName("PlanID"); + + b.Property("SubscriptionId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionAttributeValues"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SubscriptionAuditLogs", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Attribute") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)"); + + b.Property("CreateBy") + .HasColumnType("int"); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("NewValue") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("OldValue") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("SubscriptionId") + .HasColumnType("int") + .HasColumnName("SubscriptionID"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("SubscriptionAuditLogs"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SubscriptionEmailOutput", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Name") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("Value") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.ToTable("SubscriptionEmailOutput"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SubscriptionParametersOutput", b => + { + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("DisplaySequence") + .HasColumnType("int"); + + b.Property("FromList") + .HasColumnType("bit"); + + b.Property("Htmltype") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)") + .HasColumnName("HTMLType"); + + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("IsRequired") + .HasColumnType("bit"); + + b.Property("Max") + .HasColumnType("int"); + + b.Property("Min") + .HasColumnType("int"); + + b.Property("OfferAttributeId") + .HasColumnType("int") + .HasColumnName("OfferAttributeID"); + + b.Property("OfferId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlanAttributeId") + .HasColumnType("int"); + + b.Property("PlanId") + .HasColumnType("uniqueidentifier"); + + b.Property("SubscriptionId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("ValuesList") + .IsRequired() + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.HasKey("RowNumber") + .HasName("PK__Subscrip__AAAC09D8BA727059"); + + b.ToTable("SubscriptionParametersOutput"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Subscriptions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AmpOfferId") + .HasColumnType("nvarchar(max)"); + + b.Property("AmpplanId") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("AMPPlanId"); + + b.Property("Ampquantity") + .HasColumnType("int") + .HasColumnName("AMPQuantity"); + + b.Property("AmpsubscriptionId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasColumnName("AMPSubscriptionId") + .HasDefaultValueSql("(newid())"); + + b.Property("CreateBy") + .HasColumnType("int"); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ModifyDate") + .HasColumnType("datetime"); + + b.Property("Name") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("PurchaserEmail") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.Property("PurchaserTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("SubscriptionStatus") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("Term") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Users", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("UserId"), 1L, 1); + + b.Property("CreatedDate") + .HasColumnType("datetime"); + + b.Property("EmailAddress") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("FullName") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.HasKey("UserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.ValueTypes", b => + { + b.Property("ValueTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ValueTypeId"), 1L, 1); + + b.Property("CreateDate") + .HasColumnType("datetime"); + + b.Property("Htmltype") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)") + .HasColumnName("HTMLType"); + + b.Property("ValueType") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.HasKey("ValueTypeId") + .HasName("PK__ValueTyp__A51E9C5AEA096123"); + + b.ToTable("ValueTypes"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.WebJobSubscriptionStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)"); + + b.Property("InsertDate") + .HasColumnType("datetime"); + + b.Property("SubscriptionId") + .HasColumnType("uniqueidentifier"); + + b.Property("SubscriptionStatus") + .HasMaxLength(225) + .IsUnicode(false) + .HasColumnType("varchar(225)"); + + b.HasKey("Id"); + + b.ToTable("WebJobSubscriptionStatus"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.KnownUsers", b => + { + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Roles", "Role") + .WithMany("KnownUsers") + .HasForeignKey("RoleId") + .IsRequired() + .HasConstraintName("FK__KnownUser__RoleI__619B8048"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredAuditLogs", b => + { + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Subscriptions", "Subscription") + .WithMany("MeteredAuditLogs") + .HasForeignKey("SubscriptionId") + .HasConstraintName("FK__MeteredAu__Subsc__628FA481"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredDimensions", b => + { + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Plans", "Plan") + .WithMany("MeteredDimensions") + .HasForeignKey("PlanId") + .HasConstraintName("FK__MeteredDi__PlanI__6383C8BA"); + + b.Navigation("Plan"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredPlanSchedulerManagement", b => + { + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredDimensions", "MeteredDimensions") + .WithMany("MeteredPlanSchedulerManagements") + .HasForeignKey("DimensionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.SchedulerFrequency", "SchedulerFrequency") + .WithMany("MeteredPlanSchedulerManagements") + .HasForeignKey("FrequencyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Plans", "Plan") + .WithMany("MeteredPlanSchedulerManagements") + .HasForeignKey("PlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Subscriptions", "Subscriptions") + .WithMany("MeteredPlanSchedulerManagements") + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MeteredDimensions"); + + b.Navigation("Plan"); + + b.Navigation("SchedulerFrequency"); + + b.Navigation("Subscriptions"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SubscriptionAuditLogs", b => + { + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Subscriptions", "Subscription") + .WithMany("SubscriptionAuditLogs") + .HasForeignKey("SubscriptionId") + .HasConstraintName("FK__Subscript__Subsc__6477ECF3"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Subscriptions", b => + { + b.HasOne("Marketplace.SaaS.Accelerator.DataAccess.Entities.Users", "User") + .WithMany("Subscriptions") + .HasForeignKey("UserId") + .HasConstraintName("FK__Subscript__UserI__656C112C"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.MeteredDimensions", b => + { + b.Navigation("MeteredPlanSchedulerManagements"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Plans", b => + { + b.Navigation("MeteredDimensions"); + + b.Navigation("MeteredPlanSchedulerManagements"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Roles", b => + { + b.Navigation("KnownUsers"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.SchedulerFrequency", b => + { + b.Navigation("MeteredPlanSchedulerManagements"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Subscriptions", b => + { + b.Navigation("MeteredAuditLogs"); + + b.Navigation("MeteredPlanSchedulerManagements"); + + b.Navigation("SubscriptionAuditLogs"); + }); + + modelBuilder.Entity("Marketplace.SaaS.Accelerator.DataAccess.Entities.Users", b => + { + b.Navigation("Subscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/DataAccess/Migrations/20240312055030_baseline751.cs b/src/DataAccess/Migrations/20240312055030_baseline751.cs new file mode 100644 index 00000000..8b5b6c87 --- /dev/null +++ b/src/DataAccess/Migrations/20240312055030_baseline751.cs @@ -0,0 +1,20 @@ +using Marketplace.SaaS.Accelerator.DataAccess.Migrations.Custom; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Marketplace.SaaS.Accelerator.DataAccess.Migrations +{ + public partial class baseline751 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.BaselineV751_SeedData(); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.BaselineV751_DeSeedData(); + } + } +} diff --git a/src/DataAccess/Migrations/Custom/BaselineV751_Seed.cs b/src/DataAccess/Migrations/Custom/BaselineV751_Seed.cs new file mode 100644 index 00000000..1bcbed1e --- /dev/null +++ b/src/DataAccess/Migrations/Custom/BaselineV751_Seed.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Query.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Marketplace.SaaS.Accelerator.DataAccess.Migrations.Custom +{ + internal static class BaselineV751_Seed + { + public static void BaselineV751_SeedData(this MigrationBuilder migrationBuilder) + { + var seedDate = DateTime.Now; + migrationBuilder.Sql(@$" + IF NOT EXISTS (SELECT * FROM [dbo].[ApplicationConfiguration] WHERE [Name] = 'ValidateWebhookJwtToken') + BEGIN + INSERT [dbo].[ApplicationConfiguration] ( [Name], [Value], [Description]) VALUES ( N'ValidateWebhookJwtToken', N'true', N'Validates JWT token when webhook event is recieved.') + END + GO"); + } + + public static void BaselineV751_DeSeedData(this MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@$" + + IF EXISTS (SELECT * FROM [dbo].[ApplicationConfiguration] WHERE [Name] = 'ValidateWebhookJwtToken') + BEGIN + DELETE FROM [dbo].[ApplicationConfiguration] WHERE [Name] = 'ValidateWebhookJwtToken' + END + GO"); + } + } +} \ No newline at end of file diff --git a/src/Services/Utilities/ValidateJwtToken.cs b/src/Services/Utilities/ValidateJwtToken.cs new file mode 100644 index 00000000..c0100e70 --- /dev/null +++ b/src/Services/Utilities/ValidateJwtToken.cs @@ -0,0 +1,76 @@ +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Tokens; +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Threading; +using System.Threading.Tasks; +using Marketplace.SaaS.Accelerator.Services.Configurations; +using System.Security.Claims; + +namespace Marketplace.SaaS.Accelerator.Services.Utilities; + +public class ValidateJwtToken +{ + + private readonly SaaSApiClientConfiguration _saasapiConfiguration; + + public ValidateJwtToken(SaaSApiClientConfiguration saasapiConfiguration) + { + _saasapiConfiguration = saasapiConfiguration; + } + + public async Task ValidateTokenAsync(string token) + { + var configurationManager = new ConfigurationManager( + $"https://login.microsoftonline.com/{_saasapiConfiguration.TenantId}/.well-known/openid-configuration", + new OpenIdConnectConfigurationRetriever(), + new HttpDocumentRetriever()); + var openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None); + var signingKeys = openIdConfig.SigningKeys; + var audience = _saasapiConfiguration.ClientId; + + var validationParameters = new TokenValidationParameters + { + //Issuer can change based on the token version. So we are not validating the issuer + ValidateIssuer = false, + ValidateAudience = true, + ValidAudience = audience, + ValidateIssuerSigningKey = true, + IssuerSigningKeys = signingKeys, + ValidateLifetime = true, + ClockSkew = TimeSpan.FromMinutes(0) + }; + + var handler = new JwtSecurityTokenHandler(); + + //validate aud, expiry and signature using jwt validation handler + ClaimsPrincipal claimsPrincipal = handler.ValidateToken(token, validationParameters, out var validatedToken); + + // Get the 'tid' claim + Claim tidClaim = claimsPrincipal.FindFirst("tid"); + string tenantId = tidClaim?.Value; + + Claim tidfullClaim = claimsPrincipal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid"); + string tidfull = tidfullClaim?.Value; + + //For the old token v1, its 'appId' and for v2 its 'azp'. Try get both. + Claim azpClaim = claimsPrincipal.FindFirst("azp"); + string azpId = azpClaim?.Value; + + Claim appidClaim = claimsPrincipal.FindFirst("appid"); + string appId = appidClaim?.Value; + + //return false if the tenantId or azpId or appId is not matching with the configuration + if ((tenantId != _saasapiConfiguration.TenantId) && (tidfull != _saasapiConfiguration.TenantId)) + { + throw new Exception("TenantId is not matching with the configuration"); + } + if ((azpId != _saasapiConfiguration.Resource) && (appId != _saasapiConfiguration.Resource)) + { + throw new Exception("azpId or appId is not matching with the configuration"); + } + + return true; + } +}