user3231232
user3231232

Reputation: 21

How to allow users to only edit their details in a catalog

I am starting on MVC 5 tutorial and implemented roles and users as followed by the link:

http://typecastexception.com/post/2013/11/11/Extending-Identity-Accounts-and-Implementing-Role-Based-Authentication-in-ASPNET-MVC-5.aspx

With Role based authentication in place, I now have an admin who can create users and also a catalog of companies listed under Catalog Controller. Only this user when logged in with his username and password set by the admin will be redirected to their company page they are representing to make the edits instead of the free control of editing everyone else's company.

How should I go about doing this?

This is my AccountController:

 [Authorize]
public class AccountController : Controller
{
    public AccountController()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
    {
    }


    public AccountController(UserManager<ApplicationUser> userManager)
    {
        UserManager = userManager;
    }


    public UserManager<ApplicationUser> UserManager { get; private set; }


    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }


    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindAsync(model.UserName, model.Password);
            if (user != null)
            {
                await SignInAsync(user, model.RememberMe);

            }

            //if (User.Identity.AuthenticationType == "CanEdit" )
            //{
            //    return RedirectToAction()
            //}
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }


    [Authorize(Roles = "Admin")]
    public ActionResult Register()
    {
        return View();
    }


    [HttpPost]
    [Authorize(Roles = "Admin")]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = model.GetUser();
            var result = await UserManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("Index", "Account");
            }

        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }


    [Authorize(Roles = "Admin")]
    public ActionResult Manage(ManageMessageId? message)
    {
        ViewBag.StatusMessage =
            message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
            : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
            : message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
            : message == ManageMessageId.Error ? "An error has occurred."
            : "";
        ViewBag.HasLocalPassword = HasPassword();
        ViewBag.ReturnUrl = Url.Action("Manage");
        return View();
    }


    [HttpPost]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Admin")]
    public async Task<ActionResult> Manage(ManageUserViewModel model)
    {
        bool hasPassword = HasPassword();
        ViewBag.HasLocalPassword = hasPassword;
        ViewBag.ReturnUrl = Url.Action("Manage");
        if (hasPassword)
        {
            if (ModelState.IsValid)
            {
                IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
                if (result.Succeeded)
                {
                    return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
                }
                else
                {
                    AddErrors(result);
                }
            }
        }
        else
        {
            // User does not have a password so remove any validation errors caused by a missing OldPassword field
            ModelState state = ModelState["OldPassword"];
            if (state != null)
            {
                state.Errors.Clear();
            }

            if (ModelState.IsValid)
            {
                IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
                if (result.Succeeded)
                {
                    return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
                }
                else
                {
                    AddErrors(result);
                }
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult LogOff()
    {
        AuthenticationManager.SignOut();
        return RedirectToAction("Index", "Home");
    }


    protected override void Dispose(bool disposing)
    {
        if (disposing && UserManager != null)
        {
            UserManager.Dispose();
            UserManager = null;
        }
        base.Dispose(disposing);
    }


    [Authorize(Roles = "Admin")]
    public ActionResult Index()
    {
        var Db = new ApplicationDbContext();
        var users = Db.Users;
        var model = new List<EditUserViewModel>();
        foreach (var user in users)
        {
            var u = new EditUserViewModel(user);
            model.Add(u);
        }
        return View(model);
    }


    [Authorize(Roles = "Admin")]
    public ActionResult Edit(string id, ManageMessageId? Message = null)
    {
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName ==id);
        var model = new EditUserViewModel(user);
        ViewBag.MessageId = Message;
        return View(model);
    }


    [HttpPost]
    [Authorize(Roles = "Admin")]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Edit(EditUserViewModel model)
    {
        if (ModelState.IsValid)
        {
            var Db = new ApplicationDbContext();
            var user = Db.Users.First(u => u.UserName == model.UserName);
            user.FirstName = model.FirstName;
            user.LastName = model.LastName;
            user.Email = model.Email;
            Db.Entry(user).State = System.Data.Entity.EntityState.Modified;
            await Db.SaveChangesAsync();
            return RedirectToAction("Index");
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    [Authorize(Roles = "Admin")]
    public ActionResult Delete(string id = null)
    {
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == id);
        var model = new EditUserViewModel(user);
        if (user == null)
        {
            return HttpNotFound();
        }
        return View(model);
    }


    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "Admin")]
    public ActionResult DeleteConfirmed(string id)
    {
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == id);
        Db.Users.Remove(user);
        Db.SaveChanges();
        return RedirectToAction("Index");
    }


    [Authorize(Roles = "Admin")]
    public ActionResult UserRoles(string id)
    {
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == id);
        var model = new SelectUserRolesViewModel(user);
        return View(model);
    }


    [HttpPost]
    [Authorize(Roles = "Admin")]
    [ValidateAntiForgeryToken]
    public ActionResult UserRoles(SelectUserRolesViewModel model)
    {
        if (ModelState.IsValid)
        {
            var idManager = new IdentityManager();
            var Db = new ApplicationDbContext();
            var user = Db.Users.First(u => u.UserName == model.UserName);
            idManager.ClearUserRoles(user.Id);
            foreach (var role in model.Roles)
            {
                if (role.Selected)
                {
                    idManager.AddUserToRole(user.Id, role.RoleName);
                }
            }
            return RedirectToAction("index");
        }
        return View();
    }


    #region Helpers

    private IAuthenticationManager AuthenticationManager
    {
        get
        {
            return HttpContext.GetOwinContext().Authentication;
        }
    }


    private async Task SignInAsync(ApplicationUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
    }


    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;
    }


    public enum ManageMessageId
    {
        ChangePasswordSuccess,
        SetPasswordSuccess,
        RemoveLoginSuccess,
        Error
    }


    private ActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

    #endregion
}

Edit View

Catalog Controller edit view:`@model Catalog.Models.Partner

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>


@using (Html.BeginForm("Edit", "Catalog", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Company, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Company, new { @class = "form-control", autocomplete = "off" })
                @Html.ValidationMessageFor(model => model.Company)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Description, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Description, new { @class = "form-control", autocomplete = "off" })
                @Html.ValidationMessageFor(model => model.Description)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Details, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextAreaFor(model => model.Details, new { @class = "form-control", rows = 10 })
                @Html.ValidationMessageFor(model => model.Details)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.InCharge, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.InCharge, new { @class = "form-control", autocomplete = "off" })
                @Html.ValidationMessageFor(model => model.InCharge)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Email, new { @class = "form-control", autocomplete = "off" })
                @Html.ValidationMessageFor(model => model.Email)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Logo, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                <input type="file" name="file" />
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<hr />
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

`

Upvotes: 2

Views: 5937

Answers (2)

Ariel Moraes
Ariel Moraes

Reputation: 648

Despite the date of the question I will leave my answer here because this question is relevant when you are developing a application and face the need to let only the logged user to see, edit or delete (or any other action) its own records.

The example is shown using the Entity Framework and ASP.NET Identity.

First of all you need to link your object model with the user. This is represented by the IdentityUser. Note that we are using the IdentityUser class because we are using ASP.NET Identity, otherwise we could use ClaimsIdentity.

public class UserObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IdentityUser User { get; set; }
}

After this point when you save a UserObject on your Context a field referencing the Id property of IdentityUser (the default name of the table for this class is AspNetUsers) will be created along with the UserObject table.

Then to get the current user you call HttpContext.GetOwinContext().Get<UserManager>() to get the UserManager after that you call UserManager.FindById(User.Idendity.GetUserId()). Just remember to get the IdentityDbContext associated with the Owin Context when inserting new objects otherwise a exception will be thrown.

public ActionResult Create(UserObjectViewModel uoViewModel)
{
    UserObject uo = new UserObject();

    // create the UserObject using the properties from the UserObjectViewModel
    ...

    // get the User Manager
    UserManager userManager = HttpContext.GetOwinContext().Get<UserManager>();

    // assign the current User
    uo.User = userManager.FindById(User.Identity.GetUserId())

    ...
}

From now on you can call User.Identity.GetUserId() to get the current User Id and send it along with your query parameters like this:

public ActionResult Get()
{
    string currentUserId = User.Identity.GetUserId();

    yourDbContext.Where(uo => uo.User.Id == currentUserId);

    ...
}

Upvotes: 2

Ross
Ross

Reputation: 323

When grabbing the database context, add a Where() clause that will just grab the information the current user belongs to. if you add this as a namespace using Microsoft.AspNet.Identity; then you can utilize the User.Identity.GetUserId() to return the current user's id and use that in your Where() clause to view only what the user/partner can see.

Upvotes: 3

Related Questions