My current site uses two databases, one that stores the users directly and another one that stores additional data using the user email. I need to extract data (an identification number) from this other table, so I have created a model that operates like this:
ZivoyAccount.cs
namespace ZivoyPublicaciones.Models
{
public class ZivoyAccount
{
//Database for additional data
private PPublicEntities db = new PPublicEntities();
//Database with user roles
private zivoypostEntities userdb = new zivoypostEntities();
public Int64 UserIdentification {
get {
//Get the User Identity
String UserIdentity = HttpContext.Current.User.Identity.GetUserId();
var UserData = userdb.AspNetUsers.Find(UserIdentity);
//Get the User Email
var UserEmail = UserData.Email;
//Get the Data from Other Database
var ZivoyUserData = db.Users.Find(UserEmail);
return ZivoyUserData.user_gkey;
}
}
}
}
But I have been reading that this isn't the right behaviour since this variables get set for all users, and I need them to be set for each user. How could I accomplish this?
I'm calling my model on a father controller so that I can use this variable on all my child controller
AuthorizationController.cs
namespace ZivoyPublicaciones.Controllers
{
[Authorize]
public class AuthorizationController : Controller
{
protected ZivoyAccount UserProfiler = new ZivoyAccount();
}
}
Related
In my ASP.NET MVC program, I have a Model that looks like this:
public class PaycheckTypeViewModel
{
public int Id { get; set; }
public string Type { get; set; }
public int CustomSortOrder { get; set; }
}
In my database, Id is an identity column. In my controller I am creating a new record like this:
[HttpPost]
public async Task<ActionResult> Create(PaycheckTypeViewModel paycheckType)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost/api/");
var responseMessage = await client.PostAsJsonAsync<PaycheckTypeViewModel>("paycheck-type", paycheckType);
if (responseMessage.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
}
ModelState.AddModelError(string.Empty, "Error calling API to Create New Paycheck Type.");
return View();
}
The paycheckType parameter contains data like this:
CustomSortOrder 5
Id 0
Type New Paycheck
The API that handles this Post Request knows to ignore the Id because its an identity column. But my question is what is usually done in this case? I thought of using [JsonIgnore] in the Model, but I want the Id to be serialized for displaying on the screen. It doesn't break anything by having the Id in the paycheckType parameter, but I thought this might not be the best practice.
well, best practice for design api is to separate the actions and what they need.
for example for creating you don't need to pass id so create a dto without id.
but when you want to update you need id so create a dto that contains id.
just remember that a method responsible just for one action. SOLID principle
I have this method in a Controller:
[Route("api/AppSearch/Search")]
[HttpPost]
[Authorize]
public async Task<ResponseEntity<AppSearchResponse>> Search([FromBody] AppSearchRequest request, string type = "")
{
IEnumerable<AppSearchResponse> data = await SearchServiceV2.Search(request.SearchCriteria, request.AppKeyColumn, request.Filters, request.MaxResults, type);
if (data == null)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("Unknown type ", type)),
ReasonPhrase = "Unknown type"
};
throw new HttpResponseException(resp);
}
return this.AssembleSuccessResponse<AppSearchResponse>(data);
}
I want to return different data based on if the user is authorized or not. If the user is authorized, I want it to return AppSearchResponse and if not, a class with 3 members of AppSearchResponse.
My initial though is create AppSearchResponseBase that has those 3 fields and then have AppSearchResponse extend it. Then, check if the user is authorized inside the Controller method and serialize the data variable to either the AppSearchResponseBase or AppSearchResponse
e.g.
if(authorized){
return this.AssembleSuccessResponse<AppSearchResponse>(data)
} else {
return this.AssembleSuccessResponse<AppSearchResponseBase>(data)
}
Am I better off just creating a new resource like api/AppSearch/SearchUnauthorized/ ?
I would do something like have a single class of AppSearchResponse that has info like:
public AppSearchResponse{
public string Username {get;set;}
public DateTime Joined {get;set;}
// etc
public ExtendedData ExtendedData {get;set;}
}
And then only populate ExtendedData (which would contain the "secured" fields) if the user has the appropriate access. On the client side, ExtendedData would be null if the client doesn't provide the appropriate credentials or whatever.
I need your help. I wanna implement a database request with different types depending on the users role.
My current query looks like this:
var myVar = session
.Query<TypeA>()
.Where(xyz)
.ToList();
Now I wanna do it like this:
if(UserIsAdmin)
Type T = TypeA;
else
Type T = TypeB;
var myVar= session
.Query<T>()
.Where(xyz)
.ToList();
Notice TypeA an TypeB should be dynamically changed in .Query<T>.
Is there any good way to do this? Do you need more information?
Thanks in advance :-)
EDIT:
The scenario looks as follows:
I have to get different models from the database depending on the users roles. Lets say the user is Admin he can see firstname, lastname, address, if not he can only see firstname, lastname.
I thought to create two different models with these attributes and change the type in the query dynamically.
You need to understand first the consept of seperating layers.
So first of all you will have a DB layer, which in your case(though i don't know you db schema), but it should look something like:
public class UsersDbServices
{
public UserDbEntity GetUserById(int userId)
{
UserDbEntity user = null
using (context..)
{
user = context.Users.Where(u=> u.Id = userId).FirstOfDefault();
}
return user;
}
}
You will call this service from you Logic layer, note that you can use the same method for both normal user and administrator!
Now for the clinet side I would return a DTO(data transfer object) which will be the same for both cases!:
public class UserDTO
{
public int UserId { get;set;}
public string FirstName { get;set;}
public string LastName { get;set;}
public string Address { get;set;}
}
Now the last part is the controller, here you will get the data from the DBLayer(you can go through another layer of logic, but lets say its not a must), and what you will do now in the controller you can poppulate the DTO(and again you can do this all in the logic layer as well) in this way:
public class UsersController
{
UsersDbServices m_UsersDbServices = new UsersDbServices ();
[HttpGet]
public UserDTO GetUserById(userId)
{
UserDTO retVal = null;
UserDbEntity userDbEntity = m_UsersDbServices.GetUserById(userId);
if(userDbEntity == null){
throw...
}
retVal = new UserDTO()
{
FirstName = userDbEntity.FirstName;
...
};
if(!UserIsAdmin) <- kept it as a bool here
{
retVal.Address = null;
}
return retVal; <- convert to json
}
}
Another approach is to use another DTO like AdminUserDTO and RegularUserDTO
I know by creating an user service with entity framework and creating a password hash i can add one or more users to the website! but i'd like to use asp.net identity features even when i want to register batch users (like upload users list with excel or xml file).
In my scenario i want register more than one users on the website by uploading users list in xml,json or excel file. And i want to register all of them in one transaction.
Has anyone idea?
Depends at what level of abstraction you want to work at, but by just looping over the content of the default Register method, you could do something like this:
[HttpPost]
public async Task<IActionResult> RegisterLotsOfPeople(RegisterLotsModel model)
{
var successful = new List<string>();
var failed = new List<string>();
foreach (var toRegister in model.ApplicationUsers)
{
var user = new ApplicationUser {UserName = toRegister.UserName, Email = toRegister.Email};
var result = await _userManager.CreateAsync(user, toRegister.Password);
if (result.Succeeded)
{
successful.Add(toRegister.UserName);
}
else
{
failed.Add(toRegister.UserName);
}
}
return Json(new {SuccessfullyRegistered = successful, FailedToRegister = failed});
}
You'd have to post the data to the end point in JSON format.
The DTO classes are:
public class RegisterLotsModel
{
public List<UserToRegister> ApplicationUsers { get; set; }
}
public class UserToRegister
{
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
There's lots of other way to do what you're trying to achieve, but this is quick, dirty and would likely work fine. You may need to send the confirmation email in the success part of the method, if email confirmation is required.
Edit - Single Transaction
To insert all the new users in one transaction, you would have to generate their password hashes first, and then insert them into the database.
To get a hash of a given password, you can use the password hasher provided by the UserManager:
foreach (var toRegister in model.ApplicationUsers)
{
var hasher = _userManager.PasswordHasher;
var user = new ApplicationUser {UserName = toRegister.UserName, Email = toRegister.Email};
toRegister.Hashed = hasher.HashPassword(user, toRegister.Password);
}
You could then manually insert these into the database. This would be enough for basic password authentication, but if your implementation uses things like SecurityStamp, you would likely need to implement the appropriate methods when creating the user.
The big picture is this: when a user logs in, they can select an "organization" to log into from a drop down list, which has a unique ID # for each organization. When the user logs in, I want to grab their ID # from my people table. The problem is, the person could be in the table twice, like this:
ID Name OrgID
1 Brandon 1
2 Brandon 2
So, to grab the appropriate person ID, I need to check against the OrgID also. Here is what I've got in my GET method:
public ActionResult Index()
{
ViewBag.Organization = new SelectList(db.Organizations, "OrgID", "OrgName");
return View();
}
Here is my POST method:
[HttpPost, ActionName("Submit")]
public ActionResult SubmitPost(string LoginNID, string LoginPassword, LoginViewModel model)
{
//Create new instance of ActiveDirectoryHelper to access its methods
ActiveDirectoryHelper login = new ActiveDirectoryHelper();
//Get Username and Password from forms
String Username = LoginNID;
String Password = LoginPassword;
int OrgID = model.Organizations.OrgID;
//ViewBag initialization for error message
ViewBag.NumTimes = 0;
ViewBag.Message = "Error logging in, please try again.";
//LDAP Authentication
bool LoginPassed = login.IsAuthenticated(Username, Password);
int Organization = organizations.OrgID;
//if Authentication Success enter site, otherwise Return error message
if (LoginPassed == true)
{
//grabs the one person from the People model whose NID is == to Username
var peopleModel = db.People.Single(g => g.NID == Username);
//grabs the peopleID for use in making a session variable
var peopleID = peopleModel.PeopleID;
//sets a session variable to be used that is the logged in person's ID
Session["peopleID"] = peopleID;
return View("LoginSuccess");
}
else
{
ViewBag.NumTimes = 1;
ViewBag.Organization = new SelectList(db.Organizations, "OrgID", "OrgName", organizations.OrgID);
return View("Index");
}
}
Here is my ViewModel:
public class LoginViewModel
{
public Music.Models.Organizations Organizations { get; set; }
}
Any ideas as to how I can get the organization's ID # and then select the unique person ID # so I can make that a session variable? Thanks much!
Edit: updated code to reflect changes I've made from answers provided.
You can create a class of something like session information with the id to capture that immediately after login. Have your login perform login verification, then route to the session info page where you select the organization based off users available organization. Pass that to your helper class that stores it in session information.
Edit
After re-reading your question you can provide a list of organizations at the login page and do a verification against whatever you are doing currently and ensuring it matches organization too so it is only one screen.
public class LoginCredential
{
public string UserName { get; set; }
public string Password { get; set; }
public int OrganizationId { get; set; }
}
public ActionResult Index()
{
ViewBag.Organizations = new SelectList(db.Organizations, "OrgID", "OrgName");
return View();
}
Make the model for your login page a login credential
#model MyProject.Models.LoginCredential
Then when you do a submit from your form it will pass in the selected options.
[HttpPost, ActionName("Submit")]
public ActionResult Login(LoginCredential credentials)
{
String Username = credentials.UserName ;
String Password = credentials.Password ;
Int OrganizationId = credentials.OrganizationId;
/// Rest of your code goes here
}
After creating a ViewModel based on kadumel's answer, I was still getting the same error as before. Since the username and password were both getting posted back from the view, I only needed 1 model to be used by the view to get the OrgID. What I did was change the #model directive in my view to this:
#model Music.Models.Organizations
and then changed the parameters in my POST to:
public ActionResult SubmitPost(Organizations model, string LoginNID, string LoginPassword)
From there, I was able to get the OrgID by doing this:
int OrgID = model.OrgID;
From debugging, OrgID is finally getting the correct value, and it says that the value of "model" is no longer null. While this works, I'm not completely sure why, since I would think that using a ViewModel would do just the same thing because my ViewModel contained the Organizations model.
EDIT: I figured out why this was working and using a ViewModel didn't seem to: in my view I had
#Html.DropDownList("Organization", ViewData["Organizations"] as SelectList, "--Select Organization--")
The first parameter, "Organization" is nothing in my model, so of course the model was going to return null and not give me the ID. When I changed it to:
#Html.DropDownList("OrgID", ViewData["Organizations"] as SelectList, "--Select Organization--")
It passed back the ID and I was good to go.