Servicestack Roles Users and Groups - servicestack

Since roles don't contain permissions. I am a bit confused by the Roles and Permission in ServiceStack. It appears they are really the same thing? I want to implement a Group, that has roles, that has permissions. Based on the servicestack default implementation I don't think I can extend the provider and get the nested information.
How would i achieve this and still use the authentication attributes.
If i had an attribute
[RequiredPermission("CanAccessPerm")]
That is in Role:HasAccessRole That is in Group:HasAccessGroup
I would want to only use perms to determine access at the API level. Then Roles and Groups to determine who has perms. If Roles contained permissions then I could just extend the CredentialsAuthProvider TryAuthenticate and additionally look at a group table. Is there a way to do this and not rewrite the whole authentication?
Edit 12/12
I am using
container.Register(c =>
new OrmLiteAuthRepository(c.Resolve())
{
UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
});
How do I get to the IManage roles? I see i can override the IAuthRepository.
I found this link. But its not a replacement for Auth
ServiceStack - roles and permissions
-_Edit 12/29 -- It is not calling the methods in MyOrmLiteAuthRepository. Do you know why?
AppHost.cs
container.Register<IAuthRepository>(c =>
new MyOrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
{
UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
});
CustomAuthRepo
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection)
{
DbFactory = dbFactory;
NamedConnnection = namedConnnection;
}
public IDbConnectionFactory DbFactory { get; set; }
public string NamedConnnection { get; set; }
public override ICollection<string> GetPermissions(string userAuthId)
{
var permissions = base.GetPermissions(userAuthId);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
permissions = ss.UserPermissions(Convert.ToInt32(userAuthId));
}
return permissions;
}
public override bool HasPermission(string userAuthId, string permission)
{
var hasPermission = base.HasPermission(userAuthId, permission);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
hasPermission = ss.UserHasPermInRoleOrGroup(permission, Convert.ToInt32(userAuthId));
}
return hasPermission;
}
}

Roles/Permissions work similar in that a User can have multiple Roles and Permissions but they're logically different in that a Role defines the Role a person has like "Employee", "Manager", etc and Permission defines functionality they have access to like "CanSubmitPurchaseOrders", "CanRefundCustomers", etc.
ServiceStack doesn't support Roles having permissions themselves but you can implement this functionality yourself in your own Custom AuthProvider by overriding OnAuthenticated() and populating the Permissions collections of AuthUserSession with a combination of all the permissions in all the Roles a User is in. If you're not using a custom AuthProvider you can modify the Users Session by implementing the OnAuthenticated() Session of Auth Event Hooks.
Alternatively if you're using an AuthRepository like OrmLiteAuthRepository you can change how permissions are managed by overriding its IManageRoles GetPermissions() and HasPermission() APIs to also inspect the Permissions that the Users Roles have assigned to them which you would need to maintain in an out-of-band table.
Overriding OrmLiteAuthRepository
OrmLiteAuthRepository implements IManageRoles so when needed you can cast IAuthRepository to IManageRoles, e.g:
var manageRoles = (IManageRoles)container.Resolve<IAuthRepository>();
You can override OrmLiteAuthRepository and implement your own GetPermissions() and HasPermission() with normal inheritance, e.g:
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection) {}
public override ICollection<string> GetPermissions(string userAuthId)
{
return base.GetPermissions(userAuthId);
}
public override bool HasPermission(string userAuthId, string permission)
{
return base.HasPermission(userAuthId, permission);
}
}

Related

Razor pages assign roles to users

I am wondering how to create and assign roles in Razor Pages 2.1. application.
I have found how to make them for MVC application (How to create roles in asp.net core and assign them to users and http://hishambinateya.com/role-based-authorization-in-razor-pages), however it does not work for razor pages as I have no IServicesProvider instance.
What I want is just to create admin role and assign it to seeded administrator account. Something similar has been done in this tutorial https://learn.microsoft.com/en-us/aspnet/core/security/authorization/secure-data?view=aspnetcore-2.1, but it seems be sutied for MVC and does not work properly after I applied it to my application. Please help me to understand how to create and seed roles in Razor Pages.
Will be very greatfull for help!
I handle the task next way. First, I used code proposed by Paul Madson in How to create roles in asp.net core and assign them to users. Abovementioned method I have inserted into Startup.cs. It creates administrator role and assigned it to seeded user.
private void CreateRoles(IServiceProvider serviceProvider)
{
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
Task<IdentityResult> roleResult;
string email = "someone#somewhere.com";
//Check that there is an Administrator role and create if not
Task<bool> hasAdminRole = roleManager.RoleExistsAsync("Administrator");
hasAdminRole.Wait();
if (!hasAdminRole.Result)
{
roleResult = roleManager.CreateAsync(new IdentityRole("Administrator"));
roleResult.Wait();
}
//Check if the admin user exists and create it if not
//Add to the Administrator role
Task<ApplicationUser> testUser = userManager.FindByEmailAsync(email);
testUser.Wait();
if (testUser.Result == null)
{
ApplicationUser administrator = new ApplicationUser
{
Email = email,
UserName = email,
Name = email
};
Task<IdentityResult> newUser = userManager.CreateAsync(administrator, "_AStrongP#ssword!123");
newUser.Wait();
if (newUser.Result.Succeeded)
{
Task<IdentityResult> newUserRole = userManager.AddToRoleAsync(administrator, "Administrator");
newUserRole.Wait();
}
}
}
Then, in the same file in Configure method I add argument (IServiceProvider serviceProvider), so you should have something like Configure(..., IServiceProvider serviceProvider). In the end of Configure method I add
CreateRoles(serviceProvider).
To make this code work create ApplicationUser class somwhere, for example in Data folder:
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sobopedia.Data
{
public class ApplicationUser: IdentityUser
{
public string Name { get; set; }
}
}
Finally, inside ConfigureServices method substitute
services.AddIdentity<ApplicationUser>()
.AddEntityFrameworkStores<SobopediaContext>()
.AddDefaultTokenProviders();
with
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<SobopediaContext>()
.AddDefaultTokenProviders();
As a result, after programm starts in table AspNetRoles you will get a new role, while in table AspNetUsers you will have a new user acuiering administrator role.
Unfortunatelly, after you add the following code
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<SobopediaContext>()
.AddDefaultTokenProviders();
pages Login and Registration stop working. In order to handle this problem you may follow next steps:
Scaffold Identity following (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.1&tabs=visual-studio).
Then substitute IdentityUser for ApplicationUser in entire solution. Preserv only IdentityUser inheritance in ApplicationUser class.
Remove from Areas/identity/Pages/Account/Register.cs all things related to EmailSernder if you have no its implementation.
In order to check correctness of the roles system you may do as follows. In the end of ConfigureServices method in Startup.cs add this code:
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator"));
});
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeFolder("/Contact","RequireAdministratorRole");
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
If it does not worki then just add [Authorize(Roles = "Administrator")] to Contact Page model, so it will look something like this:
namespace Sobopedia.Pages
{
[Authorize(Roles = "Administrator")]
public class ContactModel : PageModel
{
public string Message { get; set; }
public void OnGet()
{
Message = "Your contact page.";
}
}
}
Now, in order to open Contact page you should be logged in with login someone#somewhere.com and password _AStrongP#ssword!123.

UserId not found error in aspnet Identity at GenerateUserIdentityAsync method

I am getting UserId not found error after registring a user and also after login.Moreoever, after registration, data is saved to database and in dbo.AspNetUsers table, id column is auto incremented and return type is int.
There is UserId Column in AspNetUserClaims table.It has 4 Col---Id,UserId,ClaimType,ClaimValue.It has Id column as auto incremented not the userId.
I was initially successfully changed Primary key from string to int by following this link---http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity.
It was running succesfully before but now it is giving me error at this line---
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> 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
return userIdentity;
}
Exception Details: System.InvalidOperationException: UserId not found.
This is the complete stack trace. you can see it here----http://pastebin.com/0hp5eAnp
It was working fine earlier but now when i added foreign key relationship with other tables, i don't know what is missing there. In the database all the tables are created properly with proper relationship between them but something is missing here.
My ApplicationUser class is something like this-------
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ApplicationUser()
{
this.Posts = new HashSet<Post>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> 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
return userIdentity;
}
public class CustomUserRole : IdentityUserRole<int> { }
public class CustomUserClaim : IdentityUserClaim<int> { }
public class CustomUserLogin : IdentityUserLogin<int> { }
public class CustomRole : IdentityRole<int, CustomUserRole>
{
public CustomRole() { }
public CustomRole(string name) { Name = name; }
}
public class CustomUserStore : UserStore<ApplicationUser, CustomRole, int,
CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public CustomUserStore(ApplicationDbContext context)
: base(context)
{
}
}
public class CustomRoleStore : RoleStore<CustomRole, int, CustomUserRole>
{
public CustomRoleStore(ApplicationDbContext context)
: base(context)
{
}
}
and my IdentityConfig.cs class file is something like this-------
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new CustomUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 1,
//RequireNonLetterOrDigit = true,
//RequireDigit = true,
//RequireLowercase = true,
//RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser, int>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser, int>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
i have seen many stackoverflow answers but not getting it to work.Can someone plzz plzz see what is missing, what should i do now.thanks in advance.
Here, in the applicationUser class, at the Id column, it showing some warning and message in tooltip like this-------
models.ApplicationUSer.ID hides inherited member
Microsoft.Aspnet.Identity.EntityFramework.IDentity
USer.Id. To make current member override
that implementation, add override keyword otherwise
add new keyword where x is just the namespace.
My StartUp.Auth.cs in App_Start folder is like this------
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager), getUserIdCallback:(id)=>(id.GetUserId<int>()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");......................................................................
and my startUp.cs file is like this----
[assembly: OwinStartupAttribute(typeof(WebApp.Startup))]
namespace WebApp
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
You will have to pull out your ApplicationUserManager to have it nice and clean and implement more methods... For example see following post (It implemented all methods with your custom Key (TKey in the example):
http://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-beta1-140211/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/UserManager.cs?ImageName=Microsoft.AspNet.Identity.Core
You will see that the error you receive GetSecurityStampAsync also is implemented there.

Writing a custom IUserPasswordStore and SignInManager.PasswordSignInAsync in Identity 2.1

Building a custom IUserPasswordStore to connect to a legacy system's username/password table. The password is hashed with custom code in the table so I need to write custom code for PasswordSignInAsync.
Do I need to override PasswordSignInAsync or is there a method I can provide that just does the hashing of the password? If I do override the entire PasswordSignInAsync is there sample code somewhere showing me what needs to be done in the method?
That was easier than I thought.
Override CheckPasswordAsync in UserManager.
For someone who wants to see the complete setup in .NET 6, this is how it looks like:
Step 1:
Add CustomUserManager to override CheckPasswordAsync:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
public class CustomUserManager<TUser> : UserManager<TUser> where TUser : IdentityUser
{
public CustomUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser>> userValidators,
IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer,
errors, services, logger)
{
}
// THIS IS ONLY CALLED FOR USERS STORED IN YOUR IDENTITY DATABASE
public override Task<bool> CheckPasswordAsync(TUser user, string password)
{
// Add custom check using user.UserName and password
return Task.FromResult(true); // Replace this with your custom check
}
}
Step 2:
Register it in your Program.cs
builder.Services
.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
})
.AddUserManager<CustomUserManager<ApplicationUser>>() <----- THIS GUY
.AddEntityFrameworkStores<ApplicationDbContext>();
ApplicationUser and ApplicationDbContext look like this:
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Step 3:
Try to Login using this:
var result = await _signInManager.PasswordSignInAsync("SomeUserNameInYourIdentityDatabase", "SomePassword", isPersistent: true, lockoutOnFailure: false);

Extension Method on Identity

im using MVC 5 and i found this :
User.Identity.Name full name mvc5
, but i dont know how use the "Extension Method on Identity"
where i put this code, and where...please help me
Extension Method on Identity:
public static class GenericPrincipalExtensions
{
public static string FullName(this IPrincipal user)
{
if (user.Identity.IsAuthenticated)
{
ClaimsIdentity claimsIdentity = user.Identity as ClaimsIdentity;
foreach (var claim in claimsIdentity.Claims)
{
if (claim.Type == "FullName")
return claim.Value;
}
return "";
}
else
return "";
}
}
Create a new static class, for example IdentityExtended
Put static method inside it, for example
Put the namespaces of Clamis and Principal
using System.Security.Claims;
using System.Security.Principal;
public static class IdentityExtended
{
public static string GetFullName(this IIdentity identity)
{
IEnumerable<Claim> claims = ((ClaimsIdentity)identity).Claims;
var FullName = claims.Where(c => c.Type == "FullName").SingleOrDefault();
return FullName.Value;
}
}
Inside View for Example write as below
<h1 class="display-4 rainbow-text">Hello, #User.Identity.GetFullName()!</h1>
Just make sure you put the class inside solution itself no inside a subfolder

MVC2 :: How do I *USE* a Custom IIdentity Class?

I am trying to store a whole truckload of information about a user from a webservice. As this is information about the currently authenticated user, I thought it would make sense to store that information in a custom IIdentity implementation.
The custom MagicMembershipProvider.GetUser(string id, bool userIsOnline) calls the webservice and returns a MagicMembershipUser instance with all the fields populated (department, phone number, other employee info).
The custom membership provider and custom membership user both work fine.
What and where is the best way to put the membership user information into the IPrincipal User object that is accessible in every controller?
I have been trying to wrap my brain around the program flow of security with IIdentity, IPrincipal and Role authorization in an MVC2 application -- but I'm really struggling here and could use some mentoring. There a Internet Ton of articles about the parts, but not much about the whole.
Edit
My best guess so far is to assign the HttpContext.Current.User in the FormsAuthenticationService:
public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName))
throw new ArgumentException("Value cannot be null or empty.", "userName");
try
{
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
MagicMembershipUser magicUser = _provider.GetUser("", false)
as MagicMembershipUser;
MagicIdentity identity = new MagicIdentity(userName, magicUser);
GenericPrincipal principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
}
catch (Exception)
{
throw;
}
}
What and where is the best way to put the membership user information into the IPrincipal User object that is accessible in every controller?
In a custom [Authorize] filter implementation. You could override the AuthorizeCore method and call the base method and if it returns true query your membership provider and inject the custom magic identity into the context.
Example:
public class MagicAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
var username = httpContext.User.Identity.Name;
var magicUser = _provider.GetUser(username, false) as MagicMembershipUser;
var identity = new MagicIdentity(username, magicUser);
var principal = new GenericPrincipal(identity, null);
httpContext.User = principal;
}
return isAuthorized;
}
}
Now all that's left is decorate your base controller with the [MagicAuthorize] attribute.

Resources