Premigrations + Updated NuGet Dependencies (#15987)

* Updated nuget packages + added migrations for OpenIddict - Currently can only be executed using unatttended installs

* Added new Premigration concept - Migrations that always runs unattended before other migrations

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

---------

Co-authored-by: Zeegaan <skrivdetud@gmail.com>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
Bjarke Berg
2024-04-10 12:30:30 +02:00
committed by GitHub
parent 082d504b33
commit fcda25af50
25 changed files with 941 additions and 41 deletions

View File

@@ -12,25 +12,25 @@
</ItemGroup>
<!-- Microsoft packages -->
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
</ItemGroup>
@@ -41,15 +41,15 @@
</ItemGroup>
<!-- Third-party packages -->
<ItemGroup>
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.0.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.0.0" />
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
<PackageVersion Include="Examine" Version="3.2.0" />
<PackageVersion Include="Examine.Core" Version="3.2.0" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.57" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.60" />
<PackageVersion Include="JsonPatch.Net" Version="2.1.0" />
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.6" />
<PackageVersion Include="MailKit" Version="4.3.0" />
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageVersion Include="MailKit" Version="4.4.0" />
<PackageVersion Include="Markdown" Version="2.2.1" />
<PackageVersion Include="MessagePack" Version="2.5.140" />
<PackageVersion Include="MiniProfiler.AspNetCore.Mvc" Version="4.3.8" />
@@ -57,9 +57,9 @@
<PackageVersion Include="ncrontab" Version="3.3.3" />
<PackageVersion Include="NPoco" Version="5.7.1" />
<PackageVersion Include="NPoco.SqlServer" Version="5.7.1" />
<PackageVersion Include="OpenIddict.Abstractions" Version="4.10.1" />
<PackageVersion Include="OpenIddict.AspNetCore" Version="4.10.1" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="4.10.1" />
<PackageVersion Include="OpenIddict.Abstractions" Version="5.4.0" />
<PackageVersion Include="OpenIddict.AspNetCore" Version="5.4.0" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="5.4.0" />
<PackageVersion Include="Serilog" Version="3.1.1" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageVersion Include="Serilog.Enrichers.Process" Version="2.0.2" />
@@ -73,7 +73,7 @@
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.Map" Version="1.0.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.0" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.1" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
<!-- Transitive pinned versions (only required because our direct dependencies have vulnerable versions of transitive dependencies) -->
@@ -87,4 +87,4 @@
<!-- Both Dazinator.Extensions.FileProviders and MiniProfiler.AspNetCore.Mvc bring in a vulnerable version of System.Text.RegularExpressions -->
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
</ItemGroup>
</Project>
</Project>

View File

@@ -36,7 +36,7 @@ public class MemberApplicationManager : OpenIdDictApplicationManagerBase, IMembe
{
DisplayName = "Umbraco member access",
ClientId = Constants.OAuthClientIds.Member,
Type = OpenIddictConstants.ClientTypes.Public,
ClientType = OpenIddictConstants.ClientTypes.Public,
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,

View File

@@ -64,7 +64,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB
{
CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html")
},
Type = OpenIddictConstants.ClientTypes.Public,
ClientType = OpenIddictConstants.ClientTypes.Public,
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
@@ -84,7 +84,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB
{
new Uri("https://oauth.pstmn.io/v1/callback"), new Uri("https://oauth.pstmn.io/v1/browser-callback")
},
Type = OpenIddictConstants.ClientTypes.Public,
ClientType = OpenIddictConstants.ClientTypes.Public,
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
@@ -106,7 +106,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB
{
CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, _authorizeCallbackPathName),
},
Type = OpenIddictConstants.ClientTypes.Public,
ClientType = OpenIddictConstants.ClientTypes.Public,
PostLogoutRedirectUris =
{
CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, _authorizeCallbackPathName),

View File

@@ -0,0 +1,277 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Umbraco.Cms.Persistence.EFCore;
#nullable disable
namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
{
[DbContext(typeof(UmbracoDbContext))]
[Migration("20240403140654_UpdateOpenIddictToV5")]
partial class UpdateOpenIddictToV5
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<string>("ApplicationType")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("ClientId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("ClientSecret")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClientType")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("ConsentType")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayNames")
.HasColumnType("nvarchar(max)");
b.Property<string>("JsonWebKeySet")
.HasColumnType("nvarchar(max)");
b.Property<string>("Permissions")
.HasColumnType("nvarchar(max)");
b.Property<string>("PostLogoutRedirectUris")
.HasColumnType("nvarchar(max)");
b.Property<string>("Properties")
.HasColumnType("nvarchar(max)");
b.Property<string>("RedirectUris")
.HasColumnType("nvarchar(max)");
b.Property<string>("Requirements")
.HasColumnType("nvarchar(max)");
b.Property<string>("Settings")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique()
.HasFilter("[ClientId] IS NOT NULL");
b.ToTable("umbracoOpenIddictApplications", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<string>("ApplicationId")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime?>("CreationDate")
.HasColumnType("datetime2");
b.Property<string>("Properties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Scopes")
.HasColumnType("nvarchar(max)");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Id");
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("umbracoOpenIddictAuthorizations", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("Descriptions")
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayNames")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Properties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Resources")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique()
.HasFilter("[Name] IS NOT NULL");
b.ToTable("umbracoOpenIddictScopes", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<string>("ApplicationId")
.HasColumnType("nvarchar(450)");
b.Property<string>("AuthorizationId")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime?>("CreationDate")
.HasColumnType("datetime2");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("datetime2");
b.Property<string>("Payload")
.HasColumnType("nvarchar(max)");
b.Property<string>("Properties")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("RedemptionDate")
.HasColumnType("datetime2");
b.Property<string>("ReferenceId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Id");
b.HasIndex("AuthorizationId");
b.HasIndex("ReferenceId")
.IsUnique()
.HasFilter("[ReferenceId] IS NOT NULL");
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("umbracoOpenIddictTokens", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Authorizations")
.HasForeignKey("ApplicationId");
b.Navigation("Application");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Tokens")
.HasForeignKey("ApplicationId");
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization")
.WithMany("Tokens")
.HasForeignKey("AuthorizationId");
b.Navigation("Application");
b.Navigation("Authorization");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Navigation("Authorizations");
b.Navigation("Tokens");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Navigation("Tokens");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
{
/// <inheritdoc />
public partial class UpdateOpenIddictToV5 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Type",
table: "umbracoOpenIddictApplications",
newName: "ClientType");
migrationBuilder.AddColumn<string>(
name: "ApplicationType",
table: "umbracoOpenIddictApplications",
type: "nvarchar(50)",
maxLength: 50,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "JsonWebKeySet",
table: "umbracoOpenIddictApplications",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Settings",
table: "umbracoOpenIddictApplications",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ApplicationType",
table: "umbracoOpenIddictApplications");
migrationBuilder.DropColumn(
name: "JsonWebKeySet",
table: "umbracoOpenIddictApplications");
migrationBuilder.DropColumn(
name: "Settings",
table: "umbracoOpenIddictApplications");
migrationBuilder.RenameColumn(
name: "ClientType",
table: "umbracoOpenIddictApplications",
newName: "Type");
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.7")
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -28,6 +28,10 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<string>("ApplicationType")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("ClientId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
@@ -35,6 +39,10 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
b.Property<string>("ClientSecret")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClientType")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
@@ -50,6 +58,9 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
b.Property<string>("DisplayNames")
.HasColumnType("nvarchar(max)");
b.Property<string>("JsonWebKeySet")
.HasColumnType("nvarchar(max)");
b.Property<string>("Permissions")
.HasColumnType("nvarchar(max)");
@@ -65,9 +76,8 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer.Migrations
b.Property<string>("Requirements")
.HasColumnType("nvarchar(max)");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Settings")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");

View File

@@ -30,6 +30,7 @@ public class SqlServerMigrationProvider : IMigrationProvider
{
EFCoreMigration.InitialCreate => typeof(Migrations.InitialCreate),
EFCoreMigration.AddOpenIddict => typeof(Migrations.AddOpenIddict),
EFCoreMigration.UpdateOpenIddictToV5 => typeof(Migrations.UpdateOpenIddictToV5),
_ => throw new ArgumentOutOfRangeException(nameof(migration), $@"Not expected migration value: {migration}")
};
}

View File

@@ -0,0 +1,269 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Umbraco.Cms.Persistence.EFCore;
#nullable disable
namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
{
[DbContext(typeof(UmbracoDbContext))]
[Migration("20240403141051_UpdateOpenIddictToV5")]
partial class UpdateOpenIddictToV5
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.3");
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ApplicationType")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("ClientId")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("ClientSecret")
.HasColumnType("TEXT");
b.Property<string>("ClientType")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("ConsentType")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("DisplayName")
.HasColumnType("TEXT");
b.Property<string>("DisplayNames")
.HasColumnType("TEXT");
b.Property<string>("JsonWebKeySet")
.HasColumnType("TEXT");
b.Property<string>("Permissions")
.HasColumnType("TEXT");
b.Property<string>("PostLogoutRedirectUris")
.HasColumnType("TEXT");
b.Property<string>("Properties")
.HasColumnType("TEXT");
b.Property<string>("RedirectUris")
.HasColumnType("TEXT");
b.Property<string>("Requirements")
.HasColumnType("TEXT");
b.Property<string>("Settings")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("umbracoOpenIddictApplications", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ApplicationId")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<DateTime?>("CreationDate")
.HasColumnType("TEXT");
b.Property<string>("Properties")
.HasColumnType("TEXT");
b.Property<string>("Scopes")
.HasColumnType("TEXT");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("TEXT");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("umbracoOpenIddictAuthorizations", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Descriptions")
.HasColumnType("TEXT");
b.Property<string>("DisplayName")
.HasColumnType("TEXT");
b.Property<string>("DisplayNames")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(200)
.HasColumnType("TEXT");
b.Property<string>("Properties")
.HasColumnType("TEXT");
b.Property<string>("Resources")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("umbracoOpenIddictScopes", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ApplicationId")
.HasColumnType("TEXT");
b.Property<string>("AuthorizationId")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<DateTime?>("CreationDate")
.HasColumnType("TEXT");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("TEXT");
b.Property<string>("Payload")
.HasColumnType("TEXT");
b.Property<string>("Properties")
.HasColumnType("TEXT");
b.Property<DateTime?>("RedemptionDate")
.HasColumnType("TEXT");
b.Property<string>("ReferenceId")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("TEXT");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorizationId");
b.HasIndex("ReferenceId")
.IsUnique();
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("umbracoOpenIddictTokens", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Authorizations")
.HasForeignKey("ApplicationId");
b.Navigation("Application");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Tokens")
.HasForeignKey("ApplicationId");
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization")
.WithMany("Tokens")
.HasForeignKey("AuthorizationId");
b.Navigation("Application");
b.Navigation("Authorization");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Navigation("Authorizations");
b.Navigation("Tokens");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Navigation("Tokens");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
{
/// <inheritdoc />
public partial class UpdateOpenIddictToV5 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Type",
table: "umbracoOpenIddictApplications",
newName: "ClientType");
migrationBuilder.AddColumn<string>(
name: "ApplicationType",
table: "umbracoOpenIddictApplications",
type: "TEXT",
maxLength: 50,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "JsonWebKeySet",
table: "umbracoOpenIddictApplications",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Settings",
table: "umbracoOpenIddictApplications",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ApplicationType",
table: "umbracoOpenIddictApplications");
migrationBuilder.DropColumn(
name: "JsonWebKeySet",
table: "umbracoOpenIddictApplications");
migrationBuilder.DropColumn(
name: "Settings",
table: "umbracoOpenIddictApplications");
migrationBuilder.RenameColumn(
name: "ClientType",
table: "umbracoOpenIddictApplications",
newName: "Type");
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.7");
modelBuilder.HasAnnotation("ProductVersion", "8.0.3");
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
@@ -23,6 +23,10 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ApplicationType")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("ClientId")
.HasMaxLength(100)
.HasColumnType("TEXT");
@@ -30,6 +34,10 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
b.Property<string>("ClientSecret")
.HasColumnType("TEXT");
b.Property<string>("ClientType")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
@@ -45,6 +53,9 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
b.Property<string>("DisplayNames")
.HasColumnType("TEXT");
b.Property<string>("JsonWebKeySet")
.HasColumnType("TEXT");
b.Property<string>("Permissions")
.HasColumnType("TEXT");
@@ -60,8 +71,7 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite.Migrations
b.Property<string>("Requirements")
.HasColumnType("TEXT");
b.Property<string>("Type")
.HasMaxLength(50)
b.Property<string>("Settings")
.HasColumnType("TEXT");
b.HasKey("Id");

View File

@@ -38,6 +38,7 @@ public class SqliteMigrationProvider : IMigrationProvider
{
EFCoreMigration.InitialCreate => typeof(Migrations.InitialCreate),
EFCoreMigration.AddOpenIddict => typeof(Migrations.AddOpenIddict),
EFCoreMigration.UpdateOpenIddictToV5 => typeof(Migrations.UpdateOpenIddictToV5),
_ => throw new ArgumentOutOfRangeException(nameof(migration), $@"Not expected migration value: {migration}")
};
}

View File

@@ -17,9 +17,9 @@ namespace Umbraco.Cms.Persistence.EFCore;
/// and insure the 'src/Umbraco.Web.UI/appsettings.json' have a connection string set with the right provider.
///
/// Create a migration for each provider.
/// <code>dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer -- --provider SqlServer</code>
/// <code>dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer -c UmbracoDbContext -- --provider SqlServer</code>
///
/// <code>dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.Sqlite -- --provider Sqlite</code>
/// <code>dotnet ef migrations add %Name% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.Sqlite -c UmbracoDbContext -- --provider Sqlite</code>
///
/// Remove the last migration for each provider.
/// <code>dotnet ef migrations remove -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer -- --provider SqlServer</code>

View File

@@ -10,8 +10,10 @@ public static partial class Constants
public static class Migrations
{
public const string UmbracoUpgradePlanName = "Umbraco.Core";
public const string UmbracoUpgradePlanPremigrationsName = "Umbraco.Core.Premigrations";
public const string KeyValuePrefix = "Umbraco.Core.Upgrader.State+";
public const string UmbracoUpgradePlanKey = KeyValuePrefix + UmbracoUpgradePlanName;
public const string UmbracoUpgradePlanPremigrationsKey = KeyValuePrefix + UmbracoUpgradePlanPremigrationsName;
}
public static class PermissionCategories

View File

@@ -0,0 +1,16 @@
namespace Umbraco.Cms.Core.Notifications;
public class RuntimePremigrationsUpgradeNotification : INotification
{
public enum PremigrationUpgradeResult
{
NotRequired = 0,
HasErrors = 1,
CoreUpgradeComplete = 100,
}
/// <summary>
/// Gets/sets the result of the upgrade
/// </summary>
public PremigrationUpgradeResult UpgradeResult { get; set; } = PremigrationUpgradeResult.NotRequired;
}

View File

@@ -85,6 +85,7 @@ public static partial class UmbracoBuilderExtensions
builder.Services.AddSingleton<PendingPackageMigrations>();
builder.AddNotificationAsyncHandler<RuntimeUnattendedInstallNotification, UnattendedInstaller>();
builder.AddNotificationAsyncHandler<RuntimeUnattendedUpgradeNotification, UnattendedUpgrader>();
builder.AddNotificationAsyncHandler<RuntimePremigrationsUpgradeNotification, PremigrationUpgrader>();
// Add runtime mode validation
builder.Services.AddSingleton<IRuntimeModeValidationService, RuntimeModeValidationService>();

View File

@@ -0,0 +1,83 @@
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Migrations.Install;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
using Umbraco.Cms.Infrastructure.Persistence;
namespace Umbraco.Cms.Infrastructure.Install;
/// <summary>
/// Handles <see cref="RuntimeUnattendedUpgradeNotification" /> to execute the unattended Umbraco upgrader
/// or the unattended Package migrations runner.
/// </summary>
public class PremigrationUpgrader : INotificationAsyncHandler<RuntimePremigrationsUpgradeNotification>
{
private readonly DatabaseBuilder _databaseBuilder;
private readonly IProfilingLogger _profilingLogger;
private readonly IRuntimeState _runtimeState;
private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory;
private readonly IKeyValueService _keyValueService;
public PremigrationUpgrader(
IProfilingLogger profilingLogger,
DatabaseBuilder databaseBuilder,
IRuntimeState runtimeState,
IUmbracoDatabaseFactory umbracoDatabaseFactory,
IKeyValueService keyValueService)
{
_profilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger));
_databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder));
_runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
_umbracoDatabaseFactory = umbracoDatabaseFactory;
_keyValueService = keyValueService;
}
public Task HandleAsync(RuntimePremigrationsUpgradeNotification notification, CancellationToken cancellationToken)
{
// no connection string set
if (_umbracoDatabaseFactory.Configured is false)
{
return Task.CompletedTask;
}
if (_databaseBuilder.IsUmbracoInstalled() is false)
{
return Task.CompletedTask;
}
var plan = new UmbracoPremigrationPlan();
if (HasMissingPremigrations(plan) is false)
{
return Task.CompletedTask;
}
using (_profilingLogger.IsEnabled(LogLevel.Verbose) is false ? null : _profilingLogger.TraceDuration<UnattendedUpgrader>(
"Starting premigration upgrade.",
"Unattended premigration completed."))
{
DatabaseBuilder.Result? result = _databaseBuilder.UpgradeSchemaAndData(plan);
if (result?.Success is false)
{
var innerException = new IOException(
"An error occurred while running the premigration upgrade.\n" + result.Message);
_runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException);
}
notification.UpgradeResult =
RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.CoreUpgradeComplete;
}
return Task.CompletedTask;
}
private bool HasMissingPremigrations(UmbracoPremigrationPlan umbracoPremigrationPlan)
{
var premigrationState = _keyValueService.GetValue(Constants.Conventions.Migrations.UmbracoUpgradePlanPremigrationsKey);
return umbracoPremigrationPlan.FinalState != premigrationState;
}
}

View File

@@ -3,5 +3,6 @@ namespace Umbraco.Cms.Persistence.EFCore.Migrations;
public enum EFCoreMigration
{
InitialCreate = 0,
AddOpenIddict = 1
AddOpenIddict = 1,
UpdateOpenIddictToV5 = 2,
}

View File

@@ -347,6 +347,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
}
}
public Result? UpgradeSchemaAndData(UmbracoPlan plan) => UpgradeSchemaAndData((MigrationPlan)plan);
/// <summary>
/// Upgrades the database schema and data by running migrations.
/// </summary>
@@ -355,7 +357,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
/// configured and it is possible to connect to the database.</para>
/// <para>Runs whichever migrations need to run.</para>
/// </remarks>
public Result? UpgradeSchemaAndData(UmbracoPlan plan)
public Result? UpgradeSchemaAndData(MigrationPlan plan)
{
try
{

View File

@@ -2318,6 +2318,14 @@ internal class DatabaseDataCreator
_database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false,
new KeyValueDto { Key = stateValueKey, Value = finalState, UpdateDate = DateTime.Now });
upgrader = new Upgrader(new UmbracoPremigrationPlan());
stateValueKey = upgrader.StateValueKey;
finalState = upgrader.Plan.FinalState;
_database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false,
new KeyValueDto { Key = stateValueKey, Value = finalState, UpdateDate = DateTime.Now });
}
private void CreateLogViewerQueryData()

View File

@@ -0,0 +1,58 @@
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade;
/// <summary>
/// Represents the Umbraco CMS pre-migration plan. - Migrations that always runs unattended before the main migration plan.
/// </summary>
/// <seealso cref="Umbraco.Cms.Infrastructure.Migrations.MigrationPlan" />
public class UmbracoPremigrationPlan : MigrationPlan
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoPlan" /> class.
/// </summary>
/// <param name="umbracoVersion">The Umbraco version.</param>
public UmbracoPremigrationPlan()
: base(Constants.Conventions.Migrations.UmbracoUpgradePlanPremigrationsName)
=> DefinePlan();
/// <inheritdoc />
/// <remarks>
/// This is set to the final migration state of 13.0, making that the lowest supported version to upgrade from.
/// </remarks>
public override string InitialState => "";
/// <summary>
/// Defines the plan.
/// </summary>
/// <remarks>
/// This is virtual for testing purposes.
/// </remarks>
protected virtual void DefinePlan()
{
// Please take great care when modifying the plan!
//
// Creating a migration: append the migration to the main chain, using a new GUID.
//
// If the new migration causes a merge conflict, because someone else also added another
// new migration, you NEED to fix the conflict by providing one default path, and paths
// out of the conflict states, eg:
//
// From("state-1")
// To<ChangeA>("state-a")
// To<ChangeB>("state-b") // Some might already be in this state, without having applied ChangeA
//
// From("state-1")
// .Merge()
// .To<ChangeA>("state-a")
// .With()
// .To<ChangeB>("state-b")
// .As("state-2");
From(InitialState);
// To 14.0.0
To<V_14_0_0.UpdateToOpenIddictV5>("{76FBF80E-37E6-462E-ADC1-25668F56151D}");
}
}

View File

@@ -0,0 +1,19 @@
using Umbraco.Cms.Persistence.EFCore.Migrations;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_14_0_0;
public class UpdateToOpenIddictV5 : MigrationBase
{
private readonly IEFCoreMigrationExecutor _efCoreMigrationExecutor;
public UpdateToOpenIddictV5(IMigrationContext context, IEFCoreMigrationExecutor efCoreMigrationExecutor)
: base(context)
{
_efCoreMigrationExecutor = efCoreMigrationExecutor;
}
protected override void Migrate()
{
_efCoreMigrationExecutor.ExecuteSingleMigrationAsync(EFCoreMigration.UpdateOpenIddictToV5);
}
}

View File

@@ -179,6 +179,26 @@ public class CoreRuntime : IRuntime
throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(StartAsync)}");
}
var premigrationUpgradeNotification = new RuntimePremigrationsUpgradeNotification();
await _eventAggregator.PublishAsync(premigrationUpgradeNotification, cancellationToken);
switch (premigrationUpgradeNotification.UpgradeResult)
{
case RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.HasErrors:
if (State.BootFailedException is null)
{
throw new InvalidOperationException($"Premigration upgrade result was {RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.HasErrors} but no {nameof(BootFailedException)} was registered");
}
// We cannot continue here, the exception will be rethrown by BootFailedMiddelware
return;
case RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.CoreUpgradeComplete:
// Upgrade is done, set reason to Run
DetermineRuntimeLevel();
break;
case RuntimePremigrationsUpgradeNotification.PremigrationUpgradeResult.NotRequired:
break;
}
// If level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade
var unattendedUpgradeNotification = new RuntimeUnattendedUpgradeNotification();
await _eventAggregator.PublishAsync(unattendedUpgradeNotification, cancellationToken);

View File

@@ -5,9 +5,9 @@
<ItemGroup>
<!-- Microsoft packages -->
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="System.Data.DataSetExtensions" Version="4.5.0" />
<PackageVersion Include="System.Data.Odbc" Version="8.0.0" />
<PackageVersion Include="System.Data.OleDb" Version="8.0.0" />
@@ -17,10 +17,10 @@
<!-- Third-party packages -->
<PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" />
<PackageVersion Include="AutoFixture.NUnit3" Version="4.18.1" />
<PackageVersion Include="Bogus" Version="34.0.2" />
<PackageVersion Include="Bogus" Version="35.5.0" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="NUnit" Version="3.14.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" PrivateAssets="all" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
</Project>

View File

@@ -7,7 +7,8 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" VersionOverride="2.9.1" />
<PackageReference Include="NJsonSchema" VersionOverride="10.9.0" />
<PackageReference Include="NJsonSchema" VersionOverride="11.0.0" />
<PackageReference Include="NJsonSchema.NewtonsoftJson" VersionOverride="11.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,6 +2,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NJsonSchema.Generation;
using NJsonSchema.NewtonsoftJson.Generation;
/// <inheritdoc />
public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator
@@ -10,7 +11,7 @@ public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator
/// Initializes a new instance of the <see cref="UmbracoJsonSchemaGenerator" /> class.
/// </summary>
public UmbracoJsonSchemaGenerator()
: base(new JsonSchemaGeneratorSettings()
: base(new NewtonsoftJsonSchemaGeneratorSettings()
{
AlwaysAllowAdditionalObjectProperties = true,
DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull,
@@ -22,7 +23,9 @@ public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator
}
})
{
Settings.SerializerSettings.Converters.Add(new StringEnumConverter());
((NewtonsoftJsonSchemaGeneratorSettings)Settings).SerializerSettings.Converters.Add(new StringEnumConverter());
}
/// <inheritdoc />