1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00

Added email and customer info to admin view

This commit is contained in:
Uncled1023 2022-08-14 17:32:48 -07:00
parent c4840e696d
commit 270615be90
20 changed files with 436 additions and 173 deletions

35
BillingService/Logger.cs Normal file
View File

@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Teknik.BillingService
{
internal class Logger : ILogger
{
private readonly TextWriter _textWriter;
public Logger(TextWriter textWriter)
{
_textWriter = textWriter;
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_textWriter.WriteLine($"[{logLevel}] {formatter(state, exception)}");
}
}
}

View File

@ -6,6 +6,7 @@ using System.Reflection;
using System.Threading.Tasks;
using CommandLine;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Teknik.Areas.Billing;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
@ -13,6 +14,7 @@ using Teknik.BillingCore;
using Teknik.BillingCore.Models;
using Teknik.Configuration;
using Teknik.Data;
using Teknik.MailService;
using Teknik.Utilities;
namespace Teknik.BillingService
@ -25,6 +27,7 @@ namespace Teknik.BillingService
private static readonly object dbLock = new object();
private static readonly object scanStatsLock = new object();
private static readonly ILogger logger = new Logger(Console.Out);
public static event Action<string> OutputEvent;
@ -78,6 +81,9 @@ namespace Teknik.BillingService
// Get Biling Service
var billingService = BillingFactory.GetBillingService(config.BillingConfig);
// Get Mail Service
var mailService = UserHelper.CreateMailService(config, logger);
// Get all customers
var customers = billingService.GetCustomers();
if (customers != null)
@ -114,7 +120,7 @@ namespace Teknik.BillingService
var emailPrice = subscriptions.SelectMany(s => s.Prices).FirstOrDefault(p => p.ProductId == config.BillingConfig.EmailProductId);
if (emailPrice != null)
{
BillingHelper.SetEmailLimits(config, user, emailPrice.Storage, true);
BillingHelper.SetEmailLimits(config, mailService, user, emailPrice.Storage, true);
}
else
{
@ -122,9 +128,9 @@ namespace Teknik.BillingService
var userInfo = await IdentityHelper.GetIdentityUserInfo(config, user.Username);
if (userInfo != null &&
userInfo.AccountType == AccountType.Premium)
BillingHelper.SetEmailLimits(config, user, config.EmailConfig.MaxSize, true);
BillingHelper.SetEmailLimits(config, mailService, user, config.EmailConfig.MaxSize, true);
else
BillingHelper.SetEmailLimits(config, user, config.EmailConfig.MaxSize, false);
BillingHelper.SetEmailLimits(config, mailService, user, config.EmailConfig.MaxSize, false);
}
}
}
@ -132,7 +138,7 @@ namespace Teknik.BillingService
public static void Output(string message)
{
Console.WriteLine(message);
logger.Log(LogLevel.Information, message);
if (OutputEvent != null)
{
OutputEvent(message);

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Teknik.MailService
{
public class EmptyMailService : IMailService
{
public bool AccountExists(string username)
{
throw new NotImplementedException();
}
public bool CreateAccount(string username, string password, long size)
{
throw new NotImplementedException();
}
public bool DeleteAccount(string username)
{
throw new NotImplementedException();
}
public bool DisableAccount(string username)
{
throw new NotImplementedException();
}
public bool EditMaxEmailsPerDay(string username, int maxPerDay)
{
throw new NotImplementedException();
}
public bool EditMaxSize(string username, long size)
{
throw new NotImplementedException();
}
public bool EditPassword(string username, string password)
{
throw new NotImplementedException();
}
public bool EnableAccount(string username)
{
throw new NotImplementedException();
}
public long GetMaxSize(string username)
{
throw new NotImplementedException();
}
public bool IsEnabled(string username)
{
throw new NotImplementedException();
}
public DateTime LastActive(string username)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
@ -7,6 +8,7 @@ namespace Teknik.MailService
public class HMailService : IMailService
{
private readonly hMailServer.Application _App;
private readonly ILogger _logger;
private string _Host { get; set; }
private string _Username { get; set; }
@ -19,7 +21,16 @@ namespace Teknik.MailService
private string _CounterPassword { get; set; }
private int _CounterPort { get; set; }
public HMailService(string host, string username, string password, string domain, string counterServer, string counterDatabase, string counterUsername, string counterPassword, int counterPort)
public HMailService(string host,
string username,
string password,
string domain,
string counterServer,
string counterDatabase,
string counterUsername,
string counterPassword,
int counterPort,
ILogger logger)
{
_Host = host;
_Username = username;
@ -32,10 +43,11 @@ namespace Teknik.MailService
_CounterPassword = counterPassword;
_CounterPort = counterPort;
_logger = logger;
_App = InitApp();
}
public void CreateAccount(string username, string password, long size)
public bool CreateAccount(string username, string password, long size)
{
var domain = _App.Domains.ItemByName[_Domain];
var newAccount = domain.Accounts.Add();
@ -45,73 +57,92 @@ namespace Teknik.MailService
newAccount.MaxSize = (int)(size / 1000000);
newAccount.Save();
return true;
}
public bool AccountExists(string username)
{
try
{
GetAccount(username);
// We didn't error out, so the email exists
return true;
}
catch { }
return false;
var account = GetAccount(username);
return account != null;
}
public void DeleteAccount(string username)
public bool DeleteAccount(string username)
{
var account = GetAccount(username);
if (account != null)
{
account.Delete();
return true;
}
return false;
}
public void EnableAccount(string username)
public bool EnableAccount(string username)
{
EditActivity(username, true);
return EditActivity(username, true);
}
public void DisableAccount(string username)
public bool DisableAccount(string username)
{
EditActivity(username, false);
return EditActivity(username, false);
}
public void EditActivity(string username, bool active)
public bool EditActivity(string username, bool active)
{
var account = GetAccount(username);
account.Active = active;
account.Save();
if (account != null)
{
account.Active = active;
account.Save();
return true;
}
return false;
}
public void EditMaxEmailsPerDay(string username, int maxPerDay)
public bool EditMaxEmailsPerDay(string username, int maxPerDay)
{
//We need to check the actual git database
MysqlDatabase mySQL = new MysqlDatabase(_CounterServer, _CounterDatabase, _CounterUsername, _CounterPassword, _CounterPort);
string sql = @"INSERT INTO mailcounter.counts (qname, lastdate, qlimit, count) VALUES ({1}, NOW(), {0}, 0)
ON DUPLICATE KEY UPDATE qlimit = {0}";
mySQL.Execute(sql, new object[] { maxPerDay, username });
return true;
}
public void EditMaxSize(string username, long size)
public long GetMaxSize(string username)
{
var account = GetAccount(username);
account.MaxSize = (int)(size / 1000000);
account.Save();
return account?.MaxSize ?? 0;
}
public void EditPassword(string username, string password)
public bool EditMaxSize(string username, long size)
{
var account = GetAccount(username);
account.Password = password;
account.Save();
if (account != null)
{
account.MaxSize = (int)(size / 1000000);
account.Save();
return true;
}
return false;
}
public bool EditPassword(string username, string password)
{
var account = GetAccount(username);
if (account != null)
{
account.Password = password;
account.Save();
return true;
}
return false;
}
public DateTime LastActive(string username)
{
var account = GetAccount(username);
return (DateTime)account.LastLogonTime;
return (DateTime)(account?.LastLogonTime ?? DateTime.MinValue);
}
private hMailServer.Application InitApp()
@ -125,14 +156,22 @@ namespace Teknik.MailService
private hMailServer.Account GetAccount(string username)
{
var domain = _App.Domains.ItemByName[_Domain];
return domain.Accounts.ItemByAddress[username];
try
{
var domain = _App.Domains.ItemByName[_Domain];
return domain.Accounts.ItemByAddress[username];
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occurred getting Email Account");
}
return null;
}
public bool Enabled(string username)
public bool IsEnabled(string username)
{
var account = GetAccount(username);
return account.Active;
return account?.Active ?? false;
}
}
}

View File

@ -8,20 +8,22 @@ namespace Teknik.MailService
DateTime LastActive(string username);
bool Enabled(string username);
bool IsEnabled(string username);
void CreateAccount(string username, string password, long size);
bool CreateAccount(string username, string password, long size);
void EditPassword(string username, string password);
bool EditPassword(string username, string password);
void EditMaxSize(string username, long size);
long GetMaxSize(string username);
void EditMaxEmailsPerDay(string username, int maxPerDay);
bool EditMaxSize(string username, long size);
void EnableAccount(string username);
bool EditMaxEmailsPerDay(string username, int maxPerDay);
void DisableAccount(string username);
bool EnableAccount(string username);
void DeleteAccount(string username);
bool DisableAccount(string username);
bool DeleteAccount(string username);
}
}

View File

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="MySql.Data" Version="8.0.29" />
</ItemGroup>

View File

@ -2,6 +2,7 @@
using Microsoft.EntityFrameworkCore;
using nClam;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -159,8 +160,8 @@ namespace Teknik.ServiceWorker
// If the IV is set, and Key is set, then scan it
if (!string.IsNullOrEmpty(upload.Key) && !string.IsNullOrEmpty(upload.IV))
{
byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key);
byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV);
var keyArray = new PooledArray(Encoding.UTF8.GetBytes(upload.Key));
var ivArray = new PooledArray(Encoding.UTF8.GetBytes(upload.IV));
long maxUploadSize = config.UploadConfig.MaxUploadFileSize;
@ -171,7 +172,7 @@ namespace Teknik.ServiceWorker
maxUploadSize = upload.User.UploadSettings.MaxUploadFileSize.Value;
}
using (AesCounterStream aesStream = new AesCounterStream(fileStream, false, keyBytes, ivBytes))
using (AesCounterStream aesStream = new AesCounterStream(fileStream, false, keyArray, ivArray))
{
ClamClient clam = new ClamClient(config.UploadConfig.ClamConfig.Server, config.UploadConfig.ClamConfig.Port);
clam.MaxStreamSize = maxUploadSize;

View File

@ -14,6 +14,7 @@ using Teknik.BillingCore.Models;
using Teknik.Configuration;
using Teknik.Data;
using Teknik.Logging;
using Teknik.MailService;
namespace Teknik.Areas.API.V1.Controllers
{
@ -21,7 +22,7 @@ namespace Teknik.Areas.API.V1.Controllers
{
public BillingAPIv1Controller(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
public async Task<IActionResult> HandleCheckoutCompleteEvent()
public async Task<IActionResult> HandleCheckoutCompleteEvent([FromServices] IMailService mailService)
{
if (!_config.BillingConfig.Enabled)
return Forbid();
@ -38,13 +39,13 @@ namespace Teknik.Areas.API.V1.Controllers
{
var subscription = billingService.GetSubscription(session.SubscriptionId);
BillingHelper.ProcessSubscription(_config, _dbContext, session.CustomerId, subscription);
BillingHelper.ProcessSubscription(_config, _dbContext, mailService, session.CustomerId, subscription);
}
return Ok();
}
public async Task<IActionResult> HandleSubscriptionChange()
public async Task<IActionResult> HandleSubscriptionChange([FromServices] IMailService mailService)
{
if (!_config.BillingConfig.Enabled)
return Forbid();
@ -60,7 +61,7 @@ namespace Teknik.Areas.API.V1.Controllers
if (subscriptionEvent == null)
return BadRequest();
BillingHelper.ProcessSubscription(_config, _dbContext, subscriptionEvent.CustomerId, subscriptionEvent);
BillingHelper.ProcessSubscription(_config, _dbContext, mailService, subscriptionEvent.CustomerId, subscriptionEvent);
return Ok();
}

View File

@ -18,6 +18,7 @@ using Teknik.ViewModels;
using Teknik.Logging;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Teknik.MailService;
namespace Teknik.Areas.Admin.Controllers
{
@ -45,7 +46,7 @@ namespace Teknik.Areas.Admin.Controllers
[HttpGet]
[TrackPageView]
public async Task<IActionResult> UserInfo(string username)
public async Task<IActionResult> UserInfo(string username, [FromServices] IMailService mailService)
{
if (UserHelper.UserExists(_dbContext, username))
{
@ -59,6 +60,13 @@ namespace Teknik.Areas.Admin.Controllers
model.AccountType = info.AccountType.Value;
if (info.AccountStatus.HasValue)
model.AccountStatus = info.AccountStatus.Value;
var email = UserHelper.GetUserEmailAddress(_config, username);
if (UserHelper.UserEmailExists(mailService, _config, email))
model.Email = email;
model.EmailEnabled = UserHelper.UserEmailEnabled(mailService, _config, email);
model.MaxEmailStorage = UserHelper.GetUserEmailMaxSize(mailService, _config, email);
return View(model);
}
return new StatusCodeResult(StatusCodes.Status404NotFound);
@ -89,11 +97,12 @@ namespace Teknik.Areas.Admin.Controllers
}
[HttpPost]
public async Task<IActionResult> GetUserSearchResults(string query, [FromServices] ICompositeViewEngine viewEngine)
public async Task<IActionResult> GetUserSearchResults(string query, [FromServices] ICompositeViewEngine viewEngine, [FromServices] IMailService mailService)
{
List<UserResultViewModel> models = new List<UserResultViewModel>();
var results = _dbContext.Users.Where(u => u.Username.Contains(query)).ToList();
var results = _dbContext.Users.Where(u => u.Username.Contains(query) ||
(u.BillingCustomer != null && u.BillingCustomer.CustomerId == query)).ToList();
if (results != null)
{
foreach (User user in results)
@ -110,7 +119,7 @@ namespace Teknik.Areas.Admin.Controllers
if (info.CreationDate.HasValue)
model.JoinDate = info.CreationDate.Value;
model.LastSeen = await UserHelper.GetLastAccountActivity(_dbContext, _config, user.Username);
model.LastSeen = await UserHelper.GetLastAccountActivity(_dbContext, _config, mailService, user.Username);
models.Add(model);
}
catch (Exception)
@ -192,12 +201,12 @@ namespace Teknik.Areas.Admin.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditUserAccountType(string username, AccountType accountType)
public async Task<IActionResult> EditUserAccountType(string username, AccountType accountType, [FromServices] IMailService mailService)
{
if (UserHelper.UserExists(_dbContext, username))
{
// Edit the user's account type
await UserHelper.EditAccountType(_dbContext, _config, username, accountType);
await UserHelper.EditAccountType(_dbContext, _config, mailService, username, accountType);
return Json(new { result = new { success = true } });
}
return new StatusCodeResult(StatusCodes.Status404NotFound);
@ -205,12 +214,28 @@ namespace Teknik.Areas.Admin.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditUserAccountStatus(string username, AccountStatus accountStatus)
public async Task<IActionResult> EditUserAccountStatus(string username, AccountStatus accountStatus, [FromServices] IMailService mailService)
{
if (UserHelper.UserExists(_dbContext, username))
{
// Edit the user's account type
await UserHelper.EditAccountStatus(_dbContext, _config, username, accountStatus);
await UserHelper.EditAccountStatus(_dbContext, _config, mailService, username, accountStatus);
return Json(new { result = new { success = true } });
}
return new StatusCodeResult(StatusCodes.Status404NotFound);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult EditEmailActive(string username, string active, [FromServices] IMailService mailService)
{
if (UserHelper.UserExists(_dbContext, username))
{
var email = UserHelper.GetUserEmailAddress(_config, username);
if (active == "Enabled")
UserHelper.EnableUserEmail(mailService, _config, email);
else
UserHelper.DisableUserEmail(mailService, _config, email);
return Json(new { result = new { success = true } });
}
return new StatusCodeResult(StatusCodes.Status404NotFound);
@ -241,14 +266,14 @@ namespace Teknik.Areas.Admin.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteAccount(string username)
public async Task<IActionResult> DeleteAccount(string username, [FromServices] IMailService mailService)
{
try
{
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
await UserHelper.DeleteAccount(_dbContext, _config, user);
await UserHelper.DeleteAccount(_dbContext, _config, mailService, user);
return Json(new { result = true });
}
}

View File

@ -12,5 +12,8 @@ namespace Teknik.Areas.Admin.ViewModels
public string Username { get; set; }
public AccountType AccountType { get; set; }
public AccountStatus AccountStatus { get; set; }
public string Email { get; set; }
public bool EmailEnabled { get; set; }
public long MaxEmailStorage { get; set; }
}
}

View File

@ -6,6 +6,7 @@
var deleteUserURL = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "DeleteAccount" })';
var editAccountType = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "EditUserAccountType" })';
var editAccountStatus = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "EditUserAccountStatus" })';
var editEmailActive = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "EditEmailActive" })';
var createInviteCode = '@Url.SubRouteUrl("admin", "Admin.Action", new { action = "CreateInviteCode" })';
var username = '@Model.Username';
</script>
@ -52,6 +53,36 @@
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 col-sm-offset-1">
Email:
</div>
<div class="col-sm-8">
@Model.Email
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 col-sm-offset-1">
Email Active:
</div>
<div class="col-sm-8">
<select class="userEmailActive">
<!option @(Model.EmailEnabled ? "selected" : string.Empty)>Enabled</!option>
<!option @(!Model.EmailEnabled ? "selected" : string.Empty)>Disabled</!option>
</select>
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 col-sm-offset-1">
Email Max Size:
</div>
<div class="col-sm-8">
<input class="emailMaxSize" value="@Model.MaxEmailStorage" />
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 col-sm-offset-1">
<button type="button" class="btn btn-info" id="createInviteCode">Create Invite Code</button>

View File

@ -10,7 +10,7 @@
<div class="col-sm-6 col-sm-offset-3">
<!form>
<div class="form-group center-block">
<input type="text" class="form-control" id="query" name="query" placeholder="Username" />
<input type="text" class="form-control" id="query" name="query" placeholder="Username/Customer ID" />
</div>
</!form>
</div>

View File

@ -6,6 +6,7 @@ using Teknik.BillingCore;
using Teknik.BillingCore.Models;
using Teknik.Configuration;
using Teknik.Data;
using Teknik.MailService;
namespace Teknik.Areas.Billing
{
@ -41,7 +42,7 @@ namespace Teknik.Areas.Billing
}
}
public static void ProcessSubscription(Config config, TeknikEntities db, string customerId, Subscription subscription)
public static void ProcessSubscription(Config config, TeknikEntities db, IMailService mailService, string customerId, Subscription subscription)
{
// They have paid, so let's get their subscription and update their user settings
var user = db.Users.FirstOrDefault(u => u.BillingCustomer != null &&
@ -51,12 +52,12 @@ namespace Teknik.Areas.Billing
var isActive = subscription.Status == SubscriptionStatus.Active;
foreach (var price in subscription.Prices)
{
ProcessPrice(config, db, user, price, isActive);
ProcessPrice(config, db, mailService, user, price, isActive);
}
}
}
public static void ProcessPrice(Config config, TeknikEntities db, User user, Price price, bool active)
public static void ProcessPrice(Config config, TeknikEntities db, IMailService mailService, User user, Price price, bool active)
{
// What type of subscription is this
if (config.BillingConfig.UploadProductId == price.ProductId)
@ -68,7 +69,7 @@ namespace Teknik.Areas.Billing
}
else if (config.BillingConfig.EmailProductId == price.ProductId)
{
SetEmailLimits(config, user, price.Storage, active);
SetEmailLimits(config, mailService, user, price.Storage, active);
}
}
@ -81,18 +82,18 @@ namespace Teknik.Areas.Billing
db.SaveChanges();
}
public static void SetEmailLimits(Config config, User user, long storage, bool active)
public static void SetEmailLimits(Config config, IMailService mailService, User user, long storage, bool active)
{
// Process an email subscription
string email = UserHelper.GetUserEmailAddress(config, user.Username);
if (active)
{
UserHelper.EnableUserEmail(config, email);
UserHelper.EditUserEmailMaxSize(config, email, storage);
UserHelper.EnableUserEmail(mailService, config, email);
UserHelper.EditUserEmailMaxSize(mailService, config, email, storage);
}
else
{
UserHelper.DisableUserEmail(config, email);
UserHelper.DisableUserEmail(mailService, config, email);
}
}
}

View File

@ -10,6 +10,8 @@ using Teknik.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Teknik.Logging;
using Teknik.Areas.Users.Utility;
using Teknik.MailService;
namespace Teknik.Areas.Contact.Controllers
{
@ -21,12 +23,23 @@ namespace Teknik.Areas.Contact.Controllers
[AllowAnonymous]
[TrackPageView]
public IActionResult Index()
public IActionResult Index([FromServices] IMailService mailService)
{
ViewBag.Title = "Contact Us";
ViewBag.Description = "Contact Teknik Support";
var model = new ContactViewModel();
return View(new ContactViewModel());
if (User.Identity.IsAuthenticated)
{
model.Name = User.Identity.Name;
var email = UserHelper.GetUserEmailAddress(_config, User.Identity.Name);
if (UserHelper.UserEmailEnabled(mailService, _config, email))
{
model.Email = email;
}
}
return View(model);
}
[HttpPost]

View File

@ -1,22 +1,5 @@
@model Teknik.Areas.Contact.ViewModels.ContactViewModel
@using Teknik.Areas.Users.Utility
@{
string username = string.Empty;
string email = string.Empty;
if (User.Identity.IsAuthenticated)
{
username = User.Identity.Name;
if (UserHelper.UserEmailEnabled(Config, UserHelper.GetUserEmailAddress(Config, username)))
{
email = UserHelper.GetUserEmailAddress(Config, username);
}
}
}
<div class="container">
<div class="row">
<div class="col-sm-12 col-lg-12">
@ -39,7 +22,7 @@
<label for="Name">
Name
</label>
<input type="text" class="form-control" id="Name" name="Name" placeholder="Enter name" required="required" value="@username" />
<input type="text" class="form-control" id="Name" name="Name" placeholder="Enter name" required="required" value="@Model.Name" />
</div>
<div class="form-group">
@Html.ValidationMessageFor(u => u.Email)
@ -50,7 +33,7 @@
<span class="input-group-addon">
<span class="fa fa-envelope"></span>
</span>
<input type="email" class="form-control" id="Email" name="Email" placeholder="Enter email" required="required" value="@email" />
<input type="email" class="form-control" id="Email" name="Email" placeholder="Enter email" required="required" value="@Model.Email" />
</div>
</div>
<div class="form-group">

View File

@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Http;
using IdentityServer4.Models;
using Teknik.Utilities.Routing;
using Teknik.BillingCore;
using Teknik.MailService;
namespace Teknik.Areas.Users.Controllers
{
@ -51,11 +52,11 @@ namespace Teknik.Areas.Users.Controllers
}
[HttpGet]
public IActionResult Login(string returnUrl)
public IActionResult Login(string returnUrl, [FromServices] IMailService mailService)
{
// Let's double check their email and git accounts to make sure they exist
string email = UserHelper.GetUserEmailAddress(_config, User.Identity.Name);
if (_config.EmailConfig.Enabled && !UserHelper.UserEmailExists(_config, email))
if (_config.EmailConfig.Enabled && !UserHelper.UserEmailExists(mailService, _config, email))
{
//UserHelper.AddUserEmail(_config, email, model.Password);
}
@ -102,7 +103,7 @@ namespace Teknik.Areas.Users.Controllers
[HttpOptions]
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Register([Bind(Prefix = "Register")]RegisterViewModel model)
public async Task<IActionResult> Register([Bind(Prefix = "Register")]RegisterViewModel model, [FromServices] IMailService mailService)
{
model.Error = false;
model.ErrorMessage = string.Empty;
@ -115,7 +116,7 @@ namespace Teknik.Areas.Users.Controllers
model.Error = true;
model.ErrorMessage = "That username is not valid";
}
if (!model.Error && !(await UserHelper.UsernameAvailable(_dbContext, _config, model.Username)))
if (!model.Error && !(await UserHelper.UsernameAvailable(_dbContext, _config, mailService, model.Username)))
{
model.Error = true;
model.ErrorMessage = "That username is not available";
@ -152,7 +153,7 @@ namespace Teknik.Areas.Users.Controllers
{
try
{
await UserHelper.CreateAccount(_dbContext, _config, Url, model.Username, model.Password, model.RecoveryEmail, model.InviteCode);
await UserHelper.CreateAccount(_dbContext, _config, mailService, Url, model.Username, model.Password, model.RecoveryEmail, model.InviteCode);
}
catch (Exception ex)
{
@ -184,7 +185,7 @@ namespace Teknik.Areas.Users.Controllers
// GET: Profile/Profile
[AllowAnonymous]
[TrackPageView]
public async Task<IActionResult> ViewProfile(string username)
public async Task<IActionResult> ViewProfile(string username, [FromServices] IMailService mailService)
{
if (string.IsNullOrEmpty(username))
{
@ -208,13 +209,15 @@ namespace Teknik.Areas.Users.Controllers
model.Username = user.Username;
if (_config.EmailConfig.Enabled)
{
model.Email = string.Format("{0}@{1}", user.Username, _config.EmailConfig.Domain);
var email = UserHelper.GetUserEmailAddress(_config, user.Username);
if (UserHelper.UserEmailEnabled(mailService, _config, email))
model.Email = email;
}
// Get the user claims for this user
model.IdentityUserInfo = await IdentityHelper.GetIdentityUserInfo(_config, user.Username);
model.LastSeen = UserHelper.GetLastAccountActivity(_dbContext, _config, user.Username, model.IdentityUserInfo);
model.LastSeen = UserHelper.GetLastAccountActivity(_dbContext, _config, mailService, user.Username, model.IdentityUserInfo);
model.UserSettings = user.UserSettings;
model.BlogSettings = user.BlogSettings;
@ -745,7 +748,7 @@ namespace Teknik.Areas.Users.Controllers
return Json(new { error = "Invalid Parameters" });
}
public async Task<IActionResult> ChangePassword(AccountSettingsViewModel settings)
public async Task<IActionResult> ChangePassword(AccountSettingsViewModel settings, [FromServices] IMailService mailService)
{
if (ModelState.IsValid)
@ -775,7 +778,7 @@ namespace Teknik.Areas.Users.Controllers
return Json(new { error = "Password resets are disabled" });
// Change their password
await UserHelper.ChangeAccountPassword(_dbContext, _config, user.Username, settings.CurrentPassword, settings.NewPassword);
await UserHelper.ChangeAccountPassword(_dbContext, _config, mailService, user.Username, settings.CurrentPassword, settings.NewPassword);
return Json(new { result = true });
}
@ -791,7 +794,7 @@ namespace Teknik.Areas.Users.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete()
public async Task<IActionResult> Delete([FromServices] IMailService mailService)
{
if (ModelState.IsValid)
{
@ -800,7 +803,7 @@ namespace Teknik.Areas.Users.Controllers
User user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user != null)
{
await UserHelper.DeleteAccount(_dbContext, _config, user);
await UserHelper.DeleteAccount(_dbContext, _config, mailService, user);
// Sign Out
await HttpContext.SignOutAsync("Cookies");
@ -945,7 +948,7 @@ namespace Teknik.Areas.Users.Controllers
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SetUserPassword(SetPasswordViewModel passwordViewModel)
public async Task<IActionResult> SetUserPassword(SetPasswordViewModel passwordViewModel, [FromServices] IMailService mailService)
{
if (ModelState.IsValid)
{
@ -973,7 +976,7 @@ namespace Teknik.Areas.Users.Controllers
try
{
await UserHelper.ResetAccountPassword(_dbContext, _config, username, code, passwordViewModel.Password);
await UserHelper.ResetAccountPassword(_dbContext, _config, mailService, username, code, passwordViewModel.Password);
_session.Remove(_AuthSessionKey);
_session.Remove("AuthCode");

View File

@ -33,6 +33,7 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;
using Teknik.Utilities.Routing;
using Microsoft.Extensions.Logging;
namespace Teknik.Areas.Users.Utility
{
@ -81,7 +82,7 @@ namespace Teknik.Areas.Users.Utility
return isValid;
}
public static async Task<bool> UsernameAvailable(TeknikEntities db, Config config, string username)
public static async Task<bool> UsernameAvailable(TeknikEntities db, Config config, IMailService mailService, string username)
{
bool isAvailable = true;
@ -89,27 +90,27 @@ namespace Teknik.Areas.Users.Utility
isAvailable &= !UsernameReserved(config, username);
isAvailable &= !await IdentityHelper.UserExists(config, username);
isAvailable &= !UserExists(db, username);
isAvailable &= !UserEmailExists(config, GetUserEmailAddress(config, username));
isAvailable &= !UserEmailExists(mailService, config, GetUserEmailAddress(config, username));
isAvailable &= !UserGitExists(config, username);
return isAvailable;
}
public static async Task<DateTime> GetLastAccountActivity(TeknikEntities db, Config config, string username)
public static async Task<DateTime> GetLastAccountActivity(TeknikEntities db, Config config, IMailService mailService, string username)
{
var userInfo = await IdentityHelper.GetIdentityUserInfo(config, username);
return GetLastAccountActivity(db, config, username, userInfo);
return GetLastAccountActivity(db, config, mailService, username, userInfo);
}
public static DateTime GetLastAccountActivity(TeknikEntities db, Config config, string username, IdentityUserInfo userInfo)
public static DateTime GetLastAccountActivity(TeknikEntities db, Config config, IMailService mailService, string username, IdentityUserInfo userInfo)
{
try
{
DateTime lastActive = new DateTime(1900, 1, 1);
if (UserEmailExists(config, GetUserEmailAddress(config, username)))
if (UserEmailExists(mailService, config, GetUserEmailAddress(config, username)))
{
DateTime emailLastActive = UserEmailLastActive(config, GetUserEmailAddress(config, username));
DateTime emailLastActive = UserEmailLastActive(mailService, config, GetUserEmailAddress(config, username));
if (lastActive < emailLastActive)
lastActive = emailLastActive;
}
@ -136,7 +137,7 @@ namespace Teknik.Areas.Users.Utility
}
}
public static async Task CreateAccount(TeknikEntities db, Config config, IUrlHelper url, string username, string password, string recoveryEmail, string inviteCode)
public static async Task CreateAccount(TeknikEntities db, Config config, IMailService mailService, IUrlHelper url, string username, string password, string recoveryEmail, string inviteCode)
{
try
{
@ -147,10 +148,10 @@ namespace Teknik.Areas.Users.Utility
string userId = (string)result.Data;
// Create an Email Account
CreateUserEmail(config, GetUserEmailAddress(config, username), password);
CreateUserEmail(mailService, config, GetUserEmailAddress(config, username), password);
// Disable the email account
DisableUserEmail(config, GetUserEmailAddress(config, username));
DisableUserEmail(mailService, config, GetUserEmailAddress(config, username));
// Create a Git Account
CreateUserGit(config, username, password, userId);
@ -189,12 +190,12 @@ namespace Teknik.Areas.Users.Utility
}
}
public static async Task ChangeAccountPassword(TeknikEntities db, Config config, string username, string currentPassword, string newPassword)
public static async Task ChangeAccountPassword(TeknikEntities db, Config config, IMailService mailService, string username, string currentPassword, string newPassword)
{
IdentityResult result = await IdentityHelper.UpdatePassword(config, username, currentPassword, newPassword);
if (result.Success)
{
ChangeServicePasswords(db, config, username, newPassword);
ChangeServicePasswords(db, config, mailService, username, newPassword);
}
else
{
@ -202,12 +203,12 @@ namespace Teknik.Areas.Users.Utility
}
}
public static async Task ResetAccountPassword(TeknikEntities db, Config config, string username, string token, string newPassword)
public static async Task ResetAccountPassword(TeknikEntities db, Config config, IMailService mailService, string username, string token, string newPassword)
{
IdentityResult result = await IdentityHelper.ResetPassword(config, username, token, newPassword);
if (result.Success)
{
ChangeServicePasswords(db, config, username, newPassword);
ChangeServicePasswords(db, config, mailService, username, newPassword);
}
else
{
@ -215,16 +216,16 @@ namespace Teknik.Areas.Users.Utility
}
}
public static void ChangeServicePasswords(TeknikEntities db, Config config, string username, string newPassword)
public static void ChangeServicePasswords(TeknikEntities db, Config config, IMailService mailService, string username, string newPassword)
{
try
{
// Make sure they have a git and email account before resetting their password
string email = GetUserEmailAddress(config, username);
if (config.EmailConfig.Enabled && UserEmailExists(config, email))
if (config.EmailConfig.Enabled && UserEmailExists(mailService, config, email))
{
// Change email password
EditUserEmailPassword(config, GetUserEmailAddress(config, username), newPassword);
EditUserEmailPassword(mailService, config, GetUserEmailAddress(config, username), newPassword);
}
if (config.GitConfig.Enabled && UserGitExists(config, username))
@ -239,7 +240,7 @@ namespace Teknik.Areas.Users.Utility
}
}
public static async Task EditAccountType(TeknikEntities db, Config config, string username, AccountType type)
public static async Task EditAccountType(TeknikEntities db, Config config, IMailService mailService, string username, AccountType type)
{
try
{
@ -257,11 +258,11 @@ namespace Teknik.Areas.Users.Utility
{
case AccountType.Basic:
// Disable their email
DisableUserEmail(config, email);
DisableUserEmail(mailService, config, email);
break;
case AccountType.Premium:
// Enable their email account
EnableUserEmail(config, email);
EnableUserEmail(mailService, config, email);
break;
}
}
@ -276,7 +277,7 @@ namespace Teknik.Areas.Users.Utility
}
}
public static async Task EditAccountStatus(TeknikEntities db, Config config, string username, AccountStatus status)
public static async Task EditAccountStatus(TeknikEntities db, Config config, IMailService mailService, string username, AccountStatus status)
{
try
{
@ -294,13 +295,13 @@ namespace Teknik.Areas.Users.Utility
{
case AccountStatus.Active:
// Enable Email
EnableUserEmail(config, email);
EnableUserEmail(mailService, config, email);
// Enable Git
EnableUserGit(config, username);
break;
case AccountStatus.Banned:
// Disable Email
DisableUserEmail(config, email);
DisableUserEmail(mailService, config, email);
// Disable Git
DisableUserGit(config, username);
break;
@ -317,7 +318,7 @@ namespace Teknik.Areas.Users.Utility
}
}
public static async Task DeleteAccount(TeknikEntities db, Config config, User user)
public static async Task DeleteAccount(TeknikEntities db, Config config, IMailService mailService, User user)
{
try
{
@ -332,8 +333,8 @@ namespace Teknik.Areas.Users.Utility
DeleteUser(db, config, user);
// Delete Email Account
if (UserEmailExists(config, GetUserEmailAddress(config, username)))
DeleteUserEmail(config, GetUserEmailAddress(config, username));
if (UserEmailExists(mailService, config, GetUserEmailAddress(config, username)))
DeleteUserEmail(mailService, config, GetUserEmailAddress(config, username));
// Delete Git Account
if (UserGitExists(config, username))
@ -621,58 +622,61 @@ If you recieved this email and you did not reset your password, you can ignore t
#region Email Management
public static string GetUserEmailAddress(Config config, string username)
{
if (config.EmailConfig == null)
return null;
return string.Format("{0}@{1}", username, config.EmailConfig.Domain);
}
public static IMailService CreateMailService(Config config)
public static IMailService CreateMailService(Config config, ILogger logger)
{
return new HMailService(
config.EmailConfig.MailHost,
config.EmailConfig.Username,
config.EmailConfig.Password,
config.EmailConfig.Domain,
config.EmailConfig.CounterDatabase.Server,
config.EmailConfig.CounterDatabase.Database,
config.EmailConfig.CounterDatabase.Username,
config.EmailConfig.CounterDatabase.Password,
config.EmailConfig.CounterDatabase.Port
);
if (config.EmailConfig != null &&
config.EmailConfig.Enabled)
return new HMailService(
config.EmailConfig.MailHost,
config.EmailConfig.Username,
config.EmailConfig.Password,
config.EmailConfig.Domain,
config.EmailConfig.CounterDatabase.Server,
config.EmailConfig.CounterDatabase.Database,
config.EmailConfig.CounterDatabase.Username,
config.EmailConfig.CounterDatabase.Password,
config.EmailConfig.CounterDatabase.Port,
logger
);
return new EmptyMailService();
}
public static bool UserEmailExists(Config config, string email)
public static bool UserEmailExists(IMailService mailService, Config config, string email)
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
return svc.AccountExists(email);
return mailService.AccountExists(email);
}
return false;
}
public static DateTime UserEmailLastActive(Config config, string email)
public static DateTime UserEmailLastActive(IMailService mailService, Config config, string email)
{
DateTime lastActive = new DateTime(1900, 1, 1);
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
var lastEmail = svc.LastActive(email);
var lastEmail = mailService.LastActive(email);
if (lastActive < lastEmail)
lastActive = lastEmail;
}
return lastActive;
}
public static bool UserEmailEnabled(Config config, string email)
public static bool UserEmailEnabled(IMailService mailService, Config config, string email)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
return svc.Enabled(email);
return mailService.IsEnabled(email);
}
}
catch (Exception ex)
@ -682,15 +686,14 @@ If you recieved this email and you did not reset your password, you can ignore t
return false;
}
public static void CreateUserEmail(Config config, string email, string password)
public static void CreateUserEmail(IMailService mailService, Config config, string email, string password)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.CreateAccount(email, password, config.EmailConfig.MaxSize);
mailService.CreateAccount(email, password, config.EmailConfig.MaxSize);
}
}
catch (Exception ex)
@ -699,15 +702,14 @@ If you recieved this email and you did not reset your password, you can ignore t
}
}
public static void EnableUserEmail(Config config, string email)
public static void EnableUserEmail(IMailService mailService, Config config, string email)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.EnableAccount(email);
mailService.EnableAccount(email);
}
}
catch (Exception ex)
@ -716,15 +718,14 @@ If you recieved this email and you did not reset your password, you can ignore t
}
}
public static void DisableUserEmail(Config config, string email)
public static void DisableUserEmail(IMailService mailService, Config config, string email)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.DisableAccount(email);
mailService.DisableAccount(email);
}
}
catch (Exception ex)
@ -733,15 +734,14 @@ If you recieved this email and you did not reset your password, you can ignore t
}
}
public static void EditUserEmailPassword(Config config, string email, string password)
public static void EditUserEmailPassword(IMailService mailService, Config config, string email, string password)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.EditPassword(email, password);
mailService.EditPassword(email, password);
}
}
catch (Exception ex)
@ -750,15 +750,14 @@ If you recieved this email and you did not reset your password, you can ignore t
}
}
public static void EditUserEmailMaxSize(Config config, string email, long size)
public static void EditUserEmailMaxSize(IMailService mailService, Config config, string email, long size)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.EditMaxSize(email, size);
mailService.EditMaxSize(email, size);
}
}
catch (Exception ex)
@ -767,15 +766,31 @@ If you recieved this email and you did not reset your password, you can ignore t
}
}
public static void EditUserEmailMaxEmailsPerDay(Config config, string email, int maxPerDay)
public static long GetUserEmailMaxSize(IMailService mailService, Config config, string email)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.EditMaxEmailsPerDay(email, maxPerDay);
return mailService.GetMaxSize(email);
}
}
catch (Exception ex)
{
throw new Exception("Unable to edit email account mailbox size.", ex);
}
return -1;
}
public static void EditUserEmailMaxEmailsPerDay(IMailService mailService, Config config, string email, int maxPerDay)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
mailService.EditMaxEmailsPerDay(email, maxPerDay);
}
}
catch (Exception ex)
@ -784,15 +799,14 @@ If you recieved this email and you did not reset your password, you can ignore t
}
}
public static void DeleteUserEmail(Config config, string email)
public static void DeleteUserEmail(IMailService mailService, Config config, string email)
{
try
{
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
var svc = CreateMailService(config);
svc.DeleteAccount(email);
mailService.DeleteAccount(email);
}
}
catch (Exception ex)

View File

@ -96,7 +96,7 @@
{
<li class="list-group-item text-right"><span class="pull-left"><strong>Public Key</strong></span> <a href="#" data-toggle="modal" data-target="#pgpSignature">@pgpFingerprint64.AddStringAtInterval(4, " ")</a></li>
}
@if (!string.IsNullOrEmpty(Model.Email) && Config.EmailConfig.Enabled && UserHelper.UserEmailEnabled(Config, Model.Email))
@if (!string.IsNullOrEmpty(Model.Email))
{
<li class="list-group-item text-right"><span class="pull-left"><strong>Email</strong></span> @(Html.Raw(User.Identity.IsAuthenticated ? $"<a href=\"mailto:{Model.Email}\">{Model.Email}</a>" : $"{Model.Username} at {Config.EmailConfig?.Domain}"))</li>
}

View File

@ -20,6 +20,10 @@ $(function () {
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>No HTML Response.</div>');
}
}
});
});
@ -43,6 +47,37 @@ $(function () {
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>No HTML Response.</div>');
}
}
});
});
$('.userEmailActive').on('change', function () {
var selected = $(this).find("option:selected").val();
$.ajax({
type: "POST",
url: editEmailActive,
data: AddAntiForgeryToken({ username: username, active: selected }),
success: function (html) {
if (html) {
if (html.result.success) {
$("#top_msg").css('display', 'none');
$("#top_msg").html('');
alert('Successfully changed the email active status for \'' + username + '\' to: ' + selected);
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(html) + '</div>');
}
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>No HTML Response.</div>');
}
}
});
});

View File

@ -30,6 +30,8 @@ using Teknik.WebCommon.Middleware;
using Teknik.WebCommon;
using Teknik.Areas.Error.Controllers;
using Teknik.Services;
using Teknik.MailService;
using Teknik.Areas.Users.Utility;
namespace Teknik
{
@ -67,6 +69,7 @@ namespace Teknik
// Resolve the services from the service provider
var config = sp.GetService<Config>();
var logger = sp.GetService<ILogger<Logger>>();
var devEnv = config?.DevEnvironment ?? true;
var defaultConn = config?.DbConnection ?? string.Empty;
var authority = config?.UserConfig?.IdentityServerConfig?.Authority ?? string.Empty;
@ -102,6 +105,7 @@ namespace Teknik
services.AddHostedService<TaskQueueService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
services.AddSingleton<ObjectCache, ObjectCache>(c => new ObjectCache(300));
services.AddSingleton<IMailService, IMailService>(s => UserHelper.CreateMailService(config, logger));
services.AddHostedService<CacheCleanerService>();
services.AddScoped<IErrorController, ErrorController>();