Browse Source

Persist identity grant store to db

Persist machine Keys to Redis
pull/223/head
Ramón Tomás 7 years ago
parent
commit
8d0ef809a8
17 changed files with 2121 additions and 21 deletions
  1. +1
    -0
      k8s/deployments.yaml
  2. +1
    -0
      src/Services/Identity/Identity.API/Configuration/Config.cs
  3. +96
    -0
      src/Services/Identity/Identity.API/Extensions/DataProtectionBuilderExtensions.cs
  4. +214
    -0
      src/Services/Identity/Identity.API/Extensions/RedisXmlRepository.cs
  5. +1
    -0
      src/Services/Identity/Identity.API/Identity.API.csproj
  6. +52
    -0
      src/Services/Identity/Identity.API/Migrations/20170604151240_Init-persisted-grant.Designer.cs
  7. +40
    -0
      src/Services/Identity/Identity.API/Migrations/20170604151240_Init-persisted-grant.cs
  8. +539
    -0
      src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20170604151338_Init-configuration.Designer.cs
  9. +501
    -0
      src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20170604151338_Init-configuration.cs
  10. +538
    -0
      src/Services/Identity/Identity.API/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs
  11. +51
    -0
      src/Services/Identity/Identity.API/Migrations/PersistedGrantDbContextModelSnapshot.cs
  12. +65
    -16
      src/Services/Identity/Identity.API/Startup.cs
  13. +7
    -4
      src/Web/WebMVC/Controllers/AccountController.cs
  14. +3
    -1
      src/Web/WebMVC/Startup.cs
  15. +4
    -0
      test/Services/FunctionalTests/FunctionalTests.csproj
  16. +4
    -0
      test/Services/IntegrationTests/IntegrationTests.csproj
  17. +4
    -0
      test/Services/UnitTest/UnitTest.csproj

+ 1
- 0
k8s/deployments.yaml View File

@ -69,6 +69,7 @@ kind: Deployment
metadata:
name: identity
spec:
replicas: 3
paused: true
template:
metadata:


+ 1
- 0
src/Services/Identity/Identity.API/Configuration/Config.cs View File

@ -80,6 +80,7 @@ namespace Identity.API.Configuration
},
ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client
AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = false,
RequireConsent = false,
AllowOfflineAccess = true,
RedirectUris = new List<string>


+ 96
- 0
src/Services/Identity/Identity.API/Extensions/DataProtectionBuilderExtensions.cs View File

@ -0,0 +1,96 @@
namespace DataProtectionExtensions
{
using System;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Net;
/// <summary>
/// Extension methods for <see cref="IDataProtectionBuilder"/> for configuring
/// data protection options.
/// </summary>
public static class DataProtectionBuilderExtensions
{
/// <summary>
/// Sets up data protection to persist session keys in Redis.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> used to set up data protection options.</param>
/// <param name="redisConnectionString">The connection string specifying the Redis instance and database for key storage.</param>
/// <returns>
/// The <paramref name="builder" /> for continued configuration.
/// </returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="builder" /> or <paramref name="redisConnectionString" /> is <see langword="null" />.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown if <paramref name="redisConnectionString" /> is empty.
/// </exception>
public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (redisConnectionString == null)
{
throw new ArgumentNullException(nameof(redisConnectionString));
}
if (redisConnectionString.Length == 0)
{
throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString));
}
var ips = Dns.GetHostAddressesAsync(redisConnectionString).Result;
return builder.Use(ServiceDescriptor.Singleton<IXmlRepository>(services => new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService<ILogger<RedisXmlRepository>>())));
}
/// <summary>
/// Updates an <see cref="IDataProtectionBuilder"/> to use the service of
/// a specific type, removing all other services of that type.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> that should use the specified service.</param>
/// <param name="descriptor">The <see cref="ServiceDescriptor"/> with the service the <paramref name="builder" /> should use.</param>
/// <returns>
/// The <paramref name="builder" /> for continued configuration.
/// </returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="builder" /> or <paramref name="descriptor" /> is <see langword="null" />.
/// </exception>
public static IDataProtectionBuilder Use(this IDataProtectionBuilder builder, ServiceDescriptor descriptor)
{
// This algorithm of removing all other services of a specific type
// before adding the new/replacement service is how the base ASP.NET
// DataProtection bits work. Due to some of the differences in how
// that base set of bits handles DI, it's better to follow suit
// and work in the same way than to try and debug weird issues.
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
for (int i = builder.Services.Count - 1; i >= 0; i--)
{
if (builder.Services[i]?.ServiceType == descriptor.ServiceType)
{
builder.Services.RemoveAt(i);
}
}
builder.Services.Add(descriptor);
return builder;
}
}
}

+ 214
- 0
src/Services/Identity/Identity.API/Extensions/RedisXmlRepository.cs View File

@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
namespace DataProtectionExtensions
{
/// <summary>
/// Key repository that stores XML encrypted keys in a Redis distributed cache.
/// </summary>
/// <remarks>
/// <para>
/// The values stored in Redis are XML documents that contain encrypted session
/// keys used for the protection of things like session state. The document contents
/// are double-encrypted - first with a changing session key; then by a master key.
/// As such, there's no risk in storing the keys in Redis - even if someone can crack
/// the master key, they still need to also crack the session key. (Other solutions
/// for sharing keys across a farm environment include writing them to files
/// on a file share.)
/// </para>
/// <para>
/// While the repository uses a hash to keep the set of encrypted keys separate, you
/// can further separate these items from other items in Redis by specifying a unique
/// database in the connection string.
/// </para>
/// <para>
/// Consumers of the repository are responsible for caching the XML items as needed.
/// Typically repositories are consumed by things like <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider"/>
/// which generates <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.CacheableKeyRing"/>
/// values that get cached. The mechanism is already optimized for caching so there's
/// no need to create a redundant cache.
/// </para>
/// </remarks>
/// <seealso cref="Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository" />
/// <seealso cref="System.IDisposable" />
public class RedisXmlRepository : IXmlRepository, IDisposable
{
/// <summary>
/// The root cache key for XML items stored in Redis
/// </summary>
public static readonly string RedisHashKey = "DataProtectionXmlRepository";
/// <summary>
/// The connection to the Redis backing store.
/// </summary>
private IConnectionMultiplexer _connection;
/// <summary>
/// Flag indicating whether the object has been disposed.
/// </summary>
private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class.
/// </summary>
/// <param name="connectionString">
/// The Redis connection string.
/// </param>
/// <param name="logger">
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="connectionString" /> or <paramref name="logger" /> is <see langword="null" />.
/// </exception>
public RedisXmlRepository(string connectionString, ILogger<RedisXmlRepository> logger)
: this(ConnectionMultiplexer.Connect(connectionString), logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class.
/// </summary>
/// <param name="connection">
/// The Redis database connection.
/// </param>
/// <param name="logger">
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="connection" /> or <paramref name="logger" /> is <see langword="null" />.
/// </exception>
public RedisXmlRepository(IConnectionMultiplexer connection, ILogger<RedisXmlRepository> logger)
{
if (connection == null)
{
throw new ArgumentNullException(nameof(connection));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
this._connection = connection;
this.Logger = logger;
// Mask the password so it doesn't get logged.
var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase);
this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration);
}
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
/// </value>
public ILogger<RedisXmlRepository> Logger { get; private set; }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing,
/// or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
}
/// <summary>
/// Gets all top-level XML elements in the repository.
/// </summary>
/// <returns>
/// An <see cref="IReadOnlyCollection{T}"/> with the set of elements
/// stored in the repository.
/// </returns>
public IReadOnlyCollection<XElement> GetAllElements()
{
var database = this._connection.GetDatabase();
var hash = database.HashGetAll(RedisHashKey);
var elements = new List<XElement>();
if (hash == null || hash.Length == 0)
{
return elements.AsReadOnly();
}
foreach (var item in hash.ToStringDictionary())
{
elements.Add(XElement.Parse(item.Value));
}
this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count);
return elements.AsReadOnly();
}
/// <summary>
/// Adds a top-level XML element to the repository.
/// </summary>
/// <param name="element">The element to add.</param>
/// <param name="friendlyName">
/// An optional name to be associated with the XML element.
/// For instance, if this repository stores XML files on disk, the friendly name may
/// be used as part of the file name. Repository implementations are not required to
/// observe this parameter even if it has been provided by the caller.
/// </param>
/// <remarks>
/// The <paramref name="friendlyName" /> parameter must be unique if specified.
/// For instance, it could be the ID of the key being stored.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="element" /> is <see langword="null" />.
/// </exception>
public void StoreElement(XElement element, string friendlyName)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
if (string.IsNullOrEmpty(friendlyName))
{
// The framework always passes in a name, but
// the contract indicates this may be null or empty.
friendlyName = Guid.NewGuid().ToString();
}
this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName);
this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString());
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
if (this._connection != null)
{
this._connection.Close();
this._connection.Dispose();
}
}
this._connection = null;
this._disposed = true;
}
}
}
}

+ 1
- 0
src/Services/Identity/Identity.API/Identity.API.csproj View File

@ -38,6 +38,7 @@
</PackageReference>
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.1" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.1" />
<PackageReference Include="StackExchange.Redis" Version="1.2.3" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">


+ 52
- 0
src/Services/Identity/Identity.API/Migrations/20170604151240_Init-persisted-grant.Designer.cs View File

@ -0,0 +1,52 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using IdentityServer4.EntityFramework.DbContexts;
namespace Identity.API.Migrations
{
[DbContext(typeof(PersistedGrantDbContext))]
[Migration("20170604151240_Init-persisted-grant")]
partial class Initpersistedgrant
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.2")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<DateTime?>("Expiration");
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50);
b.HasKey("Key");
b.HasIndex("SubjectId", "ClientId", "Type");
b.ToTable("PersistedGrants");
});
}
}
}

+ 40
- 0
src/Services/Identity/Identity.API/Migrations/20170604151240_Init-persisted-grant.cs View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Identity.API.Migrations
{
public partial class Initpersistedgrant : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PersistedGrants",
columns: table => new
{
Key = table.Column<string>(maxLength: 200, nullable: false),
ClientId = table.Column<string>(maxLength: 200, nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
Data = table.Column<string>(maxLength: 50000, nullable: false),
Expiration = table.Column<DateTime>(nullable: true),
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
Type = table.Column<string>(maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
});
migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
table: "PersistedGrants",
columns: new[] { "SubjectId", "ClientId", "Type" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PersistedGrants");
}
}
}

+ 539
- 0
src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20170604151338_Init-configuration.Designer.cs View File

@ -0,0 +1,539 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using IdentityServer4.EntityFramework.DbContexts;
namespace Identity.API.Migrations.ConfigurationDb
{
[DbContext(typeof(ConfigurationDbContext))]
[Migration("20170604151338_Init-configuration")]
partial class Initconfiguration
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.2")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<string>("DisplayName")
.HasMaxLength(200);
b.Property<bool>("Enabled");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ApiResources");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiResourceId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiResourceId")
.IsRequired();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<string>("DisplayName")
.HasMaxLength(200);
b.Property<bool>("Emphasize");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.Property<bool>("Required");
b.Property<bool>("ShowInDiscoveryDocument");
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ApiScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiScopeId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ApiScopeId");
b.ToTable("ApiScopeClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiResourceId")
.IsRequired();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<DateTime?>("Expiration");
b.Property<string>("Type")
.HasMaxLength(250);
b.Property<string>("Value")
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiSecrets");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AbsoluteRefreshTokenLifetime");
b.Property<int>("AccessTokenLifetime");
b.Property<int>("AccessTokenType");
b.Property<bool>("AllowAccessTokensViaBrowser");
b.Property<bool>("AllowOfflineAccess");
b.Property<bool>("AllowPlainTextPkce");
b.Property<bool>("AllowRememberConsent");
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken");
b.Property<bool>("AlwaysSendClientClaims");
b.Property<int>("AuthorizationCodeLifetime");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<string>("ClientName")
.HasMaxLength(200);
b.Property<string>("ClientUri")
.HasMaxLength(2000);
b.Property<bool>("EnableLocalLogin");
b.Property<bool>("Enabled");
b.Property<int>("IdentityTokenLifetime");
b.Property<bool>("IncludeJwtId");
b.Property<string>("LogoUri");
b.Property<bool>("LogoutSessionRequired");
b.Property<string>("LogoutUri");
b.Property<bool>("PrefixClientClaims");
b.Property<string>("ProtocolType")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("RefreshTokenExpiration");
b.Property<int>("RefreshTokenUsage");
b.Property<bool>("RequireClientSecret");
b.Property<bool>("RequireConsent");
b.Property<bool>("RequirePkce");
b.Property<int>("SlidingRefreshTokenLifetime");
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("Clients");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(250);
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(250);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Origin")
.IsRequired()
.HasMaxLength(150);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientCorsOrigins");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("GrantType")
.IsRequired()
.HasMaxLength(250);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientGrantTypes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientIdPRestrictions");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("PostLogoutRedirectUri")
.IsRequired()
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientPostLogoutRedirectUris");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("RedirectUri")
.IsRequired()
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientRedirectUris");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Scope")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Description")
.HasMaxLength(2000);
b.Property<DateTime?>("Expiration");
b.Property<string>("Type")
.HasMaxLength(250);
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientSecrets");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("IdentityResourceId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("IdentityResourceId");
b.ToTable("IdentityClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<string>("DisplayName")
.HasMaxLength(200);
b.Property<bool>("Emphasize");
b.Property<bool>("Enabled");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.Property<bool>("Required");
b.Property<bool>("ShowInDiscoveryDocument");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("IdentityResources");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
.WithMany("UserClaims")
.HasForeignKey("ApiResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
.WithMany("Scopes")
.HasForeignKey("ApiResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
.WithMany("UserClaims")
.HasForeignKey("ApiScopeId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
.WithMany("Secrets")
.HasForeignKey("ApiResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("Claims")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("AllowedCorsOrigins")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("AllowedGrantTypes")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("IdentityProviderRestrictions")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("PostLogoutRedirectUris")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("RedirectUris")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("AllowedScopes")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("ClientSecrets")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
.WithMany("UserClaims")
.HasForeignKey("IdentityResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 501
- 0
src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20170604151338_Init-configuration.cs View File

@ -0,0 +1,501 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Identity.API.Migrations.ConfigurationDb
{
public partial class Initconfiguration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ApiResources",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Description = table.Column<string>(maxLength: 1000, nullable: true),
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
Enabled = table.Column<bool>(nullable: false),
Name = table.Column<string>(maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiResources", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Clients",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
AbsoluteRefreshTokenLifetime = table.Column<int>(nullable: false),
AccessTokenLifetime = table.Column<int>(nullable: false),
AccessTokenType = table.Column<int>(nullable: false),
AllowAccessTokensViaBrowser = table.Column<bool>(nullable: false),
AllowOfflineAccess = table.Column<bool>(nullable: false),
AllowPlainTextPkce = table.Column<bool>(nullable: false),
AllowRememberConsent = table.Column<bool>(nullable: false),
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(nullable: false),
AlwaysSendClientClaims = table.Column<bool>(nullable: false),
AuthorizationCodeLifetime = table.Column<int>(nullable: false),
ClientId = table.Column<string>(maxLength: 200, nullable: false),
ClientName = table.Column<string>(maxLength: 200, nullable: true),
ClientUri = table.Column<string>(maxLength: 2000, nullable: true),
EnableLocalLogin = table.Column<bool>(nullable: false),
Enabled = table.Column<bool>(nullable: false),
IdentityTokenLifetime = table.Column<int>(nullable: false),
IncludeJwtId = table.Column<bool>(nullable: false),
LogoUri = table.Column<string>(nullable: true),
LogoutSessionRequired = table.Column<bool>(nullable: false),
LogoutUri = table.Column<string>(nullable: true),
PrefixClientClaims = table.Column<bool>(nullable: false),
ProtocolType = table.Column<string>(maxLength: 200, nullable: false),
RefreshTokenExpiration = table.Column<int>(nullable: false),
RefreshTokenUsage = table.Column<int>(nullable: false),
RequireClientSecret = table.Column<bool>(nullable: false),
RequireConsent = table.Column<bool>(nullable: false),
RequirePkce = table.Column<bool>(nullable: false),
SlidingRefreshTokenLifetime = table.Column<int>(nullable: false),
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clients", x => x.Id);
});
migrationBuilder.CreateTable(
name: "IdentityResources",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Description = table.Column<string>(maxLength: 1000, nullable: true),
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
Emphasize = table.Column<bool>(nullable: false),
Enabled = table.Column<bool>(nullable: false),
Name = table.Column<string>(maxLength: 200, nullable: false),
Required = table.Column<bool>(nullable: false),
ShowInDiscoveryDocument = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityResources", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ApiClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ApiResourceId = table.Column<int>(nullable: false),
Type = table.Column<string>(maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiClaims", x => x.Id);
table.ForeignKey(
name: "FK_ApiClaims_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiScopes",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ApiResourceId = table.Column<int>(nullable: false),
Description = table.Column<string>(maxLength: 1000, nullable: true),
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
Emphasize = table.Column<bool>(nullable: false),
Name = table.Column<string>(maxLength: 200, nullable: false),
Required = table.Column<bool>(nullable: false),
ShowInDiscoveryDocument = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiScopes", x => x.Id);
table.ForeignKey(
name: "FK_ApiScopes_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiSecrets",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ApiResourceId = table.Column<int>(nullable: false),
Description = table.Column<string>(maxLength: 1000, nullable: true),
Expiration = table.Column<DateTime>(nullable: true),
Type = table.Column<string>(maxLength: 250, nullable: true),
Value = table.Column<string>(maxLength: 2000, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiSecrets", x => x.Id);
table.ForeignKey(
name: "FK_ApiSecrets_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
Type = table.Column<string>(maxLength: 250, nullable: false),
Value = table.Column<string>(maxLength: 250, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientClaims", x => x.Id);
table.ForeignKey(
name: "FK_ClientClaims_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientCorsOrigins",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
Origin = table.Column<string>(maxLength: 150, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id);
table.ForeignKey(
name: "FK_ClientCorsOrigins_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientGrantTypes",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
GrantType = table.Column<string>(maxLength: 250, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientGrantTypes", x => x.Id);
table.ForeignKey(
name: "FK_ClientGrantTypes_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientIdPRestrictions",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
Provider = table.Column<string>(maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id);
table.ForeignKey(
name: "FK_ClientIdPRestrictions_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientPostLogoutRedirectUris",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
PostLogoutRedirectUri = table.Column<string>(maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id);
table.ForeignKey(
name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientRedirectUris",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
RedirectUri = table.Column<string>(maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientRedirectUris", x => x.Id);
table.ForeignKey(
name: "FK_ClientRedirectUris_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientScopes",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
Scope = table.Column<string>(maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientScopes", x => x.Id);
table.ForeignKey(
name: "FK_ClientScopes_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientSecrets",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClientId = table.Column<int>(nullable: false),
Description = table.Column<string>(maxLength: 2000, nullable: true),
Expiration = table.Column<DateTime>(nullable: true),
Type = table.Column<string>(maxLength: 250, nullable: true),
Value = table.Column<string>(maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientSecrets", x => x.Id);
table.ForeignKey(
name: "FK_ClientSecrets_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "IdentityClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
IdentityResourceId = table.Column<int>(nullable: false),
Type = table.Column<string>(maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityClaims", x => x.Id);
table.ForeignKey(
name: "FK_IdentityClaims_IdentityResources_IdentityResourceId",
column: x => x.IdentityResourceId,
principalTable: "IdentityResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiScopeClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ApiScopeId = table.Column<int>(nullable: false),
Type = table.Column<string>(maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id);
table.ForeignKey(
name: "FK_ApiScopeClaims_ApiScopes_ApiScopeId",
column: x => x.ApiScopeId,
principalTable: "ApiScopes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ApiResources_Name",
table: "ApiResources",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiClaims_ApiResourceId",
table: "ApiClaims",
column: "ApiResourceId");
migrationBuilder.CreateIndex(
name: "IX_ApiScopes_ApiResourceId",
table: "ApiScopes",
column: "ApiResourceId");
migrationBuilder.CreateIndex(
name: "IX_ApiScopes_Name",
table: "ApiScopes",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiScopeClaims_ApiScopeId",
table: "ApiScopeClaims",
column: "ApiScopeId");
migrationBuilder.CreateIndex(
name: "IX_ApiSecrets_ApiResourceId",
table: "ApiSecrets",
column: "ApiResourceId");
migrationBuilder.CreateIndex(
name: "IX_Clients_ClientId",
table: "Clients",
column: "ClientId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientClaims_ClientId",
table: "ClientClaims",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientCorsOrigins_ClientId",
table: "ClientCorsOrigins",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientGrantTypes_ClientId",
table: "ClientGrantTypes",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientIdPRestrictions_ClientId",
table: "ClientIdPRestrictions",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientPostLogoutRedirectUris_ClientId",
table: "ClientPostLogoutRedirectUris",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientRedirectUris_ClientId",
table: "ClientRedirectUris",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientScopes_ClientId",
table: "ClientScopes",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_ClientSecrets_ClientId",
table: "ClientSecrets",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_IdentityClaims_IdentityResourceId",
table: "IdentityClaims",
column: "IdentityResourceId");
migrationBuilder.CreateIndex(
name: "IX_IdentityResources_Name",
table: "IdentityResources",
column: "Name",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ApiClaims");
migrationBuilder.DropTable(
name: "ApiScopeClaims");
migrationBuilder.DropTable(
name: "ApiSecrets");
migrationBuilder.DropTable(
name: "ClientClaims");
migrationBuilder.DropTable(
name: "ClientCorsOrigins");
migrationBuilder.DropTable(
name: "ClientGrantTypes");
migrationBuilder.DropTable(
name: "ClientIdPRestrictions");
migrationBuilder.DropTable(
name: "ClientPostLogoutRedirectUris");
migrationBuilder.DropTable(
name: "ClientRedirectUris");
migrationBuilder.DropTable(
name: "ClientScopes");
migrationBuilder.DropTable(
name: "ClientSecrets");
migrationBuilder.DropTable(
name: "IdentityClaims");
migrationBuilder.DropTable(
name: "ApiScopes");
migrationBuilder.DropTable(
name: "Clients");
migrationBuilder.DropTable(
name: "IdentityResources");
migrationBuilder.DropTable(
name: "ApiResources");
}
}
}

+ 538
- 0
src/Services/Identity/Identity.API/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs View File

@ -0,0 +1,538 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using IdentityServer4.EntityFramework.DbContexts;
namespace Identity.API.Migrations.ConfigurationDb
{
[DbContext(typeof(ConfigurationDbContext))]
partial class ConfigurationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.2")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<string>("DisplayName")
.HasMaxLength(200);
b.Property<bool>("Enabled");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ApiResources");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiResourceId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiResourceId")
.IsRequired();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<string>("DisplayName")
.HasMaxLength(200);
b.Property<bool>("Emphasize");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.Property<bool>("Required");
b.Property<bool>("ShowInDiscoveryDocument");
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ApiScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiScopeId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ApiScopeId");
b.ToTable("ApiScopeClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ApiResourceId")
.IsRequired();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<DateTime?>("Expiration");
b.Property<string>("Type")
.HasMaxLength(250);
b.Property<string>("Value")
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiSecrets");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AbsoluteRefreshTokenLifetime");
b.Property<int>("AccessTokenLifetime");
b.Property<int>("AccessTokenType");
b.Property<bool>("AllowAccessTokensViaBrowser");
b.Property<bool>("AllowOfflineAccess");
b.Property<bool>("AllowPlainTextPkce");
b.Property<bool>("AllowRememberConsent");
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken");
b.Property<bool>("AlwaysSendClientClaims");
b.Property<int>("AuthorizationCodeLifetime");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<string>("ClientName")
.HasMaxLength(200);
b.Property<string>("ClientUri")
.HasMaxLength(2000);
b.Property<bool>("EnableLocalLogin");
b.Property<bool>("Enabled");
b.Property<int>("IdentityTokenLifetime");
b.Property<bool>("IncludeJwtId");
b.Property<string>("LogoUri");
b.Property<bool>("LogoutSessionRequired");
b.Property<string>("LogoutUri");
b.Property<bool>("PrefixClientClaims");
b.Property<string>("ProtocolType")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("RefreshTokenExpiration");
b.Property<int>("RefreshTokenUsage");
b.Property<bool>("RequireClientSecret");
b.Property<bool>("RequireConsent");
b.Property<bool>("RequirePkce");
b.Property<int>("SlidingRefreshTokenLifetime");
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("Clients");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(250);
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(250);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Origin")
.IsRequired()
.HasMaxLength(150);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientCorsOrigins");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("GrantType")
.IsRequired()
.HasMaxLength(250);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientGrantTypes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientIdPRestrictions");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("PostLogoutRedirectUri")
.IsRequired()
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientPostLogoutRedirectUris");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("RedirectUri")
.IsRequired()
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientRedirectUris");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Scope")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("ClientId")
.IsRequired();
b.Property<string>("Description")
.HasMaxLength(2000);
b.Property<DateTime?>("Expiration");
b.Property<string>("Type")
.HasMaxLength(250);
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2000);
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientSecrets");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("IdentityResourceId")
.IsRequired();
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("IdentityResourceId");
b.ToTable("IdentityClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Description")
.HasMaxLength(1000);
b.Property<string>("DisplayName")
.HasMaxLength(200);
b.Property<bool>("Emphasize");
b.Property<bool>("Enabled");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.Property<bool>("Required");
b.Property<bool>("ShowInDiscoveryDocument");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("IdentityResources");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
.WithMany("UserClaims")
.HasForeignKey("ApiResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
.WithMany("Scopes")
.HasForeignKey("ApiResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
.WithMany("UserClaims")
.HasForeignKey("ApiScopeId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
.WithMany("Secrets")
.HasForeignKey("ApiResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("Claims")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("AllowedCorsOrigins")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("AllowedGrantTypes")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("IdentityProviderRestrictions")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("PostLogoutRedirectUris")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("RedirectUris")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("AllowedScopes")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
.WithMany("ClientSecrets")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
{
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
.WithMany("UserClaims")
.HasForeignKey("IdentityResourceId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 51
- 0
src/Services/Identity/Identity.API/Migrations/PersistedGrantDbContextModelSnapshot.cs View File

@ -0,0 +1,51 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using IdentityServer4.EntityFramework.DbContexts;
namespace Identity.API.Migrations
{
[DbContext(typeof(PersistedGrantDbContext))]
partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.2")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<DateTime?>("Expiration");
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50);
b.HasKey("Key");
b.HasIndex("SubjectId", "ClientId", "Type");
b.ToTable("PersistedGrants");
});
}
}
}

+ 65
- 16
src/Services/Identity/Identity.API/Startup.cs View File

@ -19,6 +19,11 @@ using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.HealthChecks;
using Identity.API.Certificate;
using DataProtectionExtensions;
using System.Reflection;
using IdentityServer4.Test;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
namespace eShopOnContainers.Identity
{
@ -34,7 +39,7 @@ namespace eShopOnContainers.Identity
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
//builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
@ -45,8 +50,7 @@ namespace eShopOnContainers.Identity
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
@ -57,12 +61,12 @@ namespace eShopOnContainers.Identity
services.Configure<AppSettings>(Configuration);
services.AddMvc();
services.AddDataProtection(opts =>
{
opts.ApplicationDiscriminator = "eshop.identity";
});
services.AddMvc();
});//.PersistKeysToRedis("basket.data");
services.AddHealthChecks(checks =>
{
@ -79,20 +83,20 @@ namespace eShopOnContainers.Identity
services.AddTransient<ILoginService<ApplicationUser>, EFLoginService>();
services.AddTransient<IRedirectService, RedirectService>();
//callbacks urls from config:
Dictionary<string, string> clientUrls = new Dictionary<string, string>();
clientUrls.Add("Mvc", Configuration.GetValue<string>("MvcClient"));
clientUrls.Add("Spa", Configuration.GetValue<string>("SpaClient"));
clientUrls.Add("Xamarin", Configuration.GetValue<string>("XamarinCallback"));
var connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// Adds IdentityServer
services.AddIdentityServer(x => x.IssuerUri = "null")
.AddSigningCredential(Certificate.Get())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryIdentityResources(Config.GetResources())
.AddInMemoryClients(Config.GetClients(clientUrls))
.AddSigningCredential(Certificate.Get())
.AddAspNetIdentity<ApplicationUser>()
.Services.AddTransient<IProfileService, ProfileService>();
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.Services.AddTransient<IProfileService, ProfileService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -134,10 +138,55 @@ namespace eShopOnContainers.Identity
template: "{controller=Home}/{action=Index}/{id?}");
});
InitializeDatabase(app);
//Seed Data
var hasher = new PasswordHasher<ApplicationUser>();
new ApplicationContextSeed(hasher).SeedAsync(app, loggerFactory)
.Wait();
}
private void InitializeDatabase(IApplicationBuilder app)
{
//callbacks urls from config:
Dictionary<string, string> clientUrls = new Dictionary<string, string>();
clientUrls.Add("Mvc", Configuration.GetValue<string>("MvcClient"));
clientUrls.Add("Spa", Configuration.GetValue<string>("SpaClient"));
clientUrls.Add("Xamarin", Configuration.GetValue<string>("XamarinCallback"));
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate();
if (!context.Clients.Any())
{
foreach (var client in Config.GetClients(clientUrls))
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetResources())
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var api in Config.GetApis())
{
context.ApiResources.Add(api.ToEntity());
}
context.SaveChanges();
}
}
}
}
}

+ 7
- 4
src/Web/WebMVC/Controllers/AccountController.cs View File

@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.AspNetCore.Http.Authentication;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{
@ -17,15 +19,16 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
public ActionResult Index() => View();
[Authorize]
public IActionResult SignIn(string returnUrl)
public async Task<IActionResult> SignIn(string returnUrl)
{
var user = User as ClaimsPrincipal;
var token = await HttpContext.Authentication.GetTokenAsync("access_token");
//TODO - Not retrieving AccessToken yet
var token = user.FindFirst("access_token");
//var token = user.FindFirst("access_token");
if (token != null)
{
ViewData["access_token"] = token.Value;
ViewData["access_token"] = token;
}
// "Catalog" because UrlHelper doesn't support nameof() for controllers


+ 3
- 1
src/Web/WebMVC/Startup.cs View File

@ -44,12 +44,14 @@ namespace Microsoft.eShopOnContainers.WebMVC
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDataProtection(opts =>
{
opts.ApplicationDiscriminator = "eshop.webmvc";
});
services.AddMvc();
services.Configure<AppSettings>(Configuration);
services.AddHealthChecks(checks =>


+ 4
- 0
test/Services/FunctionalTests/FunctionalTests.csproj View File

@ -38,4 +38,8 @@
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

+ 4
- 0
test/Services/IntegrationTests/IntegrationTests.csproj View File

@ -46,4 +46,8 @@
</Content>
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

+ 4
- 0
test/Services/UnitTest/UnitTest.csproj View File

@ -28,4 +28,8 @@
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="1.1.2" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

Loading…
Cancel
Save