How to get a subset of Users from ASP.NET Identity 2 - asp.net-mvc-5

Using a slightly modified version of the default ASP.NET MVC 5 template (with Individual Accounts), I am trying to get a subset of users based on an intermediary table. I have already built up an administration UI that can return a list of all users, but now I need to limit the set of users returned based on the currently logged in user's access privileges defined in the intermediary table.
Essentially, each user will have access to 1 or more clinics, so there will be one record for each clinic to which they have access.
If the currently logged in user belongs to a given role (e.g., "Clinic Admin"), then they should have the ability to retrieve a list of any users who belong to any of the clinics to which they have access.
Can anyone help point me in the right direction? This is my first Anything.NET application, so please feel free to explain like I'm five. :-)
Thank you in advance for any help you can offer.
Additional information:
Visual Studio 2013 Update 5
Entity Framework 6
MS SQL Server 2008 R2
Here is the intermediary table's class (ClinicUser):
[Table("clinic_users")]
public class ClinicUser
{
[Key]
public virtual ApplicationUser ApplicationUsers { get; set; }
[Required]
public string Id { get; set; }
[Required]
public System.Guid provider_id { get; set; }
[Required]
public System.Guid health_system_id { get; set; }
[Required]
public System.Guid clinic_id { get; set; }
}
Here is my ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return FirstName + " " + LastName; }
}
[ForeignKey("ClinicUsers")]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
public virtual ClinicUser ClinicUsers { get; set; }
public IEnumerable<SelectListItem> RolesList { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaims(ClinicClaimsProvider.GetClaims(userIdentity));
return userIdentity;
}
}
In case it wasn't clear, what I'm really trying to do is narrow the list of ApplicationUsers to return only the list of users to which I have access to based on the clinics we have have in common.
If I were writing this as a SQL query, this would be one way to accomplish what I want (I just can't seem to quite get what I want with LINQ):
SELECT *
FROM AspNetUsers au
WHERE Id IN (
SELECT Id
FROM clinic_users
WHERE clinic_id IN (
SELECT clinic_id
FROM clinic_users
WHERE Id = 'CurrentUserId'
)
)

First of all do not user much properties in ApplicationUser class, you can manage user profiles table and connect it with application user class, so you can put lot of information about user in profile table.
Next task is organize table of clinics, branches etc... and asociate application users with them.
Next you have 2 ways:
1. asociate application users with clinics or branches.
or
2. Manage them with roles.
Here is example with Application users:
[Table("Clinics")]
public class Clinic
{
[Key]
public string Id { get; set; }
public virtual ICollection<ClinicUser> ClinicUsers { get; set; }
}
[Table("ClinicUsers")]
public class ClinicUser
{
[Key]
[Column(Order = 0)]
public string ClinicId { get; set; }
[Key]
[Column(Order = 1)]
public string UserId { get; set; }
}
So next you need Other ViewModels to display them hope this help.
UPDATE
// GET: ClinicUsers by Clinic
public async Task<ActionResult> ViewCurrentClinicUsers(string id) // This is clinis ID
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Clinic clinic = await db.clinic.FindAsync(id); // Get Selectedclinic
if (clinic == null)
{
return HttpNotFound();
}
ClinicUsers model = new ClinicUsers() // ClinicUsers model
{
clinic = clinic, // View Currentclinic
ClinicUsers = await db.ClinicUsers.Were(x => x.clinicid == clinic.id)ToListAsync()) // Get Users that asigned to current clinic
};
return View(model);
}
UPDATE 2
And Finaly if you want display clinics were is assigned current loged user
// GET: Clinics by currentuser
public async Task<ActionResult> ViewClinicsWithCurrentUserAccess()
{
var currentuserId = User.Identity.GetUserId(); // This gets currentloged user id
var currentuser = await db.Users.SingleOrDefaultAsync(x => x.Id == myUserId); // This gets currentloged user virtual
return View(await db.Clinics.Were(x => x.clinicuserid == currentuserId).ToListAsync());
}

I solved this a while back, but I thought I had better come back here and update my question with an answer, in case this might help someone else.
I updated my Clinic and ClinicUser classes accordingly:
Clinic.cs
[Table("clinics")]
public class Clinic
{
[Key]
public System.Guid ClinicId { get; set; }
public List<ClinicUser> ClinicUsers { get; set; }
}
ClinicUser.cs
[Table("clinic_users")]
public class ClinicUser
{
[Key, Column(Order = 0)]
public string UserId { get; set; }
[Key, Column(Order = 1)]
public System.Guid ClinicId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser ApplicationUser { get; set; }
[ForeignKey("ClinicId")]
public Clinic Clinic { get; set; }
}
Also, I updated the following excerpt of my ApplicationUser class from this:
[ForeignKey("ClinicUsers")]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
public virtual ClinicUser ClinicUsers { get; set; }
to this:
public List<ClinicUser> ClinicUsers { get; set; }
Finally, in my ApplicationUsersController's Index() action, I was able to use this:
public async Task<ActionResult> Index()
{
if (User.IsInRole("Admin")) return View(await UserManager.Users.ToListAsync());
var userId = User.Identity.GetUserId();
//Get the Ids of the current user's clinics
var userClinics = db.ClinicUsers.Where(cu => cu.UserId == userId).Select(cu => cu.ClinicId).ToList();
//Get all userIds of the user at the current user's clinics
var clinicUserIds = db.ClinicUsers.Where(cu => userClinics.Contains(cu.ClinicId)).ToList().Select(cu => cu.UserId);
var users = UserManager.Users.Where(u => clinicUserIds.Contains(u.Id));
return View(await users.ToListAsync());
}
In essence, if the user has the "Admin" role, then they will get a list of all users in the database. If they aren't, they will only get a list of the users that also belong to the clinics they have in common.
It may not be perfect, but it works. If anyone has any suggestions on how to improve this, I would be glad to hear it.
Again, my thanks to Archil (https://stackoverflow.com/users/4089212/archil-labadze) for his helpful responses.

Related

Servicestack OrmLite deleting many to many

Let's say I have a ListingEvent class and a UserAccount class.
A ListingEvent can have many UsersAttending and a UserAccount can attend many ListingEvents.
The classes look like:
public class UserAccount
{
[AutoIncrement]
[PrimaryKey]
public int Id {
get ;
set;
}
public string Name {
get ;
set;
}
public UserAccount()
{
ListingEventsAttending = new List<UserAccountListingEvent> ();
}
[Reference]
public List<UserAccountListingEvent> ListingEventsAttending {
get;
set;
}
}
public class UserAccountListingEvent{
[AutoIncrement]
[PrimaryKey]
public int Id {
get;
set;
}
public Model.AttendingStatus Status { get; set; }
[References(typeof(UserAccount))]
public int UserAccountId {
get;
set;
}
[References(typeof(ListingEvent))]
public int ListingEventId {
get;
set;
}
}
public class ListingEvent
{
public ListingEvent()
{
UsersAttending = new List<UserAccountListingEvent>();
}
[AutoIncrement]
[PrimaryKey]
public int Id {
get ;
set;
}
public string Name { get; set; }
[Reference]
public List<UserAccountListingEvent> UsersAttending { get; set; }
public void RemoveUserAttending(UserAccount user)
{
if (user == null)
{
return;
}
UsersAttending.RemoveAll(u => u.UserAccountId == user.Id);
}
}
And I get a ListingEvent that has my UserAccount attending with:
var listingEvent = db.LoadSingleById<Model.ListingEvent> (request.Id);
And I can see that the user with the correct Id is attending so call RemoveUserAttending to remove the user. I can now see the user is not attending so I call:
db.Save (listingEvent, references: true);
But - now when I go to fetch that ListingEvent again the user is back to attending.
So my question is:
should the above work as expected?
if not - how should I be doing this?
db.Save() only INSERT or UPDATE entities i.e. it doesn't DELETE them.
To delete, retrieve the entities or entity Ids you want to delete and use OrmLite's db.Delete* API's explicitly, e.g. something like:
var removeUsersAttendingIds = listingEvent.UsersAttending
.Where(u => u.UserAccountId == user.Id)
.Select(u => u.Id);
db.DeleteByIds<UserAccountListingEvent>(removeUsersAttendingIds);

Save changes in database controller class asp.net mvc5 identity

Visual Studio 2013, ASP.NEt MVC 5 Identity
Please someone help me in how to save the information in the database through controller. Let me explain...I want the following to happen:
The user when logged is able update his Education information. After making changes in the fields, he will press the save button and the information is saved in the database. This works fine when i am doing so with the default aspnet users class, i take the usermanager. update method and the info is saved, but i am unable to do so with for any other table. I would be really thankful if someone helps me.
Here's the controller class edit method
public async Task<ActionResult> Edit(string id, Education education)
{
if (!User.Identity.IsAuthenticated)
{
Response.Redirect("~/Account/Login");
}
var db = new ApplicationDbContext();
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
educationdb.Qualification = education.Qualification;
educationdb.School = education.School;
educationdb.SchFrom = education.SchFrom;
educationdb.SchTo = education.SchTo;
educationdb.College = education.College;
educationdb.ClgFrom = education.ClgFrom;
educationdb.ClgTo = education.ClgTo;
educationdb.University = education.University;
educationdb.UniFrom = education.UniTo;
educationdb.Description = education.Description;
db.Entry(educationdb).State = System.Data.Entity.EntityState.Modified;
await db.SaveChangesAsync();
//return RedirectToAction("Index");
return View();
}
this is the model class:
namespace theme1.Models
{
public class Education
{
public string EducationID { get; set; }
public string UserID { get; set; }
public ApplicationUser User { get; set; }
public string Qualification { get; set; }
public string School { get; set; }
[DataType(DataType.Date)]
public DateTime SchFrom { get; set; }
[DataType(DataType.Date)]
public DateTime SchTo { get; set; }
public string College { get; set; }
[DataType(DataType.Date)]
public DateTime ClgFrom { get; set; }
[DataType(DataType.Date)]
public DateTime ClgTo { get; set; }
public string University { get; set; }
[DataType(DataType.Date)]
public DateTime UniFrom { get; set; }
[DataType(DataType.Date)]
public DateTime UniTo { get; set; }
public string Description { get; set; }
}
}
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID); this line gives me an error Exception Details: System.InvalidOperationException: Sequence contains no elements
Source Error:
Line 109: var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Instead of :
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Try this :
var educationdb = db.Edu.Where(u => u.EducationID == education.EducationID).FirstOrDefault();
OR
Instead of :
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Try this :
var educationdb = db.Edu.FirstOrDefault(u => u.EducationID == education.EducationID);
I suspect that your missing the DBSet for Education model in ApplicationContext.
If this doesn't fix it, could you please provide the code for your ApplicationContext class?
I cant comment on your discussion with Kartikeya since I am new to stackoverflow and lack reputation.
But after you changed your lambda:
var educationdb = db.Edu.Where(u => u.EducationID ==
education.EducationID).FirstOrDefault();
From your discussion with Kartikeya It sounds like you are thinking of Creating rather than editing. You cant edit something that doesn´t exist.
// GET: Model
public ActionResult Create()
{
return View();
}
in MVC 5 it is very easy to scaffold the view by right clicking inside that Action and chosing "add view" and insert the information you need, and when you have created the view, create your post method for Create:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include ="EducationID, UserId....")] Education education)
{
if (ModelState.IsValid)
{
//any extra logic you might want
db.Edu.Add(education);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(education);
}

ServiceStack issue with object serialization between test method and service

Good day,
We are experiencing an issue with serialization where a request object set with a value for one property ends up being received by the service with the value assigned to a different property. Please see below for more information.
We are using the 3.9.71 version of ServiceStack NuGet packages. The solution is made up of the following projects:
Project.Host: Used for self-hosting ServiceStack and contains Service classes.
Project.DTO: All services DTOs and surrounding classes.
Project.Tests: Contains unit tests.
The problems has been identified to only one class/service, namely MinimalUser and MinimalUserService, which you can see code for both below:
MinimalUser.cs
namespace Project.DTO
{
[Route("/User/{Identity}", "GET")]
[Route("/User/{Username}", "GET")]
[Route("/User/{DisplayName}", "GET")]
public class MinimalUser : IReturn<MinimalUser>
{
#region Properties
public int? Identity { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Language { get; set; }
public string TimeZone { get; set; }
public string Culture { get; set; }
public List<string> Roles { get; set; }
public List<string> Permissions { get; set; }
public DiscUserDetails DiscUserDetails { get; set; }
#endregion
#region Constructors
public MinimalUser() { }
public MinimalUser(UserAuth auth)
{
if (auth != null)
{
this.Identity = auth.Id;
this.Username = auth.UserName;
this.DisplayName = auth.DisplayName;
this.Email = auth.Email;
this.FirstName = auth.FirstName;
this.LastName = auth.LastName;
this.Language = auth.Language;
this.TimeZone = auth.TimeZone;
this.Culture = auth.Culture;
this.Roles = auth.Roles;
this.Permissions = auth.Permissions;
this.DiscUserDetails = auth.Get<DiscUserDetails>();
}
}
#endregion
#region Methods
public static MinimalUser FromUserAuth(UserAuth auth)
{
return auth == null ? new MinimalUser() : new MinimalUser
{
Identity = auth.Id,
Username = auth.UserName,
DisplayName = auth.DisplayName,
Email = auth.Email,
FirstName = auth.FirstName,
LastName = auth.LastName,
Language = auth.Language,
TimeZone = auth.TimeZone,
Culture = auth.Culture,
Roles = auth.Roles,
Permissions = auth.Permissions,
DiscUserDetails = auth.Get<DiscUserDetails>()
};
}
#endregion
}
}
DiscUserDetails.cs
namespace Project.DTO
{
public class DiscUserDetails
{
public int? LocationId { get; set; }
public bool IsActive { get; set; }
public byte NumberOfFailedLoginAttempts { get; set; }
public bool MustChangePasswordAtNextLogon { get; set; }
public int? LastAcceptedPolicyId { get; set; }
}
}
MinimalUserService.cs
namespace Project.Services
{
[Authenticate]
[RequiredRole(new string[] { RoleNames.Admin })]
public class MinimalUserService : Service
{
IUserAuthRepository authRepo = AppHost.Resolve<IUserAuthRepository>() as OrmLiteAuthRepository;
/// <summary>
/// Return a minimalist structure of user insensitive information.
/// </summary>
/// <param name="request">The request containing the ID of the user.</param>
/// <returns>A minimalist structure of user insensitive information.</returns>
public object Get(MinimalUser request)
{
if (request.Identity != null)
return new MinimalUser(authRepo.GetUserAuth(request.Identity.ToString()));
else if (request.Username != null)
return new MinimalUser(authRepo.GetUserAuthByUserName(request.Username));
else
return null;
}
}
}
From my test project, I run the following test:
[TestMethod]
public void GetMinimalUserByUsername()
{
AuthResponse authResponse = client.Post<AuthResponse>("/auth", new Auth
{
UserName = "accountwithadminrole",
Password = "blablabla",
RememberMe = true,
provider = CredentialsAuthProvider.Name
});
MinimalUser request = new MinimalUser
{
DisplayName = BaseAccounts.System,
};
MinimalUser user = client.Get<MinimalUser>(request);
Assert.IsNotNull(user);
}
I clearly see, before issuing the client.Get method, that the request object have all its properties set to null except for the DisplayName which has the value "system". When this request is received by the MinimalUserService Get method, the value "system" is now assigned to the property UserName and DisplayName is null.
Also, I've tried to comment properties one by one in the MinimalUser class, suspecting one of its field could be causing serialization problem and I would end up having random 'Bad Request' when commenting a certain number of properties. Although, I could comment a properties randomly and one property that previously caused a 'Bad Request' would not do it depending on others properties commented out.
I'm really confused about how this can possibly happens. I feel the service and DTO are simple compared to others from this same project but I'm sure I'm doing something really stupid here.
Don't hesitate to ask for more details, it will be my pleasure to give all information you need.
Thank you.
The reason for Username to be populated instead of DisplayName is because of the routes you have defined for MinimalUser. In MinimalUser.cs you defined 2 identical routes:
[Route("/User/{Identity}", "GET")]
[Route("/User/{Username}", "GET")]
[Route("/User/{DisplayName}", "GET")]
Username and Displayname are both strings. This makes it impossible for ServiceStack to determine the appropriate route direct the request to as it cannot differentiate between the routes. You can fix this by either removing a route, or by adding additional text to one of the routes; eg /User/ByDisplayName/{Username}.

DDD/CQRS, Entity has access to Query, Command?

public class PageRoleService
{
public void SetRoles(Page page, User activeUser)
{
var rb = page.Project.ProjectType.GetRoleFor(activeUser.UserType);
page.RolesForPage.Add(activeUser, rb);
var managers = GetAllManagersOf(activeUser);
foreach (var m in managers)
{
page.RolesForPage.Add(m, rb);
}
}
}
public class Project : Entity
{
public ProjectType ProjectType { get; set; }
public IList<Page> Pages { get; set; }
}
public class Page : Entity
{
public string Name { get; set; }
public Project Project { get; set; }
public IDictionary<User, RoleBehaviour> RolesForPage { get; set; }
}
public class ProjectType : Entity
{
public IQueryProcessor QueryProcessor { get; set; }
public IList<RoleBehaviour> RoleBehaviours { get; set; }
public RoleBehaviour GetRoleFor(USerType userType)
{
var behaviour = return QueryProcessor.Execute(new GetRolesByUserAndProjectTypeQuery() {
ProjectType = this,
UserType = userType
});
// Filter behaviour attributes for project type properties, business rules, etc...
// FilterBehaviour(behaviour);
return behaviour;
}
}
public class GetRolesByUserAndProjectTypeQuery
{
public UserType UserType { get; set; }
public ProjectType ProjectType { get; set; }
}
public class GetRolesByUserAndProjectTypeQueryHandler
{
public Db Db { get; set; }
public RoleBehaviour Execute(GetRolesByUserAndProjectTypeQuery query)
{
return Db.FirstOrDefault(r => r.UserType == query.UserType && r.ProjectType == query.projectType);
}
}
public class RoleBehaviour : Entity
{
public Role ROleForArea1 { get; set; }
public Role ROleForArea2 { get; set; }
public UserType UserType { get; set; }
public ProjectType ProjectType { get; set; }
public IDictionary<string, string> Attributes { get; set; }
}
public enum UserType
{
A,
B,
C,
D
}
public class Role : Entity
{
public IList<string> Permissions { get; set; }
}
I don't use repository, no need data abstraction, I use CQRS for crud operations. (CreateProjectCommand, GetRolesByUserAndProjectTypeQuery, etc..)
Users related a lot of project and page. Users have more than role for each Page Entity and is dynamically created when user (client) request to fetch All projects page or single page item.
My Page Role Service determinates page roles for active user and its managers. My MVC Controller use PageRoleService.
PageRoleService is Application Service or Domain Service or .....?
QueryProcessor in Entity (ProjectType) is invalid approach? How can handle this/their problems without lazy or eager loading?
RoleBehaviour is Entity or Value Object?
PageRoleService is a service or business logic in domain?
I know that I'm some years later, but:
I would put away the base class Entity, because it looks that this are just Dtos returned by the queryhandler (infact GetRolesByUserAndProjectTypeQueryHandler.Execute returns a RoleBehaviour).
Given this, I think that:
PageRoleService is a simple service that completes a Dto, hence it looks a kind of factory
Given that ProjectType here has two different roles (a Dto and Entity, and this is against CQRS), if:
it's a Dto, then use a service/factory/ORM to load extra data on it
it's an Entity, try to load all the data that's needed by it. This because there're great changes that you'll need it on the way to execute your command (great explanation about DDD and entities).
The object has it's own identity? Has it an Id that, even if things will change, remains the same? Looking at it, it looks just a Dto, with nothing really interesting (at business level).
see 1.

Entity Framework Insert object with related object

I am a newbie with Entity Framework and I need to insert an object Comment that has a related FK object User into the database.
public Class Comment
{
public int CommentID { get; set; }
public string CommentContent { get; set; }
public virtual User User { get; set; }
public virtual DateTime CommentCreationTime { get; set; }
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string UserPassword { get; set; }
public string UserImageUrl{get; set;}
public DateTime UserCreationDate { get; set; }
public virtual List<Comment> Comments { get; set; }
}
public void AddComment()
{
User user = new User() { UserID = 1 };
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
var ctx = new WallContext();
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}
Ideally, with T-SQL, if I know the PRIMARY KEY of my User object, I could just insert my Comment object and specify the PK of my 'User' in the insert statement.
I have tried to do the same with Entity Framework and it doesn't seem to work. It would be overkill to have to first fetch the User object from the database just to insert a new 'Comment'.
Please, how can I achieve this ?
You need to attach the user object to the context so that the context knows its an existing entity
public void AddComment()
{
var ctx = new WallContext();
User user = new User() { UserID = 1 };
ctx.Users.Attach(user);
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}

Resources