summaryrefslogtreecommitdiff
path: root/Project-Unite/Controllers
diff options
context:
space:
mode:
Diffstat (limited to 'Project-Unite/Controllers')
-rw-r--r--Project-Unite/Controllers/AccountController.cs513
-rw-r--r--Project-Unite/Controllers/AdminController.cs567
-rw-r--r--Project-Unite/Controllers/ForumController.cs305
-rw-r--r--Project-Unite/Controllers/HomeController.cs31
-rw-r--r--Project-Unite/Controllers/LegalController.cs23
-rw-r--r--Project-Unite/Controllers/ManageController.cs382
-rw-r--r--Project-Unite/Controllers/ModeratorController.cs320
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