diff --git a/ServerMaint/ArgumentOptions.cs b/ServerMaint/ArgumentOptions.cs index 6fd1642..8b08960 100644 --- a/ServerMaint/ArgumentOptions.cs +++ b/ServerMaint/ArgumentOptions.cs @@ -43,6 +43,18 @@ namespace ServerMaint [Option('f', "last-seen-file", Required = false, HelpText = "The file in which you want the last seen stats to be saved to")] public string LastSeenFile { get; set; } + [Option('i', "invalid", DefaultValue = false, Required = false, HelpText = "Generate a list of invalid accounts")] + public bool GenerateInvalid { get; set; } + + [Option('t', "invalid-file", Required = false, HelpText = "The file in which you want the invalid accounts to be saved to")] + public string InvalidFile { get; set; } + + [Option('o', "to-clean", DefaultValue = false, Required = false, HelpText = "Generate a list of accounts to be cleaned")] + public bool GenerateCleaning { get; set; } + + [Option('n', "to-clean-file", Required = false, HelpText = "The file in which you want the accounts to be cleaned to be saved to")] + public string CleaningFile { get; set; } + // Omitting long name, default --verbose [Option(HelpText = "Prints all messages to standard output.")] public bool Verbose { get; set; } diff --git a/ServerMaint/Program.cs b/ServerMaint/Program.cs index cfe2517..118a31c 100644 --- a/ServerMaint/Program.cs +++ b/ServerMaint/Program.cs @@ -83,6 +83,18 @@ namespace ServerMaint GenerateLastSeen(config, db, options.LastSeenFile); } + // Generates a file for all of the invalid accounts + if (options.GenerateInvalid) + { + GenerateInvalidAccounts(config, db, options.InvalidFile); + } + + // Generates a file for all of the accounts to be cleaned + if (options.GenerateCleaning) + { + GenerateCleaningList(config, db, options.CleaningFile, options.DaysBeforeDeletion); + } + Output(string.Format("[{0}] Finished Server Maintenance Process.", DateTime.Now)); return 0; } @@ -217,7 +229,7 @@ namespace ServerMaint mail.Body = string.Format(@" The account {0} does not meet the requirements for a valid username. -The username must match the following Regex Pattern: {1} +The username must meet the following requirements: {1} It must also be greater than or equal to {2} characters in length, and less than or equal to {3} characters in length. This email is to let you know that this account will be deleted in {4} days ({5}) in order to comply with the username restrictions. If you would like to keep your data, you should create a new account and transfer the data over to the new account. @@ -226,7 +238,7 @@ In order to make the process as easy as possible, you can reply to this email to Thank you for your continued use of Teknik! -- Teknik Administration", account, config.UserConfig.UsernameFilter, config.UserConfig.MinUsernameLength, config.UserConfig.MaxUsernameLength, 30, DateTime.Now.AddDays(30).ToShortDateString(), 15, DateTime.Now.AddDays(15).ToShortDateString()); +- Teknik Administration", account, config.UserConfig.UsernameFilterLabel, config.UserConfig.MinUsernameLength, config.UserConfig.MaxUsernameLength, 30, DateTime.Now.AddDays(30).ToShortDateString(), 15, DateTime.Now.AddDays(15).ToShortDateString()); mail.BodyEncoding = UTF8Encoding.UTF8; mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never; @@ -287,94 +299,56 @@ Thank you for your continued use of Teknik! public static void CleanEmail(Config config, TeknikEntities db) { - if (config.EmailConfig.Enabled) + Output(string.Format("[{0}] Started Cleaning of Orphaned Email Accounts.", DateTime.Now)); + List emails = GetOrphanedEmail(config, db); + foreach (string email in emails) { - Output(string.Format("[{0}] Started Cleaning of Orphaned Email Accounts.", DateTime.Now)); - List curUsers = db.Users.ToList(); - int totalAccounts = 0; - - // Connect to hmailserver COM - var app = new hMailServer.Application(); - app.Connect(); - app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password); - - var domain = app.Domains.ItemByName[config.EmailConfig.Domain]; - var accounts = domain.Accounts; - for (int i = 0; i < accounts.Count; i++) - { - var account = accounts[i]; - - bool userExists = curUsers.Exists(u => UserHelper.GetUserEmailAddress(config, u.Username) == account.Address); - bool isReserved = UserHelper.GetReservedUsernames(config).Exists(r => UserHelper.GetUserEmailAddress(config, r).ToLower() == account.Address.ToLower()); - if (!userExists && !isReserved) - { - // User doesn't exist, and it isn't reserved. Let's nuke it. - UserHelper.DeleteUserEmail(config, account.Address); - totalAccounts++; - } - } - - if (totalAccounts > 0) - { - // Add to transparency report if any users were removed - Takedown report = db.Takedowns.Create(); - report.Requester = TAKEDOWN_REPORTER; - report.RequesterContact = config.SupportEmail; - report.DateRequested = DateTime.Now; - report.Reason = "Orphaned Email Account"; - report.ActionTaken = string.Format("{0} Accounts Removed", totalAccounts); - report.DateActionTaken = DateTime.Now; - db.Takedowns.Add(report); - db.SaveChanges(); - } - - Output(string.Format("[{0}] Finished Cleaning of Orphaned Email Accounts. {1} Accounts Removed.", DateTime.Now, totalAccounts)); + // User doesn't exist, and it isn't reserved. Let's nuke it. + UserHelper.DeleteUserEmail(config, email); } + + if (emails.Count > 0) + { + // Add to transparency report if any users were removed + Takedown report = db.Takedowns.Create(); + report.Requester = TAKEDOWN_REPORTER; + report.RequesterContact = config.SupportEmail; + report.DateRequested = DateTime.Now; + report.Reason = "Orphaned Email Account"; + report.ActionTaken = string.Format("{0} Accounts Removed", emails.Count); + report.DateActionTaken = DateTime.Now; + db.Takedowns.Add(report); + db.SaveChanges(); + } + + Output(string.Format("[{0}] Finished Cleaning of Orphaned Email Accounts. {1} Accounts Removed.", DateTime.Now, emails.Count)); } public static void CleanGit(Config config, TeknikEntities db) { - if (config.GitConfig.Enabled) + Output(string.Format("[{0}] Started Cleaning of Orphaned Git Accounts.", DateTime.Now)); + List gitAccounts = GetOrphanedGit(config, db); + foreach (string account in gitAccounts) { - Output(string.Format("[{0}] Started Cleaning of Orphaned Git Accounts.", DateTime.Now)); - List curUsers = db.Users.ToList(); - int totalAccounts = 0; - - // We need to check the actual git database - MysqlDatabase mySQL = new MysqlDatabase(config.GitConfig.Database); - string sql = @"SELECT gogs.user.login_name AS login_name, gogs.user.lower_name AS username FROM gogs.user"; - var results = mySQL.Query(sql); - - if (results != null && results.Any()) - { - foreach (var account in results) - { - bool userExists = curUsers.Exists(u => UserHelper.GetUserEmailAddress(config, u.Username).ToLower() == account["login_name"].ToString().ToLower()); - bool isReserved = UserHelper.GetReservedUsernames(config).Exists(r => UserHelper.GetUserEmailAddress(config, r) == account["login_name"].ToString().ToLower()); - if (!userExists && !isReserved) - { - UserHelper.DeleteUserGit(config, account["username"].ToString()); - totalAccounts++; - } - } - } - - if (totalAccounts > 0) - { - // Add to transparency report if any users were removed - Takedown report = db.Takedowns.Create(); - report.Requester = TAKEDOWN_REPORTER; - report.RequesterContact = config.SupportEmail; - report.DateRequested = DateTime.Now; - report.Reason = "Orphaned Git Account"; - report.ActionTaken = string.Format("{0} Accounts Removed", totalAccounts); - report.DateActionTaken = DateTime.Now; - db.Takedowns.Add(report); - db.SaveChanges(); - } - - Output(string.Format("[{0}] Finished Cleaning of Orphaned Git Accounts. {1} Accounts Removed.", DateTime.Now, totalAccounts)); + // User doesn't exist, and it isn't reserved. Let's nuke it. + UserHelper.DeleteUserGit(config, account); } + + if (gitAccounts.Count > 0) + { + // Add to transparency report if any users were removed + Takedown report = db.Takedowns.Create(); + report.Requester = TAKEDOWN_REPORTER; + report.RequesterContact = config.SupportEmail; + report.DateRequested = DateTime.Now; + report.Reason = "Orphaned Git Account"; + report.ActionTaken = string.Format("{0} Accounts Removed", gitAccounts.Count); + report.DateActionTaken = DateTime.Now; + db.Takedowns.Add(report); + db.SaveChanges(); + } + + Output(string.Format("[{0}] Finished Cleaning of Orphaned Git Accounts. {1} Accounts Removed.", DateTime.Now, gitAccounts.Count)); } public static void GenerateLastSeen(Config config, TeknikEntities db, string fileName) @@ -401,15 +375,107 @@ Thank you for your continued use of Teknik! Output(string.Format("[{0}] Finished Generating Last Activity List.", DateTime.Now)); } + public static void GenerateInvalidAccounts(Config config, TeknikEntities db, string fileName) + { + Output(string.Format("[{0}] Started Generation of Invalid Account List.", DateTime.Now)); + List invalidAccounts = GetInvalidAccounts(config, db); + StringBuilder sb = new StringBuilder(); + sb.AppendLine("Username,Last Activity,Creation Date,Last Website Activity,Last Email Activity,Last Git Activity"); + foreach (string account in invalidAccounts) + { + User user = UserHelper.GetUser(db, account); + sb.AppendLine(string.Format("{0},{1},{2},{3},{4},{5}", + user.Username, + UserHelper.GetLastAccountActivity(db, config, user).ToString("g"), + user.JoinDate.ToString("g"), + user.LastSeen.ToString("g"), + UserHelper.UserEmailLastActive(config, UserHelper.GetUserEmailAddress(config, user.Username)).ToString("g"), + UserHelper.UserGitLastActive(config, user.Username).ToString("g"))); + } + string dir = Path.GetDirectoryName(fileName); + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + File.WriteAllText(fileName, sb.ToString()); + Output(string.Format("[{0}] Finished Generating Invalid Account List.", DateTime.Now)); + } + + public static void GenerateCleaningList(Config config, TeknikEntities db, string fileName, int maxDays) + { + Output(string.Format("[{0}] Started Generation of Accounts to Clean List.", DateTime.Now)); + List invalidAccounts = GetInvalidAccounts(config, db); + List inactiveAccounts = GetInactiveAccounts(config, db, maxDays); + List emailAccounts = GetOrphanedEmail(config, db); + List gitAccounts = GetOrphanedGit(config, db); + + StringBuilder sb = new StringBuilder(); + sb.AppendLine("Invalid Account Cleaning"); + sb.AppendLine("Username,Last Activity,Creation Date,Last Website Activity,Last Email Activity,Last Git Activity"); + foreach (string account in invalidAccounts) + { + User user = UserHelper.GetUser(db, account); + sb.AppendLine(string.Format("{0},{1},{2},{3},{4},{5}", + user.Username, + UserHelper.GetLastAccountActivity(db, config, user).ToString("g"), + user.JoinDate.ToString("g"), + user.LastSeen.ToString("g"), + UserHelper.UserEmailLastActive(config, UserHelper.GetUserEmailAddress(config, user.Username)).ToString("g"), + UserHelper.UserGitLastActive(config, user.Username).ToString("g"))); + } + + sb.AppendLine(); + sb.AppendLine("Inactive Account Cleaning"); + sb.AppendLine("Username,Last Activity,Creation Date,Last Website Activity,Last Email Activity,Last Git Activity"); + foreach (string account in inactiveAccounts) + { + User user = UserHelper.GetUser(db, account); + sb.AppendLine(string.Format("{0},{1},{2},{3},{4},{5}", + user.Username, + UserHelper.GetLastAccountActivity(db, config, user).ToString("g"), + user.JoinDate.ToString("g"), + user.LastSeen.ToString("g"), + UserHelper.UserEmailLastActive(config, UserHelper.GetUserEmailAddress(config, user.Username)).ToString("g"), + UserHelper.UserGitLastActive(config, user.Username).ToString("g"))); + } + + sb.AppendLine(); + sb.AppendLine("Orphaned Email Cleaning"); + sb.AppendLine("Email,Last Activity"); + foreach (string account in emailAccounts) + { + sb.AppendLine(string.Format("{0},{1}", + account, + UserHelper.UserEmailLastActive(config, account).ToString("g"))); + } + + sb.AppendLine(); + sb.AppendLine("Orphaned Git Cleaning"); + sb.AppendLine("Username,Last Activity"); + foreach (string account in gitAccounts) + { + sb.AppendLine(string.Format("{0},{1}", + account, + UserHelper.UserGitLastActive(config, account).ToString("g"))); + } + + string dir = Path.GetDirectoryName(fileName); + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + File.WriteAllText(fileName, sb.ToString()); + Output(string.Format("[{0}] Finished Generating Accounts to Clean List.", DateTime.Now)); + } + public static List GetInvalidAccounts(Config config, TeknikEntities db) { List foundUsers = new List(); List curUsers = db.Users.ToList(); foreach (User user in curUsers) { - // If the username is reserved, don't worry about it + // If the username is reserved, let's add it to the list if (UserHelper.UsernameReserved(config, user.Username)) { + foundUsers.Add(user.Username); continue; } @@ -500,6 +566,63 @@ Thank you for your continued use of Teknik! return foundUsers; } + public static List GetOrphanedEmail(Config config, TeknikEntities db) + { + List foundEmail = new List(); + if (config.EmailConfig.Enabled) + { + List curUsers = db.Users.ToList(); + + // Connect to hmailserver COM + var app = new hMailServer.Application(); + app.Connect(); + app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password); + + var domain = app.Domains.ItemByName[config.EmailConfig.Domain]; + var accounts = domain.Accounts; + for (int i = 0; i < accounts.Count; i++) + { + var account = accounts[i]; + + bool userExists = curUsers.Exists(u => UserHelper.GetUserEmailAddress(config, u.Username) == account.Address); + bool isReserved = UserHelper.GetReservedUsernames(config).Exists(r => UserHelper.GetUserEmailAddress(config, r).ToLower() == account.Address.ToLower()); + if (!userExists && !isReserved) + { + foundEmail.Add(account.Address); + } + } + } + return foundEmail; + } + + public static List GetOrphanedGit(Config config, TeknikEntities db) + { + List foundGit = new List(); + if (config.GitConfig.Enabled) + { + List curUsers = db.Users.ToList(); + + // We need to check the actual git database + MysqlDatabase mySQL = new MysqlDatabase(config.GitConfig.Database); + string sql = @"SELECT gogs.user.login_name AS login_name, gogs.user.lower_name AS username FROM gogs.user"; + var results = mySQL.Query(sql); + + if (results != null && results.Any()) + { + foreach (var account in results) + { + bool userExists = curUsers.Exists(u => UserHelper.GetUserEmailAddress(config, u.Username).ToLower() == account["login_name"].ToString().ToLower()); + bool isReserved = UserHelper.GetReservedUsernames(config).Exists(r => UserHelper.GetUserEmailAddress(config, r) == account["login_name"].ToString().ToLower()); + if (!userExists && !isReserved) + { + foundGit.Add(account["username"].ToString()); + } + } + } + } + return foundGit; + } + public static void Output(string message) { Console.WriteLine(message); diff --git a/Teknik/Areas/Help/Views/Help/Mail.cshtml b/Teknik/Areas/Help/Views/Help/Mail.cshtml index 6cd3a89..ab219d7 100644 --- a/Teknik/Areas/Help/Views/Help/Mail.cshtml +++ b/Teknik/Areas/Help/Views/Help/Mail.cshtml @@ -49,7 +49,7 @@
  • Port:
    143 (993 SSL)
  • User Name:
    @string.Format("[username]@{0}", Model.Config.EmailConfig.Domain)
  • Connection Security:
    None (SSL/TLS)
  • -
  • Authentication method:
    Password
  • +
  • Authentication method:
    Normal Password
  • @@ -59,7 +59,7 @@
  • Port:
    25 (465 SSL)
  • User Name:
    @string.Format("[username]@{0}", Model.Config.EmailConfig.Domain)
  • Connection Security:
    None (SSL/TLS)
  • -
  • Authentication method:
    Password
  • +
  • Authentication method:
    Normal Password
  • diff --git a/Teknik/Areas/TOS/Views/TOS/Index.cshtml b/Teknik/Areas/TOS/Views/TOS/Index.cshtml index 4ac2ca2..0aa0fc0 100644 --- a/Teknik/Areas/TOS/Views/TOS/Index.cshtml +++ b/Teknik/Areas/TOS/Views/TOS/Index.cshtml @@ -13,7 +13,7 @@
  • Any Malware uploads or otherwise hosted/linked content will be removed without notice.
  • Email is limited to a maximum of 100 outbound email messages per day. This is to prevent spam accounts. If your account is flagged as spamming, it will be deleted without notice.
  • Copyrighted content will be removed only after a valid DMCA is recieved and verified.
  • -
  • Inactive Accounts with no data will be deleted after 180 days. +
  • Inactive Accounts with no data will be deleted after 365 days (1 Year).
    • Activity is defined as logging into the Teknik Website, into the Git website, into the mail service (Either through webmail or via another client), or Modifying/Adding a Git Repository.
    • Data is defined as Blog Posts/Comments, Podcast Comments, Emails, and Git Repositories.
    • @@ -24,7 +24,7 @@
      -

      Last Modified May 18, 2016

      +

      Last Modified May 19, 2016

      diff --git a/Teknik/Areas/User/Views/User/Register.cshtml b/Teknik/Areas/User/Views/User/Register.cshtml index 06c7af5..fb3eb9a 100644 --- a/Teknik/Areas/User/Views/User/Register.cshtml +++ b/Teknik/Areas/User/Views/User/Register.cshtml @@ -20,7 +20,7 @@

      - Username must match the following pattern @Model.Config.UserConfig.UsernameFilter
      + Username must meet the following requirements: @Model.Config.UserConfig.UsernameFilterLabel
      and the length must be greater than @Model.Config.UserConfig.MinUsernameLength and less than @Model.Config.UserConfig.MaxUsernameLength characters

      diff --git a/Teknik/Configuration/UserConfig.cs b/Teknik/Configuration/UserConfig.cs index 0d8d7f4..ca10720 100644 --- a/Teknik/Configuration/UserConfig.cs +++ b/Teknik/Configuration/UserConfig.cs @@ -11,6 +11,7 @@ namespace Teknik.Configuration public bool RegistrationEnabled { get; set; } public bool LoginEnabled { get; set; } public string UsernameFilter { get; set; } + public string UsernameFilterLabel { get; set; } public int MinUsernameLength { get; set; } public int MaxUsernameLength { get; set; } public string ReservedUsernameDefinitionFile { get; set; } @@ -20,6 +21,7 @@ namespace Teknik.Configuration RegistrationEnabled = true; LoginEnabled = true; UsernameFilter = "^[a-zA-Z0-9_-]+(?:\\.[a-zA-Z0-9_-]+)*$"; + UsernameFilterLabel = "AlphaNumeric Characters with Dashes, Underlines, and 0-1 Periods not in the beginning or end."; MinUsernameLength = 1; MaxUsernameLength = 35; ReservedUsernameDefinitionFile = string.Empty;