I have an MVC3 application that uses the standard accountcontroller that comes with visual studio to authenticate users.
I want to share specific parts of my program with people, much like google shares documents in google docs when you do that via email; in other words I don't want to give anyone access to the pages (In which case I could just remove the Authorize attributes) but I do want users to share pages based on a url with a hash in it, and have them skip the login.
I think I would like to generate the hash based on a page and link it to an anonymous user, which would then have to be auto-logged in if the hash is correct
How would I do that?
Create a table in database with shared pages information(controller, action, documentId, hash, expiresAt, etc)
Override Authorize attribute and in OnAuthorizationvalidate url params in your database.
public class SharedAuthorize:AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var documentHash = int.Parse(filterContext.RouteData.Values["hash"].ToString());
if (!HashRepository.CanWeRead(documentHash,controller, action, documentId))
{
return false;
}
return true;
}
}
This is just an idea=))
Related
I originally asked this question when Identity was in beta. The classes and interfaces have changed considerably since then and it appears the RTW version has some modifications again over the RC version. In principle I need to achieve the following.
authenticate the local login against my usertable tblMembers which contains the userid field and password which are the two items I need to authenticate.
have access to my tblMember record/class via the Controller.User property (Prior to MVC5 identity I had achieved this using the membership provider methods.) regardless of if the user logged in via the localuser method or via one of the other OAuth providers (Twitter, Google etc).
Ability to display my own custom username despite the login method. Local users login with a userid 1234567 and a password, ideally I would like to display "John Smith (1234567)" regardless of the authentication method (local/Twitter etc)
Initially I'm unsure as to what my memberclass should be inheriting from It appears from the
aspIdentitySample project that I should be using IdentityUser?
public partial class tblMember
{
public int id { get; set; }
public string membership_id { get; set; }
public string password { get; set; }
....other fields
}
Are there any new or updated examples of integrating your existing database/user tables with the ASP.NET Identity system?
I am also adding the identity tables to my database. If you create a new web project in visual studio 2013 you will see that now in RTM everything works better than RC plus you will see the
following table
public class ApplicationUser : IdentityUser
{
}
So Instead of ApplicationUser you can call your table tblMembers
public class tblMembers : IdentityUser
{
}
your table tblMembers will inherit Id Username Password security stamp and a discriminator column saying this is a tblMemeber
without making custom classes for authentication the easiest thing to do would be just to make the username the combination of your old usernames and userids. Then store the users real name or old username without the user id in a separate column.
have the users register with the built in user login and they can go to manage account and click use another service to log in. This will link the Google account to their regular account, so no matter which one they use it will log them in to the same account. If you have users with connected table information, I suggest you seed your table with all the users with something similar to the register method found in the template.Then just match the new combined username and Id to the old ones and populate data where needed in sql management studio.
Again a lot of issues in RC with extending IdentityUsers have been fixed. Microsoft is already adding more features to the identity user store and this tutorial http://www.windowsazure.com/en-us/develop/net/tutorials/web-site-with-sql-database/ is supposed to be updated soon. I plan on making my own walk through when i'm done changing my database but for now I hope my suggestions even though they are a simpler solution than you might want to implement.
You can do this easily by modifying the IdentityModel.cs as per the below:
Override OnModelCreating in your DbContext then add the following, this will change AspNetUser table to "Users" you can also change the field names the default Id column will become User_Id.
modelBuilder.Entity<IdentityUser>()
.ToTable("Users", "dbo").Property(p => p.Id).HasColumnName("User_Id");
or simply the below if you want to keep all the standard column names:
modelBuilder.Entity<IdentityUser>()
.ToTable("Users", "dbo")
Full example below (this should be in your IdentityModel.cs file) i changed my ApplicationUser class to be called User.
public class User : IdentityUser
{
public string PasswordOld { get; set; }
public DateTime DateCreated { get; set; }
public bool Activated { get; set; }
public bool UserRole { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>()
.ToTable("Users", "dbo").Property(p => p.Id).HasColumnName("User_Id");
modelBuilder.Entity<User>()
.ToTable("Users", "dbo").Property(p => p.Id).HasColumnName("User_Id");
}
}
Please note i have not managed to get this working if the current table exists.
Also note whatever columns you do not map the default ones will be created.
Hope that helps.
I'm starting to think (partially due to the lack of information in this area), that it may be easier to user the default identity classes, but to provide referential integrity to my own user table from the AspNetUsers table.
If i add a custom linking field into the AspNetUsers table is it possible to access my tables from the Controllers.User property? i.e. Controller.User.tblMember.Orders?
When navigating to a new webpage, is there a "Best Practice" for passing Ids around.
For example, a person registers to use a website, they get given an Id, this needs to be passed around the rest of the website/pages where it is used to retrieve relevant data from a database.
If the Id is passed in the url: http://myWebsite.com/User/Details/1234, the user could change it to
http://myWebsite.com/User/Details/4567 and potentially retireve a different user's details.
Putting this value in a hidden field and then POSTing wouldn't be great either as "view source" would display the value.
Many thanks
That's why you should always verify that this id belongs to the currently authenticated user. The currently authenticated user is stored in the forms authentication cookie and is something that the user cannot change because the value is encrypted. This cookie is emitted when the user logs in and you can access it everywhere where you have an instance to HttpContextBase (which is pretty much almost anywhere in the V and C parts of the MVC pattern).
For example, like this:
[Authorize]
public ActionResult Foo(int id)
{
string currentUser = httpContext.User.Identity.Name;
// TODO: go ahead and check in your backed that the id
// belongs to the currently connected user
...
}
Obviously writing those checks over and over again in all controller actions could quickly become boring, not to mention the DRYness of the approach. That's why it is recommended to write a custom Authorize attribute which will perform those checks before even entering into the controller action. Then you will decorate your controller actions with this custom attribute and you will know for sure that if the code has reached inside the action it means that the current user is the owner of the id passed as parameter. The way this id is passed as parameter doesn't really matter. Could be route data, query string, POST, whatever. The user can modify it as much as he likes. The important part is that you ensure that the value he entered is coherent with your domain authorization logic.
So:
public class AuthorizeOwnerAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the user is either not authenticated or not authorized
// no need to continue any further
return false;
}
// at this stage we know that the user is authenticated and
// authorized (in roles), so let's go ahead and see who this
// user is
string username = httpContext.User.Identity.Name;
// now let's read the id. In this example I fetch it from
// the route data but you could adapt according to your needs
string id = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
// Now that we know the user and the id let's go ahead and
// check in our backend if the user is really the owner
// of this id:
return IsOwner(username, id);
}
private bool IsOwner(string username, string id)
{
// go ahead and hit the backend
throw new NotImplementedException();
}
}
and then:
[AuthorizeOwner]
public ActionResult Foo(int id)
{
...
}
I understood how to communicate between Web, Worker role and the flow in MVC architecture.
My question is, after I query the data from a table in web role, how can the controller in MVC get this data to diplay in the view?
I tried using a global static variable in webrole, where the data gets populated, but when I access the static variable from the controller, it only returned 'null'. Why am I getting a null?
Thanks.
ok, in case you use the storage client, the implementation would be like:
Create your Model:
public class MyEntity : Microsoft.WindowsAzure.StorageClient.TableServiceEntity
{
public MyEntity()
{
PartitionKey = DateTime.UtcNow.ToString("MMddyyyy");
RowKey = string.Format("{0:10}_{1}",
DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
}
// Define the properties.
public string Title { get; set; }
public string Name { get; set; }
}
}
2. Define your context class:
public class MyDataContext : TableServiceContext
{
public MyDataContext(string baseAddress,
StorageCredentials credentials)
: base(baseAddress, credentials)
{ }
public IQueryable GetMyEntity
{
get
{
return this.CreateQuery("MyTableName");
}
}
}
Implement your controller action method:
public ActionResult Index()
{
var context = new MyDataContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
var results = from g in context.GetMyEntity
where g.PartitionKey ==
DateTime.UtcNow.ToString("MMddyyyy")
select g;
return View(results.FirstOrDefault());
}
this is reference code only, which is very ugly and will hardly work as it is, but it still provides an example of how you can query the table storage in your MVC project.
are we talking about an application whose MVC part is hosted in a worker role and which gets data from a web role which is querying the table storage? Or are we talking about a ASP.NET MVC application here which is hosted in a web role?
static variables is not a good idea at all because of concurrency issues.
in case of scenario 1, how do you communicate with a web role? via web service call directly?
you cold simply call the service from your controller or delegate the call to another layer and then put this data in your model which is then displayed by the corresponding view.
have you tried debugging this application locally using the [azure local dev env][1]
[1]: http://blogs.msdn.com/b/morebits/archive/2010/12/01/using-windows-azure-development-environment-essentials.aspx ? or do you use the real azure infrastructure? Are you sure you are getting the data from your query? maybe the query is wrong? have you observed any exceptions?
we need more information here to be able to help you
In my MVC application I am using membership service . I need a page to list the users. But there are 1000's of users are in my application. So i don't need to display all of them in one page.
I am planning to give a search option .I mean admin user can search by specifying user role and how many users can show in one page.How can i do this ? Any ideas?
current code
Model
public MembershipUserCollection Users { get; set; }
Controller
model.Users = Membership.GetAllUsers();
But i am getting all users in the application.
You probably want to query your role provider:
public ActionResult Foo()
{
string[] usernamesInRole = Roles.GetUsersInRole("some_role");
...
}
So I want to create a login page where when you enter your login credentials as a admin you get acces. If you are not a admin you get redirected back to the login page. In my database I have a field of boolean type:
isAdmin <--datatype(byte")
So how can you the best way do this?! I would like to do this in the repository pattern way as it gets easier to unit test it then.
I have googled this a lot and starting to get a bit confused on the matter. How many classes, models etc should I have?! I'm guessing one controller would do. Anyone got any good ideas?! I've read some on the DCI pattern about user roles but as it basically "only" to check that boolean in the database maybe it is overkill? Thankful for all feedback.
If I understand correctly, I had a similar issue. It seems from your question that you are not using the default membership provider (at least as is). I didn't either. So what I did was create a new authorization attribute. In your case it could look something like this:
public class AdminOnlyAttribute : AuthorizeAttribute {
IUserRepository _UserRepository;
public SimpleUser SimpleUser { get; set; }
public AdminOnlyAttribute() {
_UserRepository = new SqlUserRepository(new DbContext());
}
protected override bool AuthorizeCore(HttpContextBase httpContext) {
bool baseAuthorized = base.AuthorizeCore(httpContext);
if (!baseAuthorized) {
return false;
}
//Here you use your repository to check if a user is an admin or not
bool isAdmin = _UserRepository.IsAdmin(int.Parse(httpContext.User.Identity.Name));
if (!isAdmin) {
return false;
}
return true;
}
}
The repository method IsAdmin could be as simple as a query to check the boolean corresponding to the supplied user's ID. Something like this (please double check if SingleOrDefault() is necessary or not):
public bool IsAdmin(int userID) {
bool isAdmin = (from user in db.Users
where user.ID == userID
select user.isAdmin).SingleOrDefault();
return isAdmin;
}
And then use this in the action you want like so:
[AdminOnly]
public ActionResult Index(){
//Code here...
}
When this returns false, your ActionResult will be an HttpUnauthorizedResult which in theory should redirect to the login page.
You should create a custom Membership Provider and check the user isAdmin as part of ValidateUser.
Alternatively if other users are allowed in, use a custom role provider.
The following link is a good place to start
http://theintegrity.co.uk/2010/11/asp-net-mvc-2-custom-membership-provider-tutorial-part-1/
Is your isAdmin column a bit or a byte? It should probably be a bit. You could just create a query that checks the credentials and the IsAdmin column. If a row is returned then the login was successful.