diff options
Diffstat (limited to 'Project-Unite/Controllers')
| -rw-r--r-- | Project-Unite/Controllers/AccountController.cs | 513 | ||||
| -rw-r--r-- | Project-Unite/Controllers/AdminController.cs | 567 | ||||
| -rw-r--r-- | Project-Unite/Controllers/ForumController.cs | 305 | ||||
| -rw-r--r-- | Project-Unite/Controllers/HomeController.cs | 31 | ||||
| -rw-r--r-- | Project-Unite/Controllers/LegalController.cs | 23 | ||||
| -rw-r--r-- | Project-Unite/Controllers/ManageController.cs | 382 | ||||
| -rw-r--r-- | Project-Unite/Controllers/ModeratorController.cs | 320 |
7 files changed, 2141 insertions, 0 deletions
diff --git a/Project-Unite/Controllers/AccountController.cs b/Project-Unite/Controllers/AccountController.cs new file mode 100644 index 0000000..d742484 --- /dev/null +++ b/Project-Unite/Controllers/AccountController.cs @@ -0,0 +1,513 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.Owin; +using Microsoft.Owin.Security; +using Project_Unite.Models; + +namespace Project_Unite.Controllers +{ + [Authorize] + public class AccountController : Controller + { + private ApplicationSignInManager _signInManager; + private ApplicationUserManager _userManager; + + public AccountController() + { + } + + public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager ) + { + UserManager = userManager; + SignInManager = signInManager; + } + + public ApplicationSignInManager SignInManager + { + get + { + return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); + } + private set + { + _signInManager = value; + } + } + + public ApplicationUserManager UserManager + { + get + { + return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); + } + private set + { + _userManager = value; + } + } + + public async Task<ActionResult> ResendConf() + { + var uid = User.Identity.GetUserId(); + var usr = new ApplicationDbContext().Users.FirstOrDefault(x => x.Id == uid); + if (usr == null) + return new HttpStatusCodeResult(404); + if (usr.EmailConfirmed == true) + { + ViewBag.IsConfirmed = true; + return View(); + } + string code = await UserManager.GenerateEmailConfirmationTokenAsync(uid); + var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = uid, code = code }, protocol: Request.Url.Scheme); + await UserManager.SendEmailAsync(uid, "Confirm your account", $@"### Please confirm your account. + +A user has registered to the ShiftOS website using this email address as a sign-in address. If this was you, please click [this link]({callbackUrl}). + +**If this was not you**: + +The addressed used to send this message is not a no-reply address. In fact, my name is Michael, admin of the site. We may have a bit of an identity theft issue, or something, going on here, but there's something I can do to help - all you have to do is reply to this email. I have the IP address, display name and ID of the user who triggered this email - just let me know and I can purge the account for you and ban the user's IP address and you won't have issues anymore. Thanks! + +**User ID:** {uid} +**Display name:** {usr.DisplayName} +**Last known IP address:** {usr.LastKnownIPAddress}"); + + return View(); + } + + // + // GET: /Account/Login + [AllowAnonymous] + public ActionResult Login(string returnUrl) + { + ViewBag.ReturnUrl = returnUrl; + return View(); + } + + // + // POST: /Account/Login + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) + { + if (!ModelState.IsValid) + { + return View(model); + } + + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, change to shouldLockout: true + var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); + switch (result) + { + case SignInStatus.Success: + return RedirectToLocal(returnUrl); + case SignInStatus.LockedOut: + return View("Lockout"); + case SignInStatus.RequiresVerification: + return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); + case SignInStatus.Failure: + default: + ModelState.AddModelError("", "Invalid login attempt."); + return View(model); + } + } + + // + // GET: /Account/VerifyCode + [AllowAnonymous] + public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe) + { + // Require that the user has already logged in via username/password or external login + if (!await SignInManager.HasBeenVerifiedAsync()) + { + return View("Error"); + } + return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/VerifyCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + // The following code protects for brute force attacks against the two factor codes. + // If a user enters incorrect codes for a specified amount of time then the user account + // will be locked out for a specified amount of time. + // You can configure the account lockout settings in IdentityConfig + var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser); + switch (result) + { + case SignInStatus.Success: + return RedirectToLocal(model.ReturnUrl); + case SignInStatus.LockedOut: + return View("Lockout"); + case SignInStatus.Failure: + default: + ModelState.AddModelError("", "Invalid code."); + return View(model); + } + } + + // + // GET: /Account/Register + [AllowAnonymous] + public ActionResult Register() + { + return View(); + } + + // + // POST: /Account/Register + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> Register(RegisterViewModel model) + { + if (ModelState.IsValid) + { + var user = new ApplicationUser { UserName = model.Email, Email = model.Email, DisplayName = model.Username, Codepoints = 0, JoinedAt = DateTime.Now, MutedAt = DateTime.Now, BannedAt = DateTime.Now, LastLogin = DateTime.Now }; + var result = await UserManager.CreateAsync(user, model.Password); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false); + + // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 + // Send an email with this link + string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); + var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); + await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>"); + UserManager.AddToRole(user.Id, ACL.LowestPriorityRole().Id); + return RedirectToAction("Index", "Home"); + } + AddErrors(result); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/ConfirmEmail + [AllowAnonymous] + public async Task<ActionResult> ConfirmEmail(string userId, string code) + { + if (userId == null || code == null) + { + return View("Error"); + } + var result = await UserManager.ConfirmEmailAsync(userId, code); + return View(result.Succeeded ? "ConfirmEmail" : "Error"); + } + + // + // GET: /Account/ForgotPassword + [AllowAnonymous] + public ActionResult ForgotPassword() + { + return View(); + } + + // + // POST: /Account/ForgotPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model) + { + if (ModelState.IsValid) + { + var user = await UserManager.FindByNameAsync(model.Email); + if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id))) + { + // Don't reveal that the user does not exist or is not confirmed + return View("ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 + // Send an email with this link + string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id); + var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); + await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>"); + return RedirectToAction("ForgotPasswordConfirmation", "Account"); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/ForgotPasswordConfirmation + [AllowAnonymous] + public ActionResult ForgotPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/ResetPassword + [AllowAnonymous] + public ActionResult ResetPassword(string code) + { + return code == null ? View("Error") : View(); + } + + // + // POST: /Account/ResetPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await UserManager.FindByNameAsync(model.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToAction("ResetPasswordConfirmation", "Account"); + } + var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password); + if (result.Succeeded) + { + return RedirectToAction("ResetPasswordConfirmation", "Account"); + } + AddErrors(result); + return View(); + } + + // + // GET: /Account/ResetPasswordConfirmation + [AllowAnonymous] + public ActionResult ResetPasswordConfirmation() + { + return View(); + } + + // + // POST: /Account/ExternalLogin + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public ActionResult ExternalLogin(string provider, string returnUrl) + { + // Request a redirect to the external login provider + return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); + } + + // + // GET: /Account/SendCode + [AllowAnonymous] + public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe) + { + var userId = await SignInManager.GetVerifiedUserIdAsync(); + if (userId == null) + { + return View("Error"); + } + var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId); + var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); + return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/SendCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> SendCode(SendCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(); + } + + // Generate the token and send it + if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider)) + { + return View("Error"); + } + return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); + } + + // + // GET: /Account/ExternalLoginCallback + [AllowAnonymous] + public async Task<ActionResult> ExternalLoginCallback(string returnUrl) + { + var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); + if (loginInfo == null) + { + return RedirectToAction("Login"); + } + + // Sign in the user with this external login provider if the user already has a login + var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false); + switch (result) + { + case SignInStatus.Success: + return RedirectToLocal(returnUrl); + case SignInStatus.LockedOut: + return View("Lockout"); + case SignInStatus.RequiresVerification: + return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false }); + case SignInStatus.Failure: + default: + // If the user does not have an account, then prompt the user to create an account + ViewBag.ReturnUrl = returnUrl; + ViewBag.LoginProvider = loginInfo.Login.LoginProvider; + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email }); + } + } + + // + // POST: /Account/ExternalLoginConfirmation + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) + { + if (User.Identity.IsAuthenticated) + { + return RedirectToAction("Index", "Manage"); + } + + if (ModelState.IsValid) + { + // Get the information about the user from the external login provider + var info = await AuthenticationManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return View("ExternalLoginFailure"); + } + var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; + var result = await UserManager.CreateAsync(user); + if (result.Succeeded) + { + result = await UserManager.AddLoginAsync(user.Id, info.Login); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + return RedirectToLocal(returnUrl); + } + } + AddErrors(result); + } + + ViewBag.ReturnUrl = returnUrl; + return View(model); + } + + // + // POST: /Account/LogOff + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult LogOff() + { + AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); + return RedirectToAction("Index", "Home"); + } + + // + // GET: /Account/ExternalLoginFailure + [AllowAnonymous] + public ActionResult ExternalLoginFailure() + { + return View(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_userManager != null) + { + _userManager.Dispose(); + _userManager = null; + } + + if (_signInManager != null) + { + _signInManager.Dispose(); + _signInManager = null; + } + } + + base.Dispose(disposing); + } + + #region Helpers + // Used for XSRF protection when adding external logins + private const string XsrfKey = "XsrfId"; + + private IAuthenticationManager AuthenticationManager + { + get + { + return HttpContext.GetOwinContext().Authentication; + } + } + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError("", error); + } + } + + private ActionResult RedirectToLocal(string returnUrl) + { + if (Url.IsLocalUrl(returnUrl)) + { + return Redirect(returnUrl); + } + return RedirectToAction("Index", "Home"); + } + + internal class ChallengeResult : HttpUnauthorizedResult + { + public ChallengeResult(string provider, string redirectUri) + : this(provider, redirectUri, null) + { + } + + public ChallengeResult(string provider, string redirectUri, string userId) + { + LoginProvider = provider; + RedirectUri = redirectUri; + UserId = userId; + } + + public string LoginProvider { get; set; } + public string RedirectUri { get; set; } + public string UserId { get; set; } + + public override void ExecuteResult(ControllerContext context) + { + var properties = new AuthenticationProperties { RedirectUri = RedirectUri }; + if (UserId != null) + { + properties.Dictionary[XsrfKey] = UserId; + } + context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); + } + } + #endregion + } +}
\ No newline at end of file diff --git a/Project-Unite/Controllers/AdminController.cs b/Project-Unite/Controllers/AdminController.cs new file mode 100644 index 0000000..f8983d2 --- /dev/null +++ b/Project-Unite/Controllers/AdminController.cs @@ -0,0 +1,567 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Entity; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.Owin; +using Project_Unite.Models; + +namespace Project_Unite.Controllers +{ + //We have a custom ACL implementation so we do not need to use the ASP.NET role system to check if a user has an ACL rule. + [Authorize] + public class AdminController : Controller + { + private ApplicationDbContext db = new ApplicationDbContext(); + + public ActionResult Index() + { + ViewBag.Admin = true; + return View(); + } + + + public ActionResult DeleteForum(string id) + { + var frm = db.ForumCategories.FirstOrDefault(x => x.Id == id); + if (frm == null) + return new HttpStatusCodeResult(404); + + //Purge ALL DATA RELATED TO THIS CATEGORY. + DeleteCategoryRecursive(frm); + db.SaveChanges(); + + return RedirectToAction("Forums"); + } + + + public void DeleteCategoryRecursive(ForumCategory start) + { + foreach (var c in start.Children.ToArray()) + { + DeleteCategoryRecursive(c); + } + + foreach (var topic in start.Topics.ToArray()) + { + DeleteTopic(topic); + } + db.ForumCategories.Remove(db.ForumCategories.FirstOrDefault(x => x.Id == start.Id)); + + } + + public void DeleteTopic(ForumTopic topic) + { + foreach(var post in topic.Posts.ToArray()) + { + DeletePost(post); + } + db.ForumTopics.Remove(topic); + } + + public ActionResult AccessControl() + { + var model = new Dictionary<string, ForumPermission[]>(); + var db = new ApplicationDbContext(); + var forums = db.ForumCategories.ToArray(); + foreach(var forum in forums) + { + ACL.UpdateACLDefinitions(forum.Id); + if(forum.Id != "root") + { + if (!model.ContainsKey(forum.Id)) + { + model.Add(forum.Id, forum.Permissions); + } + } + } + return View(new AdminAccessControlViewModel(model)); + } + + public ActionResult SetPermission(string id, string role, string permission) + { + if (!ACL.Granted(User.Identity.Name, "CanAccessAdminCP")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanEditRoles")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanEditForumCategories")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var frm = db.ForumCategories.FirstOrDefault(x => x.Id == id); + if (frm == null) + return new HttpStatusCodeResult(403); + var rolePerm = db.ForumPermissions.Where(x => x.CategoryId == frm.Id).FirstOrDefault(x => x.RoleId == role); + if (rolePerm == null) + return new HttpStatusCodeResult(404); + + rolePerm.Permissions = (PermissionPreset)Enum.Parse(typeof(PermissionPreset), permission); + + db.AuditLogs.Add(new Models.AuditLog(User.Identity.GetUserId(), AuditLogLevel.Admin, "User altered the ACL definition for forum ID " + id + ", role ID " + role + ", to permission \"" + permission + "\".")); + db.SaveChanges(); + + return RedirectToAction("AccessControl"); + + } + + public void DeletePost(ForumPost post) + { + db.ForumPosts.Remove(post); + } + + + // GET: Admin/Forums + public ActionResult Forums() + { + ViewBag.Admin = true; + var cats = db.ForumCategories.First(x => x.Id == "root").Children.Where(x=>x.Id != "root"); + return View(cats); + } + + + + public ActionResult AddForumCategory(string parentId) + { + ViewBag.Admin = true; + if (parentId == null) + parentId = "root"; + + var model = new AddForumCategoryViewModel(); + model.PossibleParents = GetForumCategories(); + model.Parent = (parentId); + return View(model); + } + + public ActionResult RaisePriority(string id) + { + var uid = User.Identity.Name; + if(ACL.Granted(uid, "CanEditRoles")) + { + var db = new ApplicationDbContext(); + var role = db.Roles.FirstOrDefault(x => x.Id == id) as Role; + if (role == null) + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + if (role.Priority == db.Roles.Count() - 1) + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + Role higherUp = null; + foreach(var r in db.Roles) + { + if ((r as Role).Priority == role.Priority + 1) + higherUp = r as Role; + } + + higherUp.Priority--; + role.Priority++; + db.SaveChanges(); + + return RedirectToAction("Roles"); + } + else + { + return new HttpStatusCodeResult(403); + } + } + + public async Task<ActionResult> RemoveUserFromRole(string id, string usr) + { + if(ACL.CanManageRole(User.Identity.Name, id)) + { + var uman = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); + await uman.RemoveFromRoleAsync(usr, id); + return RedirectToAction("RoleDetails", new { @id = id }); + } + else + { + return new HttpStatusCodeResult(403); + } + } + + public ActionResult LowerPriority(string id) + { + var uid = User.Identity.Name; + if (ACL.Granted(uid, "CanEditRoles")) + { + var db = new ApplicationDbContext(); + var role = db.Roles.FirstOrDefault(x => x.Id == id) as Role; + if (role == null) + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + if (role.Priority == 0) + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + Role higherUp = null; + foreach (var r in db.Roles) + { + if ((r as Role).Priority == role.Priority - 1) + higherUp = r as Role; + } + + if(higherUp != null) + higherUp.Priority++; + role.Priority--; + db.SaveChanges(); + + return RedirectToAction("Roles"); + } + else + { + return new HttpStatusCodeResult(403); + } + } + + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult AddForumCategory(AddForumCategoryViewModel model) + { + try + { + if (model == null) + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + if (string.IsNullOrWhiteSpace(model.Name)) + { + ViewBag.Error = "Please specify a name for this forum category."; + return View(model); + } + + string DisallowedChars = "/.,\\][;':\"|?><!@#$%^&*()_+-=`~}{ "; + + string id = model.Name.ToLower(); + + foreach (var c in DisallowedChars) + { + id = id.Replace(c, '_'); + } + + + var frm = new ForumCategory(); + + frm.Name = model.Name; + frm.Id = id; + + frm.Description = model.Description; + frm.LinkUrl = null; + frm.Parent = db.ForumCategories.FirstOrDefault(x => x.Id == model.Parent).Id; + db.ForumCategories.Add(frm); + + db.SaveChanges(); + if (model.StealPermissionsFrom != "root") + { + var frmToSteal = db.ForumCategories.FirstOrDefault(x => x.Id == model.StealPermissionsFrom); + ACL.UpdateACLDefinitions(frmToSteal.Id); //Just to be sure.. + foreach(var perm in frmToSteal.Permissions) + { + var aclEntry = new ForumPermission(); + aclEntry.CategoryId = frm.Id; + aclEntry.RoleId = perm.RoleId; + aclEntry.Permissions = perm.Permissions; + aclEntry.Id = Guid.NewGuid().ToString(); + db.ForumPermissions.Add(aclEntry); + } + db.SaveChanges(); + } + else + { + ACL.UpdateACLDefinitions(frm.Id); + //This sets the permission data to the default values. + } + + + + return RedirectToAction("Forums"); + } + catch (Exception ex) + { + ViewBag.Error = ex.ToString(); + return View(model); + } + } + + public ActionResult AnonymizeUser(string id) + { + if (!ACL.Granted(User.Identity.Name, "CanAcessAdminCP")) + return new HttpStatusCodeResult(HttpStatusCode.Forbidden); + if (!ACL.Granted(User.Identity.Name, "CanAnonymizeUser")) + return new HttpStatusCodeResult(HttpStatusCode.Forbidden); + var db = new ApplicationDbContext(); + var user = db.Users.FirstOrDefault(x => x.Id == id); + if (user == null) + return new HttpStatusCodeResult(404); + + user.UserName = Guid.NewGuid().ToString() + "@system"; + user.Email = Guid.NewGuid().ToString() + "@system"; + user.PasswordHash = Guid.NewGuid().ToString(); + user.EmailConfirmed = false; + user.Hobbies = ""; + user.Interests = ""; + user.Bio = @"# User anonymized. + +This user has been anonymized by an administrator."; + user.AvatarUrl = ""; + user.BannerUrl = ""; + var uman = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); + foreach(var role in user.Roles) + { + uman.RemoveFromRole(user.Id, role.RoleId); + } + uman.AddToRole(user.Id, ACL.LowestPriorityRole().Id); + db.SaveChanges(); + return RedirectToAction("Users"); + } + + + public ActionResult Logs() + { + if (!ACL.Granted(User.Identity.Name, "CanAccessAdminCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + + return View(db.AuditLogs); + } + + + public ActionResult Users() + { + return View(new ApplicationDbContext().Users); + } + + public ActionResult EditForum(string id) + { + ViewBag.Admin = true; + var frm = db.ForumCategories.FirstOrDefault(x => x.Id == id); + if (frm == null) + return new HttpStatusCodeResult(404); + + var m = new AddForumCategoryViewModel(); + m.Id = frm.Id; + m.Parent = frm.Parent; + m.Description = frm.Description; + m.Name = frm.Name; + m.PossibleParents = GetForumCategories(); + return View(m); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult EditForum(string id, AddForumCategoryViewModel m) + { + try + { + m.Id = id; + var frm = db.ForumCategories.FirstOrDefault(x => x.Id == id); + + frm.Name = m.Name; + frm.Description = m.Description; + + + + frm.Parent = m.Parent; + db.SaveChanges(); + + return RedirectToAction("Forums"); + } + catch (Exception ex) + { + ViewBag.Error = ex.ToString(); + return View(m); + } + } + + + + + + + // GET: Admin + public ActionResult Roles() + { + ViewBag.Admin = true; + return View(db.IdentityRoles.ToList()); + } + + // GET: Admin/RoleDetails/5 + public ActionResult RoleDetails(string id) + { + ViewBag.Admin = true; + if (id == null) + { + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + } + Role role = db.IdentityRoles.Find(id); + if (role == null) + { + return HttpNotFound(); + } + return View(role); + } + + // GET: Admin/Create + public ActionResult CreateRole() + { + ViewBag.Admin = true; + return View(); + } + + // POST: Admin/Create + // To protect from overposting attacks, please enable the specific properties you want to bind to, for + // more details see http://go.microsoft.com/fwlink/?LinkId=317598. + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult CreateRole(Role role) + { + if (ModelState.IsValid) + { + db.IdentityRoles.Add(role); + db.SaveChanges(); + return RedirectToAction("Roles"); + } + + return View(role); + } + + // GET: Admin/AddUserToRole + public ActionResult AddUserToRole() + { + ViewBag.Admin = true; + return View(new AddUserToRoleViewModel()); + } + + // POST: Admin/AddUserToRole + [HttpPost] + public async Task<ActionResult> AddUserToRole(AddUserToRoleViewModel model) + { + var r = db.Roles.FirstOrDefault(role => role.Name == model.RoleId); + var user = db.Users.FirstOrDefault(u => u.DisplayName == model.Username); + var uMan = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); + + var isInRole = await uMan.IsInRoleAsync(user.Id, r.Id); + + if(isInRole) + { + ViewBag.Error = $"{model.Username} is already in the {r.Name} role."; + return View(model); + } + else + { + await uMan.AddToRoleAsync(user.Id, r.Name); + return RedirectToAction("Roles"); + } + + + } + + // GET: Admin/Edit/5 + public ActionResult EditRole(string id) + { + ViewBag.Admin = true; + if (id == null) + { + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + } + Role role = db.IdentityRoles.Find(id); + if (role == null) + { + return HttpNotFound(); + } + return View(role); + } + + // POST: Admin/Edit/5 + // To protect from overposting attacks, please enable the specific properties you want to bind to, for + // more details see http://go.microsoft.com/fwlink/?LinkId=317598. + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult EditRole(Role role) + { + if (ModelState.IsValid) + { + db.Entry(role).State = EntityState.Modified; + db.SaveChanges(); + return RedirectToAction("Index"); + } + return View(role); + } + + // GET: Admin/Delete/5 + public ActionResult DeleteRole(string id) + { + ViewBag.Admin = true; + if (id == null) + { + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + } + Role role = db.IdentityRoles.Find(id); + if (role == null) + { + return HttpNotFound(); + } + return View(role); + } + + // POST: Admin/Delete/5 + [HttpPost, ActionName("Delete")] + [ValidateAntiForgeryToken] + public ActionResult DeleteRoleConfirmed(string id) + { + Role role = db.IdentityRoles.Find(id); + db.IdentityRoles.Remove(role); + db.SaveChanges(); + return RedirectToAction("Index"); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + db.Dispose(); + } + base.Dispose(disposing); + } + + public List<SelectListItem> GetForumCategories() + { + var items = new List<SelectListItem>(); + items.Add(new SelectListItem + { + Value = "root", + Text = "Top Level" + }); + + foreach(var cat in getChildren("root", 1)) + { + items.Add(cat); + } + + return items; + + } + + private IEnumerable<SelectListItem> getChildren(string id, int dashcount) + { + var lst = new List<SelectListItem>(); + db = new ApplicationDbContext(); + foreach (var cat in db.ForumCategories.FirstOrDefault(c => c.Id == id).Children.Where(x=>x.Id != "root")) + { + string dashes = ""; + for (int i = 0; i <= dashcount; i++) + dashes += "-"; + lst.Add(new SelectListItem + { + Text = dashes + " " + cat.Name, + Value = cat.Id, + }); + lst.AddRange(getChildren(cat.Id, dashcount + 1)); + } + return lst; + } + } +} diff --git a/Project-Unite/Controllers/ForumController.cs b/Project-Unite/Controllers/ForumController.cs new file mode 100644 index 0000000..9ac2685 --- /dev/null +++ b/Project-Unite/Controllers/ForumController.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Microsoft.AspNet.Identity; +using Project_Unite.Models; + +namespace Project_Unite.Controllers +{ + public class ForumController : Controller + { + public ApplicationDbContext db = new ApplicationDbContext(); + + // GET: Forum + public ActionResult Index() + { + var toplevels = (db.ForumCategories.FirstOrDefault(x => x.Id == "root").Children); + + + return View(toplevels); + } + + //GET: ViewForum + public ActionResult ViewForum(string id) + { + try + { + var cat = db.ForumCategories.FirstOrDefault(m => m.Id == id); + if (!ACL.CanSee(User.Identity.Name, id)) + return new HttpStatusCodeResult(403); + + return View(cat); + } + catch + { + return new HttpStatusCodeResult(404); + } + } + + public const string BadIdChars = "~`!@#$%^&*()-+={}[]|\\:;'\"<,>.?/"; + + public ActionResult PostReply(string id, string fid) + { + if (!ACL.CanReply(User.Identity.Name, fid)) + return new HttpStatusCodeResult(403); + + var model = new CreateTopicViewModel(); + model.Category = id; + return View(model); + } + + [Authorize] + public ActionResult Dislike(string id) + { + var db = new ApplicationDbContext(); + var topic = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + var uid = User.Identity.GetUserId(); + if (topic == null) + return new HttpStatusCodeResult(404); + if (topic.AuthorId == User.Identity.GetUserId()) + return RedirectToAction("ViewTopic", new { id = id, triedtolikeowntopic = true }); + var like = db.Likes.Where(x => x.Topic == topic.Id).FirstOrDefault(x => x.User == uid); + if (like != null) + { + if (like.IsDislike == false) + { + like.IsDislike = true; + } + else + { + db.Likes.Remove(like); + } + } + else + { + like = new Models.Like(); + like.Id = Guid.NewGuid().ToString(); + like.User = User.Identity.GetUserId(); + like.Topic = topic.Id; + like.LikedAt = DateTime.Now; + like.IsDislike = true; + db.Likes.Add(like); + } + db.SaveChanges(); + return RedirectToAction("ViewTopic", new { id = id }); + } + + [Authorize] + public ActionResult Like(string id) + { + var db = new ApplicationDbContext(); + var topic = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + var uid = User.Identity.GetUserId(); + if (topic == null) + return new HttpStatusCodeResult(404); + if (topic.AuthorId == User.Identity.GetUserId()) + return RedirectToAction("ViewTopic", new { id = id, triedtolikeowntopic = true }); + var like = db.Likes.Where(x=>x.Topic==topic.Id).FirstOrDefault(x => x.User == uid); + if (like != null) + { + if (like.IsDislike == true) + { + like.IsDislike = false; + } + else + { + db.Likes.Remove(like); + } + } + else + { + like = new Models.Like(); + like.Id = Guid.NewGuid().ToString(); + like.User = User.Identity.GetUserId(); + like.Topic = topic.Id; + like.LikedAt = DateTime.Now; + like.IsDislike = false; + db.Likes.Add(like); + } + db.SaveChanges(); + return RedirectToAction("ViewTopic", new { id = id }); + } + + [Authorize] + public ActionResult EditPost(string id) + { + var db = new ApplicationDbContext(); + var topic = db.ForumPosts.FirstOrDefault(x => x.Id == id); + var uid = User.Identity.GetUserId(); + string acl_perm = "CanEditPosts"; + if (topic == null) + return new HttpStatusCodeResult(404); + if (topic.AuthorId == User.Identity.GetUserId()) + acl_perm = "CanEditOwnPosts"; + if (!ACL.Granted(User.Identity.Name, acl_perm)) + return new HttpStatusCodeResult(403); + var model = new EditPostViewModel(); + model.Id = topic.Id; + model.Contents = topic.Body; + return View(model); + } + + [Authorize] + public ActionResult DeletePost(string id) + { + var db = new ApplicationDbContext(); + var topic = db.ForumPosts.FirstOrDefault(x => x.Id == id); + var uid = User.Identity.GetUserId(); + string acl_perm = "CanDeletePosts"; + if (topic == null) + return new HttpStatusCodeResult(404); + if (topic.AuthorId == User.Identity.GetUserId()) + acl_perm = "CanDeleteOwnPosts"; + if (!ACL.Granted(User.Identity.Name, acl_perm)) + return new HttpStatusCodeResult(403); + db.ForumPosts.Remove(topic); + var parent = db.ForumTopics.FirstOrDefault(x => x.Id == topic.Parent); + db.SaveChanges(); + if (parent.Posts.Length == 0) + { + string cat = parent.Parent; + db.ForumTopics.Remove(parent); + db.SaveChanges(); + RedirectToAction("ViewForum", new { id = cat }); + } + return RedirectToAction("ViewTopic", new { id = db.ForumTopics.FirstOrDefault(x => x.Id == topic.Parent).Discriminator}); + } + + + [HttpPost] + [ValidateAntiForgeryToken] + [Authorize] + public ActionResult EditPost(EditPostViewModel model) + { + var db = new ApplicationDbContext(); + var topic = db.ForumPosts.FirstOrDefault(x => x.Id == model.Id); + var uid = User.Identity.GetUserId(); + string acl_perm = "CanEditPosts"; + if (topic == null) + return new HttpStatusCodeResult(404); + if (topic.AuthorId == User.Identity.GetUserId()) + acl_perm = "CanEditOwnPosts"; + if (!ACL.Granted(User.Identity.Name, acl_perm)) + return new HttpStatusCodeResult(403); + var edit = new ForumPostEdit(); + edit.EditedAt = DateTime.Now; + edit.EditReason = model.EditReason; + edit.Id = Guid.NewGuid().ToString(); + edit.Parent = topic.Id; + edit.PreviousState = topic.Body; + edit.UserId = uid; + db.ForumPostEdits.Add(edit); + topic.Body = model.Contents; + db.SaveChanges(); + return RedirectToAction("ViewTopic", new { id = db.ForumTopics.FirstOrDefault(x => x.Id == topic.Parent).Discriminator }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + [Authorize] + public ActionResult PostReply(CreateTopicViewModel model) + { + + + var db = new ApplicationDbContext(); + var topic = db.ForumTopics.FirstOrDefault(x => x.Discriminator == model.Category); + if (topic == null) + return new HttpStatusCodeResult(404); + if (!ACL.CanReply(User.Identity.Name, topic.Parent)) + return new HttpStatusCodeResult(403); + + var post = new ForumPost(); + post.AuthorId = User.Identity.GetUserId(); + post.Body = model.Body; + post.Id = Guid.NewGuid().ToString(); + post.Parent = topic.Id; + post.PostedAt = DateTime.Now; + db.ForumPosts.Add(post); + db.SaveChanges(); + + return RedirectToAction("ViewTopic", new { id = topic.Discriminator }); + + } + + + public ActionResult CreateTopic(string id) + { + if (!ACL.CanPost(User.Identity.Name, id)) + return new HttpStatusCodeResult(403); + + var model = new CreateTopicViewModel(); + model.Category = id; + return View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + [Authorize] + public ActionResult CreateTopic(CreateTopicViewModel model) + { + if (!ACL.CanPost(User.Identity.Name, model.Category)) + return new HttpStatusCodeResult(403); + + + var db = new ApplicationDbContext(); + var forum = db.ForumCategories.FirstOrDefault(x => x.Id == model.Category); + if (forum == null) + return new HttpStatusCodeResult(404); + + string subjectId = model.Subject; + char[] badChars = subjectId.Where(x => !AllowedChars.Contains(x)).ToArray(); + + foreach(var c in badChars) + { + subjectId = subjectId.Replace(c, '_'); + } + + while (subjectId.Contains("__")) + { + subjectId = subjectId.Replace("__", "_"); + } + + var topic = new ForumTopic(); + topic.AuthorId = User.Identity.GetUserId(); + topic.Id = Guid.NewGuid().ToString(); + topic.Discriminator = subjectId + "_" + db.ForumTopics.Count().ToString(); + topic.StartedAt = DateTime.Now; + topic.Parent = forum.Id; + topic.IsAnnounce = model.IsAnnounce; + topic.IsSticky = model.IsSticky; + topic.IsGlobal = model.IsGlobal; + topic.Subject = model.Subject; + var post = new ForumPost(); + post.AuthorId = topic.AuthorId; + post.Body = model.Body; + post.Id = Guid.NewGuid().ToString(); + post.Parent = topic.Id; + post.PostedAt = topic.StartedAt; + db.ForumTopics.Add(topic); + db.ForumPosts.Add(post); + db.SaveChanges(); + + return RedirectToAction("ViewTopic", new { id = topic.Discriminator }); + + } + + const string AllowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz"; + + [Authorize] + public ActionResult ViewTopic(string id, bool triedtolikeowntopic = false) + { + if (triedtolikeowntopic) + ViewBag.Error = "You cannot like or dislike your own topic!"; + var db = new ApplicationDbContext(); + var topic = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + if (topic == null) + return new HttpStatusCodeResult(404); + if (!ACL.CanSee(User.Identity.Name, topic.Parent)) + return new HttpStatusCodeResult(403); + + return View(topic); + } + } +}
\ No newline at end of file diff --git a/Project-Unite/Controllers/HomeController.cs b/Project-Unite/Controllers/HomeController.cs new file mode 100644 index 0000000..a3a8f7f --- /dev/null +++ b/Project-Unite/Controllers/HomeController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web; +using System.Web.Mvc; + +namespace Project_Unite.Controllers +{ + public class HomeController : Controller + { + public ActionResult Index() + { + return View(); + } + + public ActionResult About() + { + ViewBag.Message = "Your application description page."; + + return View(); + } + + public ActionResult Contact() + { + ViewBag.Message = "Your contact page."; + + return View(); + } + } +}
\ No newline at end of file diff --git a/Project-Unite/Controllers/LegalController.cs b/Project-Unite/Controllers/LegalController.cs new file mode 100644 index 0000000..9eef768 --- /dev/null +++ b/Project-Unite/Controllers/LegalController.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; + +namespace Project_Unite.Controllers +{ + public class LegalController : Controller + { + // GET: Legal/TOS + public ActionResult TOS() + { + return View(); + } + + // GET: Legal/Privacy + public ActionResult Privacy() + { + return View(); + } + } +}
\ No newline at end of file diff --git a/Project-Unite/Controllers/ManageController.cs b/Project-Unite/Controllers/ManageController.cs new file mode 100644 index 0000000..5c3a3e7 --- /dev/null +++ b/Project-Unite/Controllers/ManageController.cs @@ -0,0 +1,382 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.Owin; +using Microsoft.Owin.Security; +using Project_Unite.Models; + +namespace Project_Unite.Controllers +{ + [Authorize] + public class ManageController : Controller + { + private ApplicationSignInManager _signInManager; + private ApplicationUserManager _userManager; + + public ManageController() + { + } + + public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager) + { + UserManager = userManager; + SignInManager = signInManager; + } + + public ApplicationSignInManager SignInManager + { + get + { + return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); + } + private set + { + _signInManager = value; + } + } + + public ApplicationUserManager UserManager + { + get + { + return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); + } + private set + { + _userManager = value; + } + } + + // + // GET: /Manage/Index + public async Task<ActionResult> Index(ManageMessageId? message) + { + ViewBag.StatusMessage = + message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." + : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." + : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." + : message == ManageMessageId.Error ? "An error has occurred." + : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." + : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." + : ""; + + var userId = User.Identity.GetUserId(); + var usr = UserManager.FindById(userId); + return View(usr); + } + + // + // POST: /Manage/RemoveLogin + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey) + { + ManageMessageId? message; + var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey)); + if (result.Succeeded) + { + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + message = ManageMessageId.RemoveLoginSuccess; + } + else + { + message = ManageMessageId.Error; + } + return RedirectToAction("ManageLogins", new { Message = message }); + } + + // + // GET: /Manage/AddPhoneNumber + public ActionResult AddPhoneNumber() + { + return View(); + } + + // + // POST: /Manage/AddPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + // Generate the token and send it + var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), model.Number); + if (UserManager.SmsService != null) + { + var message = new IdentityMessage + { + Destination = model.Number, + Body = "Your security code is: " + code + }; + await UserManager.SmsService.SendAsync(message); + } + return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number }); + } + + // + // POST: /Manage/EnableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> EnableTwoFactorAuthentication() + { + await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true); + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + return RedirectToAction("Index", "Manage"); + } + + // + // POST: /Manage/DisableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> DisableTwoFactorAuthentication() + { + await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false); + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + return RedirectToAction("Index", "Manage"); + } + + // + // GET: /Manage/VerifyPhoneNumber + public async Task<ActionResult> VerifyPhoneNumber(string phoneNumber) + { + var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), phoneNumber); + // Send an SMS through the SMS provider to verify the phone number + return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); + } + + // + // POST: /Manage/VerifyPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code); + if (result.Succeeded) + { + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess }); + } + // If we got this far, something failed, redisplay form + ModelState.AddModelError("", "Failed to verify phone"); + return View(model); + } + + // + // POST: /Manage/RemovePhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> RemovePhoneNumber() + { + var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null); + if (!result.Succeeded) + { + return RedirectToAction("Index", new { Message = ManageMessageId.Error }); + } + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess }); + } + + // + // GET: /Manage/ChangePassword + public ActionResult ChangePassword() + { + return View(); + } + + // + // POST: /Manage/ChangePassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword); + if (result.Succeeded) + { + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess }); + } + AddErrors(result); + return View(model); + } + + // + // GET: /Manage/SetPassword + public ActionResult SetPassword() + { + return View(); + } + + // + // POST: /Manage/SetPassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task<ActionResult> SetPassword(SetPasswordViewModel model) + { + if (ModelState.IsValid) + { + var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword); + if (result.Succeeded) + { + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user != null) + { + await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); + } + return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess }); + } + AddErrors(result); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Manage/ManageLogins + public async Task<ActionResult> ManageLogins(ManageMessageId? message) + { + ViewBag.StatusMessage = + message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." + : message == ManageMessageId.Error ? "An error has occurred." + : ""; + var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); + if (user == null) + { + return View("Error"); + } + var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()); + var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList(); + ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1; + return View(new ManageLoginsViewModel + { + CurrentLogins = userLogins, + OtherLogins = otherLogins + }); + } + + // + // POST: /Manage/LinkLogin + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult LinkLogin(string provider) + { + // Request a redirect to the external login provider to link a login for the current user + return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId()); + } + + // + // GET: /Manage/LinkLoginCallback + public async Task<ActionResult> LinkLoginCallback() + { + var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); + if (loginInfo == null) + { + return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); + } + var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login); + return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); + } + + protected override void Dispose(bool disposing) + { + if (disposing && _userManager != null) + { + _userManager.Dispose(); + _userManager = null; + } + + base.Dispose(disposing); + } + +#region Helpers + // Used for XSRF protection when adding external logins + private const string XsrfKey = "XsrfId"; + + private IAuthenticationManager AuthenticationManager + { + get + { + return HttpContext.GetOwinContext().Authentication; + } + } + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError("", error); + } + } + + private bool HasPassword() + { + var user = UserManager.FindById(User.Identity.GetUserId()); + if (user != null) + { + return user.PasswordHash != null; + } + return false; + } + + private bool HasPhoneNumber() + { + var user = UserManager.FindById(User.Identity.GetUserId()); + if (user != null) + { + return user.PhoneNumber != null; + } + return false; + } + + public enum ManageMessageId + { + AddPhoneSuccess, + ChangePasswordSuccess, + SetTwoFactorSuccess, + SetPasswordSuccess, + RemoveLoginSuccess, + RemovePhoneSuccess, + Error + } + +#endregion + } +}
\ No newline at end of file diff --git a/Project-Unite/Controllers/ModeratorController.cs b/Project-Unite/Controllers/ModeratorController.cs new file mode 100644 index 0000000..7872112 --- /dev/null +++ b/Project-Unite/Controllers/ModeratorController.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web; +using System.Web.Mvc; +using Microsoft.AspNet.Identity; +using Project_Unite.Models; + +namespace Project_Unite.Controllers +{ + [Authorize] + public class ModeratorController : Controller + { + // GET: Moderator + public ActionResult Index() + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + ViewBag.Moderator = true; + return View(); + } + + public ActionResult UserDetails(string id) + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var usr = db.Users.FirstOrDefault(x => x.DisplayName == id); + if (usr == null || !ACL.Granted(User.Identity.Name, "CanViewUserInfo")) + return new HttpStatusCodeResult(403); + return View(usr); + } + + public ActionResult Users() + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanViewUserInfo")) + return new HttpStatusCodeResult(403); + + return View(new ApplicationDbContext().Users); + } + + public ActionResult Unban(string id, string returnUrl = "") + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanIssueBan")) + return new HttpStatusCodeResult(403); + var db = new ApplicationDbContext(); + + var usr = db.Users.FirstOrDefault(x => x.Id == id); + if (usr == null) + return new HttpStatusCodeResult(404); + if (usr.IsBanned == false) //we don't need to re-unban the user... save the SQL queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + usr.IsBanned = false; + + db.AuditLogs.Add(new Models.AuditLog(User.Identity.GetUserId(), AuditLogLevel.Moderator, $@"Moderator has unbanned {ACL.UserNameRaw(id)}.")); + + db.SaveChanges(); + + if (string.IsNullOrWhiteSpace(returnUrl)) + return RedirectToAction("Users"); + else + return Redirect(returnUrl); + } + + + public ActionResult Ban(string id, string returnUrl = "") + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanIssueBan")) + return new HttpStatusCodeResult(403); + var db = new ApplicationDbContext(); + + var usr = db.Users.FirstOrDefault(x => x.Id == id); + if (usr == null) + return new HttpStatusCodeResult(404); + if (usr.IsBanned == true) //we don't need to re-ban the user... save the SQL queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + usr.IsBanned = true; + usr.BannedAt = DateTime.Now; + usr.BannedBy = User.Identity.GetUserId(); + + db.AuditLogs.Add(new Models.AuditLog(User.Identity.GetUserId(), AuditLogLevel.Moderator, $@"Moderator has banned {ACL.UserNameRaw(id)}.")); + + db.SaveChanges(); + + if (string.IsNullOrWhiteSpace(returnUrl)) + return RedirectToAction("Users"); + else + return Redirect(returnUrl); + } + + + public ActionResult Unmute(string id, string returnUrl = "") + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanIssueMute")) + return new HttpStatusCodeResult(403); + var db = new ApplicationDbContext(); + + var usr = db.Users.FirstOrDefault(x => x.Id == id); + if (usr == null) + return new HttpStatusCodeResult(404); + if (usr.IsMuted == false) //we don't need to re-unmute the user... save the SQL queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + usr.IsMuted = false; + + db.AuditLogs.Add(new Models.AuditLog(User.Identity.GetUserId(), AuditLogLevel.Moderator, $@"Moderator has un-muted {ACL.UserNameRaw(id)}.")); + + db.SaveChanges(); + + if (string.IsNullOrWhiteSpace(returnUrl)) + return RedirectToAction("Users"); + else + return Redirect(returnUrl); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public ActionResult ChangeUserName(string id, ApplicationUser model, string returnUrl = "") + { + string acl_r = "CanEditUsernames"; + if (id == User.Identity.GetUserId()) + acl_r = "CanEditUsername"; + + if (!ACL.Granted(User.Identity.Name, acl_r)) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var usr = db.Users.FirstOrDefault(x => x.Id == id); + if (usr == null) + return new HttpStatusCodeResult(404); + + usr.DisplayName = model.DisplayName; + + db.SaveChanges(); + + if (string.IsNullOrWhiteSpace(returnUrl)) + return RedirectToAction("Users"); + else + return Redirect(returnUrl); + + } + + public ActionResult Lock(string id) + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var forum = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + if (forum == null) + return new HttpStatusCodeResult(404); + string perm = "CanLockTopics"; + var uid = User.Identity.GetUserId(); + if (forum.AuthorId == uid) + perm = "CanLockOwnTopics"; + + if (!ACL.Granted(User.Identity.Name, perm)) + return new HttpStatusCodeResult(403); + + if (forum.IsLocked == true) //Save the DB queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + forum.IsLocked = true; + + db.AuditLogs.Add(new AuditLog(uid, AuditLogLevel.Moderator, $"User has locked topic \"{forum.Subject}\" by {ACL.UserNameRaw(forum.AuthorId)}.")); + db.SaveChanges(); + + return RedirectToAction("ViewTopic", "Forum", new { id = id }); + } + + public ActionResult Unlock(string id) + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var forum = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + if (forum == null) + return new HttpStatusCodeResult(404); + string perm = "CanUnlockTopics"; + var uid = User.Identity.GetUserId(); + if (forum.AuthorId == uid) + perm = "CanUnlockOwnTopics"; + + if (!ACL.Granted(User.Identity.Name, perm)) + return new HttpStatusCodeResult(403); + + if (forum.IsLocked == false) //Save the DB queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + forum.IsLocked = false; + + db.AuditLogs.Add(new AuditLog(uid, AuditLogLevel.Moderator, $"User has unlocked topic \"{forum.Subject}\" by {ACL.UserNameRaw(forum.AuthorId)}.")); + + db.SaveChanges(); + + return RedirectToAction("ViewTopic", "Forum", new { id = id }); + } + + public ActionResult List(string id) + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var forum = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + if (forum == null) + return new HttpStatusCodeResult(404); + string perm = "CanUnlistTopics"; + var uid = User.Identity.GetUserId(); + if (forum.AuthorId == uid) + perm = "CanUnlistOwnTopics"; + + if (!ACL.Granted(User.Identity.Name, perm)) + return new HttpStatusCodeResult(403); + + if (forum.IsUnlisted == false) //Save the DB queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + forum.IsUnlisted = false; + + + db.SaveChanges(); + db.AuditLogs.Add(new AuditLog(uid, AuditLogLevel.Moderator, $"User has listed topic \"{forum.Subject}\" by {ACL.UserNameRaw(forum.AuthorId)}.")); + return RedirectToAction("ViewTopic", "Forum", new { id = id }); + } + + public ActionResult Unlist(string id) + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + var forum = db.ForumTopics.FirstOrDefault(x => x.Discriminator == id); + if (forum == null) + return new HttpStatusCodeResult(404); + string perm = "CanUnlistTopics"; + var uid = User.Identity.GetUserId(); + if (forum.AuthorId == uid) + perm = "CanUnlistOwnTopics"; + + if (!ACL.Granted(User.Identity.Name, perm)) + return new HttpStatusCodeResult(403); + + if (forum.IsUnlisted == true) //Save the DB queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + forum.IsUnlisted = true; + + db.AuditLogs.Add(new AuditLog(uid, AuditLogLevel.Moderator, $"User has unlisted topic \"{forum.Subject}\" by {ACL.UserNameRaw(forum.AuthorId)}.")); + db.SaveChanges(); + + return RedirectToAction("ViewTopic", "Forum", new { id = id }); + } + + + public ActionResult Bans() + { + var model = new ModeratorBanListViewModel(); + var db = new ApplicationDbContext(); + + model.UserBans = db.Users.Where(x => x.IsBanned == true); + model.IPBans = db.BannedIPs; + + return View(model); + } + + public ActionResult Logs() + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + + var db = new ApplicationDbContext(); + + return View(db.AuditLogs.Where(x => x.Level != AuditLogLevel.Admin)); + } + + public ActionResult Mute(string id, string returnUrl = "") + { + if (!ACL.Granted(User.Identity.Name, "CanAccessModCP")) + return new HttpStatusCodeResult(403); + if (!ACL.Granted(User.Identity.Name, "CanIssueMute")) + return new HttpStatusCodeResult(403); + var db = new ApplicationDbContext(); + + var usr = db.Users.FirstOrDefault(x => x.Id == id); + if (usr == null) + return new HttpStatusCodeResult(404); + if (usr.IsMuted == true) //we don't need to re-mute the user... save the SQL queries... + return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + + usr.IsMuted = true; + usr.MutedAt = DateTime.Now; + usr.MutedBy = User.Identity.GetUserId(); + + db.AuditLogs.Add(new Models.AuditLog(User.Identity.GetUserId(), AuditLogLevel.Moderator, $@"Moderator has muted {ACL.UserNameRaw(id)}.")); + + db.SaveChanges(); + + if (string.IsNullOrWhiteSpace(returnUrl)) + return RedirectToAction("Users"); + else + return Redirect(returnUrl); + } + + } +}
\ No newline at end of file |
