Browse Source

Moved user modification to helper class.

Added cleaning of users to server maintainence program.
Added cli args to server maint program.
Modified last activity to also grab from email and git.
tags/2.0.3
Teknikode 3 years ago
parent
commit
19f5d28eaa

+ 60
- 0
ServerMaint/ArgumentOptions.cs View File

@@ -0,0 +1,60 @@
using CommandLine;
using CommandLine.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ServerMaint
{
public class ArgumentOptions
{
[ParserState]
public IParserState LastParserState { get; set; }

[Option('a', "all", DefaultValue = false, Required = false, HelpText = "Run All Processes")]
public bool RunAll { get; set; }

[Option('u', "clean-users", DefaultValue = false, Required = false, HelpText = "Clean all inactive users")]
public bool CleanUsers { get; set; }

[Option('s', "scan", DefaultValue = false, Required = false, HelpText = "Scan all uploads for viruses")]
public bool ScanUploads { get; set; }

[Option('c', "config", Required = false, HelpText = "The path to the teknik config file")]
public string Config { get; set; }

[Option('d', "days", DefaultValue = 90, Required = false, HelpText = "Days before the user is deleted")]
public int DaysBeforeDeletion { get; set; }

[Option('e', "emails", DefaultValue = 2, Required = false, HelpText = "Number of emails to send before deletion")]
public int EmailsToSend { get; set; }

// Omitting long name, default --verbose
[Option(HelpText = "Prints all messages to standard output.")]
public bool Verbose { get; set; }

[HelpOption]
public string GetUsage()
{
var help = new HelpText();

// ...
if (this.LastParserState.Errors.Any())
{
var errors = help.RenderParsingErrorsText(this, 2); // indent with two spaces

if (!string.IsNullOrEmpty(errors))
{
help.AddPreOptionsLine(string.Concat(Environment.NewLine, "ERROR(S):"));
help.AddPreOptionsLine(errors);
}
}

// ...
return help;
}

}
}

+ 130
- 19
ServerMaint/Program.cs View File

@@ -4,9 +4,14 @@ using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using System.Text;
using Teknik.Areas.Transparency.Models;
using Teknik.Areas.Upload.Models;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
using Teknik.Configuration;
using Teknik.Helpers;
using Teknik.Models;
@@ -16,26 +21,54 @@ namespace ServerMaint
public class Program
{
private static string currentPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
private static string parentPath = Directory.GetParent(currentPath).FullName;
private static string virusFile = Path.Combine(currentPath, "virusLogs.txt");
private static string errorFile = Path.Combine(currentPath, "errorLogs.txt");
private static string configPath = Path.Combine(parentPath, "App_Data");
private static string configPath = currentPath;

private const string TAKEDOWN_REPORTER = "Teknik Automated System";

public static event Action<string> OutputEvent;

public static void Main(string[] args)
public static int Main(string[] args)
{

// Let's clean some stuff!!
try
{
Config config = Config.Load(configPath);
TeknikEntities db = new TeknikEntities();
ArgumentOptions options = new ArgumentOptions();
var parser = new CommandLine.Parser(config => config.HelpWriter = Console.Out);
if (parser.ParseArguments(args, options))
{
if (!string.IsNullOrEmpty(options.Config))
configPath = options.Config;

if (Directory.Exists(configPath))
{
Config config = Config.Load(configPath);
TeknikEntities db = new TeknikEntities();

// Scan all the uploads for viruses, and remove the bad ones
if (config.UploadConfig.VirusScanEnable)
// Scan all the uploads for viruses, and remove the bad ones
if (options.ScanUploads && config.UploadConfig.VirusScanEnable)
{
ScanUploads(config, db);
}

// Cleans all inactive users
if (options.CleanUsers)
{
CleanUsers(config, db, options.DaysBeforeDeletion, options.EmailsToSend);
}
Output(string.Format("[{0}] Finished Server Maintainence Process.", DateTime.Now));
return 0;
}
else
{
string msg = string.Format("[{0}] Config File does not exist.", DateTime.Now);
File.AppendAllLines(errorFile, new List<string> { msg });
Output(msg);
}
}
else
{
ScanUploads(config, db);
Output(options.GetUsage());
}
}
catch (Exception ex)
@@ -44,6 +77,7 @@ namespace ServerMaint
File.AppendAllLines(errorFile, new List<string> { msg });
Output(msg);
}
return -1;
}

public static void ScanUploads(Config config, TeknikEntities db)
@@ -87,15 +121,15 @@ namespace ServerMaint
string msg = string.Format("[{0}] Virus Detected: {1} - {2} - {3}", DateTime.Now, upload.Url, upload.FileName, scanResult.InfectedFiles.First().VirusName);
File.AppendAllLines(virusFile, new List<string> { msg });
Output(msg);
//// Delete from the DB
//db.Uploads.Remove(upload);
//db.SaveChanges();
//// Delete the File
//if (File.Exists(filePath))
//{
// File.Delete(filePath);
//}
// Delete from the DB
db.Uploads.Remove(upload);
db.SaveChanges();
// Delete the File
if (File.Exists(filePath))
{
File.Delete(filePath);
}
break;
case ClamScanResults.Error:
string errorMsg = string.Format("[{0}] Scan Error: {1}", DateTime.Now, scanResult.RawResult);
@@ -111,9 +145,86 @@ namespace ServerMaint
}
}

// Add to transparency report if any were found
Takedown report = db.Takedowns.Create();
report.Requester = TAKEDOWN_REPORTER;
report.RequesterContact = config.SupportEmail;
report.DateRequested = DateTime.Now;
report.Reason = "Malware Found";
report.ActionTaken = string.Format("{0} Uploads removed", totalViruses);
report.DateActionTaken = DateTime.Now;
db.Takedowns.Add(report);
db.SaveChanges();

Output(string.Format("Scanning Complete. {0} Scanned | {1} Viruses Found | {2} Total Files", totalScans, totalViruses, totalCount));
}

public static void CleanUsers(Config config, TeknikEntities db, int maxDays, int numEmails)
{
int totalUsers = 0;

List<User> curUsers = db.Users.ToList();
foreach (User user in curUsers)
{
DateTime lastActivity = UserHelper.GetLastActivity(db, config, user);

TimeSpan inactiveTime = DateTime.Now.Subtract(lastActivity);

// If older than max days, delete
if (inactiveTime >= new TimeSpan(maxDays, 0, 0, 0, 0))
{
UserHelper.DeleteUser(db, config, user);
continue;
}

// Otherwise, send an email if they are within +-1 day of email days
int daysForEmail = (int)Math.Floor((double)(maxDays / (numEmails + 1)));
for (int i = daysForEmail; i < maxDays; i = i + daysForEmail)
{
if (inactiveTime.Days == daysForEmail)
{
string email = string.Format("{0}@{1}", user.Username, config.EmailConfig.Domain);
// Let's send them an email
SmtpClient client = new SmtpClient();
client.Host = config.ContactConfig.Host;
client.Port = config.ContactConfig.Port;
client.EnableSsl = config.ContactConfig.SSL;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = true;
client.Credentials = new NetworkCredential(config.ContactConfig.Username, config.ContactConfig.Password);
client.Timeout = 5000;

MailMessage mail = new MailMessage(config.SupportEmail, email);
mail.Subject = "Inactive Account Notice";
mail.Body = string.Format(@"
The account {0} has not had any activity for {1} days. After {2} days of inactivity, this account will be deleted permanently.

In order to avoid this, login into your email, or teknik website.

Thank you for your use of Teknik and I hope you decide to come back.

- Teknik Administration", user.Username, inactiveTime.Days, maxDays);
mail.BodyEncoding = UTF8Encoding.UTF8;
mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;

client.Send(mail);
break;
}
}
}

// 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 = "User Inactive";
report.ActionTaken = string.Format("{0} Users Removed", totalUsers);
report.DateActionTaken = DateTime.Now;
db.Takedowns.Add(report);
db.SaveChanges();
}

public static void Output(string message)
{
Console.WriteLine(message);

+ 12
- 7
ServerMaint/ServerMaint.csproj View File

@@ -52,8 +52,12 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="CommandLine, Version=1.9.71.2, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL">
<HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
@@ -77,11 +81,11 @@
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SecurityDriven.Inferno, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Inferno.1.1.0\lib\net451\SecurityDriven.Inferno.dll</HintPath>
<Reference Include="SecurityDriven.Inferno, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Inferno.1.2.0\lib\net452\SecurityDriven.Inferno.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -94,6 +98,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ArgumentOptions.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
@@ -120,12 +125,12 @@
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\GitVersionTask.3.4.1\build\dotnet\GitVersionTask.targets" Condition="Exists('..\packages\GitVersionTask.3.4.1\build\dotnet\GitVersionTask.targets')" />
<Import Project="..\packages\GitVersionTask.3.5.3\build\dotnet\GitVersionTask.targets" Condition="Exists('..\packages\GitVersionTask.3.5.3\build\dotnet\GitVersionTask.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\GitVersionTask.3.4.1\build\dotnet\GitVersionTask.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GitVersionTask.3.4.1\build\dotnet\GitVersionTask.targets'))" />
<Error Condition="!Exists('..\packages\GitVersionTask.3.5.3\build\dotnet\GitVersionTask.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GitVersionTask.3.5.3\build\dotnet\GitVersionTask.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

+ 5
- 4
ServerMaint/packages.config View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net452" />
<package id="BouncyCastle" version="1.8.1" targetFramework="net452" />
<package id="CommandLineParser" version="1.9.71" targetFramework="net452" />
<package id="EntityFramework" version="6.1.3" targetFramework="net452" />
<package id="GitVersionTask" version="3.4.1" targetFramework="net452" developmentDependency="true" />
<package id="Inferno" version="1.1.0" targetFramework="net452" />
<package id="GitVersionTask" version="3.5.3" targetFramework="net452" developmentDependency="true" />
<package id="Inferno" version="1.2.0" targetFramework="net452" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net452" />
<package id="Microsoft.AspNet.Identity.EntityFramework" version="2.2.1" targetFramework="net452" />
<package id="nClam" version="2.0.6.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net452" />
</packages>

+ 15
- 206
Teknik/Areas/User/Controllers/UserController.cs View File

@@ -18,6 +18,7 @@ using Teknik.Models;
using Teknik.ViewModels;
using System.Windows;
using System.Net;
using Teknik.Areas.Users.Utility;

namespace Teknik.Areas.Users.Controllers
{
@@ -52,7 +53,7 @@ namespace Teknik.Areas.Users.Controllers
model.Email = string.Format("{0}@{1}", user.Username, Config.EmailConfig.Domain);
}
model.JoinDate = user.JoinDate;
model.LastSeen = user.LastSeen;
model.LastSeen = UserHelper.GetLastActivity(db, Config, user);

model.UserSettings = user.UserSettings;
model.BlogSettings = user.BlogSettings;
@@ -208,7 +209,7 @@ namespace Teknik.Areas.Users.Controllers
{
if (Config.UserConfig.RegistrationEnabled)
{
if (Utility.UserHelper.UserExists(db, model.Username))
if (UserHelper.UserExists(db, model.Username))
{
return Json(new { error = "That username already exists." });
}
@@ -216,51 +217,9 @@ namespace Teknik.Areas.Users.Controllers
{
return Json(new { error = "Passwords must match." });
}

try
{
string email = string.Format("{0}@{1}", model.Username, Config.EmailConfig.Domain);
// If Email Server is enabled
if (Config.EmailConfig.Enabled)
{
// 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];
try
{
var account = domain.Accounts.ItemByAddress[email];
return Json(new { error = "That email already exists." });
}
catch { }

// If we got an exception, then the email doesnt exist and we continue on!
var newAccount = domain.Accounts.Add();
newAccount.Address = email;
newAccount.Password = model.Password;
newAccount.Active = true;
newAccount.MaxSize = Config.EmailConfig.MaxSize;

newAccount.Save();
}

// If Git is enabled
if (Config.GitConfig.Enabled)
{
// Add gogs user
using (var client = new WebClient())
{
var obj = new { source_id = Config.GitConfig.SourceId, username = model.Username, email = email, login_name = email, password = model.Password };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
client.Headers[HttpRequestHeader.ContentType] = "application/json";
Uri baseUri = new Uri(Config.GitConfig.Host);
Uri finalUri = new Uri(baseUri, "api/v1/admin/users?token=" + Config.GitConfig.AccessToken);
string result = client.UploadString(finalUri, "POST", json);
}
}

// Add User
User newUser = db.Users.Create();
newUser.JoinDate = DateTime.Now;
newUser.Username = model.Username;
@@ -268,18 +227,12 @@ namespace Teknik.Areas.Users.Controllers
newUser.UserSettings = new UserSettings();
newUser.BlogSettings = new BlogSettings();
newUser.UploadSettings = new UploadSettings();
db.Users.Add(newUser);
db.SaveChanges();

// Generate blog for the user
var newBlog = db.Blogs.Create();
newBlog.UserId = newUser.UserId;
db.Blogs.Add(newBlog);
db.SaveChanges();

UserHelper.AddUser(db, Config, newUser, model.Password);
}
catch (Exception ex)
{
return Json(new { error = "Unable to create the user." });
return Json(new { error = ex.Message });
}
return Login(new LoginViewModel { Username = model.Username, Password = model.Password, RememberMe = false, ReturnUrl = model.ReturnUrl });
}
@@ -293,9 +246,10 @@ namespace Teknik.Areas.Users.Controllers
{
if (ModelState.IsValid)
{
User user = Utility.UserHelper.GetUser(db, User.Identity.Name);
User user = UserHelper.GetUser(db, User.Identity.Name);
if (user != null)
{
bool changePass = false;
string email = string.Format("{0}@{1}", User.Identity.Name, Config.EmailConfig.Domain);
// Changing Password?
if (!string.IsNullOrEmpty(curPass) && (!string.IsNullOrEmpty(newPass) || !string.IsNullOrEmpty(newPassConfirm)))
@@ -311,37 +265,7 @@ namespace Teknik.Areas.Users.Controllers
return Json(new { error = "New Password Must Match." });
}
user.HashedPassword = SHA384.Hash(User.Identity.Name, newPass);

// Update Email Pass
if (Config.EmailConfig.Enabled)
{
try
{
var app = new hMailServer.Application();
app.Connect();
app.Authenticate(Config.EmailConfig.Username, Config.EmailConfig.Password);
var domain = app.Domains.ItemByName[Config.EmailConfig.Domain];
var account = domain.Accounts.ItemByAddress[email];
account.Password = newPass;
account.Save();
}
catch (COMException)
{ }
}

// Update Git Pass
if (Config.GitConfig.Enabled)
{
using (var client = new WebClient())
{
var obj = new { source_id = Config.GitConfig.SourceId, email = email, password = newPass };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
client.Headers[HttpRequestHeader.ContentType] = "application/json";
Uri baseUri = new Uri(Config.GitConfig.Host);
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + User.Identity.Name + "?token=" + Config.GitConfig.AccessToken);
string result = client.UploadString(finalUri, "PATCH", json);
}
}
changePass = true;
}

// PGP Key valid?
@@ -360,9 +284,7 @@ namespace Teknik.Areas.Users.Controllers

user.UploadSettings.SaveKey = saveKey;
user.UploadSettings.ServerSideEncrypt = serverSideEncrypt;

db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
UserHelper.SaveUser(db, Config, user, changePass, newPass);
return Json(new { result = true });
}
return Json(new { error = "User does not exist" });
@@ -375,130 +297,17 @@ namespace Teknik.Areas.Users.Controllers
{
if (ModelState.IsValid)
{
if (Config.EmailConfig.Enabled)
{
try
{
// Delete Email
var app = new hMailServer.Application();
app.Connect();
app.Authenticate(Config.EmailConfig.Username, Config.EmailConfig.Password);
var domain = app.Domains.ItemByName[Config.EmailConfig.Domain];
var account = domain.Accounts.ItemByAddress[string.Format("{0}@{1}", User.Identity.Name, Config.EmailConfig.Domain)];
if (account != null)
{
account.Delete();
}
}
catch (COMException)
{
}
catch (Exception)
{
return Json(new { error = "Unable to delete email account." });
}
}

// Delete Git
if (Config.GitConfig.Enabled)
User user = UserHelper.GetUser(db, User.Identity.Name);
if (user != null)
{
try
{
Uri baseUri = new Uri(Config.GitConfig.Host);
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + User.Identity.Name + "?token=" + Config.GitConfig.AccessToken);
WebRequest request = WebRequest.Create(finalUri);
request.Method = "DELETE";

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode != HttpStatusCode.NotFound && response.StatusCode != HttpStatusCode.OK)
{
return Json(new { error = "Unable to delete git account. Response Code: " + response.StatusCode });
}
}
catch (HttpException htex)
{
if (htex.GetHttpCode() != 404)
return Json(new { error = "Unable to delete git account. Http Exception: " + htex.Message });
UserHelper.DeleteUser(db, Config, user);
}
catch (Exception ex)
{
// This error signifies the user doesn't exist, so we can continue deleting
if (ex.Message != "The remote server returned an error: (404) Not Found.")
{
return Json(new { error = "Unable to delete git account. Exception: " + ex.Message });
}
}
}

// Update uploads
List<Upload.Models.Upload> uploads = db.Uploads.Include("User").Where(u => u.User.Username == User.Identity.Name).ToList();
if (uploads != null)
{
foreach (Upload.Models.Upload upload in uploads)
{
upload.UserId = null;
db.Entry(upload).State = EntityState.Modified;
return Json(new { error = ex.Message });
}
}

// Update pastes
List<Paste.Models.Paste> pastes = db.Pastes.Include("User").Where(u => u.User.Username == User.Identity.Name).ToList();
if (pastes != null)
{
foreach (Paste.Models.Paste paste in pastes)
{
paste.UserId = null;
db.Entry(paste).State = EntityState.Modified;
}
}

// Update shortened urls
List<ShortenedUrl> shortUrls = db.ShortenedUrls.Include("User").Where(u => u.User.Username == User.Identity.Name).ToList();
if (shortUrls != null)
{
foreach (ShortenedUrl shortUrl in shortUrls)
{
shortUrl.UserId = null;
db.Entry(shortUrl).State = EntityState.Modified;
}
}

// Delete Blogs
Blog.Models.Blog blog = db.Blogs.Include("BlogPosts").Include("BlogPosts.Comments").Include("User").Where(u => u.User.Username == User.Identity.Name).FirstOrDefault();
if (blog != null)
{
db.Blogs.Remove(blog);
}

// Delete post comments
List<BlogPostComment> postComments = db.BlogComments.Include("User").Where(u => u.User.Username == User.Identity.Name).ToList();
if (postComments != null)
{
foreach (BlogPostComment postComment in postComments)
{
db.BlogComments.Remove(postComment);
}
}

// Delete post comments
List<Podcast.Models.PodcastComment> podComments = db.PodcastComments.Include("User").Where(u => u.User.Username == User.Identity.Name).ToList();
if (podComments != null)
{
foreach (Podcast.Models.PodcastComment podComment in podComments)
{
db.PodcastComments.Remove(podComment);
}
}

// Delete User
User user = Utility.UserHelper.GetUser(db, User.Identity.Name);
if (user != null)
{
user.UserSettings = db.UserSettings.Find(user.UserId);
user.BlogSettings = db.BlogSettings.Find(user.UserId);
user.UploadSettings = db.UploadSettings.Find(user.UserId);
db.Users.Remove(user);
db.SaveChanges();
// Sign Out
Logout();
return Json(new { result = true });

+ 286
- 0
Teknik/Areas/User/Utility/UserHelper.cs View File

@@ -1,12 +1,18 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using Teknik.Areas.Blog.Models;
using Teknik.Areas.Shortener.Models;
using Teknik.Areas.Users.Models;
using Teknik.Configuration;
using Teknik.Helpers;
using Teknik.Models;

namespace Teknik.Areas.Users.Utility
@@ -16,6 +22,12 @@ namespace Teknik.Areas.Users.Utility
public static User GetUser(TeknikEntities db, string username)
{
User user = db.Users.Where(b => b.Username == username).FirstOrDefault();
if (user != null)
{
user.UserSettings = db.UserSettings.Find(user.UserId);
user.BlogSettings = db.BlogSettings.Find(user.UserId);
user.UploadSettings = db.UploadSettings.Find(user.UserId);
}

return user;
}
@@ -31,6 +43,280 @@ namespace Teknik.Areas.Users.Utility
return false;
}

public static void AddUser(TeknikEntities db, Config config, User user, string password)
{
try
{
string email = string.Format("{0}@{1}", user.Username, config.EmailConfig.Domain);
// If Email Server is enabled
if (config.EmailConfig.Enabled)
{
// 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];
try
{
var account = domain.Accounts.ItemByAddress[email];
throw new Exception("That email already exists.");
}
catch { }

// If we got an exception, then the email doesnt exist and we continue on!
var newAccount = domain.Accounts.Add();
newAccount.Address = email;
newAccount.Password = password;
newAccount.Active = true;
newAccount.MaxSize = config.EmailConfig.MaxSize;

newAccount.Save();
}

// If Git is enabled
if (config.GitConfig.Enabled)
{
// Add gogs user
using (var client = new WebClient())
{
var obj = new { source_id = config.GitConfig.SourceId, username = user.Username, email = email, login_name = email, password = password };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
client.Headers[HttpRequestHeader.ContentType] = "application/json";
Uri baseUri = new Uri(config.GitConfig.Host);
Uri finalUri = new Uri(baseUri, "api/v1/admin/users?token=" + config.GitConfig.AccessToken);
string result = client.UploadString(finalUri, "POST", json);
}
}

// Add User
db.Users.Add(user);
db.SaveChanges();

// Generate blog for the user
var newBlog = db.Blogs.Create();
newBlog.UserId = user.UserId;
db.Blogs.Add(newBlog);
db.SaveChanges();
}
catch (Exception ex)
{
throw new Exception("Unable to create user.", ex);
}
}

public static void SaveUser(TeknikEntities db, Config config, User user, bool changePass, string newPass)
{
string email = string.Format("{0}@{1}", user.Username, config.EmailConfig.Domain);
// Changing Password?
if (changePass)
{
// Update Email Pass
if (config.EmailConfig.Enabled)
{
try
{
var app = new hMailServer.Application();
app.Connect();
app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
var account = domain.Accounts.ItemByAddress[email];
account.Password = newPass;
account.Save();
}
catch (COMException)
{ }
}

// Update Git Pass
if (config.GitConfig.Enabled)
{
using (var client = new WebClient())
{
var obj = new { source_id = config.GitConfig.SourceId, email = email, password = newPass };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
client.Headers[HttpRequestHeader.ContentType] = "application/json";
Uri baseUri = new Uri(config.GitConfig.Host);
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + user.Username + "?token=" + config.GitConfig.AccessToken);
string result = client.UploadString(finalUri, "PATCH", json);
}
}
}
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
}

public static void DeleteUser(TeknikEntities db, Config config, User user)
{
// Check to see if we need to delete their email.
if (config.EmailConfig.Enabled)
{
try
{
// Delete Email
var app = new hMailServer.Application();
app.Connect();
app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
var account = domain.Accounts.ItemByAddress[string.Format("{0}@{1}", user.Username, config.EmailConfig.Domain)];
if (account != null)
{
account.Delete();
}
}
catch (COMException)
{
}
catch (Exception ex)
{
throw new Exception("Unable to delete email account.", ex);
}
}

// Delete Git
if (config.GitConfig.Enabled)
{
try
{
Uri baseUri = new Uri(config.GitConfig.Host);
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + user.Username + "?token=" + config.GitConfig.AccessToken);
WebRequest request = WebRequest.Create(finalUri);
request.Method = "DELETE";

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode != HttpStatusCode.NotFound && response.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Unable to delete git account. Response Code: " + response.StatusCode);
}
}
catch (HttpException htex)
{
if (htex.GetHttpCode() != 404)
throw new Exception("Unable to delete git account. Http Exception: " + htex.Message);
}
catch (Exception ex)
{
// This error signifies the user doesn't exist, so we can continue deleting
if (ex.Message != "The remote server returned an error: (404) Not Found.")
{
throw new Exception("Unable to delete git account. Exception: " + ex.Message);
}
}
}

// Update uploads
List<Upload.Models.Upload> uploads = db.Uploads.Include("User").Where(u => u.User.Username == user.Username).ToList();
if (uploads != null)
{
foreach (Upload.Models.Upload upload in uploads)
{
upload.UserId = null;
db.Entry(upload).State = EntityState.Modified;
}
}

// Update pastes
List<Paste.Models.Paste> pastes = db.Pastes.Include("User").Where(u => u.User.Username == user.Username).ToList();
if (pastes != null)
{
foreach (Paste.Models.Paste paste in pastes)
{
paste.UserId = null;
db.Entry(paste).State = EntityState.Modified;
}
}

// Update shortened urls
List<ShortenedUrl> shortUrls = db.ShortenedUrls.Include("User").Where(u => u.User.Username == user.Username).ToList();
if (shortUrls != null)
{
foreach (ShortenedUrl shortUrl in shortUrls)
{
shortUrl.UserId = null;
db.Entry(shortUrl).State = EntityState.Modified;
}
}

// Delete Blogs
Blog.Models.Blog blog = db.Blogs.Include("BlogPosts").Include("BlogPosts.Comments").Include("User").Where(u => u.User.Username == user.Username).FirstOrDefault();
if (blog != null)
{
db.Blogs.Remove(blog);
}

// Delete post comments
List<BlogPostComment> postComments = db.BlogComments.Include("User").Where(u => u.User.Username == user.Username).ToList();
if (postComments != null)
{
foreach (BlogPostComment postComment in postComments)
{
db.BlogComments.Remove(postComment);
}
}

// Delete post comments
List<Podcast.Models.PodcastComment> podComments = db.PodcastComments.Include("User").Where(u => u.User.Username == user.Username).ToList();
if (podComments != null)
{
foreach (Podcast.Models.PodcastComment podComment in podComments)
{
db.PodcastComments.Remove(podComment);
}
}

// Delete User
db.Users.Remove(user);
db.SaveChanges();
}

public static DateTime GetLastActivity(TeknikEntities db, Config config, User user)
{
DateTime lastActive = new DateTime(1900, 1, 1);
string email = string.Format("{0}@{1}", user.Username, config.EmailConfig.Domain);

if (config.EmailConfig.Enabled)
{
// Connect to hmailserver COM
var app = new hMailServer.Application();
app.Connect();
app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);

try
{
var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
var account = domain.Accounts.ItemByAddress[email];
DateTime lastEmail = (DateTime)account.LastLogonTime;
if (lastActive < lastEmail)
lastActive = lastEmail;
}
catch (Exception ex) { }
}
if (config.GitConfig.Enabled)
{
// We need to check the actual git database
MysqlDatabase mySQL = new MysqlDatabase(config.GitConfig.Database);
string sql = @"SELECT MAX(gogs.repository.updated) AS LastUpdate
FROM gogs.repository
INNER JOIN gogs.user
WHERE gogs.user.login_name = {0} AND gogs.user.id = gogs.repository.owner_id";
object result = mySQL.ScalarQuery(sql, new object[] { email });

if (result != null)
{
DateTime tmpLast = lastActive;
DateTime.TryParse(result.ToString(), out tmpLast);
if (lastActive < tmpLast)
lastActive = tmpLast;
}
}

if (lastActive < user.LastSeen)
lastActive = user.LastSeen;

return lastActive;
}

public static HttpCookie CreateAuthCookie(string username, bool remember, string domain, bool local)
{
Config config = Config.Load();

+ 1
- 1
Teknik/Areas/User/Views/User/Index.cshtml View File

@@ -73,7 +73,7 @@
<li class="list-group-item text-right"><span class="pull-left"><strong>Joined</strong></span> <time datetime="@Model.LastSeen.ToString("o")">@Model.JoinDate.ToString("MMMM dd, yyyy")</time></li>
@if (OwnProfile && User.Identity.IsAuthenticated)
{
<li class="list-group-item text-right"><span class="pull-left"><strong>Last Seen</strong></span> <time datetime="@Model.LastSeen.ToString("o")">@Model.LastSeen.ToString("MMMM dd, yyyy")</time></li>
<li class="list-group-item text-right"><span class="pull-left"><strong>Last Seen</strong></span> <time datetime="@Model.LastSeen.ToString("o")">@Model.LastSeen.ToString("MMMM dd, yyyy hh:mm:ss")</time></li>
}
@if (!string.IsNullOrEmpty(pgpFingerprint))
{

+ 3
- 0
Teknik/Configuration/GitConfig.cs View File

@@ -16,12 +16,15 @@ namespace Teknik.Configuration

public int SourceId { get; set; }

public DatabaseConfig Database { get; set; }

public GitConfig()
{
Enabled = true;
Host = string.Empty;
AccessToken = string.Empty;
SourceId = 1;
Database = new DatabaseConfig();
}
}
}

+ 1
- 0
Teknik/Teknik.csproj View File

@@ -612,6 +612,7 @@
<Folder Include="Areas\TOS\Views\TOS\" />
<Folder Include="Areas\Transparency\Views\Shared\" />
<Folder Include="Areas\Upload\Views\Shared\" />
<Folder Include="Areas\User\Repositories\" />
<Folder Include="Areas\User\Views\Shared\" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save