diff --git a/.config/identity.env b/.config/identity.env index 8261da9..5bb4d0b 100644 --- a/.config/identity.env +++ b/.config/identity.env @@ -5,17 +5,12 @@ IdentityHost=https://pixel.docker.localhost/pauth #Plugin configuration Plugins__Collection__0__Type=EmailSender Plugins__Collection__0__Path=Plugins/Messenger -Plugins__Collection__0__Name=Pixel.Identity.Messenger.Email +Plugins__Collection__0__Name=Pixel.Identity.Messenger.Console Plugins__Collection__1__Type=DbStore Plugins__Collection__1__Path=Plugins/DbStore Plugins__Collection__1__Name=Pixel.Identity.Store.PostgreSQL -#SMTP configuration for sending mails -SMTP_Host=smtp.ethereal.email -SMTP_Port=587 -SMTP_UserName= -SMTP_Password= -SMTP_From=admin@pixel.com +IdentityOptions_SignIn_RequireConfirmedAccount=false ConnectionStrings__PostgreServerConnection=User ID=postgresadmin;Password=postgrespass;Server=postgres;Port=5432;Database=pixel_identity_db; diff --git a/Pixel.Identity.sln b/Pixel.Identity.sln index a6e30e5..bf61723 100644 --- a/Pixel.Identity.sln +++ b/Pixel.Identity.sln @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixel.Identity.Store.Sql.Sh EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixel.Identity.Store.SqlServer", "src\Pixel.Identity.Store.SqlServer\Pixel.Identity.Store.SqlServer.csproj", "{E67F20E6-B228-467A-9C40-1B38EAD35367}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pixel.Identity.Messenger.Console", "src\Pixel.Identity.Messenger.Console\Pixel.Identity.Messenger.Console.csproj", "{137631D2-6F14-43A2-885D-F61C63B7058C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -153,6 +155,18 @@ Global {E67F20E6-B228-467A-9C40-1B38EAD35367}.Release|x64.Build.0 = Release|Any CPU {E67F20E6-B228-467A-9C40-1B38EAD35367}.Release|x86.ActiveCfg = Release|Any CPU {E67F20E6-B228-467A-9C40-1B38EAD35367}.Release|x86.Build.0 = Release|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Debug|x64.ActiveCfg = Debug|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Debug|x64.Build.0 = Debug|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Debug|x86.ActiveCfg = Debug|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Debug|x86.Build.0 = Debug|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Release|Any CPU.Build.0 = Release|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Release|x64.ActiveCfg = Release|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Release|x64.Build.0 = Release|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Release|x86.ActiveCfg = Release|Any CPU + {137631D2-6F14-43A2-885D-F61C63B7058C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Pixel.Identity.Messenger.Console/EmailSender.cs b/src/Pixel.Identity.Messenger.Console/EmailSender.cs new file mode 100644 index 0000000..d03dbc1 --- /dev/null +++ b/src/Pixel.Identity.Messenger.Console/EmailSender.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Logging; +using Pixel.Identity.Core; +using System.Text; + +namespace Pixel.Identity.Messenger.Console; + +/// +/// implementation that prints the message as information +/// on console and log and doesn't actually send the mail. +/// +public class EmailSender : IEmailSender +{ + private readonly ILogger logger; + + public EmailSender(ILogger logger) + { + this.logger = logger; + } + + public Task SendEmailAsync(string email, string subject, string htmlMessage) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"----------------------------------------------------"); + sb.AppendLine($"{email}:{subject}"); + sb.AppendLine("message:[redacted]"); + sb.AppendLine($"----------------------------------------------------"); + this.logger.LogInformation(sb.ToString()); + System.Console.WriteLine(sb.ToString()); + return Task.CompletedTask; + } +} diff --git a/src/Pixel.Identity.Messenger.Console/EmailSenderPlugin.cs b/src/Pixel.Identity.Messenger.Console/EmailSenderPlugin.cs new file mode 100644 index 0000000..4e4425e --- /dev/null +++ b/src/Pixel.Identity.Messenger.Console/EmailSenderPlugin.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Pixel.Identity.Core; +using Pixel.Identity.Core.Plugins; + +namespace Pixel.Identity.Messenger.Console; + +public class EmailSenderPlugin : IServicePlugin +{ + public void ConfigureService(IServiceCollection services, IConfiguration configuration) + { + services.AddTransient(); + } +} diff --git a/src/Pixel.Identity.Messenger.Console/Pixel.Identity.Messenger.Console.csproj b/src/Pixel.Identity.Messenger.Console/Pixel.Identity.Messenger.Console.csproj new file mode 100644 index 0000000..de945fc --- /dev/null +++ b/src/Pixel.Identity.Messenger.Console/Pixel.Identity.Messenger.Console.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/Pixel.Identity.Provider/Dockerfile b/src/Pixel.Identity.Provider/Dockerfile index e4f2927..251e88e 100644 --- a/src/Pixel.Identity.Provider/Dockerfile +++ b/src/Pixel.Identity.Provider/Dockerfile @@ -16,12 +16,14 @@ COPY ["src/Pixel.Identity.UI.Client/Pixel.Identity.UI.Client.csproj", "src/Pixel #plugin projects COPY ["src/Pixel.Identity.Messenger.Email/Pixel.Identity.Messenger.Email.csproj", "src/Pixel.Identity.Messenger.Email/"] +COPY ["src/Pixel.Identity.Messenger.Console/Pixel.Identity.Messenger.Console.csproj", "src/Pixel.Identity.Messenger.Console/"] COPY ["src/Pixel.Identity.Store.Sql.Shared/Pixel.Identity.Store.Sql.Shared.csproj", "src/Pixel.Identity.Store.Sql.Shared/"] COPY ["src/Pixel.Identity.Store.SqlServer/Pixel.Identity.Store.SqlServer.csproj", "src/Pixel.Identity.Store.SqlServer/"] COPY ["src/Pixel.Identity.Store.PostgreSQL/Pixel.Identity.Store.PostgreSQL.csproj", "src/Pixel.Identity.Store.PostgreSQL/"] COPY ["src/Pixel.Identity.Store.Mongo/Pixel.Identity.Store.Mongo.csproj", "src/Pixel.Identity.Store.Mongo/"] RUN dotnet restore "src/Pixel.Identity.Messenger.Email/Pixel.Identity.Messenger.Email.csproj" +RUN dotnet restore "src/Pixel.Identity.Messenger.Console/Pixel.Identity.Messenger.Console.csproj" RUN dotnet restore "src/Pixel.Identity.Provider/Pixel.Identity.Provider.csproj" RUN dotnet restore "src/Pixel.Identity.Store.Mongo/Pixel.Identity.Store.Mongo.csproj" RUN dotnet restore "src/Pixel.Identity.Store.SqlServer/Pixel.Identity.Store.SqlServer.csproj" diff --git a/src/Pixel.Identity.Provider/Pixel.Identity.Provider.csproj b/src/Pixel.Identity.Provider/Pixel.Identity.Provider.csproj index 5ae4fe4..e3f20e1 100644 --- a/src/Pixel.Identity.Provider/Pixel.Identity.Provider.csproj +++ b/src/Pixel.Identity.Provider/Pixel.Identity.Provider.csproj @@ -50,7 +50,8 @@ - + + diff --git a/src/Pixel.Identity.Provider/appsettings.Development.json b/src/Pixel.Identity.Provider/appsettings.Development.json index 9bda565..21766c0 100644 --- a/src/Pixel.Identity.Provider/appsettings.Development.json +++ b/src/Pixel.Identity.Provider/appsettings.Development.json @@ -10,16 +10,21 @@ "Collection": [ { "Type": "EmailSender", - "Path": "Plugins/Messenger", - "Name": "Pixel.Identity.Messenger.Email" + "Path": "Plugins\\Messenger", + "Name": "Pixel.Identity.Messenger.Console" }, { "Type": "DbStore", - "Path": "Plugins/DbStore", - "Name": "Pixel.Identity.Store.PostgreSQL" + "Path": "Plugins\\DbStore", + "Name": "Pixel.Identity.Store.Mongo" } ] }, + "IdentityOptions": { + "SignIn": { + "RequireConfirmedAccount": false + } + }, "Identity": { "Certificates": { "EncryptionCertificatePath": "E:\\Git\\Pixel.Identity\\.certificates\\identity-encryption.pfx", @@ -28,13 +33,6 @@ "SigningCertificateKey": "" } }, - "SMTP": { - "Host": "smtp.ethereal.email", - "Port": 587, - "UserName": "", - "Password": "", - "From": "admin@pixel.com" - }, "ConnectionStrings": { "SqlServerConnection": "Server=(localdb)\\mssqllocaldb;Database=pixel-identity-db;Trusted_Connection=True;MultipleActiveResultSets=true", "PostgreServerConnection": "User ID=postgresadmin;Password=postgrespass;Server=postgres;Port=5432;Database=pixel_identity_db;" diff --git a/src/Pixel.Identity.Store.Mongo/MongoConfigurator.cs b/src/Pixel.Identity.Store.Mongo/MongoConfigurator.cs index 7848d09..70879b7 100644 --- a/src/Pixel.Identity.Store.Mongo/MongoConfigurator.cs +++ b/src/Pixel.Identity.Store.Mongo/MongoConfigurator.cs @@ -49,14 +49,33 @@ public IdentityBuilder ConfigureIdentity(IConfiguration configuration, IServiceC cm.UnmapMember(m => m.RoleId); }); + var identityOptions = new IdentityOptions(); + configuration.GetSection(nameof(IdentityOptions)).Bind(identityOptions); + var mongoDbSettings = configuration.GetSection(nameof(MongoDbSettings)).Get(); return services.AddIdentity(options => { options.ClaimsIdentity.UserNameClaimType = OpenIddict.Abstractions.OpenIddictConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIddict.Abstractions.OpenIddictConstants.Claims.Subject; options.ClaimsIdentity.RoleClaimType = OpenIddict.Abstractions.OpenIddictConstants.Claims.Role; - options.SignIn.RequireConfirmedAccount = true; - //options.User.RequireUniqueEmail = true; + + options.SignIn.RequireConfirmedPhoneNumber = identityOptions.SignIn.RequireConfirmedPhoneNumber; + options.SignIn.RequireConfirmedEmail = identityOptions.SignIn.RequireConfirmedEmail; + options.SignIn.RequireConfirmedAccount = identityOptions.SignIn.RequireConfirmedAccount; + + options.User.AllowedUserNameCharacters = identityOptions.User.AllowedUserNameCharacters; + options.User.RequireUniqueEmail = identityOptions.User.RequireUniqueEmail; + + options.Password.RequiredLength = identityOptions.Password.RequiredLength; + options.Password.RequiredUniqueChars = identityOptions.Password.RequiredUniqueChars; + options.Password.RequireNonAlphanumeric = identityOptions.Password.RequireNonAlphanumeric; + options.Password.RequireLowercase = identityOptions.Password.RequireLowercase; + options.Password.RequireUppercase = identityOptions.Password.RequireUppercase; + options.Password.RequireDigit = identityOptions.Password.RequireDigit; + + options.Lockout.AllowedForNewUsers = identityOptions.Lockout.AllowedForNewUsers; + options.Lockout.MaxFailedAccessAttempts = identityOptions.Lockout.MaxFailedAccessAttempts; + options.Lockout.DefaultLockoutTimeSpan = identityOptions.Lockout.DefaultLockoutTimeSpan; }) .AddRoles() .AddMongoDbStore public IdentityBuilder ConfigureIdentity(IConfiguration configuration, IServiceCollection services) { + var identityOptions = new IdentityOptions(); + configuration.GetSection(nameof(IdentityOptions)).Bind(identityOptions); + return services.AddDbContext(options => { options.UseNpgsql(configuration.GetConnectionString("PostgreServerConnection")); @@ -44,7 +47,24 @@ public IdentityBuilder ConfigureIdentity(IConfiguration configuration, IServiceC options.ClaimsIdentity.UserNameClaimType = Claims.Name; options.ClaimsIdentity.UserIdClaimType = Claims.Subject; options.ClaimsIdentity.RoleClaimType = Claims.Role; - options.SignIn.RequireConfirmedAccount = true; + + options.SignIn.RequireConfirmedPhoneNumber = identityOptions.SignIn.RequireConfirmedPhoneNumber; + options.SignIn.RequireConfirmedEmail = identityOptions.SignIn.RequireConfirmedEmail; + options.SignIn.RequireConfirmedAccount = identityOptions.SignIn.RequireConfirmedAccount; + + options.User.AllowedUserNameCharacters = identityOptions.User.AllowedUserNameCharacters; + options.User.RequireUniqueEmail = identityOptions.User.RequireUniqueEmail; + + options.Password.RequiredLength = identityOptions.Password.RequiredLength; + options.Password.RequiredUniqueChars = identityOptions.Password.RequiredUniqueChars; + options.Password.RequireNonAlphanumeric = identityOptions.Password.RequireNonAlphanumeric; + options.Password.RequireLowercase = identityOptions.Password.RequireLowercase; + options.Password.RequireUppercase = identityOptions.Password.RequireUppercase; + options.Password.RequireDigit = identityOptions.Password.RequireDigit; + + options.Lockout.AllowedForNewUsers = identityOptions.Lockout.AllowedForNewUsers; + options.Lockout.MaxFailedAccessAttempts = identityOptions.Lockout.MaxFailedAccessAttempts; + options.Lockout.DefaultLockoutTimeSpan = identityOptions.Lockout.DefaultLockoutTimeSpan; }) .AddRoles() .AddRoleStore() diff --git a/src/Pixel.Identity.Store.SqlServer/SqlConfigurator.cs b/src/Pixel.Identity.Store.SqlServer/SqlConfigurator.cs index 51b10e3..8469878 100644 --- a/src/Pixel.Identity.Store.SqlServer/SqlConfigurator.cs +++ b/src/Pixel.Identity.Store.SqlServer/SqlConfigurator.cs @@ -30,6 +30,9 @@ public void ConfigureAutoMap(IServiceCollection services) /// public IdentityBuilder ConfigureIdentity(IConfiguration configuration, IServiceCollection services) { + var identityOptions = new IdentityOptions(); + configuration.GetSection(nameof(IdentityOptions)).Bind(identityOptions); + return services.AddDbContext(options => { options.UseSqlServer(configuration.GetConnectionString("SqlServerConnection")); @@ -44,7 +47,24 @@ public IdentityBuilder ConfigureIdentity(IConfiguration configuration, IServiceC options.ClaimsIdentity.UserNameClaimType = Claims.Name; options.ClaimsIdentity.UserIdClaimType = Claims.Subject; options.ClaimsIdentity.RoleClaimType = Claims.Role; - options.SignIn.RequireConfirmedAccount = true; + + options.SignIn.RequireConfirmedPhoneNumber = identityOptions.SignIn.RequireConfirmedPhoneNumber; + options.SignIn.RequireConfirmedEmail = identityOptions.SignIn.RequireConfirmedEmail; + options.SignIn.RequireConfirmedAccount = identityOptions.SignIn.RequireConfirmedAccount; + + options.User.AllowedUserNameCharacters = identityOptions.User.AllowedUserNameCharacters; + options.User.RequireUniqueEmail = identityOptions.User.RequireUniqueEmail; + + options.Password.RequiredLength = identityOptions.Password.RequiredLength; + options.Password.RequiredUniqueChars = identityOptions.Password.RequiredUniqueChars; + options.Password.RequireNonAlphanumeric = identityOptions.Password.RequireNonAlphanumeric; + options.Password.RequireLowercase = identityOptions.Password.RequireLowercase; + options.Password.RequireUppercase = identityOptions.Password.RequireUppercase; + options.Password.RequireDigit = identityOptions.Password.RequireDigit; + + options.Lockout.AllowedForNewUsers = identityOptions.Lockout.AllowedForNewUsers; + options.Lockout.MaxFailedAccessAttempts = identityOptions.Lockout.MaxFailedAccessAttempts; + options.Lockout.DefaultLockoutTimeSpan = identityOptions.Lockout.DefaultLockoutTimeSpan; }) .AddRoles() .AddRoleStore()