The next generation of the Teknik Services. Written in ASP.NET. https://www.teknik.io/
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 50KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Mail;
  7. using System.Runtime.InteropServices;
  8. using System.Security.Cryptography;
  9. using System.Text;
  10. using System.Text.RegularExpressions;
  11. using System.Threading.Tasks;
  12. using System.Web;
  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.Utilities;
  18. using Teknik.Models;
  19. using Teknik.Utilities.Cryptography;
  20. using MD5 = Teknik.Utilities.Cryptography.MD5;
  21. using SHA256 = Teknik.Utilities.Cryptography.SHA256;
  22. using SHA384 = Teknik.Utilities.Cryptography.SHA384;
  23. using Teknik.Data;
  24. using Microsoft.EntityFrameworkCore;
  25. using Microsoft.AspNetCore.Http;
  26. using System.Security.Claims;
  27. using Microsoft.AspNetCore.Authentication.Cookies;
  28. using Teknik.MailService;
  29. using Teknik.GitService;
  30. namespace Teknik.Areas.Users.Utility
  31. {
  32. public static class UserHelper
  33. {
  34. #region Account Management
  35. public static List<string> GetReservedUsernames(Config config)
  36. {
  37. List<string> foundNames = new List<string>();
  38. if (config != null)
  39. {
  40. string path = config.UserConfig.ReservedUsernameDefinitionFile;
  41. if (File.Exists(path))
  42. {
  43. string[] names = File.ReadAllLines(path);
  44. foundNames = names.ToList();
  45. }
  46. }
  47. return foundNames;
  48. }
  49. public static bool UsernameReserved(Config config, string username)
  50. {
  51. // Load reserved usernames
  52. List<string> reserved = GetReservedUsernames(config);
  53. return (reserved.Exists(u => u.ToLower() == username.ToLower()));
  54. }
  55. public static bool ValidUsername(Config config, string username)
  56. {
  57. bool isValid = true;
  58. // Must be something there
  59. isValid &= !string.IsNullOrEmpty(username);
  60. // Is the format correct?
  61. Regex reg = new Regex(config.UserConfig.UsernameFilter);
  62. isValid &= reg.IsMatch(username);
  63. // Meets the min length?
  64. isValid &= (username.Length >= config.UserConfig.MinUsernameLength);
  65. // Meets the max length?
  66. isValid &= (username.Length <= config.UserConfig.MaxUsernameLength);
  67. return isValid;
  68. }
  69. public static bool UsernameAvailable(TeknikEntities db, Config config, string username)
  70. {
  71. bool isAvailable = true;
  72. isAvailable &= ValidUsername(config, username);
  73. isAvailable &= !UsernameReserved(config, username);
  74. isAvailable &= !UserExists(db, username);
  75. isAvailable &= !UserEmailExists(config, GetUserEmailAddress(config, username));
  76. isAvailable &= !UserGitExists(config, username);
  77. return isAvailable;
  78. }
  79. public static DateTime GetLastAccountActivity(TeknikEntities db, Config config, User user)
  80. {
  81. try
  82. {
  83. DateTime lastActive = new DateTime(1900, 1, 1);
  84. if (UserEmailExists(config, GetUserEmailAddress(config, user.Username)))
  85. {
  86. DateTime emailLastActive = UserEmailLastActive(config, GetUserEmailAddress(config, user.Username));
  87. if (lastActive < emailLastActive)
  88. lastActive = emailLastActive;
  89. }
  90. if (UserGitExists(config, user.Username))
  91. {
  92. DateTime gitLastActive = UserGitLastActive(config, user.Username);
  93. if (lastActive < gitLastActive)
  94. lastActive = gitLastActive;
  95. }
  96. if (UserExists(db, user.Username))
  97. {
  98. DateTime userLastActive = UserLastActive(db, config, user);
  99. if (lastActive < userLastActive)
  100. lastActive = userLastActive;
  101. }
  102. return lastActive;
  103. }
  104. catch (Exception ex)
  105. {
  106. throw new Exception("Unable to determine last account activity.", ex);
  107. }
  108. }
  109. public static string GeneratePassword(Config config, User user, string password)
  110. {
  111. try
  112. {
  113. string username = user.Username.ToLower();
  114. if (user.Transfers.ToList().Exists(t => t.Type == TransferTypes.CaseSensitivePassword))
  115. {
  116. username = user.Username;
  117. }
  118. byte[] hashBytes = SHA384.Hash(username, password);
  119. string hash = hashBytes.ToHex();
  120. if (user.Transfers.ToList().Exists(t => t.Type == TransferTypes.ASCIIPassword))
  121. {
  122. hash = Encoding.ASCII.GetString(hashBytes);
  123. }
  124. if (user.Transfers.ToList().Exists(t => t.Type == TransferTypes.Sha256Password))
  125. {
  126. hash = SHA256.Hash(password, config.Salt1, config.Salt2);
  127. }
  128. return hash;
  129. }
  130. catch (Exception ex)
  131. {
  132. throw new Exception("Unable to generate password.", ex);
  133. }
  134. }
  135. public static string GenerateAuthToken(TeknikEntities db, string username)
  136. {
  137. try
  138. {
  139. bool validToken = false;
  140. string token = string.Empty;
  141. while (!validToken)
  142. {
  143. username = username.ToLower();
  144. byte[] hashBytes = SHA384.Hash(username, StringHelper.RandomString(24));
  145. token = hashBytes.ToHex();
  146. // Make sure it isn't a duplicate
  147. string hashedToken = SHA256.Hash(token);
  148. if (!db.AuthTokens.Where(t => t.HashedToken == hashedToken).Any())
  149. {
  150. validToken = true;
  151. }
  152. }
  153. return token;
  154. }
  155. catch (Exception ex)
  156. {
  157. throw new Exception("Unable to generate user auth token.", ex);
  158. }
  159. }
  160. public static void AddAccount(TeknikEntities db, Config config, User user, string password)
  161. {
  162. try
  163. {
  164. // Create an Email Account
  165. AddUserEmail(config, GetUserEmailAddress(config, user.Username), password);
  166. // Create a Git Account
  167. AddUserGit(config, user.Username, password);
  168. // Add User
  169. AddUser(db, config, user, password);
  170. }
  171. catch (Exception ex)
  172. {
  173. throw new Exception("Unable to create account.", ex);
  174. }
  175. }
  176. public static void EditAccount(TeknikEntities db, Config config, User user)
  177. {
  178. EditAccount(db, config, user, false, string.Empty);
  179. }
  180. public static void EditAccount(TeknikEntities db, Config config, User user, bool changePass, string password)
  181. {
  182. try
  183. {
  184. // Changing Password?
  185. if (changePass)
  186. {
  187. // Make sure they have a git and email account before resetting their password
  188. string email = GetUserEmailAddress(config, user.Username);
  189. if (config.EmailConfig.Enabled && !UserEmailExists(config, email))
  190. {
  191. AddUserEmail(config, email, password);
  192. }
  193. if (config.GitConfig.Enabled && !UserGitExists(config, user.Username))
  194. {
  195. AddUserGit(config, user.Username, password);
  196. }
  197. // Change email password
  198. EditUserEmailPassword(config, GetUserEmailAddress(config, user.Username), password);
  199. // Update Git password
  200. EditUserGitPassword(config, user.Username, password);
  201. }
  202. // Update User
  203. EditUser(db, config, user, changePass, password);
  204. }
  205. catch (Exception ex)
  206. {
  207. throw new Exception("Unable to edit account.", ex);
  208. }
  209. }
  210. public static void EditAccountType(TeknikEntities db, Config config, string username, AccountType type)
  211. {
  212. try
  213. {
  214. if (!UserExists(db, username))
  215. throw new Exception($"The user provided does not exist: {username}");
  216. // Get the user to edit
  217. User user = GetUser(db, username);
  218. string email = GetUserEmailAddress(config, username);
  219. // Edit the user type
  220. user.AccountType = type;
  221. EditUser(db, config, user);
  222. // Add/Remove account type features depending on the type
  223. switch (type)
  224. {
  225. case AccountType.Basic:
  226. // Set the email size to 1GB
  227. EditUserEmailMaxSize(config, email, config.EmailConfig.MaxSize);
  228. // Set the email max/day to 100
  229. EditUserEmailMaxEmailsPerDay(config, email, 100);
  230. break;
  231. case AccountType.Premium:
  232. // Set the email size to 5GB
  233. EditUserEmailMaxSize(config, email, 5000);
  234. // Set the email max/day to infinite (-1)
  235. EditUserEmailMaxEmailsPerDay(config, email, -1);
  236. break;
  237. }
  238. }
  239. catch (Exception ex)
  240. {
  241. throw new Exception($"Unable to edit the account type [{type}] for: {username}", ex);
  242. }
  243. }
  244. public static void EditAccountStatus(TeknikEntities db, Config config, string username, AccountStatus status)
  245. {
  246. try
  247. {
  248. if (!UserExists(db, username))
  249. throw new Exception($"The user provided does not exist: {username}");
  250. // Get the user to edit
  251. User user = GetUser(db, username);
  252. string email = GetUserEmailAddress(config, username);
  253. // Edit the user type
  254. user.AccountStatus = status;
  255. EditUser(db, config, user);
  256. // Add/Remove account type features depending on the type
  257. switch (status)
  258. {
  259. case AccountStatus.Active:
  260. // Enable Email
  261. EnableUserEmail(config, email);
  262. // Enable Git
  263. EnableUserGit(config, username);
  264. break;
  265. case AccountStatus.Banned:
  266. // Disable Email
  267. DisableUserEmail(config, email);
  268. // Disable Git
  269. DisableUserGit(config, username);
  270. break;
  271. }
  272. }
  273. catch (Exception ex)
  274. {
  275. throw new Exception($"Unable to edit the account status [{status}] for: {username}", ex);
  276. }
  277. }
  278. public static void DeleteAccount(TeknikEntities db, Config config, User user)
  279. {
  280. try
  281. {
  282. // Delete Email Account
  283. if (UserEmailExists(config, GetUserEmailAddress(config, user.Username)))
  284. DeleteUserEmail(config, GetUserEmailAddress(config, user.Username));
  285. // Delete Git Account
  286. if (UserGitExists(config, user.Username))
  287. DeleteUserGit(config, user.Username);
  288. // Delete User Account
  289. DeleteUser(db, config, user);
  290. }
  291. catch (Exception ex)
  292. {
  293. throw new Exception("Unable to delete account.", ex);
  294. }
  295. }
  296. #endregion
  297. #region User Management
  298. public static User GetUser(TeknikEntities db, string username)
  299. {
  300. User user = db.Users
  301. .Include(u => u.UserSettings)
  302. .Include(u => u.SecuritySettings)
  303. .Include(u => u.BlogSettings)
  304. .Include(u => u.UploadSettings)
  305. .Where(b => b.Username == username).FirstOrDefault();
  306. return user;
  307. }
  308. public static User GetUserFromToken(TeknikEntities db, string username, string token)
  309. {
  310. if (token != null && !string.IsNullOrEmpty(username))
  311. {
  312. string hashedToken = SHA256.Hash(token);
  313. return db.Users.FirstOrDefault(u => u.AuthTokens.Select(a => a.HashedToken).Contains(hashedToken) && u.Username == username);
  314. }
  315. return null;
  316. }
  317. public static bool UserExists(TeknikEntities db, string username)
  318. {
  319. User user = GetUser(db, username);
  320. if (user != null)
  321. {
  322. return true;
  323. }
  324. return false;
  325. }
  326. public static DateTime UserLastActive(TeknikEntities db, Config config, User user)
  327. {
  328. try
  329. {
  330. DateTime lastActive = new DateTime(1900, 1, 1);
  331. if (lastActive < user.LastSeen)
  332. lastActive = user.LastSeen;
  333. return lastActive;
  334. }
  335. catch (Exception ex)
  336. {
  337. throw new Exception("Unable to determine last user activity.", ex);
  338. }
  339. }
  340. public static void UpdateTokenLastUsed(TeknikEntities db, string username, string token, DateTime lastUsed)
  341. {
  342. User foundUser = GetUser(db, username);
  343. if (foundUser != null)
  344. {
  345. // Update the user's last seen date
  346. if (foundUser.LastSeen < lastUsed)
  347. {
  348. foundUser.LastSeen = lastUsed;
  349. db.Entry(foundUser).State = EntityState.Modified;
  350. }
  351. string hashedToken = SHA256.Hash(token);
  352. List<AuthToken> tokens = foundUser.AuthTokens.Where(t => t.HashedToken == hashedToken).ToList();
  353. if (tokens != null)
  354. {
  355. foreach (AuthToken foundToken in tokens)
  356. {
  357. foundToken.LastDateUsed = lastUsed;
  358. db.Entry(foundToken).State = EntityState.Modified;
  359. }
  360. }
  361. db.SaveChanges();
  362. }
  363. }
  364. public static bool UserPasswordCorrect(TeknikEntities db, Config config, User user, string password)
  365. {
  366. try
  367. {
  368. string hash = GeneratePassword(config, user, password);
  369. return db.Users.Any(b => b.Username == user.Username && b.HashedPassword == hash);
  370. }
  371. catch (Exception ex)
  372. {
  373. throw new Exception("Unable to determine if password is correct.", ex);
  374. }
  375. }
  376. public static bool UserTokenCorrect(TeknikEntities db, string username, string token)
  377. {
  378. User foundUser = GetUserFromToken(db, username, token);
  379. if (foundUser != null)
  380. {
  381. return true;
  382. }
  383. return false;
  384. }
  385. public static bool UserHasRoles(User user, params string[] roles)
  386. {
  387. bool hasRole = true;
  388. if (user != null)
  389. {
  390. // Check if they have the role specified
  391. if (roles.Any())
  392. {
  393. foreach (string role in roles)
  394. {
  395. if (!string.IsNullOrEmpty(role))
  396. {
  397. if (user.UserRoles.Where(ur => ur.Role.Name == role).Any())
  398. {
  399. // They have the role!
  400. return true;
  401. }
  402. else
  403. {
  404. // They don't have this role, so let's reset the hasRole
  405. hasRole = false;
  406. }
  407. }
  408. else
  409. {
  410. // Only set this if we haven't failed once already
  411. hasRole &= true;
  412. }
  413. }
  414. }
  415. else
  416. {
  417. // No roles to check, so they pass!
  418. return true;
  419. }
  420. }
  421. else
  422. {
  423. hasRole = false;
  424. }
  425. return hasRole;
  426. }
  427. public static void TransferUser(TeknikEntities db, Config config, User user, string password)
  428. {
  429. try
  430. {
  431. List<TransferType> transfers = user.Transfers.ToList();
  432. for (int i = 0; i < transfers.Count; i++)
  433. {
  434. TransferType transfer = transfers[i];
  435. switch (transfer.Type)
  436. {
  437. case TransferTypes.Sha256Password:
  438. case TransferTypes.CaseSensitivePassword:
  439. case TransferTypes.ASCIIPassword:
  440. user.HashedPassword = SHA384.Hash(user.Username.ToLower(), password).ToHex();
  441. break;
  442. default:
  443. break;
  444. }
  445. user.Transfers.Remove(transfer);
  446. }
  447. db.Entry(user).State = EntityState.Modified;
  448. db.SaveChanges();
  449. }
  450. catch (Exception ex)
  451. {
  452. throw new Exception("Unable to transfer user info.", ex);
  453. }
  454. }
  455. public static void AddUser(TeknikEntities db, Config config, User user, string password)
  456. {
  457. try
  458. {
  459. // Add User
  460. user.HashedPassword = GeneratePassword(config, user, password);
  461. db.Users.Add(user);
  462. db.SaveChanges();
  463. // Generate blog for the user
  464. var newBlog = new Blog.Models.Blog();
  465. newBlog.UserId = user.UserId;
  466. db.Blogs.Add(newBlog);
  467. db.SaveChanges();
  468. }
  469. catch (Exception ex)
  470. {
  471. throw new Exception("Unable to create user.", ex);
  472. }
  473. }
  474. public static void EditUser(TeknikEntities db, Config config, User user)
  475. {
  476. EditUser(db, config, user, false, string.Empty);
  477. }
  478. public static void EditUser(TeknikEntities db, Config config, User user, bool changePass, string password)
  479. {
  480. try
  481. {
  482. // Changing Password?
  483. if (changePass)
  484. {
  485. // Update User password
  486. user.HashedPassword = SHA384.Hash(user.Username.ToLower(), password).ToHex();
  487. // Remove any password transfer items for the account
  488. for (int i = 0; i < user.Transfers.Count; i++)
  489. {
  490. TransferType type = user.Transfers.ToList()[i];
  491. if (type.Type == TransferTypes.ASCIIPassword || type.Type == TransferTypes.CaseSensitivePassword || type.Type == TransferTypes.Sha256Password)
  492. {
  493. user.Transfers.Remove(type);
  494. i--;
  495. }
  496. }
  497. }
  498. db.Entry(user).State = EntityState.Modified;
  499. db.SaveChanges();
  500. }
  501. catch (Exception ex)
  502. {
  503. throw new Exception(string.Format("Unable to edit user {0}.", user.Username), ex);
  504. }
  505. }
  506. public static void DeleteUser(TeknikEntities db, Config config, User user)
  507. {
  508. try
  509. {
  510. // Update uploads
  511. List<Upload.Models.Upload> uploads = db.Uploads.Where(u => u.User.Username == user.Username).ToList();
  512. if (uploads.Any())
  513. {
  514. foreach (Upload.Models.Upload upload in uploads)
  515. {
  516. upload.UserId = null;
  517. db.Entry(upload).State = EntityState.Modified;
  518. }
  519. db.SaveChanges();
  520. }
  521. // Update pastes
  522. List<Paste.Models.Paste> pastes = db.Pastes.Where(u => u.User.Username == user.Username).ToList();
  523. if (pastes.Any())
  524. {
  525. foreach (Paste.Models.Paste paste in pastes)
  526. {
  527. paste.UserId = null;
  528. db.Entry(paste).State = EntityState.Modified;
  529. }
  530. db.SaveChanges();
  531. }
  532. // Update shortened urls
  533. List<ShortenedUrl> shortUrls = db.ShortenedUrls.Where(u => u.User.Username == user.Username).ToList();
  534. if (shortUrls.Any())
  535. {
  536. foreach (ShortenedUrl shortUrl in shortUrls)
  537. {
  538. shortUrl.UserId = null;
  539. db.Entry(shortUrl).State = EntityState.Modified;
  540. }
  541. db.SaveChanges();
  542. }
  543. // Update vaults
  544. List<Vault.Models.Vault> vaults = db.Vaults.Where(u => u.User.Username == user.Username).ToList();
  545. if (vaults.Any())
  546. {
  547. foreach (Vault.Models.Vault vault in vaults)
  548. {
  549. vault.UserId = null;
  550. db.Entry(vault).State = EntityState.Modified;
  551. }
  552. db.SaveChanges();
  553. }
  554. // Delete Blogs
  555. Blog.Models.Blog blog = db.Blogs.Where(u => u.User.Username == user.Username).FirstOrDefault();
  556. if (blog != null)
  557. {
  558. db.Blogs.Remove(blog);
  559. db.SaveChanges();
  560. }
  561. // Delete post comments
  562. List<BlogPostComment> postComments = db.BlogPostComments.Where(u => u.User.Username == user.Username).ToList();
  563. if (postComments.Any())
  564. {
  565. foreach (BlogPostComment postComment in postComments)
  566. {
  567. db.BlogPostComments.Remove(postComment);
  568. }
  569. db.SaveChanges();
  570. }
  571. // Delete podcast comments
  572. List<Podcast.Models.PodcastComment> podComments = db.PodcastComments.Where(u => u.User.Username == user.Username).ToList();
  573. if (podComments.Any())
  574. {
  575. foreach (Podcast.Models.PodcastComment podComment in podComments)
  576. {
  577. db.PodcastComments.Remove(podComment);
  578. }
  579. db.SaveChanges();
  580. }
  581. // Delete Recovery Email Verifications
  582. List<RecoveryEmailVerification> verCodes = db.RecoveryEmailVerifications.Where(r => r.User.Username == user.Username).ToList();
  583. if (verCodes.Any())
  584. {
  585. foreach (RecoveryEmailVerification verCode in verCodes)
  586. {
  587. db.RecoveryEmailVerifications.Remove(verCode);
  588. }
  589. db.SaveChanges();
  590. }
  591. // Delete Password Reset Verifications
  592. List<ResetPasswordVerification> verPass = db.ResetPasswordVerifications.Where(r => r.User.Username == user.Username).ToList();
  593. if (verPass.Any())
  594. {
  595. foreach (ResetPasswordVerification ver in verPass)
  596. {
  597. db.ResetPasswordVerifications.Remove(ver);
  598. }
  599. db.SaveChanges();
  600. }
  601. // Delete Owned Invite Codes
  602. List<InviteCode> ownedCodes = db.InviteCodes.Where(i => i.Owner.Username == user.Username).ToList();
  603. if (ownedCodes.Any())
  604. {
  605. foreach (InviteCode code in ownedCodes)
  606. {
  607. db.InviteCodes.Remove(code);
  608. }
  609. db.SaveChanges();
  610. }
  611. // Delete Claimed Invite Code
  612. List<InviteCode> claimedCodes = db.InviteCodes.Where(i => i.ClaimedUser.Username == user.Username).ToList();
  613. if (claimedCodes.Any())
  614. {
  615. foreach (InviteCode code in claimedCodes)
  616. {
  617. db.InviteCodes.Remove(code);
  618. }
  619. db.SaveChanges();
  620. }
  621. // Delete Auth Tokens
  622. List<AuthToken> authTokens = db.AuthTokens.Where(t => t.User.UserId == user.UserId).ToList();
  623. if (authTokens.Any())
  624. {
  625. foreach (AuthToken authToken in authTokens)
  626. {
  627. db.AuthTokens.Remove(authToken);
  628. }
  629. db.SaveChanges();
  630. }
  631. // Delete User
  632. db.Users.Remove(user);
  633. db.SaveChanges();
  634. }
  635. catch (Exception ex)
  636. {
  637. throw new Exception(string.Format("Unable to delete user {0}.", user.Username), ex);
  638. }
  639. }
  640. public static string CreateRecoveryEmailVerification(TeknikEntities db, Config config, User user)
  641. {
  642. // Check to see if there already is a verification code for the user
  643. List<RecoveryEmailVerification> verCodes = db.RecoveryEmailVerifications.Where(r => r.User.Username == user.Username).ToList();
  644. if (verCodes != null && verCodes.Any())
  645. {
  646. foreach (RecoveryEmailVerification verCode in verCodes)
  647. {
  648. db.RecoveryEmailVerifications.Remove(verCode);
  649. }
  650. }
  651. // Create a new verification code and add it
  652. string verifyCode = StringHelper.RandomString(24);
  653. RecoveryEmailVerification ver = new RecoveryEmailVerification();
  654. ver.UserId = user.UserId;
  655. ver.Code = verifyCode;
  656. ver.DateCreated = DateTime.Now;
  657. db.RecoveryEmailVerifications.Add(ver);
  658. db.SaveChanges();
  659. return verifyCode;
  660. }
  661. public static void SendRecoveryEmailVerification(Config config, string username, string email, string resetUrl, string verifyUrl)
  662. {
  663. SmtpClient client = new SmtpClient();
  664. client.Host = config.ContactConfig.EmailAccount.Host;
  665. client.Port = config.ContactConfig.EmailAccount.Port;
  666. client.EnableSsl = config.ContactConfig.EmailAccount.SSL;
  667. client.DeliveryMethod = SmtpDeliveryMethod.Network;
  668. client.UseDefaultCredentials = true;
  669. client.Credentials = new NetworkCredential(config.ContactConfig.EmailAccount.Username, config.ContactConfig.EmailAccount.Password);
  670. client.Timeout = 5000;
  671. MailMessage mail = new MailMessage(new MailAddress(config.ContactConfig.EmailAccount.EmailAddress, "Teknik"), new MailAddress(email, username));
  672. mail.Subject = "Recovery Email Validation";
  673. mail.Body = string.Format(@"Hello {0},
  674. You are recieving this email because you have specified this email address as your recovery email. In the event that you forget your password, you can visit {1} and request a temporary password reset key be sent to this email. You will then be able to reset and choose a new password.
  675. In order to verify that you own this email, please click the following link or paste it into your browser: {2}
  676. If you recieved this email and you did not sign up for an account, please email us at {3} and ignore the verification link.
  677. - Teknik", username, resetUrl, verifyUrl, config.SupportEmail);
  678. mail.BodyEncoding = UTF8Encoding.UTF8;
  679. mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;
  680. client.Send(mail);
  681. }
  682. public static bool VerifyRecoveryEmail(TeknikEntities db, Config config, string username, string code)
  683. {
  684. User user = GetUser(db, username);
  685. RecoveryEmailVerification verCode = db.RecoveryEmailVerifications.Where(r => r.User.Username == username && r.Code == code).FirstOrDefault();
  686. if (verCode != null)
  687. {
  688. // We have a match, so clear out the verifications for that user
  689. List<RecoveryEmailVerification> verCodes = db.RecoveryEmailVerifications.Where(r => r.User.Username == username).ToList();
  690. if (verCodes != null && verCodes.Any())
  691. {
  692. foreach (RecoveryEmailVerification ver in verCodes)
  693. {
  694. db.RecoveryEmailVerifications.Remove(ver);
  695. }
  696. }
  697. // Update the user
  698. user.SecuritySettings.RecoveryVerified = true;
  699. db.Entry(user).State = EntityState.Modified;
  700. db.SaveChanges();
  701. return true;
  702. }
  703. return false;
  704. }
  705. public static string CreateResetPasswordVerification(TeknikEntities db, Config config, User user)
  706. {
  707. // Check to see if there already is a verification code for the user
  708. List<ResetPasswordVerification> verCodes = db.ResetPasswordVerifications.Where(r => r.User.Username == user.Username).ToList();
  709. if (verCodes != null && verCodes.Any())
  710. {
  711. foreach (ResetPasswordVerification verCode in verCodes)
  712. {
  713. db.ResetPasswordVerifications.Remove(verCode);
  714. }
  715. }
  716. // Create a new verification code and add it
  717. string verifyCode = StringHelper.RandomString(24);
  718. ResetPasswordVerification ver = new ResetPasswordVerification();
  719. ver.UserId = user.UserId;
  720. ver.Code = verifyCode;
  721. ver.DateCreated = DateTime.Now;
  722. db.ResetPasswordVerifications.Add(ver);
  723. db.SaveChanges();
  724. return verifyCode;
  725. }
  726. public static void SendResetPasswordVerification(Config config, string username, string email, string resetUrl)
  727. {
  728. SmtpClient client = new SmtpClient();
  729. client.Host = config.ContactConfig.EmailAccount.Host;
  730. client.Port = config.ContactConfig.EmailAccount.Port;
  731. client.EnableSsl = config.ContactConfig.EmailAccount.SSL;
  732. client.DeliveryMethod = SmtpDeliveryMethod.Network;
  733. client.UseDefaultCredentials = true;
  734. client.Credentials = new NetworkCredential(config.ContactConfig.EmailAccount.Username, config.ContactConfig.EmailAccount.Password);
  735. client.Timeout = 5000;
  736. MailMessage mail = new MailMessage(new MailAddress(config.ContactConfig.EmailAccount.EmailAddress, "Teknik"), new MailAddress(email, username));
  737. mail.Subject = "Password Reset Request";
  738. mail.Body = string.Format(@"Hello {0},
  739. You are recieving this email because either you or someone has requested a password reset for your account and this email was specified as the recovery email.
  740. To proceed in resetting your password, please click the following link or paste it into your browser: {1}
  741. If you recieved this email and you did not reset your password, you can ignore this email and email us at {2} to prevent it occuring again.
  742. - Teknik", username, resetUrl, config.SupportEmail);
  743. mail.BodyEncoding = UTF8Encoding.UTF8;
  744. mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;
  745. client.Send(mail);
  746. }
  747. public static bool VerifyResetPassword(TeknikEntities db, Config config, string username, string code)
  748. {
  749. User user = GetUser(db, username);
  750. ResetPasswordVerification verCode = db.ResetPasswordVerifications.Where(r => r.User.Username == username && r.Code == code).FirstOrDefault();
  751. if (verCode != null)
  752. {
  753. // We have a match, so clear out the verifications for that user
  754. List<ResetPasswordVerification> verCodes = db.ResetPasswordVerifications.Where(r => r.User.Username == username).ToList();
  755. if (verCodes != null && verCodes.Any())
  756. {
  757. foreach (ResetPasswordVerification ver in verCodes)
  758. {
  759. db.ResetPasswordVerifications.Remove(ver);
  760. }
  761. }
  762. db.SaveChanges();
  763. return true;
  764. }
  765. return false;
  766. }
  767. #endregion
  768. #region Email Management
  769. public static string GetUserEmailAddress(Config config, string username)
  770. {
  771. return string.Format("{0}@{1}", username, config.EmailConfig.Domain);
  772. }
  773. public static IMailService CreateMailService(Config config)
  774. {
  775. return new HMailService(
  776. config.EmailConfig.Username,
  777. config.EmailConfig.Password,
  778. config.EmailConfig.Domain,
  779. config.EmailConfig.CounterDatabase.Server,
  780. config.EmailConfig.CounterDatabase.Database,
  781. config.EmailConfig.CounterDatabase.Username,
  782. config.EmailConfig.CounterDatabase.Password,
  783. config.EmailConfig.CounterDatabase.Port
  784. );
  785. }
  786. public static bool UserEmailExists(Config config, string email)
  787. {
  788. // If Email Server is enabled
  789. if (config.EmailConfig.Enabled)
  790. {
  791. var svc = CreateMailService(config);
  792. return svc.AccountExists(email);
  793. }
  794. return false;
  795. }
  796. public static DateTime UserEmailLastActive(Config config, string email)
  797. {
  798. DateTime lastActive = new DateTime(1900, 1, 1);
  799. if (config.EmailConfig.Enabled)
  800. {
  801. var svc = CreateMailService(config);
  802. var lastEmail = svc.LastActive(email);
  803. if (lastActive < lastEmail)
  804. lastActive = lastEmail;
  805. }
  806. return lastActive;
  807. }
  808. public static void AddUserEmail(Config config, string email, string password)
  809. {
  810. try
  811. {
  812. // If Email Server is enabled
  813. if (config.EmailConfig.Enabled)
  814. {
  815. var svc = CreateMailService(config);
  816. svc.CreateAccount(email, password, config.EmailConfig.MaxSize);
  817. }
  818. }
  819. catch (Exception ex)
  820. {
  821. throw new Exception("Unable to add email.", ex);
  822. }
  823. }
  824. public static void EnableUserEmail(Config config, string email)
  825. {
  826. try
  827. {
  828. // If Email Server is enabled
  829. if (config.EmailConfig.Enabled)
  830. {
  831. var svc = CreateMailService(config);
  832. svc.EnableAccount(email);
  833. }
  834. }
  835. catch (Exception ex)
  836. {
  837. throw new Exception("Unable to enable email account.", ex);
  838. }
  839. }
  840. public static void DisableUserEmail(Config config, string email)
  841. {
  842. try
  843. {
  844. // If Email Server is enabled
  845. if (config.EmailConfig.Enabled)
  846. {
  847. var svc = CreateMailService(config);
  848. svc.DisableAccount(email);
  849. }
  850. }
  851. catch (Exception ex)
  852. {
  853. throw new Exception("Unable to disable email account.", ex);
  854. }
  855. }
  856. public static void EditUserEmailPassword(Config config, string email, string password)
  857. {
  858. try
  859. {
  860. // If Email Server is enabled
  861. if (config.EmailConfig.Enabled)
  862. {
  863. var svc = CreateMailService(config);
  864. svc.EditPassword(email, password);
  865. }
  866. }
  867. catch (Exception ex)
  868. {
  869. throw new Exception("Unable to edit email account password.", ex);
  870. }
  871. }
  872. public static void EditUserEmailMaxSize(Config config, string email, int size)
  873. {
  874. try
  875. {
  876. // If Email Server is enabled
  877. if (config.EmailConfig.Enabled)
  878. {
  879. var svc = CreateMailService(config);
  880. svc.EditMaxSize(email, size);
  881. }
  882. }
  883. catch (Exception ex)
  884. {
  885. throw new Exception("Unable to edit email account mailbox size.", ex);
  886. }
  887. }
  888. public static void EditUserEmailMaxEmailsPerDay(Config config, string email, int maxPerDay)
  889. {
  890. try
  891. {
  892. // If Email Server is enabled
  893. if (config.EmailConfig.Enabled)
  894. {
  895. var svc = CreateMailService(config);
  896. svc.EditMaxEmailsPerDay(email, maxPerDay);
  897. }
  898. }
  899. catch (Exception ex)
  900. {
  901. throw new Exception("Unable to edit email account mailbox size.", ex);
  902. }
  903. }
  904. public static void DeleteUserEmail(Config config, string email)
  905. {
  906. try
  907. {
  908. // If Email Server is enabled
  909. if (config.EmailConfig.Enabled)
  910. {
  911. var svc = CreateMailService(config);
  912. svc.DeleteAccount(email);
  913. }
  914. }
  915. catch (Exception ex)
  916. {
  917. throw new Exception("Unable to delete email account.", ex);
  918. }
  919. }
  920. #endregion
  921. #region Git Management
  922. public static IGitService CreateGitService(Config config)
  923. {
  924. return new GiteaService(
  925. config.GitConfig.SourceId,
  926. config.GitConfig.Host,
  927. config.GitConfig.AccessToken,
  928. config.GitConfig.Database.Server,
  929. config.GitConfig.Database.Database,
  930. config.GitConfig.Database.Username,
  931. config.GitConfig.Database.Password,
  932. config.GitConfig.Database.Port
  933. );
  934. }
  935. public static bool UserGitExists(Config config, string username)
  936. {
  937. if (config.GitConfig.Enabled)
  938. {
  939. try
  940. {
  941. var svc = CreateGitService(config);
  942. return svc.AccountExists(username);
  943. }
  944. catch { }
  945. }
  946. return false;
  947. }
  948. public static DateTime UserGitLastActive(Config config, string username)
  949. {
  950. DateTime lastActive = new DateTime(1900, 1, 1);
  951. if (config.GitConfig.Enabled)
  952. {
  953. // Git user exists?
  954. if (!UserGitExists(config, username))
  955. {
  956. throw new Exception($"Git User '{username}' does not exist.");
  957. }
  958. string email = GetUserEmailAddress(config, username);
  959. var svc = CreateGitService(config);
  960. DateTime tmpLast = svc.LastActive(email);
  961. if (lastActive < tmpLast)
  962. lastActive = tmpLast;
  963. }
  964. return lastActive;
  965. }
  966. public static void AddUserGit(Config config, string username, string password)
  967. {
  968. try
  969. {
  970. // If Git is enabled
  971. if (config.GitConfig.Enabled)
  972. {
  973. string email = GetUserEmailAddress(config, username);
  974. var svc = CreateGitService(config);
  975. svc.CreateAccount(username, email, password);
  976. }
  977. }
  978. catch (Exception ex)
  979. {
  980. throw new Exception("Unable to add git account.", ex);
  981. }
  982. }
  983. public static void EditUserGitPassword(Config config, string username, string password)
  984. {
  985. try
  986. {
  987. // If Git is enabled
  988. if (config.GitConfig.Enabled)
  989. {
  990. // Git user exists?
  991. if (!UserGitExists(config, username))
  992. {
  993. throw new Exception($"Git User '{username}' does not exist.");
  994. }
  995. string email = GetUserEmailAddress(config, username);
  996. var svc = CreateGitService(config);
  997. svc.EditPassword(username, email, password);
  998. }
  999. }
  1000. catch (Exception ex)
  1001. {
  1002. throw new Exception("Unable to edit git account password.", ex);
  1003. }
  1004. }
  1005. public static void EnableUserGit(Config config, string username)
  1006. {
  1007. try
  1008. {
  1009. // If Git is enabled
  1010. if (config.GitConfig.Enabled)
  1011. {
  1012. // Git user exists?
  1013. if (!UserGitExists(config, username))
  1014. {
  1015. throw new Exception($"Git User '{username}' does not exist.");
  1016. }
  1017. string email = GetUserEmailAddress(config, username);
  1018. var svc = CreateGitService(config);
  1019. svc.EnableAccount(username, email);
  1020. }
  1021. }
  1022. catch (Exception ex)
  1023. {
  1024. throw new Exception("Unable to enable git account.", ex);
  1025. }
  1026. }
  1027. public static void DisableUserGit(Config config, string username)
  1028. {
  1029. try
  1030. {
  1031. // If Git is enabled
  1032. if (config.GitConfig.Enabled)
  1033. {
  1034. // Git user exists?
  1035. if (!UserGitExists(config, username))
  1036. {
  1037. throw new Exception($"Git User '{username}' does not exist.");
  1038. }
  1039. string email = GetUserEmailAddress(config, username);
  1040. var svc = CreateGitService(config);
  1041. svc.EnableAccount(username, email);
  1042. }
  1043. }
  1044. catch (Exception ex)
  1045. {
  1046. throw new Exception("Unable to disable git account.", ex);
  1047. }
  1048. }
  1049. public static void DeleteUserGit(Config config, string username)
  1050. {
  1051. try
  1052. {
  1053. // If Git is enabled
  1054. if (config.GitConfig.Enabled)
  1055. {
  1056. // Git user exists?
  1057. if (!UserGitExists(config, username))
  1058. {
  1059. throw new Exception($"Git User '{username}' does not exist.");
  1060. }
  1061. var svc = CreateGitService(config);
  1062. svc.DeleteAccount(username);
  1063. }
  1064. }
  1065. catch (Exception ex)
  1066. {
  1067. throw new Exception("Unable to delete git account.", ex);
  1068. }
  1069. }
  1070. public static void CreateUserGitTwoFactor(Config config, string username, string secret, int unixTime)
  1071. {
  1072. try
  1073. {
  1074. // If Git is enabled
  1075. if (config.GitConfig.Enabled)
  1076. {
  1077. // Git user exists?
  1078. if (!UserGitExists(config, username))
  1079. {
  1080. throw new Exception($"Git User '{username}' does not exist.");
  1081. }
  1082. // Generate the scratch token
  1083. string token = StringHelper.RandomString(8);
  1084. // Get the Encryption Key from the git secret key
  1085. byte[] keyBytes = MD5.Hash(Encoding.UTF8.GetBytes(config.GitConfig.SecretKey));
  1086. // Modify the input secret
  1087. byte[] secBytes = Encoding.UTF8.GetBytes(secret);
  1088. // Generate the encrypted secret using AES CGM
  1089. byte[] encValue = Aes128CFB.Encrypt(secBytes, keyBytes);
  1090. string finalSecret = Convert.ToBase64String(encValue);
  1091. // Create connection to the DB
  1092. Utilities.MysqlDatabase mySQL = new Utilities.MysqlDatabase(config.GitConfig.Database.Server, config.GitConfig.Database.Database, config.GitConfig.Database.Username, config.GitConfig.Database.Password, config.GitConfig.Database.Port);
  1093. mySQL.MysqlErrorEvent += (sender, s) =>
  1094. {
  1095. throw new Exception("Unable to edit git account two factor. Mysql Exception: " + s);
  1096. };
  1097. // Get the user's UID
  1098. string email = GetUserEmailAddress(config, username);
  1099. string userSelect = @"SELECT gogs.user.id FROM gogs.user WHERE gogs.user.login_name = {0}";
  1100. var uid = mySQL.ScalarQuery(userSelect, new object[] { email });
  1101. // See if they have Two Factor already
  1102. string sqlSelect = @"SELECT tf.id
  1103. FROM gogs.two_factor tf
  1104. LEFT JOIN gogs.user u ON u.id = tf.uid
  1105. WHERE u.login_name = {0}";
  1106. var result = mySQL.ScalarQuery(sqlSelect, new object[] { email });
  1107. if (result != null)
  1108. {
  1109. // They have an entry! Let's update it
  1110. string update = @"UPDATE gogs.two_factor tf SET tf.uid = {1}, tf.secret = {2}, tf.scratch_token = {3}, tf.updated_unix = {4} WHERE tf.id = {0}";
  1111. mySQL.Execute(update, new object[] { result, uid, finalSecret, token, unixTime });
  1112. }
  1113. else
  1114. {
  1115. // They need a new entry
  1116. string insert = @"INSERT INTO gogs.two_factor (uid, secret, scratch_token, created_unix, updated_unix) VALUES ({0}, {1}, {2}, {3}, {4})";
  1117. mySQL.Execute(insert, new object[] { uid, finalSecret, token, unixTime, 0 });
  1118. }
  1119. }
  1120. }
  1121. catch (Exception ex)
  1122. {
  1123. throw new Exception("Unable to edit git account two factor.", ex);
  1124. }
  1125. }
  1126. public static void DeleteUserGitTwoFactor(Config config, string username)
  1127. {
  1128. try
  1129. {
  1130. // If Git is enabled
  1131. if (config.GitConfig.Enabled)
  1132. {
  1133. // Git user exists?
  1134. if (!UserGitExists(config, username))
  1135. {
  1136. throw new Exception($"Git User '{username}' does not exist.");
  1137. }
  1138. // Create connection to the DB
  1139. Utilities.MysqlDatabase mySQL = new Utilities.MysqlDatabase(config.GitConfig.Database.Server, config.GitConfig.Database.Database, config.GitConfig.Database.Username, config.GitConfig.Database.Password, config.GitConfig.Database.Port);
  1140. // Get the user's UID
  1141. string email = GetUserEmailAddress(config, username);
  1142. // See if they have Two Factor already
  1143. string deleteSql = @"DELETE tf.*
  1144. FROM gogs.two_factor tf
  1145. LEFT JOIN gogs.user u ON u.id = tf.uid
  1146. WHERE u.login_name = {0}";
  1147. mySQL.Execute(deleteSql, new object[] { email });
  1148. }
  1149. }
  1150. catch (Exception ex)
  1151. {
  1152. throw new Exception("Unable to delete git account two factor.", ex);
  1153. }
  1154. }
  1155. #endregion
  1156. public static ClaimsIdentity CreateClaimsIdentity(TeknikEntities db, string username)
  1157. {
  1158. User user = GetUser(db, username);
  1159. if (user != null)
  1160. {
  1161. var claims = new List<Claim>
  1162. {
  1163. new Claim(ClaimTypes.Name, user.Username)
  1164. };
  1165. // Add their roles
  1166. foreach (var role in user.UserRoles)
  1167. {
  1168. claims.Add(new Claim(ClaimTypes.Role, role.Role.Name));
  1169. }
  1170. return new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
  1171. }
  1172. return null;
  1173. }
  1174. public static Tuple<CookieOptions, string> CreateTrustedDeviceCookie(Config config, string username, string domain, bool local)
  1175. {
  1176. byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
  1177. byte[] key = Guid.NewGuid().ToByteArray();
  1178. string token = Convert.ToBase64String(time.Concat(key).ToArray());
  1179. var trustCookie = new CookieOptions()
  1180. {
  1181. HttpOnly = true,
  1182. Secure = true,
  1183. Expires = DateTime.Now.AddYears(1)
  1184. };
  1185. // Set domain dependent on where it's being ran from
  1186. if (local) // localhost
  1187. {
  1188. trustCookie.Domain = null;
  1189. }
  1190. else if (config.DevEnvironment) // dev.example.com
  1191. {
  1192. trustCookie.Domain = string.Format("dev.{0}", domain);
  1193. }
  1194. else // A production instance
  1195. {
  1196. trustCookie.Domain = string.Format(".{0}", domain);
  1197. }
  1198. return new Tuple<CookieOptions, string>(trustCookie, token);
  1199. }
  1200. }
  1201. }