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 23KB

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