The next generation of the Teknik Services. Written in ASP.NET. Fork for blog tags.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UserHelper.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data.Entity;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Web;
  11. using System.Web.Security;
  12. using Teknik.Areas.Blog.Models;
  13. using Teknik.Areas.Shortener.Models;
  14. using Teknik.Areas.Users.Models;
  15. using Teknik.Configuration;
  16. using Teknik.Helpers;
  17. using Teknik.Models;
  18. namespace Teknik.Areas.Users.Utility
  19. {
  20. public static class UserHelper
  21. {
  22. public static List<string> GetReservedUsernames(Config config)
  23. {
  24. List<string> foundNames = new List<string>();
  25. if (config != null)
  26. {
  27. string path = config.UserConfig.ReservedUsernameDefinitionFile;
  28. if (File.Exists(path))
  29. {
  30. string[] names = File.ReadAllLines(path);
  31. foundNames = names.ToList();
  32. }
  33. }
  34. return foundNames;
  35. }
  36. #region User Management
  37. public static User GetUser(TeknikEntities db, string username)
  38. {
  39. User user = db.Users.Where(b => b.Username == username).FirstOrDefault();
  40. if (user != null)
  41. {
  42. user.UserSettings = db.UserSettings.Find(user.UserId);
  43. user.BlogSettings = db.BlogSettings.Find(user.UserId);
  44. user.UploadSettings = db.UploadSettings.Find(user.UserId);
  45. }
  46. return user;
  47. }
  48. public static bool UserExists(TeknikEntities db, string username)
  49. {
  50. User user = GetUser(db, username);
  51. if (user != null)
  52. {
  53. return true;
  54. }
  55. return false;
  56. }
  57. public static bool ValidUsername(TeknikEntities db, Config config, string username)
  58. {
  59. bool isValid = true;
  60. // Load reserved usernames
  61. List<string> reserved = GetReservedUsernames(config);
  62. if (reserved.Exists(u => u.ToLower() == username.ToLower()))
  63. isValid = false;
  64. return isValid;
  65. }
  66. public static bool UsernameAvailable(TeknikEntities db, Config config, string username)
  67. {
  68. bool isAvailable = true;
  69. isAvailable &= ValidUsername(db, config, username);
  70. isAvailable &= !UserExists(db, username);
  71. isAvailable &= !UserEmailExists(config, GetUserEmailAddress(config, username));
  72. isAvailable &= !UserGitExists(config, username);
  73. return isAvailable;
  74. }
  75. public static DateTime GetLastActivity(TeknikEntities db, Config config, User user)
  76. {
  77. try
  78. {
  79. DateTime lastActive = new DateTime(1900, 1, 1);
  80. DateTime emailLastActive = UserEmailLastActive(config, GetUserEmailAddress(config, user.Username));
  81. if (lastActive < emailLastActive)
  82. lastActive = emailLastActive;
  83. DateTime gitLastActive = UserGitLastActive(config, user.Username);
  84. if (lastActive < gitLastActive)
  85. lastActive = gitLastActive;
  86. if (lastActive < user.LastSeen)
  87. lastActive = user.LastSeen;
  88. return lastActive;
  89. }
  90. catch (Exception ex)
  91. {
  92. throw new Exception("Unable to determine last activity.", ex);
  93. }
  94. }
  95. public static void AddUser(TeknikEntities db, Config config, User user, string password)
  96. {
  97. try
  98. {
  99. // Create an Email Account
  100. AddUserEmail(config, GetUserEmailAddress(config, user.Username), password);
  101. // Create a Git Account
  102. AddUserGit(config, user.Username, password);
  103. // Add User
  104. user.HashedPassword = SHA384.Hash(user.Username, password);
  105. db.Users.Add(user);
  106. db.SaveChanges();
  107. // Generate blog for the user
  108. var newBlog = db.Blogs.Create();
  109. newBlog.UserId = user.UserId;
  110. db.Blogs.Add(newBlog);
  111. db.SaveChanges();
  112. }
  113. catch (Exception ex)
  114. {
  115. throw new Exception("Unable to create user.", ex);
  116. }
  117. }
  118. public static void EditUser(TeknikEntities db, Config config, User user, bool changePass, string password)
  119. {
  120. try
  121. {
  122. // Changing Password?
  123. if (changePass)
  124. {
  125. // Change email password
  126. EditUserEmailPassword(config, GetUserEmailAddress(config, user.Username), password);
  127. // Update Git password
  128. EditUserGitPassword(config, user.Username, password);
  129. // Update User password
  130. user.HashedPassword = SHA384.Hash(user.Username, password);
  131. }
  132. db.Entry(user).State = EntityState.Modified;
  133. db.SaveChanges();
  134. }
  135. catch (Exception ex)
  136. {
  137. throw new Exception("Unable to edit user.", ex);
  138. }
  139. }
  140. public static void DeleteUser(TeknikEntities db, Config config, User user)
  141. {
  142. try
  143. {
  144. // Delete Email Account
  145. if (UserEmailExists(config, GetUserEmailAddress(config, user.Username)))
  146. DeleteUserEmail(config, GetUserEmailAddress(config, user.Username));
  147. // Delete Git Account
  148. if (UserGitExists(config, user.Username))
  149. DeleteUserGit(config, user.Username);
  150. // Update uploads
  151. List<Upload.Models.Upload> uploads = db.Uploads.Include("User").Where(u => u.User.Username == user.Username).ToList();
  152. if (uploads != null)
  153. {
  154. foreach (Upload.Models.Upload upload in uploads)
  155. {
  156. upload.UserId = null;
  157. db.Entry(upload).State = EntityState.Modified;
  158. }
  159. }
  160. // Update pastes
  161. List<Paste.Models.Paste> pastes = db.Pastes.Include("User").Where(u => u.User.Username == user.Username).ToList();
  162. if (pastes != null)
  163. {
  164. foreach (Paste.Models.Paste paste in pastes)
  165. {
  166. paste.UserId = null;
  167. db.Entry(paste).State = EntityState.Modified;
  168. }
  169. }
  170. // Update shortened urls
  171. List<ShortenedUrl> shortUrls = db.ShortenedUrls.Include("User").Where(u => u.User.Username == user.Username).ToList();
  172. if (shortUrls != null)
  173. {
  174. foreach (ShortenedUrl shortUrl in shortUrls)
  175. {
  176. shortUrl.UserId = null;
  177. db.Entry(shortUrl).State = EntityState.Modified;
  178. }
  179. }
  180. // Delete Blogs
  181. Blog.Models.Blog blog = db.Blogs.Include("BlogPosts").Include("BlogPosts.Comments").Include("User").Where(u => u.User.Username == user.Username).FirstOrDefault();
  182. if (blog != null)
  183. {
  184. db.Blogs.Remove(blog);
  185. }
  186. // Delete post comments
  187. List<BlogPostComment> postComments = db.BlogComments.Include("User").Where(u => u.User.Username == user.Username).ToList();
  188. if (postComments != null)
  189. {
  190. foreach (BlogPostComment postComment in postComments)
  191. {
  192. db.BlogComments.Remove(postComment);
  193. }
  194. }
  195. // Delete podcast comments
  196. List<Podcast.Models.PodcastComment> podComments = db.PodcastComments.Include("User").Where(u => u.User.Username == user.Username).ToList();
  197. if (podComments != null)
  198. {
  199. foreach (Podcast.Models.PodcastComment podComment in podComments)
  200. {
  201. db.PodcastComments.Remove(podComment);
  202. }
  203. }
  204. // Delete User
  205. db.Users.Remove(user);
  206. db.SaveChanges();
  207. }
  208. catch (Exception ex)
  209. {
  210. throw new Exception("Unable to delete user.", ex);
  211. }
  212. }
  213. #endregion
  214. #region Email Management
  215. public static string GetUserEmailAddress(Config config, string username)
  216. {
  217. return string.Format("{0}@{1}", username, config.EmailConfig.Domain);
  218. }
  219. public static bool UserEmailExists(Config config, string email)
  220. {
  221. // If Email Server is enabled
  222. if (config.EmailConfig.Enabled)
  223. {
  224. // Connect to hmailserver COM
  225. var app = new hMailServer.Application();
  226. app.Connect();
  227. app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
  228. try
  229. {
  230. var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
  231. var account = domain.Accounts.ItemByAddress[email];
  232. // We didn't error out, so the email exists
  233. return true;
  234. }
  235. catch { }
  236. }
  237. return false;
  238. }
  239. public static DateTime UserEmailLastActive(Config config, string email)
  240. {
  241. DateTime lastActive = new DateTime(1900, 1, 1);
  242. if (config.EmailConfig.Enabled)
  243. {
  244. var app = new hMailServer.Application();
  245. app.Connect();
  246. app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
  247. try
  248. {
  249. var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
  250. var account = domain.Accounts.ItemByAddress[email];
  251. DateTime lastEmail = (DateTime)account.LastLogonTime;
  252. if (lastActive < lastEmail)
  253. lastActive = lastEmail;
  254. }
  255. catch { }
  256. }
  257. return lastActive;
  258. }
  259. public static void AddUserEmail(Config config, string email, string password)
  260. {
  261. try
  262. {
  263. // If Email Server is enabled
  264. if (config.EmailConfig.Enabled)
  265. {
  266. // Connect to hmailserver COM
  267. var app = new hMailServer.Application();
  268. app.Connect();
  269. app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
  270. var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
  271. var newAccount = domain.Accounts.Add();
  272. newAccount.Address = email;
  273. newAccount.Password = password;
  274. newAccount.Active = true;
  275. newAccount.MaxSize = config.EmailConfig.MaxSize;
  276. newAccount.Save();
  277. }
  278. }
  279. catch (Exception ex)
  280. {
  281. throw new Exception("Unable to add email.", ex);
  282. }
  283. }
  284. public static void EditUserEmailPassword(Config config, string email, string password)
  285. {
  286. try
  287. {
  288. // If Email Server is enabled
  289. if (config.EmailConfig.Enabled)
  290. {
  291. var app = new hMailServer.Application();
  292. app.Connect();
  293. app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
  294. var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
  295. var account = domain.Accounts.ItemByAddress[email];
  296. account.Password = password;
  297. account.Save();
  298. }
  299. }
  300. catch (Exception ex)
  301. {
  302. throw new Exception("Unable to edit email account password.", ex);
  303. }
  304. }
  305. public static void DeleteUserEmail(Config config, string email)
  306. {
  307. try
  308. {
  309. // If Email Server is enabled
  310. if (config.EmailConfig.Enabled)
  311. {
  312. var app = new hMailServer.Application();
  313. app.Connect();
  314. app.Authenticate(config.EmailConfig.Username, config.EmailConfig.Password);
  315. var domain = app.Domains.ItemByName[config.EmailConfig.Domain];
  316. var account = domain.Accounts.ItemByAddress[email];
  317. if (account != null)
  318. {
  319. account.Delete();
  320. }
  321. }
  322. }
  323. catch (Exception ex)
  324. {
  325. throw new Exception("Unable to delete email account.", ex);
  326. }
  327. }
  328. #endregion
  329. #region Git Management
  330. public static bool UserGitExists(Config config, string username)
  331. {
  332. if (config.GitConfig.Enabled)
  333. {
  334. try
  335. {
  336. Uri baseUri = new Uri(config.GitConfig.Host);
  337. Uri finalUri = new Uri(baseUri, "api/v1/users/" + username + "?token=" + config.GitConfig.AccessToken);
  338. WebRequest request = WebRequest.Create(finalUri);
  339. request.Method = "GET";
  340. HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  341. if (response.StatusCode == HttpStatusCode.OK)
  342. {
  343. return true;
  344. }
  345. }
  346. catch { }
  347. }
  348. return false;
  349. }
  350. public static DateTime UserGitLastActive(Config config, string username)
  351. {
  352. DateTime lastActive = new DateTime(1900, 1, 1);
  353. if (config.GitConfig.Enabled)
  354. {
  355. string email = GetUserEmailAddress(config, username);
  356. // We need to check the actual git database
  357. MysqlDatabase mySQL = new MysqlDatabase(config.GitConfig.Database);
  358. string sql = @"SELECT
  359. CASE
  360. WHEN MAX(gogs.action.created) >= MAX(gogs.user.updated) THEN MAX(gogs.action.created)
  361. WHEN MAX(gogs.user.updated) >= MAX(gogs.action.created) THEN MAX(gogs.user.updated)
  362. ELSE MAX(gogs.user.updated)
  363. END AS LastUpdate
  364. FROM gogs.user
  365. LEFT JOIN gogs.action ON gogs.user.id = gogs.action.act_user_id
  366. WHERE gogs.user.login_name = {0}";
  367. var results = mySQL.Query(sql, new object[] { email });
  368. if (results != null && results.Any())
  369. {
  370. var result = results.First();
  371. DateTime tmpLast = lastActive;
  372. DateTime.TryParse(result["LastUpdate"].ToString(), out tmpLast);
  373. if (lastActive < tmpLast)
  374. lastActive = tmpLast;
  375. }
  376. }
  377. return lastActive;
  378. }
  379. public static void AddUserGit(Config config, string username, string password)
  380. {
  381. try
  382. {
  383. // If Git is enabled
  384. if (config.GitConfig.Enabled)
  385. {
  386. string email = GetUserEmailAddress(config, username);
  387. // Add gogs user
  388. using (var client = new WebClient())
  389. {
  390. var obj = new { source_id = config.GitConfig.SourceId, username = username, email = email, login_name = email, password = password };
  391. string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
  392. client.Headers[HttpRequestHeader.ContentType] = "application/json";
  393. Uri baseUri = new Uri(config.GitConfig.Host);
  394. Uri finalUri = new Uri(baseUri, "api/v1/admin/users?token=" + config.GitConfig.AccessToken);
  395. string result = client.UploadString(finalUri, "POST", json);
  396. }
  397. }
  398. }
  399. catch (Exception ex)
  400. {
  401. throw new Exception("Unable to add git account.", ex);
  402. }
  403. }
  404. public static void EditUserGitPassword(Config config, string username, string password)
  405. {
  406. try
  407. {
  408. // If Git is enabled
  409. if (config.GitConfig.Enabled)
  410. {
  411. string email = GetUserEmailAddress(config, username);
  412. using (var client = new WebClient())
  413. {
  414. var obj = new { source_id = config.GitConfig.SourceId, email = email, password = password };
  415. string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
  416. client.Headers[HttpRequestHeader.ContentType] = "application/json";
  417. Uri baseUri = new Uri(config.GitConfig.Host);
  418. Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + username + "?token=" + config.GitConfig.AccessToken);
  419. string result = client.UploadString(finalUri, "PATCH", json);
  420. }
  421. }
  422. }
  423. catch (Exception ex)
  424. {
  425. throw new Exception("Unable to edit git account password.", ex);
  426. }
  427. }
  428. public static void DeleteUserGit(Config config, string username)
  429. {
  430. try
  431. {
  432. // If Git is enabled
  433. if (config.GitConfig.Enabled)
  434. {
  435. try
  436. {
  437. Uri baseUri = new Uri(config.GitConfig.Host);
  438. Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + username + "?token=" + config.GitConfig.AccessToken);
  439. WebRequest request = WebRequest.Create(finalUri);
  440. request.Method = "DELETE";
  441. HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  442. if (response.StatusCode != HttpStatusCode.NotFound && response.StatusCode != HttpStatusCode.OK)
  443. {
  444. throw new Exception("Unable to delete git account. Response Code: " + response.StatusCode);
  445. }
  446. }
  447. catch (HttpException htex)
  448. {
  449. if (htex.GetHttpCode() != 404)
  450. throw new Exception("Unable to delete git account. Http Exception: " + htex.Message);
  451. }
  452. catch (Exception ex)
  453. {
  454. // This error signifies the user doesn't exist, so we can continue deleting
  455. if (ex.Message != "The remote server returned an error: (404) Not Found.")
  456. {
  457. throw new Exception("Unable to delete git account. Exception: " + ex.Message);
  458. }
  459. }
  460. }
  461. }
  462. catch (Exception ex)
  463. {
  464. throw new Exception("Unable to delete git account.", ex);
  465. }
  466. }
  467. #endregion
  468. public static HttpCookie CreateAuthCookie(string username, bool remember, string domain, bool local)
  469. {
  470. Config config = Config.Load();
  471. HttpCookie authcookie = FormsAuthentication.GetAuthCookie(username, remember);
  472. authcookie.Name = "TeknikAuth";
  473. authcookie.HttpOnly = true;
  474. authcookie.Secure = true;
  475. // Set domain dependent on where it's being ran from
  476. if (local) // localhost
  477. {
  478. authcookie.Domain = null;
  479. }
  480. else if (config.DevEnvironment) // dev.example.com
  481. {
  482. authcookie.Domain = string.Format("dev.{0}", domain);
  483. }
  484. else // A production instance
  485. {
  486. authcookie.Domain = string.Format(".{0}", domain);
  487. }
  488. return authcookie;
  489. }
  490. }
  491. }