I am creating an ASP.NET MVC application with Identity and I decided to go with Azure Table Storage since it's very cheap and fast for my needs. The application will have two types of accounts: single user and multi-users with one owner. So I modeled this:
public class User : TableEntity, IUser
{
/// <summary>
/// The Email of the owner of the account
/// </summary>
public string Id { get { return PartitionKey; } set { PartitionKey = value; } }
/// <summary>
/// The Email of the user of the account
/// </summary>
public string UserName { get { return RowKey; } set { RowKey = value; } }
//other fields not relevant
}
I am wondering how to make this effective for logging in and searching for a user, however. The Store for Identity has this:
public async Task<User> FindByIdAsync(string userId)
{
try
{
TableResult operation = await UsersTable.ExecuteAsync(TableOperation.Retrieve<User>(userId, userId));
return operation.Result as User;
}
catch (Exception)
{
return null;
}
}
If the user trying to log in is the owner of the account, this will be a Single Point query and, according to the docs, have the best performance.
However, if the user is not the owner, that would result in a full table scan since a PartitionKey would not be available to filter on it, so the query would have the worst performance.
The other idea I have would be to make the user the PartitionKey and the owner the RowKey, but that would make it every single search a Range Query and have less performance than the Single Point queries.
How could I best design this? This is a new app, so there is no existing user base.
A few recommendations I would like to make (based on the app we've built which also uses Table Storage exclusively).
Introduce a new entity called Account or Subscription. You can use a GUID to uniquely identify an account.
Along with user information (user id, password etc.) in the user's table, store the account information as well. So at all times when you're authenticating a user, you're making point queries. Also store the user's role (Member or Owner) in the user's table. Doing this way, you would know if the user is an individual user or part of a team. If the user is part of a team, then what the role of that user in that team (Owner or Member).
Store 2 copies of user data. First copy is what you're using now. Second copy essentially associate a user with an account where the PartitionKey of the second copy is the account id and row key is the PartitionKey of the first copy. This will help you find the users in a particular team.
Individual User Login Scenario
So an individual user logs in using their username/password. You retrieve the information about the user and there you find out that the user is an individual user based on the account type.
Team Owner Login Scenario
So when a team owner logs in, based on the login you will find out that the user is a team owner. If the team owner needs to find the list of all the users in the team, you do another query (it will be the PartitionKey query only) which will give you information about all the users in the team.
Team User Login Scenario
So when a team user logs in, based on the login you will find out that the user is a team user. Since the user is a team user, he/she need not query the table to find out about other users in the team. Even if they do, you will do another query (same as team owner query) to get the list of users in a team.
Related
I am creating an application in that there is role-based module management and it is changed anytime by anytime.
Scenario:
If a user has access to create and view Employee than a user can do only create and view employees but in future admin change user's role from creating and view to view and delete than a user can only do that activity.
I try with [Authorize(Roles ="Staff")] but if admin change runtime than it isn't managed.
Can anyone look into this and get back to me?
This is a complicated question and there isn't a right answer but there are several ways to do it.
First I will assume you are using stateless auth using a claim based jwt the simplest way is writing your own Policy that will read user roles before every request, this is the simplest way to do it and fastest to implement.
internal class DatabaseRoles : IAuthorizationRequirement
{
public string Role { get; }
public DatabaseRoles(string role)
{
Role = role;
}
}
internal class DatabaseRolesHandler : AuthorizationHandler<DatabaseRoles>
{
private readonly UserManager<IdentityUser> userManager;
public DatabaseRolesHandler(UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)
{
this.userManager = userManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, DatabaseRoles requirement)
{
//NOTE this is the out of the box implementation of roles and simple query to get the roles from the EF backed database. I would recoment makeing a custom privelages store for this and not using roles for this but access rights
var user = await userManager.FindByIdAsync(userManager.GetUserId(context.User));
if (await userManager.IsInRoleAsync(user, requirement.Role))
{
context.Succeed(requirement);
}
}
}
But this solution is not that performant because it requires a call to the database on every request. This is fine on small loads but might create problems in traffic.
The other way is to reevoke the all user tokens when the roles change but this is super complicated. I am sure if you create some fast access store for roles like redis there will be no issues to do the check on every call. Also I do not recommend creating your own user storage because it's a nightmare to maintain and keep up to date in regards to security standards.
If you are using Session/Cookie to store the logged in user details, you can empty the details whenever Admin make role changes. On every action you can check the role in Session/Cookie and move forward.
Now as soon as user clicks anywhere on the screen which hits the controller. The condition will be checked and the User will be logged out as the Session/Cookie object is empty.
To generate the needed tokens - consumer key, consumer secret, token ID, token secret - we are creating an integration, and access tokens, and assigning them to an employee with a specific role that has access to TBA. (Refer to https://medium.com/#morrisdev/netsuite-token-based-authentication-tba-342c7df56386)
Isn't it possible then, to get that employee's specific role, without little to no hassle?
I'm trying to do so, but I couldn't find a way, so I just started listing all possible employees and require that the person authenticating, other than supplying their 4 tokens (along with their account Id), to also supply their role, which seems stupid.
(Once I have the employees, I can pretty much find the one with the needed role, granted they are the only one using it.)
private static void GetEmployees()
{
EmployeeSearch search = new EmployeeSearch();
EmployeeSearchBasic esb = new EmployeeSearchBasic();
esb.isInactive = new SearchBooleanField();
esb.isInactive.searchValue = false;
esb.isInactive.searchValueSpecified = true;
search.basic = esb;
SearchResult res = Client.Service.search(search);
res.pageSize = 2000;
res.pageSizeSpecified = true;
if (res.status.isSuccess)
{
Record[] searchRecords = res.recordList;
if (searchRecords != null && searchRecords.Length >= 1)
{
//Do something...
}
else
{
//Do something...
}
}
else
{
throw new Exception("Couldn't find any employees.");
}
}
The reason I'm searching for their role, is to make sure it has the needed permissions I'll be using. If I don't need their role to do this task, because it is again tied to the tokens in some way, please let me know how and I'll edit the context where needed.
I'm using the following webservices - https://webservices.netsuite.com/wsdl/v2017_2_0/netsuite.wsdl
You cannot get this information using only SuiteTalk calls.
One user can have many roles. In addition Netsuite roles are pretty malleable so unless you are dealing with a standard role they are not much use in determining permissions.
So rather than getting a role you need to check specific permissions.
Your better option would be to create a companion RESTlet that you can query using the same tokens as you are querying SuiteTalk with. Then you can query the RESTlet for permissions ( or just get it to give you all the permissions you care about so you only have to call it once.
You can also require the customer set up the tokens on a user and role that give you what you need. To do this you have to specify the role's permissions which can be a hassle to communicate so finally:
You can also create a Role and Integration record in your dev account and make them available as a bundle. You'd then require your customer to install the bundle and use the role and integration record for the tokens and you wouldn't have to query anything.
Could you create a Restlet in NetSuite?
/**
*#NApiVersion 2.x
*#NScriptType Restlet
*/
define(['N/log', 'N/runtime'],
function(log, runtime) {
function _get(context) {
var cUser = runtime.getCurrentUser();
return cUser.role;
}
return {
get: _get
};
}
);
If you can make an HTTPS request to the restlet deployment url using the token, the restlet would return the role.
In my MVC application user registration is implemented in two ways; the user register and then approved by the Administrator; or the Administrator can create a user. My question is: will it be possible to send a temporary password and then the user has to change it after first login, or can I flag this user to use external authentication first time.
I would appreciate your suggestions.
You can define a UserAccount class like this:
public class UserAccount
{
public int AccountId { get; set;}
public UserAccountState AccountState { get; set; }
public Guid ActivationCode { get; set; }
public string Password { get; set; }
}
Where UserAccountState is
public enum UserAccountState
{
PendingActivation = 0,
UsingTempPassword = 1
Normal = 2
}
When a new user just signed up. You can put his account to the PendingActivation state and send him a link to activate the account, something like this
www.MySite.com/Activate?code=F3D17EE
When user clicks on the link, you match the user account with the code, and do the following:
Generate a temp password for the account, e.g "TempPass12"
Change the account state to UsingTempPassword
Show the following message to user
"Your account is now activated. Click here to login with your temp password TempPass12"
After user login to your site with the temp password, your code should detect that the UserAccountState is in the UsingTempPassword state and subsequently redirect the user to the change password page.
After a new password is provided by the user, the account can be put to the Normal state.
Add a column in your password table, something like 'ForceToChangePassword'. Check that column every time an user logged in, if it was set to true, redirect user to the change password page.
My opinion is to use roles than using new columns, and checking things every time user logged in as it is not good when we thinking about performances.
Create three new roles it could be
Created - User created by admin
Registered - User registered by them self
Approved - Approved by admin
In your case if the user registered them self, then add them to ROLE 'Registered'. If the user created by admin then add them to ROLE 'Created'. Once admin approved or user change there password first time login, then you can add them to ROlE 'Approved'.
Then you can handle user self registration and admin creation controller actions to add users to correct ROlE.
There is a column called 'EmailConfirmed' already there, so that you can use that column for your purpose. Update that column when the user approved or successfully change the password on first login.
As you know that password field is nullable, so that you don't need to insert temporary passwords, (but you could if you want). You can keep password field as null and update it when the user first login. You need to change your views to support this scenario.
You can use asp.net identity framework supported methods in order to achieve this.
GenerateEmailConfirmationTokenAsync
GenerateEmailConfirmationToken
IsEmailConfirmedAsync
ConfirmEmailAsync
This role based scenario may help you to categorize users depending on there role and restrict access easily using [Authorize(Role = "RoleName")].
Let me know if you need anymore details.
Hope this helps.
I need to have different connection strings for different users. There are several threads which present similar scenarios, none of which have the solution -- how to establish a dynamic connection to a database for a user at login.
I've gotten as far as being able to dynamically get the connection string, but not quite sure what to do to set it/them (and keep different strings active for different users).
I have a ClientDbContext Model in which I have
namespace MyProject.Models
{ public class ClientDbContext : DbContext
{
public ClientDbContext(string DefaultConnection)
: base("DefaultConnection")
{
}
In my ClientController
using MyProject.Models;
namespace MyProject.Controllers
{
public class ClientController : Controller
{
private ClientDbContext db = new ClientDbContext("DefaultConnection");
Big picture here.... The goal is to dynamically set a connection to a specific database (catalog) when a user logs in. There could be several dozen different databases.
Process (currently working :-))
User gets login page (Account).
Enters login information (Account).
Login controller (Account) checks for valid entry; matches
user-entered string to a previously entered string in the user
profile; and matches user entered string to expected name of
database (in a dbStrings table).
I can retrieve a specific database name and string for the user during login. I created a temp error message to show me the name and string.
What should happen next...
The selected string should be used to create a db connection for the user.
I'm stuck on this ....
Then, the user is redirected to the Index for Clients (Clients) -- this is working
and the selected connection remains for the user (until the user closes the browser or logs out - ends the session).
Not quite sure how to set this....
Other users could log in with different credentials. The system has to keep the connections discrete for each authenticated user.
Sorry for the basic question, first time with Web MVC4 in C#...
I'm creating a web interface for an application I've written in C#/SQL. I've been able to attach the MVC4 framework to the SQL DB. Now I want to secure what people can do based on group membership in AD. I have the authentication in my web.config set to "Windows" and it properly displays the User.Identity.Name that i'm logged in with. So I know it's pulling up the current logged in user. More over, I need to be able to authenticate a user outside of the active directory domain in the case of an android or ipad device. I haven't gotten that far yet though... for the most part, I'd like to auto authenticate the logged in user if possible then prompt for a username/password if none exists.
Ok, also I already know how to pull group membership for a user in AD. But I need to run that AD query and store that information somewhere that can be accessed on each page. Then on each page how do I access that variable?
For example, I don't want to display a menu option if they don't have access to it so that variable needs to be used to either display or not display the menu option that's being secured. Also, I assume I need to add that security on the webpage as well so that if someone tries to go there manually they cannot.
I assume I don't want to use session variables for security reasons..
In the past with Adobe Flex I used a singleton to manage the session state. I did a search out there and people are saying that it's probably not a good idea in C#. Not many examples of this anyway...
What are you doing to do this?
Here is what I would recommend. Start looking for examples of the ActiveDirectoryMembershipProvider Class. This MembershipProvider combined with Forms Authentication will provide you with a secure system to authenticate users.
Once authenticated, you need to authorize your users to access resources by combining the Active Directory Role Provider(ADRP) (to determine User Groups) with the standard way of Securing your MVC Application.
To get you started I created these simple extension methods when you can extend to use the ADRP (as I haven't used the ADRP).
public static class IPrincipalExtensions
{
private static _adminName = "Administrator";
public static bool IsAnonymous(this IPrincipal instance)
{
return (instance == null);
}
public static bool IsAdminOrInRole(this IPrincipal instance, string role)
{
if (instance == null
|| instance.Identity == null
|| !instance.Identity.IsAuthenticated)
{
return false;
}
bool result = instance.IsInRole(role)
|| instance.IsInRole(IPrincipalExtensions._adminName));
return result;
}
}
Then I also extended the default AuthorizeAttibute to give me an attribute I can use solely for Administrators:
public class AuthorizeAdministratorAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
bool result = false;
IPrincipal user = httpContext.User;
if (user.Identity.IsAuthenticated)
{
result = user.IsAdmin();
}
return result;
}
}
This uses the same extension methods provided in my IPrincipalExtensions so I don't repeat myself. Some might find this overkill as the following two lines are equal:
[Authorize("Administrator")]
[AuthorizeAdministrator]
However, since the first example is using a string, a simple mistype denies access, and if I decided to change the role/group name to "Admins" it becomes more difficult. So using the second one (which I could argue is strongly typed) if the group changes, I only have to change the name in one location.