diff --git a/Notesnook.API/Program.cs b/Notesnook.API/Program.cs index 427d2fa..1ec6fc7 100644 --- a/Notesnook.API/Program.cs +++ b/Notesnook.API/Program.cs @@ -17,10 +17,15 @@ You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ +#if !DEBUG +using System.Net; +#endif +using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Streetwriters.Common; +using System.Linq; namespace Notesnook.API { @@ -28,8 +33,6 @@ namespace Notesnook.API { public static async Task Main(string[] args) { - DotNetEnv.Env.TraversePath().Load(); - IHost host = CreateHostBuilder(args).Build(); await host.RunAsync(); } @@ -43,16 +46,7 @@ namespace Notesnook.API .UseKestrel((options) => { options.Limits.MaxRequestBodySize = long.MaxValue; -#if DEBUG - options.ListenAnyIP(int.Parse(Servers.NotesnookAPI.Port)); -#else - options.ListenAnyIP(443, listenerOptions => - { - listenerOptions.UseHttps(Servers.OriginSSLCertificate); - }); - options.ListenAnyIP(80); - options.Listen(IPAddress.Parse(Servers.NotesnookAPI.Hostname), int.Parse(Servers.NotesnookAPI.Port)); -#endif + options.ListenAnyIP(Servers.NotesnookAPI.Port); }); }); } diff --git a/Notesnook.API/Services/S3Service.cs b/Notesnook.API/Services/S3Service.cs index e41f32d..041dfd7 100644 --- a/Notesnook.API/Services/S3Service.cs +++ b/Notesnook.API/Services/S3Service.cs @@ -19,6 +19,7 @@ along with this program. If not, see . using System; using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -41,7 +42,6 @@ namespace Notesnook.API.Services public S3Service() { - var config = new AmazonS3Config { #if DEBUG diff --git a/Notesnook.API/Services/UserService.cs b/Notesnook.API/Services/UserService.cs index ccd155d..8604a97 100644 --- a/Notesnook.API/Services/UserService.cs +++ b/Notesnook.API/Services/UserService.cs @@ -29,6 +29,7 @@ using Notesnook.API.Models.Responses; using Streetwriters.Common; using Streetwriters.Common.Enums; using Streetwriters.Common.Extensions; +using Streetwriters.Common.Interfaces; using Streetwriters.Common.Messages; using Streetwriters.Common.Models; using Streetwriters.Data.Interfaces; @@ -73,14 +74,17 @@ namespace Notesnook.API.Services Salt = GetSalt() }); - await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage + if (!Constants.IS_SELF_HOSTED) { - AppId = ApplicationType.NOTESNOOK, - Provider = SubscriptionProvider.STREETWRITERS, - Type = SubscriptionType.BASIC, - UserId = response.UserId, - StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), - }); + await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage + { + AppId = ApplicationType.NOTESNOOK, + Provider = SubscriptionProvider.STREETWRITERS, + Type = SubscriptionType.BASIC, + UserId = response.UserId, + StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + }); + } await Slogger.Info(nameof(CreateUserAsync), "New user created.", JsonSerializer.Serialize(response)); } @@ -90,31 +94,49 @@ namespace Notesnook.API.Services UserResponse response = await httpClient.ForwardAsync(this.HttpContextAccessor, $"{Servers.IdentityServer.ToString()}/account", HttpMethod.Get); if (!response.Success) return response; - SubscriptionResponse subscriptionResponse = await httpClient.ForwardAsync(this.HttpContextAccessor, $"{Servers.SubscriptionServer}/subscriptions", HttpMethod.Get); - if (repair && subscriptionResponse.StatusCode == 404) + ISubscription subscription = null; + if (Constants.IS_SELF_HOSTED) { - await Slogger.Error(nameof(GetUserAsync), "Repairing user subscription.", JsonSerializer.Serialize(response)); - // user was partially created. We should continue the process here. - await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage + subscription = new Subscription { AppId = ApplicationType.NOTESNOOK, Provider = SubscriptionProvider.STREETWRITERS, - Type = SubscriptionType.TRIAL, - UserId = response.UserId, - StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), - ExpiryTime = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds() - }); - // just a dummy object - subscriptionResponse.Subscription = new Subscription - { - AppId = ApplicationType.NOTESNOOK, - Provider = SubscriptionProvider.STREETWRITERS, - Type = SubscriptionType.TRIAL, + Type = SubscriptionType.PREMIUM, UserId = response.UserId, StartDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), - ExpiryDate = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds() + // this date doesn't matter as the subscription is static. + ExpiryDate = DateTimeOffset.UtcNow.AddYears(1).ToUnixTimeMilliseconds() }; } + else + { + SubscriptionResponse subscriptionResponse = await httpClient.ForwardAsync(this.HttpContextAccessor, $"{Servers.SubscriptionServer}/subscriptions", HttpMethod.Get); + if (repair && subscriptionResponse.StatusCode == 404) + { + await Slogger.Error(nameof(GetUserAsync), "Repairing user subscription.", JsonSerializer.Serialize(response)); + // user was partially created. We should continue the process here. + await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage + { + AppId = ApplicationType.NOTESNOOK, + Provider = SubscriptionProvider.STREETWRITERS, + Type = SubscriptionType.TRIAL, + UserId = response.UserId, + StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + ExpiryTime = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds() + }); + // just a dummy object + subscriptionResponse.Subscription = new Subscription + { + AppId = ApplicationType.NOTESNOOK, + Provider = SubscriptionProvider.STREETWRITERS, + Type = SubscriptionType.TRIAL, + UserId = response.UserId, + StartDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + ExpiryDate = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds() + }; + } + subscription = subscriptionResponse.Subscription; + } var userSettings = await Repositories.UsersSettings.FindOneAsync((u) => u.UserId == response.UserId); if (repair && userSettings == null) @@ -130,7 +152,7 @@ namespace Notesnook.API.Services } response.AttachmentsKey = userSettings.AttachmentsKey; response.Salt = userSettings.Salt; - response.Subscription = subscriptionResponse.Subscription; + response.Subscription = subscription; return response; } @@ -153,11 +175,14 @@ namespace Notesnook.API.Services Repositories.Attachments.DeleteByUserId(userId); Repositories.UsersSettings.Delete((u) => u.UserId == userId); - await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.DeleteSubscriptionTopic, new DeleteSubscriptionMessage + if (!Constants.IS_SELF_HOSTED) { - AppId = ApplicationType.NOTESNOOK, - UserId = userId - }); + await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.DeleteSubscriptionTopic, new DeleteSubscriptionMessage + { + AppId = ApplicationType.NOTESNOOK, + UserId = userId + }); + } await WampServers.MessengerServer.PublishMessageAsync(WampServers.MessengerServer.Topics.SendSSETopic, new SendSSEMessage { diff --git a/Notesnook.API/Startup.cs b/Notesnook.API/Startup.cs index 9ad014a..a71f7a3 100644 --- a/Notesnook.API/Startup.cs +++ b/Notesnook.API/Startup.cs @@ -73,7 +73,11 @@ namespace Notesnook.API // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - var dbSettings = Configuration.GetSection("MongoDbSettings").Get(); + var dbSettings = new DbSettings + { + ConnectionString = Constants.MONGODB_CONNECTION_STRING, + DatabaseName = Constants.MONGODB_DATABASE_NAME + }; services.AddSingleton(dbSettings); services.TryAddSingleton(); diff --git a/Streetwriters.Common/Clients.cs b/Streetwriters.Common/Clients.cs index 6b912ef..eeadfcd 100644 --- a/Streetwriters.Common/Clients.cs +++ b/Streetwriters.Common/Clients.cs @@ -22,13 +22,14 @@ using System.Collections.Generic; using System.Linq; using Streetwriters.Common.Enums; using Streetwriters.Common.Interfaces; +using Streetwriters.Common.Messages; using Streetwriters.Common.Models; namespace Streetwriters.Common { public class Clients { - private static IClient Notesnook = new Client + private static Client Notesnook = new Client { Id = "notesnook", Name = "Notesnook", @@ -36,20 +37,34 @@ namespace Streetwriters.Common SenderName = Constants.NOTESNOOK_SENDER_NAME, Type = ApplicationType.NOTESNOOK, AppId = ApplicationType.NOTESNOOK, + AccountRecoveryRedirectURL = $"{Constants.NOTESNOOK_APP_HOST}/account/verified", + EmailConfirmedRedirectURL = $"{Constants.NOTESNOOK_APP_HOST}/account/recovery", + OnEmailConfirmed = async (userId) => + { + await WampServers.MessengerServer.PublishMessageAsync(WampServers.MessengerServer.Topics.SendSSETopic, new SendSSEMessage + { + UserId = userId, + Message = new Message + { + Type = "emailConfirmed", + Data = null + } + }); + } }; - public static Dictionary ClientsMap = new Dictionary + public static Dictionary ClientsMap = new Dictionary { { "notesnook", Notesnook } }; - public static IClient FindClientById(string id) + public static Client FindClientById(string id) { if (!IsValidClient(id)) return null; return ClientsMap[id]; } - public static IClient FindClientByAppId(ApplicationType appId) + public static Client FindClientByAppId(ApplicationType appId) { switch (appId) { diff --git a/Streetwriters.Common/Constants.cs b/Streetwriters.Common/Constants.cs index 35a27b7..6f5a723 100644 --- a/Streetwriters.Common/Constants.cs +++ b/Streetwriters.Common/Constants.cs @@ -23,25 +23,47 @@ namespace Streetwriters.Common { public class Constants { + public static bool IS_SELF_HOSTED => Environment.GetEnvironmentVariable("SELF_HOSTED") == "1"; + // S3 related - public static string S3_ACCESS_KEY = Environment.GetEnvironmentVariable("S3_ACCESS_KEY"); - public static string S3_ACCESS_KEY_ID = Environment.GetEnvironmentVariable("S3_ACCESS_KEY_ID"); - public static string S3_SERVICE_URL = Environment.GetEnvironmentVariable("S3_HOST"); - public static string S3_REGION = Environment.GetEnvironmentVariable("S3_HOST"); + public static string S3_ACCESS_KEY => Environment.GetEnvironmentVariable("S3_ACCESS_KEY"); + public static string S3_ACCESS_KEY_ID => Environment.GetEnvironmentVariable("S3_ACCESS_KEY_ID"); + public static string S3_SERVICE_URL => Environment.GetEnvironmentVariable("S3_SERVICE_URL"); + public static string S3_REGION => Environment.GetEnvironmentVariable("S3_REGION"); // SMTP settings - public static string SMTP_USERNAME = Environment.GetEnvironmentVariable("SMTP_USERNAME"); - public static string SMTP_PASSWORD = Environment.GetEnvironmentVariable("SMTP_PASSWORD"); - public static string SMTP_HOST = Environment.GetEnvironmentVariable("SMTP_HOST"); - public static string SMTP_PORT = Environment.GetEnvironmentVariable("SMTP_PORT"); - public static string SMTP_REPLYTO_NAME = Environment.GetEnvironmentVariable("SMTP_REPLYTO_NAME"); - public static string SMTP_REPLYTO_EMAIL = Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL"); - public static string NOTESNOOK_SENDER_EMAIL = Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL"); - public static string NOTESNOOK_SENDER_NAME = Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_NAME"); + public static string SMTP_USERNAME => Environment.GetEnvironmentVariable("SMTP_USERNAME"); + public static string SMTP_PASSWORD => Environment.GetEnvironmentVariable("SMTP_PASSWORD"); + public static string SMTP_HOST => Environment.GetEnvironmentVariable("SMTP_HOST"); + public static string SMTP_PORT => Environment.GetEnvironmentVariable("SMTP_PORT"); + public static string SMTP_REPLYTO_NAME => Environment.GetEnvironmentVariable("SMTP_REPLYTO_NAME"); + public static string SMTP_REPLYTO_EMAIL => Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL"); + public static string NOTESNOOK_SENDER_EMAIL => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL"); + public static string NOTESNOOK_SENDER_NAME => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_NAME"); - public static string NOTESNOOK_API_SECRET = Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET"); + public static string NOTESNOOK_APP_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_APP_HOST"); + public static string NOTESNOOK_API_SECRET => Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET"); // MessageBird is used for SMS sending - public static string MESSAGEBIRD_ACCESS_KEY = Environment.GetEnvironmentVariable("MESSAGEBIRD_ACCESS_KEY"); + public static string MESSAGEBIRD_ACCESS_KEY => Environment.GetEnvironmentVariable("MESSAGEBIRD_ACCESS_KEY"); + + // internal + public static string ORIGIN_CERT_PATH => Environment.GetEnvironmentVariable("ORIGIN_CERT_PATH"); + public static string ORIGIN_CERT_KEY_PATH => Environment.GetEnvironmentVariable("ORIGIN_CERT_KEY_PATH"); + public static string MONGODB_CONNECTION_STRING => Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING"); + public static string MONGODB_DATABASE_NAME => Environment.GetEnvironmentVariable("MONGODB_DATABASE_NAME"); + + // Server discovery + public static int NOTESNOOK_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_PORT")); + public static string NOTESNOOK_SERVER_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_HOST"); + public static string NOTESNOOK_SERVER_DOMAIN => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_DOMAIN"); + + public static int IDENTITY_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("IDENTITY_SERVER_PORT")); + public static string IDENTITY_SERVER_HOST => Environment.GetEnvironmentVariable("IDENTITY_SERVER_HOST"); + public static string IDENTITY_SERVER_DOMAIN => Environment.GetEnvironmentVariable("IDENTITY_SERVER_DOMAIN"); + + public static int SSE_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SSE_SERVER_PORT")); + public static string SSE_SERVER_HOST => Environment.GetEnvironmentVariable("SSE_SERVER_HOST"); + public static string SSE_SERVER_DOMAIN => Environment.GetEnvironmentVariable("SSE_SERVER_DOMAIN"); } } \ No newline at end of file diff --git a/Streetwriters.Common/Interfaces/IClient.cs b/Streetwriters.Common/Interfaces/IClient.cs index 1925785..3932ded 100644 --- a/Streetwriters.Common/Interfaces/IClient.cs +++ b/Streetwriters.Common/Interfaces/IClient.cs @@ -17,6 +17,8 @@ You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ +using System; +using System.Threading.Tasks; using Streetwriters.Common.Enums; namespace Streetwriters.Common.Interfaces @@ -29,5 +31,8 @@ namespace Streetwriters.Common.Interfaces ApplicationType AppId { get; set; } string SenderEmail { get; set; } string SenderName { get; set; } + string EmailConfirmedRedirectURL { get; } + string AccountRecoveryRedirectURL { get; } + Func OnEmailConfirmed { get; set; } } } diff --git a/Streetwriters.Common/Models/Client.cs b/Streetwriters.Common/Models/Client.cs index 56c992a..2abb4e6 100644 --- a/Streetwriters.Common/Models/Client.cs +++ b/Streetwriters.Common/Models/Client.cs @@ -17,9 +17,11 @@ You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ +using System; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using System.Text.Json.Serialization; +using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Streetwriters.Common.Enums; @@ -35,5 +37,9 @@ namespace Streetwriters.Common.Models public ApplicationType AppId { get; set; } public string SenderEmail { get; set; } public string SenderName { get; set; } + public string EmailConfirmedRedirectURL { get; set; } + public string AccountRecoveryRedirectURL { get; set; } + + public Func OnEmailConfirmed { get; set; } } } diff --git a/Streetwriters.Common/Servers.cs b/Streetwriters.Common/Servers.cs index 9ed6d04..4c925ec 100644 --- a/Streetwriters.Common/Servers.cs +++ b/Streetwriters.Common/Servers.cs @@ -28,7 +28,7 @@ namespace Streetwriters.Common { public class Server { - public string Port { get; set; } + public int Port { get; set; } public bool IsSecure { get; set; } public string Hostname { get; set; } public string Domain { get; set; } @@ -38,7 +38,7 @@ namespace Streetwriters.Common var url = ""; url += IsSecure ? "https" : "http"; url += $"://{Hostname}"; - url += IsSecure ? "" : $":{Port}"; + url += IsSecure || Port == 80 ? "" : $":{Port}"; return url; } @@ -47,7 +47,7 @@ namespace Streetwriters.Common var url = ""; url += IsSecure ? "ws" : "ws"; url += $"://{Hostname}"; - url += $":{Port}"; + url += Port == 80 ? "" : $":{Port}"; return url; } } @@ -77,74 +77,39 @@ namespace Streetwriters.Common public readonly static string HOST = GetLocalIPv4(NetworkInterfaceType.Ethernet); public static Server S3Server { get; } = new() { - Port = "4568", + Port = 4568, Hostname = HOST, IsSecure = false, Domain = HOST }; #else private readonly static string HOST = "localhost"; - public readonly static X509Certificate2 OriginSSLCertificate = X509Certificate2.CreateFromPemFile(Environment.GetEnvironmentVariable("ORIGIN_CERT_PATH"), Environment.GetEnvironmentVariable("ORIGIN_CERT_KEY_PATH")); + public readonly static X509Certificate2 OriginSSLCertificate = string.IsNullOrEmpty(Constants.ORIGIN_CERT_PATH) || string.IsNullOrEmpty(Constants.ORIGIN_CERT_KEY_PATH) ? null : X509Certificate2.CreateFromPemFile(Constants.ORIGIN_CERT_PATH, Environment.GetEnvironmentVariable(Constants.ORIGIN_CERT_KEY_PATH)); #endif public static Server NotesnookAPI { get; } = new() { - Domain = "api.notesnook.com", - Port = "5264", -#if DEBUG - IsSecure = false, - Hostname = HOST, -#else - IsSecure = true, - Hostname = "10.0.0.5", -#endif + Domain = Constants.NOTESNOOK_SERVER_DOMAIN, + Port = Constants.NOTESNOOK_SERVER_PORT, + Hostname = Constants.NOTESNOOK_SERVER_HOST, }; public static Server MessengerServer { get; } = new() { - Domain = "events.streetwriters.co", - Port = "7264", -#if DEBUG - IsSecure = false, - Hostname = HOST, -#else - IsSecure = true, - Hostname = "10.0.0.6", -#endif + Domain = Constants.SSE_SERVER_DOMAIN, + Port = Constants.SSE_SERVER_PORT, + Hostname = Constants.SSE_SERVER_HOST, }; public static Server IdentityServer { get; } = new() { - Domain = "auth.streetwriters.co", - IsSecure = false, - Port = "8264", -#if DEBUG - Hostname = HOST, -#else - Hostname = "10.0.0.4", -#endif + Domain = Constants.IDENTITY_SERVER_DOMAIN, + Port = Constants.IDENTITY_SERVER_PORT, + Hostname = Constants.IDENTITY_SERVER_HOST, }; public static Server SubscriptionServer { get; } = new() { Domain = "subscriptions.streetwriters.co", - IsSecure = false, - Port = "9264", -#if DEBUG - Hostname = HOST, -#else - Hostname = "10.0.0.4", -#endif - }; - public static Server PaymentsServer { get; } = new() - { - Domain = "payments.streetwriters.co", - IsSecure = false, - Port = "6264", -#if DEBUG - Hostname = HOST, -#else - Hostname = "10.0.0.4", -#endif }; } } diff --git a/Streetwriters.Identity/Controllers/AccountController.cs b/Streetwriters.Identity/Controllers/AccountController.cs index 039d981..69d2f5a 100644 --- a/Streetwriters.Identity/Controllers/AccountController.cs +++ b/Streetwriters.Identity/Controllers/AccountController.cs @@ -17,7 +17,6 @@ You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ -using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -29,13 +28,10 @@ using IdentityServer4.Stores; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using MongoDB.Bson; using Streetwriters.Common; -using Streetwriters.Common.Enums; using Streetwriters.Common.Messages; using Streetwriters.Common.Models; using Streetwriters.Identity.Enums; -using Streetwriters.Identity.Handlers; using Streetwriters.Identity.Interfaces; using Streetwriters.Identity.Models; using static IdentityServer4.IdentityServerConstants; @@ -80,16 +76,15 @@ namespace Streetwriters.Identity.Controllers var result = await UserManager.ConfirmEmailAsync(user, code); if (!result.Succeeded) return BadRequest(result.Errors.ToErrors()); - foreach (var handler in ClientHandlers.Handlers) + + if (await UserManager.IsInRoleAsync(user, client.Id)) { - if (await UserManager.IsInRoleAsync(user, client.Id)) - { - await handler.Value.OnEmailConfirmed(userId); - // if (client.WelcomeEmailTemplateId != null) - // await EmailSender.SendWelcomeEmailAsync(user.Email, client); - } + await client.OnEmailConfirmed(userId); + // if (client.WelcomeEmailTemplateId != null) + // await EmailSender.SendWelcomeEmailAsync(user.Email, client); } - var redirectUrl = $"{ClientHandlers.GetClientHandler(client.Type)?.EmailConfirmedRedirectURL}?userId={userId}"; + + var redirectUrl = $"{client.EmailConfirmedRedirectURL}?userId={userId}"; return RedirectPermanent(redirectUrl); } // case TokenType.CHANGE_EMAIL: @@ -111,7 +106,7 @@ namespace Streetwriters.Identity.Controllers return BadRequest("Invalid token."); var authorizationCode = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "PasswordResetAuthorizationCode"); - var redirectUrl = $"{ClientHandlers.GetClientHandler(client.Type)?.AccountRecoveryRedirectURL}?userId={userId}&code={authorizationCode}"; + var redirectUrl = $"{client.AccountRecoveryRedirectURL}?userId={userId}&code={authorizationCode}"; return RedirectPermanent(redirectUrl); } default: diff --git a/Streetwriters.Identity/Controllers/SignupController.cs b/Streetwriters.Identity/Controllers/SignupController.cs index f6957a3..6de0aec 100644 --- a/Streetwriters.Identity/Controllers/SignupController.cs +++ b/Streetwriters.Identity/Controllers/SignupController.cs @@ -17,6 +17,7 @@ You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ +using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; @@ -30,6 +31,7 @@ using Streetwriters.Common.Models; using Streetwriters.Identity.Enums; using Streetwriters.Identity.Interfaces; using Streetwriters.Identity.Models; +using Streetwriters.Identity.Services; namespace Streetwriters.Identity.Controllers { @@ -97,7 +99,8 @@ namespace Streetwriters.Identity.Controllers var user = await UserManager.FindByEmailAsync(form.Email); await UserManager.AddToRoleAsync(user, client.Id); - // await UserManager.AddClaimAsync(user, new Claim("verified", "false")); + if (Constants.IS_SELF_HOSTED) + await UserManager.AddClaimAsync(user, UserService.SubscriptionTypeToClaim(client.Id, Common.Enums.SubscriptionType.PREMIUM)); var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.TokenLink(user.Id.ToString(), code, client.Id, TokenType.CONFRIM_EMAIL, Request.Scheme); diff --git a/Streetwriters.Identity/Handlers/ClientHandlers.cs b/Streetwriters.Identity/Handlers/ClientHandlers.cs deleted file mode 100644 index b9828d8..0000000 --- a/Streetwriters.Identity/Handlers/ClientHandlers.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* -This file is part of the Notesnook Sync Server project (https://notesnook.com/) - -Copyright (C) 2022 Streetwriters (Private) Limited - -This program is free software: you can redistribute it and/or modify -it under the terms of the Affero GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -Affero GNU General Public License for more details. - -You should have received a copy of the Affero GNU General Public License -along with this program. If not, see . -*/ - -using System.Collections.Generic; -using Streetwriters.Common; -using Streetwriters.Common.Enums; -using Streetwriters.Identity.Interfaces; - -namespace Streetwriters.Identity.Handlers -{ - public class ClientHandlers - { - public static Dictionary Handlers { get; set; } = new Dictionary - { - { ApplicationType.NOTESNOOK, new NotesnookHandler() } - }; - - public static IAppHandler GetClientHandler(ApplicationType type) - { - return Handlers[type]; - } - } -} \ No newline at end of file diff --git a/Streetwriters.Identity/Handlers/NotesnookHandler.cs b/Streetwriters.Identity/Handlers/NotesnookHandler.cs deleted file mode 100644 index b440865..0000000 --- a/Streetwriters.Identity/Handlers/NotesnookHandler.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* -This file is part of the Notesnook Sync Server project (https://notesnook.com/) - -Copyright (C) 2022 Streetwriters (Private) Limited - -This program is free software: you can redistribute it and/or modify -it under the terms of the Affero GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -Affero GNU General Public License for more details. - -You should have received a copy of the Affero GNU General Public License -along with this program. If not, see . -*/ - -using System; -using System.Threading.Tasks; -using Streetwriters.Common; -using Streetwriters.Common.Enums; -using Streetwriters.Common.Messages; -using Streetwriters.Identity.Interfaces; - -namespace Streetwriters.Identity.Handlers -{ - public class NotesnookHandler : IAppHandler - { - public string Host { get; } - public string EmailConfirmedRedirectURL { get; } - public string AccountRecoveryRedirectURL { get; } - - public NotesnookHandler() - { -#if DEBUG - Host = "http://localhost:3000"; -#else - Host = "https://app.notesnook.com"; -#endif - EmailConfirmedRedirectURL = $"{this.Host}/account/verified"; - AccountRecoveryRedirectURL = $"{this.Host}/account/recovery"; - } - public async Task OnEmailConfirmed(string userId) - { - await WampServers.MessengerServer.PublishMessageAsync(WampServers.MessengerServer.Topics.SendSSETopic, new SendSSEMessage - { - UserId = userId, - Message = new Message - { - Type = "emailConfirmed", - Data = null - } - }); - } - } -} \ No newline at end of file diff --git a/Streetwriters.Identity/Interfaces/IAppHandler.cs b/Streetwriters.Identity/Interfaces/IAppHandler.cs deleted file mode 100644 index ca8b3cb..0000000 --- a/Streetwriters.Identity/Interfaces/IAppHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* -This file is part of the Notesnook Sync Server project (https://notesnook.com/) - -Copyright (C) 2022 Streetwriters (Private) Limited - -This program is free software: you can redistribute it and/or modify -it under the terms of the Affero GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -Affero GNU General Public License for more details. - -You should have received a copy of the Affero GNU General Public License -along with this program. If not, see . -*/ - -using System.Threading.Tasks; - -namespace Streetwriters.Identity.Interfaces -{ - public interface IAppHandler - { - string Host { get; } - string EmailConfirmedRedirectURL { get; } - string AccountRecoveryRedirectURL { get; } - Task OnEmailConfirmed(string userId); - } -} \ No newline at end of file diff --git a/Streetwriters.Identity/MessageHandlers/CreateSubscription.cs b/Streetwriters.Identity/MessageHandlers/CreateSubscription.cs index d1b5ec3..4e4ba6d 100644 --- a/Streetwriters.Identity/MessageHandlers/CreateSubscription.cs +++ b/Streetwriters.Identity/MessageHandlers/CreateSubscription.cs @@ -21,16 +21,9 @@ using System.Threading.Tasks; using Streetwriters.Common.Messages; using Streetwriters.Common.Models; using Streetwriters.Common; -using System.Text.Json; -using Streetwriters.Data.Repositories; -using Streetwriters.Data.Interfaces; -using Streetwriters.Common.Interfaces; -using System; using Microsoft.AspNetCore.Identity; -using Streetwriters.Common.Enums; using System.Security.Claims; using System.Linq; -using Streetwriters.Identity.Interfaces; using Streetwriters.Identity.Services; namespace Streetwriters.Identity.MessageHandlers diff --git a/Streetwriters.Identity/MessageHandlers/DeleteSubscription.cs b/Streetwriters.Identity/MessageHandlers/DeleteSubscription.cs index a413918..7840e1d 100644 --- a/Streetwriters.Identity/MessageHandlers/DeleteSubscription.cs +++ b/Streetwriters.Identity/MessageHandlers/DeleteSubscription.cs @@ -17,21 +17,12 @@ You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ -using System; using System.Threading.Tasks; -using Streetwriters.Common.Enums; using Streetwriters.Common.Messages; -using Streetwriters.Common.Interfaces; using Streetwriters.Common.Models; using Streetwriters.Common; -using System.Text.Json; -using System.IO; -using Streetwriters.Data.Repositories; -using Streetwriters.Data.Interfaces; using Microsoft.AspNetCore.Identity; using System.Linq; -using IdentityServer4.Stores; -using Streetwriters.Identity.Interfaces; namespace Streetwriters.Identity.MessageHandlers { diff --git a/Streetwriters.Identity/Program.cs b/Streetwriters.Identity/Program.cs index 71bc587..8464dba 100644 --- a/Streetwriters.Identity/Program.cs +++ b/Streetwriters.Identity/Program.cs @@ -33,8 +33,6 @@ namespace Streetwriters.Identity { public static async Task Main(string[] args) { - DotNetEnv.Env.TraversePath().Load(); - IHost host = CreateHostBuilder(args).Build(); await host.RunAsync(); } @@ -48,7 +46,13 @@ namespace Streetwriters.Identity }) .ConfigureWebHostDefaults(webBuilder => { - webBuilder.UseStartup().UseUrls(Servers.IdentityServer.ToString()); + webBuilder + .UseStartup() + .UseKestrel((options) => + { + options.Limits.MaxRequestBodySize = long.MaxValue; + options.ListenAnyIP(Servers.IdentityServer.Port); + }); }); } } diff --git a/Streetwriters.Identity/Services/EmailSender.cs b/Streetwriters.Identity/Services/EmailSender.cs index a7247c6..81cafb2 100644 --- a/Streetwriters.Identity/Services/EmailSender.cs +++ b/Streetwriters.Identity/Services/EmailSender.cs @@ -50,9 +50,11 @@ namespace Streetwriters.Identity.Services public class EmailSender : IEmailSender, IAsyncDisposable { NNGnuPGContext NNGnuPGContext { get; set; } + SmtpClient mailClient; public EmailSender(IConfiguration configuration) { NNGnuPGContext = new NNGnuPGContext(configuration.GetSection("PgpKeySettings")); + mailClient = new SmtpClient(); } EmailTemplate Email2FATemplate = new EmailTemplate @@ -90,12 +92,6 @@ namespace Streetwriters.Identity.Services Subject = "Failed login attempt on your {{app_name}} account", }; - SmtpClient mailClient; - public EmailSender() - { - mailClient = new SmtpClient(); - } - public async Task Send2FACodeEmailAsync(string email, string code, IClient client) { var template = new EmailTemplate @@ -179,34 +175,40 @@ namespace Streetwriters.Identity.Services private async Task SendEmailAsync(string email, IEmailTemplate template, IClient client) { + if (!mailClient.IsConnected) + { + if (int.TryParse(Constants.SMTP_PORT, out int port)) + { + await mailClient.ConnectAsync(Constants.SMTP_HOST, port, MailKit.Security.SecureSocketOptions.StartTls); + } + else + { + throw new InvalidDataException("SMTP_PORT is not a valid integer value."); + } + } + + if (!mailClient.IsAuthenticated) + await mailClient.AuthenticateAsync(Constants.SMTP_USERNAME, Constants.SMTP_PASSWORD); + + var message = new MimeMessage(); + var sender = new MailboxAddress(client.SenderName, client.SenderEmail); + message.From.Add(sender); + message.To.Add(new MailboxAddress("", email)); + message.Subject = await Template.Parse(template.Subject).RenderAsync(template.Data); + + if (!string.IsNullOrEmpty(Constants.SMTP_REPLYTO_NAME) && !string.IsNullOrEmpty(Constants.SMTP_REPLYTO_EMAIL)) + message.ReplyTo.Add(new MailboxAddress(Constants.SMTP_REPLYTO_NAME, Constants.SMTP_REPLYTO_EMAIL)); + + message.Body = await GetEmailBodyAsync(template, client, sender); + + await mailClient.SendAsync(message); + } + + private async Task GetEmailBodyAsync(IEmailTemplate template, IClient client, MailboxAddress sender) + { + var builder = new BodyBuilder(); try { - if (!mailClient.IsConnected) - { - if (int.TryParse(Constants.SMTP_PORT, out int port)) - { - await mailClient.ConnectAsync(Constants.SMTP_HOST, port, MailKit.Security.SecureSocketOptions.StartTls); - } - else - { - throw new InvalidDataException("SMTP_PORT is not a valid integer value."); - } - } - - if (!mailClient.IsAuthenticated) - await mailClient.AuthenticateAsync(Constants.SMTP_USERNAME, Constants.SMTP_PASSWORD); - - var message = new MimeMessage(); - var sender = new MailboxAddress(client.SenderName, client.SenderEmail); - message.From.Add(sender); - message.To.Add(new MailboxAddress("", email)); - message.Subject = await Template.Parse(template.Subject).RenderAsync(template.Data); - - if (!string.IsNullOrEmpty(Constants.SMTP_REPLYTO_NAME) && !string.IsNullOrEmpty(Constants.SMTP_REPLYTO_EMAIL)) - message.ReplyTo.Add(new MailboxAddress(Constants.SMTP_REPLYTO_NAME, Constants.SMTP_REPLYTO_EMAIL)); - - var builder = new BodyBuilder(); - builder.TextBody = await Template.Parse(template.Text).RenderAsync(template.Data); builder.HtmlBody = await Template.Parse(template.Html).RenderAsync(template.Data); @@ -222,17 +224,16 @@ namespace Streetwriters.Identity.Services outputStream.Seek(0, SeekOrigin.Begin); builder.Attachments.Add($"{client.Id}_pub.asc", Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(outputStream.ToArray()))); } - message.Body = MultipartSigned.Create(NNGnuPGContext, sender, DigestAlgorithm.Sha256, builder.ToMessageBody()); + return MultipartSigned.Create(NNGnuPGContext, sender, DigestAlgorithm.Sha256, builder.ToMessageBody()); } else { - message.Body = builder.ToMessageBody(); + return builder.ToMessageBody(); } - await mailClient.SendAsync(message); } - catch (Exception ex) + catch (PrivateKeyNotFoundException) { - Console.Error.WriteLine(ex.Message); + return builder.ToMessageBody(); } } diff --git a/Streetwriters.Identity/Services/SMSSender.cs b/Streetwriters.Identity/Services/SMSSender.cs index de27a9b..dcd23bf 100644 --- a/Streetwriters.Identity/Services/SMSSender.cs +++ b/Streetwriters.Identity/Services/SMSSender.cs @@ -32,7 +32,8 @@ namespace Streetwriters.Identity.Services private Client client; public SMSSender() { - client = Client.CreateDefault(Constants.MESSAGEBIRD_ACCESS_KEY); + if (!string.IsNullOrEmpty(Constants.MESSAGEBIRD_ACCESS_KEY)) + client = Client.CreateDefault(Constants.MESSAGEBIRD_ACCESS_KEY); } public string SendOTP(string number, IClient app) diff --git a/Streetwriters.Identity/Startup.cs b/Streetwriters.Identity/Startup.cs index bf7eddf..e566796 100644 --- a/Streetwriters.Identity/Startup.cs +++ b/Streetwriters.Identity/Startup.cs @@ -59,7 +59,7 @@ namespace Streetwriters.Identity // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - var connectionString = Configuration["MongoDbSettings:ConnectionString"]; + var connectionString = Constants.MONGODB_CONNECTION_STRING; services.AddTransient(); services.AddTransient(); diff --git a/Streetwriters.Messenger/Program.cs b/Streetwriters.Messenger/Program.cs index 972bf65..5eeee8f 100644 --- a/Streetwriters.Messenger/Program.cs +++ b/Streetwriters.Messenger/Program.cs @@ -45,16 +45,7 @@ namespace Streetwriters.Messenger .UseKestrel((options) => { options.Limits.MaxRequestBodySize = long.MaxValue; -#if DEBUG - options.ListenAnyIP(int.Parse(Servers.MessengerServer.Port)); -#else - options.ListenAnyIP(443, listenerOptions => - { - listenerOptions.UseHttps(Servers.OriginSSLCertificate); - }); - options.ListenAnyIP(80); - options.Listen(IPAddress.Parse(Servers.MessengerServer.Hostname), int.Parse(Servers.MessengerServer.Port)); -#endif + options.ListenAnyIP(Servers.MessengerServer.Port); }); }); }