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.

ManageController.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using IdentityServer4;
  7. using IdentityServer4.EntityFramework.DbContexts;
  8. using IdentityServer4.EntityFramework.Entities;
  9. using IdentityServer4.EntityFramework.Mappers;
  10. using IdentityServer4.Models;
  11. using IdentityServer4.Stores;
  12. using Microsoft.AspNetCore.Authorization;
  13. using Microsoft.AspNetCore.Identity;
  14. using Microsoft.AspNetCore.Mvc;
  15. using Microsoft.Extensions.Logging;
  16. using Teknik.Configuration;
  17. using Teknik.IdentityServer.Models;
  18. using Teknik.IdentityServer.Models.Manage;
  19. using Teknik.Logging;
  20. using Teknik.Utilities;
  21. namespace Teknik.IdentityServer.Controllers
  22. {
  23. [Authorize(Policy = "Internal", AuthenticationSchemes = "Bearer")]
  24. [Route("[controller]/[action]")]
  25. [ApiController]
  26. public class ManageController : DefaultController
  27. {
  28. private readonly UserManager<ApplicationUser> _userManager;
  29. private readonly SignInManager<ApplicationUser> _signInManager;
  30. public ManageController(
  31. ILogger<Logger> logger,
  32. Config config,
  33. UserManager<ApplicationUser> userManager,
  34. SignInManager<ApplicationUser> signInManager) : base(logger, config)
  35. {
  36. _userManager = userManager;
  37. _signInManager = signInManager;
  38. }
  39. [HttpPost]
  40. public async Task<IActionResult> CreateUser(NewUserModel model)
  41. {
  42. if (string.IsNullOrEmpty(model.Username))
  43. return new JsonResult(new { success = false, message = "Username is required" });
  44. if (string.IsNullOrEmpty(model.Password))
  45. return new JsonResult(new { success = false, message = "Password is required" });
  46. var identityUser = new ApplicationUser(model.Username)
  47. {
  48. Id = Guid.NewGuid().ToString(),
  49. UserName = model.Username,
  50. AccountStatus = model.AccountStatus,
  51. AccountType = model.AccountType,
  52. Email = model.RecoveryEmail,
  53. EmailConfirmed = model.RecoveryVerified,
  54. PGPPublicKey = model.PGPPublicKey
  55. };
  56. var result = await _userManager.CreateAsync(identityUser, model.Password);
  57. if (result.Succeeded)
  58. {
  59. return new JsonResult(new { success = true });
  60. }
  61. return new JsonResult(new { success = false, message = "Unable to create user.", identityErrors = result.Errors });
  62. }
  63. [HttpPost]
  64. public async Task<IActionResult> DeleteUser(DeleteUserModel model)
  65. {
  66. if (string.IsNullOrEmpty(model.Username))
  67. return new JsonResult(new { success = false, message = "Username is required" });
  68. var foundUser = await _userManager.FindByNameAsync(model.Username);
  69. if (foundUser != null)
  70. {
  71. var result = await _userManager.DeleteAsync(foundUser);
  72. if (result.Succeeded)
  73. return new JsonResult(new { success = true });
  74. else
  75. return new JsonResult(new { success = false, message = "Unable to delete user.", identityErrors = result.Errors });
  76. }
  77. return new JsonResult(new { success = false, message = "User does not exist." });
  78. }
  79. [HttpGet]
  80. public async Task<IActionResult> UserExists(string username)
  81. {
  82. if (string.IsNullOrEmpty(username))
  83. return new JsonResult(new { success = false, message = "Username is required" });
  84. var foundUser = await _userManager.FindByNameAsync(username);
  85. return new JsonResult(new { success = true, data = foundUser != null });
  86. }
  87. [HttpGet]
  88. public async Task<IActionResult> GetUserInfo(string username)
  89. {
  90. if (string.IsNullOrEmpty(username))
  91. return new JsonResult(new { success = false, message = "Username is required" });
  92. var foundUser = await _userManager.FindByNameAsync(username);
  93. if (foundUser != null)
  94. {
  95. return new JsonResult(new { success = true, data = foundUser.ToJson() });
  96. }
  97. return new JsonResult(new { success = false, message = "User does not exist." });
  98. }
  99. [HttpPost]
  100. public async Task<IActionResult> CheckPassword(CheckPasswordModel model)
  101. {
  102. if (string.IsNullOrEmpty(model.Username))
  103. return new JsonResult(new { success = false, message = "Username is required" });
  104. if (string.IsNullOrEmpty(model.Password))
  105. return new JsonResult(new { success = false, message = "Password is required" });
  106. var foundUser = await _userManager.FindByNameAsync(model.Username);
  107. if (foundUser != null)
  108. {
  109. bool valid = await _userManager.CheckPasswordAsync(foundUser, model.Password);
  110. return new JsonResult(new { success = true, data = valid });
  111. }
  112. return new JsonResult(new { success = false, message = "User does not exist." });
  113. }
  114. [HttpPost]
  115. public async Task<IActionResult> GeneratePasswordResetToken(GeneratePasswordResetTokenModel model)
  116. {
  117. if (string.IsNullOrEmpty(model.Username))
  118. return new JsonResult(new { success = false, message = "Username is required" });
  119. var foundUser = await _userManager.FindByNameAsync(model.Username);
  120. if (foundUser != null)
  121. {
  122. string token = await _userManager.GeneratePasswordResetTokenAsync(foundUser);
  123. return new JsonResult(new { success = true, data = token });
  124. }
  125. return new JsonResult(new { success = false, message = "User does not exist." });
  126. }
  127. [HttpPost]
  128. public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
  129. {
  130. if (string.IsNullOrEmpty(model.Username))
  131. return new JsonResult(new { success = false, message = "Username is required" });
  132. if (string.IsNullOrEmpty(model.Token))
  133. return new JsonResult(new { success = false, message = "Token is required" });
  134. if (string.IsNullOrEmpty(model.Password))
  135. return new JsonResult(new { success = false, message = "Password is required" });
  136. var foundUser = await _userManager.FindByNameAsync(model.Username);
  137. if (foundUser != null)
  138. {
  139. var result = await _userManager.ResetPasswordAsync(foundUser, model.Token, model.Password);
  140. if (result.Succeeded)
  141. return new JsonResult(new { success = true });
  142. else
  143. return new JsonResult(new { success = false, message = "Unable to reset password.", identityErrors = result.Errors });
  144. }
  145. return new JsonResult(new { success = false, message = "User does not exist." });
  146. }
  147. [HttpPost]
  148. public async Task<IActionResult> UpdatePassword(UpdatePasswordModel model)
  149. {
  150. if (string.IsNullOrEmpty(model.Username))
  151. return new JsonResult(new { success = false, message = "Username is required" });
  152. if (string.IsNullOrEmpty(model.CurrentPassword))
  153. return new JsonResult(new { success = false, message = "Current Password is required" });
  154. if (string.IsNullOrEmpty(model.NewPassword))
  155. return new JsonResult(new { success = false, message = "New Password is required" });
  156. var foundUser = await _userManager.FindByNameAsync(model.Username);
  157. if (foundUser != null)
  158. {
  159. var result = await _userManager.ChangePasswordAsync(foundUser, model.CurrentPassword, model.NewPassword);
  160. if (result.Succeeded)
  161. return new JsonResult(new { success = true });
  162. else
  163. return new JsonResult(new { success = false, message = "Unable to update password.", identityErrors = result.Errors });
  164. }
  165. return new JsonResult(new { success = false, message = "User does not exist." });
  166. }
  167. [HttpPost]
  168. public async Task<IActionResult> UpdateEmail(UpdateEmailModel model)
  169. {
  170. if (string.IsNullOrEmpty(model.Username))
  171. return new JsonResult(new { success = false, message = "Username is required" });
  172. var foundUser = await _userManager.FindByNameAsync(model.Username);
  173. if (foundUser != null)
  174. {
  175. var result = await _userManager.SetEmailAsync(foundUser, model.Email);
  176. if (result.Succeeded)
  177. {
  178. var token = await _userManager.GenerateEmailConfirmationTokenAsync(foundUser);
  179. return new JsonResult(new { success = true, data = token });
  180. }
  181. else
  182. return new JsonResult(new { success = false, message = "Unable to update email address.", identityErrors = result.Errors });
  183. }
  184. return new JsonResult(new { success = false, message = "User does not exist." });
  185. }
  186. [HttpPost]
  187. public async Task<IActionResult> VerifyEmail(VerifyEmailModel model)
  188. {
  189. if (string.IsNullOrEmpty(model.Username))
  190. return new JsonResult(new { success = false, message = "Username is required" });
  191. if (string.IsNullOrEmpty(model.Token))
  192. return new JsonResult(new { success = false, message = "Token is required" });
  193. var foundUser = await _userManager.FindByNameAsync(model.Username);
  194. if (foundUser != null)
  195. {
  196. var result = await _userManager.ConfirmEmailAsync(foundUser, model.Token);
  197. if (result.Succeeded)
  198. return new JsonResult(new { success = true });
  199. else
  200. return new JsonResult(new { success = false, message = "Unable to verify email address.", identityErrors = result.Errors });
  201. }
  202. return new JsonResult(new { success = false, message = "User does not exist." });
  203. }
  204. [HttpPost]
  205. public async Task<IActionResult> UpdateAccountStatus(UpdateAccountStatusModel model)
  206. {
  207. if (string.IsNullOrEmpty(model.Username))
  208. return new JsonResult(new { success = false, message = "Username is required" });
  209. var foundUser = await _userManager.FindByNameAsync(model.Username);
  210. if (foundUser != null)
  211. {
  212. foundUser.AccountStatus = model.AccountStatus;
  213. var result = await _userManager.UpdateAsync(foundUser);
  214. if (result.Succeeded)
  215. return new JsonResult(new { success = true });
  216. else
  217. return new JsonResult(new { success = false, message = "Unable to update account status.", identityErrors = result.Errors });
  218. }
  219. return new JsonResult(new { success = false, message = "User does not exist." });
  220. }
  221. [HttpPost]
  222. public async Task<IActionResult> UpdateAccountType(UpdateAccountTypeModel model)
  223. {
  224. if (string.IsNullOrEmpty(model.Username))
  225. return new JsonResult(new { success = false, message = "Username is required" });
  226. var foundUser = await _userManager.FindByNameAsync(model.Username);
  227. if (foundUser != null)
  228. {
  229. foundUser.AccountType = model.AccountType;
  230. var result = await _userManager.UpdateAsync(foundUser);
  231. if (result.Succeeded)
  232. return new JsonResult(new { success = true });
  233. else
  234. return new JsonResult(new { success = false, message = "Unable to update account type.", identityErrors = result.Errors });
  235. }
  236. return new JsonResult(new { success = false, message = "User does not exist." });
  237. }
  238. [HttpPost]
  239. public async Task<IActionResult> UpdatePGPPublicKey(UpdatePGPPublicKeyModel model)
  240. {
  241. if (string.IsNullOrEmpty(model.Username))
  242. return new JsonResult(new { success = false, message = "Username is required" });
  243. var foundUser = await _userManager.FindByNameAsync(model.Username);
  244. if (foundUser != null)
  245. {
  246. foundUser.PGPPublicKey = model.PGPPublicKey;
  247. var result = await _userManager.UpdateAsync(foundUser);
  248. if (result.Succeeded)
  249. return new JsonResult(new { success = true });
  250. else
  251. return new JsonResult(new { success = false, message = "Unable to update pgp public key.", identityErrors = result.Errors });
  252. }
  253. return new JsonResult(new { success = false, message = "User does not exist." });
  254. }
  255. [HttpGet]
  256. public async Task<IActionResult> Get2FAKey(string username)
  257. {
  258. if (string.IsNullOrEmpty(username))
  259. return new JsonResult(new { success = false, message = "Username is required" });
  260. var foundUser = await _userManager.FindByNameAsync(username);
  261. if (foundUser != null)
  262. {
  263. string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser);
  264. return new JsonResult(new { success = true, data = FormatKey(unformattedKey) });
  265. }
  266. return new JsonResult(new { success = false, message = "User does not exist." });
  267. }
  268. [HttpPost]
  269. public async Task<IActionResult> Reset2FAKey(Reset2FAKeyModel model)
  270. {
  271. if (string.IsNullOrEmpty(model.Username))
  272. return new JsonResult(new { success = false, message = "Username is required" });
  273. var foundUser = await _userManager.FindByNameAsync(model.Username);
  274. if (foundUser != null)
  275. {
  276. await _userManager.ResetAuthenticatorKeyAsync(foundUser);
  277. string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser);
  278. return new JsonResult(new { success = true, data = FormatKey(unformattedKey) });
  279. }
  280. return new JsonResult(new { success = false, message = "User does not exist." });
  281. }
  282. [HttpPost]
  283. public async Task<IActionResult> Enable2FA(Enable2FAModel model)
  284. {
  285. if (string.IsNullOrEmpty(model.Username))
  286. return new JsonResult(new { success = false, message = "Username is required" });
  287. if (string.IsNullOrEmpty(model.Code))
  288. return new JsonResult(new { success = false, message = "Code is required" });
  289. var foundUser = await _userManager.FindByNameAsync(model.Username);
  290. if (foundUser != null)
  291. {
  292. // Strip spaces and hypens
  293. var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
  294. var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
  295. foundUser, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
  296. if (is2faTokenValid)
  297. {
  298. var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, true);
  299. if (result.Succeeded)
  300. {
  301. var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10);
  302. return new JsonResult(new { success = true, data = recoveryCodes.ToArray() });
  303. }
  304. else
  305. return new JsonResult(new { success = false, message = "Unable to set Two-Factor Authentication.", identityErrors = result.Errors });
  306. }
  307. return new JsonResult(new { success = false, message = "Verification code is invalid." });
  308. }
  309. return new JsonResult(new { success = false, message = "User does not exist." });
  310. }
  311. [HttpPost]
  312. public async Task<IActionResult> Disable2FA(Disable2FAModel model)
  313. {
  314. if (string.IsNullOrEmpty(model.Username))
  315. return new JsonResult(new { success = false, message = "Username is required" });
  316. var foundUser = await _userManager.FindByNameAsync(model.Username);
  317. if (foundUser != null)
  318. {
  319. var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, false);
  320. if (result.Succeeded)
  321. return new JsonResult(new { success = true });
  322. else
  323. return new JsonResult(new { success = false, message = "Unable to disable Two-Factor Authentication.", identityErrors = result.Errors });
  324. }
  325. return new JsonResult(new { success = false, message = "User does not exist." });
  326. }
  327. [HttpPost]
  328. public async Task<IActionResult> GenerateRecoveryCodes(GenerateRecoveryCodesModel model)
  329. {
  330. if (string.IsNullOrEmpty(model.Username))
  331. return new JsonResult(new { success = false, message = "Username is required" });
  332. var foundUser = await _userManager.FindByNameAsync(model.Username);
  333. if (foundUser != null)
  334. {
  335. if (foundUser.TwoFactorEnabled)
  336. {
  337. var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10);
  338. return new JsonResult(new { success = true, data = recoveryCodes.ToArray() });
  339. }
  340. return new JsonResult(new { success = false, message = "Two-Factor Authentication is not enabled." });
  341. }
  342. return new JsonResult(new { success = false, message = "User does not exist." });
  343. }
  344. [HttpGet]
  345. public async Task<IActionResult> GetClient(string username, string clientId, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
  346. {
  347. if (string.IsNullOrEmpty(username))
  348. return new JsonResult(new { success = false, message = "Username is required" });
  349. if (string.IsNullOrEmpty(clientId))
  350. return new JsonResult(new { success = false, message = "Client Id is required" });
  351. var client = configContext.Clients.FirstOrDefault(c =>
  352. c.ClientId == clientId &&
  353. c.Properties.Exists(p =>
  354. p.Key == "username" &&
  355. p.Value.ToLower() == username.ToLower())
  356. );
  357. if (client != null)
  358. {
  359. var foundClient = await clientStore.FindClientByIdAsync(client.ClientId);
  360. return new JsonResult(new { success = true, data = foundClient });
  361. }
  362. return new JsonResult(new { success = false, message = "Client does not exist." });
  363. }
  364. [HttpGet]
  365. public async Task<IActionResult> GetClients(string username, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
  366. {
  367. if (string.IsNullOrEmpty(username))
  368. return new JsonResult(new { success = false, message = "Username is required" });
  369. var foundClientIds = configContext.Clients.Where(c =>
  370. c.Properties.Exists(p =>
  371. p.Key == "username" &&
  372. p.Value.ToLower() == username.ToLower())
  373. ).Select(c => c.ClientId);
  374. var clients = new List<IdentityServer4.Models.Client>();
  375. foreach (var clientId in foundClientIds)
  376. {
  377. var foundClient = await clientStore.FindClientByIdAsync(clientId);
  378. if (foundClient != null)
  379. clients.Add(foundClient);
  380. }
  381. return new JsonResult(new { success = true, data = clients });
  382. }
  383. [HttpPost]
  384. public IActionResult CreateClient(CreateClientModel model, [FromServices] ConfigurationDbContext configContext)
  385. {
  386. // Generate a unique client ID
  387. var clientId = StringHelper.RandomString(20, "abcdefghjkmnpqrstuvwxyz1234567890");
  388. while (configContext.Clients.Where(c => c.ClientId == clientId).FirstOrDefault() != null)
  389. {
  390. clientId = StringHelper.RandomString(20, "abcdefghjkmnpqrstuvwxyz1234567890");
  391. }
  392. var clientSecret = StringHelper.RandomString(40, "abcdefghjkmnpqrstuvwxyz1234567890");
  393. var client = new IdentityServer4.Models.Client
  394. {
  395. Properties = new Dictionary<string, string>()
  396. {
  397. { "username", model.Username }
  398. },
  399. ClientId = clientId,
  400. ClientName = model.Name,
  401. AllowedGrantTypes = new List<string>()
  402. {
  403. GrantType.AuthorizationCode,
  404. GrantType.ClientCredentials
  405. },
  406. ClientSecrets =
  407. {
  408. new IdentityServer4.Models.Secret(clientSecret.Sha256())
  409. },
  410. RequireConsent = true,
  411. RedirectUris =
  412. {
  413. model.RedirectURI
  414. },
  415. PostLogoutRedirectUris =
  416. {
  417. model.PostLogoutRedirectURI
  418. },
  419. AllowedScopes = model.AllowedScopes,
  420. AllowOfflineAccess = true
  421. };
  422. configContext.Clients.Add(client.ToEntity());
  423. configContext.SaveChanges();
  424. return new JsonResult(new { success = true, data = new { id = clientId, secret = clientSecret } });
  425. }
  426. [HttpPost]
  427. public IActionResult DeleteClient(DeleteClientModel model, [FromServices] ConfigurationDbContext configContext)
  428. {
  429. var foundClient = configContext.Clients.Where(c => c.ClientId == model.ClientId).FirstOrDefault();
  430. if (foundClient != null)
  431. {
  432. configContext.Clients.Remove(foundClient);
  433. configContext.SaveChanges();
  434. return new JsonResult(new { success = true });
  435. }
  436. return new JsonResult(new { success = false, message = "Client does not exist." });
  437. }
  438. private string FormatKey(string unformattedKey)
  439. {
  440. var result = new StringBuilder();
  441. int currentPosition = 0;
  442. while (currentPosition + 4 < unformattedKey.Length)
  443. {
  444. result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
  445. currentPosition += 4;
  446. }
  447. if (currentPosition < unformattedKey.Length)
  448. {
  449. result.Append(unformattedKey.Substring(currentPosition));
  450. }
  451. return result.ToString().ToLowerInvariant();
  452. }
  453. }
  454. }