Can't Get All Users in a Role using built in methods - c#

I am using the default ASP identity modelin my MVC application.
as per this post, I want to get a list of users which are in a role:
var users = Roles.GetUsersInRole("employee");
However, this always throws
The role 'employee' was not found.
Even though that role definitely does exist.
I can confirm this, as I am able to call :
User.IsInRole("employee")
For the current user, and this returns true.
I also noticed that these methods always return empty arrays:
var r = Roles.GetAllRoles();
var au = Membership.GetAllUsers();
Why is this?
I know I can get the roles by directly making calls to the database context, but why can't I use the built in methods?

Related

Prompt user for additional information during an Open Id Connect event?

Using asp.net Core, Mvc and OpenIdConnect, is it possible to prompt an authenticated user for additional information during the ODIC authentication process, and then redirect back to the originally-desired page?
To give a concrete example: in our system one person, represented by an email address, can have multiple user ids that they may wish to operate under. Assume my email address is tregan#domain.com, and I have 3 user ids to choose from: treganCat, treganDog, treganMouse. When I hit a Controller action that is decorated with the [Authorize] attribute I first go through OpenIdConnect authentication, and one of the claims returned is an email address.
Using that email address, I want the application to prompt me to select the identity that I want to run under (treganDog, treganCat, or treganMouse).
From there, I want the application to take the user id that I selected, interrogate a database for the roles that go along with the selected user id, and load those roles as claims to my identity.
Finally, I want the application to send me on to my desired page (which is the protected Controller method that I originally attempted to visit).
Is this possible?
I'm using an Owin Startup class; the code below "works" except for the fictional line "var identityGuid = [return value from the prompt];" ("fictional" because it represents what I would like to occur, but in fact a series of redirects would be needed).
My example below uses the OnTicketReceived event, but that selection is arbitrary, I would be willing to do this in any event.
services.AddAuthentication(authenticationOptions =>
{
authenticationOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
authenticationOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(openIdConnectOptions =>
{
openIdConnectOptions.Authority = Configuration["PingOne:Authority"];
openIdConnectOptions.CallbackPath = "/Callback";
openIdConnectOptions.ClientId = Configuration["PingOne:ClientId"];
openIdConnectOptions.ClientSecret = Configuration["PingOne:ClientSecret"];
openIdConnectOptions.ResponseType = "code";
openIdConnectOptions.Events.OnTicketReceived = (ticketReceivedContext) =>
{
var emailClaim =
ticketReceivedContext.Principal.Claims.FirstOrDefault(o =>
o.Type == ClaimTypes.Email);
string emailAddress = emailClaim.Value;
//here is where I would like to prompt the user to select an identity based on the email address
//the selected identity is represented by a guid
var identityGuid = [return value from the prompt];
var roles = new MyRepository(myContext).GetRolesForUserId(identityGuid);
var claims = new List<Claim>();
foreach (string role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
ticketReceivedContext.Principal.AddIdentity(new ClaimsIdentity(claims));
return Task.CompletedTask;
};
});
This is impersonation where there is a real user and you need to identify the impersonated user after login.
You will need to complete the login first, return to the app and configure the principal. Then render a UI and receive the selected choice.
You then need your UI to call the back end and tell it to update claims in the auth cookie. Not sure if you'll get this to work though - the impersonated user may need separate storage - such as a second cookie.
This highlights that it can be useful to separate the token / credential the UI receives from the claims the back end works with.
I use the below design a lot for REST APIs that serve UIs directly - though it may be overkill for your solution:
https://authguidance.com/2017/10/03/api-tokens-claims/
I think what I want to do is simply not possible without either figuring out a way to do it inside PingOne or writing my own IdentityServer and taking care of the extra steps there.
I decided to instead write a custom middleware that fires after the Authentication middleware, as described in this SO question: In asp.net core, why is await context.ChallengeAsync() not working as expected?

How do I use Manatee.Trello with multiple user accounts?

I've been trying the following to retrieve data:
void InitializeTrello()
{
TrelloConfiguration.Serializer = new ManateeSerializer();
TrelloConfiguration.Deserializer = new ManateeSerializer();
TrelloConfiguration.JsonFactory = new ManateeFactory();
TrelloConfiguration.RestClientProvider = new Manatee.Trello.WebApi.WebApiClientProvider();
TrelloConfiguration.ThrowOnTrelloError = true;
}
T DownloadDataFromTrello<T>(TrelloAccount account, Func<T> func)
{
TrelloConfiguration.Cache.Clear();
TrelloAuthorization.Default.AppKey = account.AppKey;
TrelloAuthorization.Default.UserToken = account.UserToken;
T result = func();
TrelloProcessor.Flush();
return result;
}
Method DownloadDataFromTrello is being called a few times with different AppKey and UserToken parametres. I receive the same data every call despite calling TrelloConfiguration.Cache.Clear() inside the function.
I would like to use library without resorting to dirty tricks with unloading static classes and retain the lazy loading functionality. Does anyone know how to use this library with multiple user accounts properly?
All of the entity constructors take a second parameter: a TrelloAuthorization that defaults to TrelloAuthorization.Default. The entity instance uses this authorization throughout its lifetime.
var customAuth = new TrelloAuthorization
{
AppKey = "your app key",
UserToken = "a user's token"
}
var card = new Card("card id", customAuth);
The default cache only looks at the entity ID as the key so even if you change the default authorization you would get the same instances back (using the old auth) if the system is pulling them from a cache (e.g. a card is downloaded as part of a List.Cards enumeration). If you explicitly create the entity through a constructor (as above) the new entity is added to the cache, but only the first one will be returned since it's matched only on ID.
To consider the auth as a match for the key, I'd have to either update the default cache or expose the auth so that you can write your own cache and set the TrelloConfiguration.Cache property. I'm not sure which I prefer right now.
Using a custom auth (possibly in combination with periodically clearing the cache) is currently your best option. Please feel free to create an issue or let me know here if this is a feature you'd like.

Getting more out of Request.HttpContext.User.Identity

When using MVC Core Identity, I am wondering if it is possible to get more information out of Request.HttpContext.User.Identity?
Currently, when I look at available, all it gives me back is Name, AuthenticationType, and IsAuthenticated.
What I would like to do for example is also get the Email address as well.
//Get User Manager From Owin Context
var userManager = HttpContext.GetOwinContext().GetUserManager<UserManager>();
var user = userManager.FindById(Convert.ToInt32(User.Identity.GetUserId()));
Using the above codes, it will return the full user object. I believe that solves your problem?

Why is my List.GetUserEffectivePermissions() method not working?

I'm developing a process in C# with the SharePoint 2013 Client Side Object Model. I need to retrieve the SharePoint List Permissions of a given user, that will be different than the user that is executing the code.
using SP = Microsoft.SharePoint.Client;
SP.ClientContext SpContext = new SP.ClientContext("SITEURL");
SP.Web SiteWeb = SpContext.Web;
SP.List Lst = SpContext.Web.Lists.GetByTitle("LIST");
var ClientUserEffPerms = Lst.GetUserEffectivePermissions(#"<domain>\<username>");
SpContext.Load(SiteWeb, S => S.EffectiveBasePermissions);
SpContext.Load(Lst, L => L.EffectiveBasePermissions);
SpContext.ExecuteQuery();
After this code executes, the ClientUserEffPerms.Value (BasePermissions) object does not represent the permissions of the given user correctly. The object isn't null, but it represents the user as having no permissions. The user has at minimum view and edit permissions and I can confirm this by viewing/editing List Items using the web browser as this user.
The code executing user has permission to enumerate permissions at both the Web and List level. I've confirmed this with the code below, both booleans resolve to true.
bool SvcUserHasSiteEnumPermsPerm = SiteWeb.EffectiveBasePermissions.Has(SP.PermissionKind.EnumeratePermissions);
bool SvcUserHasListEnumPermsPerm = Lst.EffectiveBasePermissions.Has(SP.PermissionKind.EnumeratePermissions);
Can anyone help me determine what is wrong with my GetUserEffectivePermissions() method?
When you call GetUserEffectivePermissions you need to pass in the full claims token version of the login name, which looks something like this:
i:0#.w|domain\user
You can get this by loading the LoginName property on a user object:
clientContext.Load(clientContext.Web.CurrentUser, i => i.LoginName);
clientContext.ExecuteQuery();
Of course, that's for the current user, so you'll need to acquire the user you actually want first.

Active Directory Group Membership Checking in .Net 4.5

I have an ASP.Net MVC application using Windows Authentication, and I am checking group membership for security on controller actions.
Simple as it sounds, I've found no other Question that can resolve the problem I am experiencing.
First Attempt: [Authorize]
The classic method is to simply slap an Authorize data annotation attribute on the controller action and go to town:
[Authorize(Roles = #"domain\groupName1")]
No dice. I am prompted for credentials. Usually this means something is wrong with the Windows Authentication configuration but it's setup fine: (1) HttpContext.User is a WindowsPrincipal object, and (2) I confirmed another known group name works.
Second Attempt: IsInRole()
The next step taken was to go a more old fashioned route and use IPrincipal.IsInRole(), and again, one returns false, the other true.
var wp = (WindowsPrincipal)User;
// false
var inGroup1 = wp.IsInRole(#"domain\groupName1");
// true
var inGroup2 = wp.IsInRole(#"domain\groupName2");
Stumped... so I hit up my systems nerds and we double check everything. User is a group member? Yes. Group name is spelled correctly? Yes. The next step was to snag the SID.
Third Attempt: Search Identity's Group Collection
In my controller I check the WindowsIdentity and look through the group collection for the SID of the troublesome group:
var wi = (WindowsIdentity)wp.Identity;
var group = wi.Groups.SingleOrDefault(g => g.Value == "group1-sidValue");
The group variable is the SecurityIdentifier object. Because it is not null, we can be certain that this current user is a member of the group that both the [Authorize()] or IsInRole() attempts fail to confirm.
Fourth Attempt: DirectoryServices.AccountManagement
At this point, I'm going nuts and add reference to the AccountManagement APIs. I search the domain context for the GroupPrincipal by both name and SID:
var pc = new PrincipalContext(ContextType.Domain, "domain");
var gp1byName = GroupPrincipal.FindByIdentity(pc, "groupName1")
var gp1bySid = GroupPrincipal.FindByIdentity(pc, IdentityType.Sid, "group1-sidValue");
Both group principal variables are ripe with the same object, and I verified through a watch variable that the principal's Members collection contains a UserPrincipal object with the same SID as the current WindowsPrincipal on HttpContext.
Question:
What in the hell have I missed here? Why would both role checking methodologies fail when it is plain and clear through object exploration that the user is a valid member of this given group?
The fact that one group checks fine and the other does not seems the most strange part at this point.
Answer:
Essentially it's translation issues between WindowsIdentity and NTAccount (both of these System.Security.Principal) and lastly, the actual Active Directory entry.
When validating a WindowsIdentity against AD, if you want to use anything other than the Sam or the Sid, you will need to use System.DirectoryServices.AccountManagement.
Caveat: In .Net 4.5 the security principals include Claims but that's out of context.
Long Explanation:
In a Windows Authenticated web application, HttpContext.User is a WindowsPrincipal object wrapping an underlying WindowsIdentity.
WindowsIdentity has for most intents and purposes only two properties with which the authenticated user can be identified: Name and User.
These properties translate to two properties on the identity's corresponding AD account entry:
WindowsIdentity.Name = SamAccountName
WindowsIdentity.User = SID
The [Authorize] filter attribute ultimately calls IsInRole(string role) on the underlying principal... and the IsInRole() string overload instantiates an NTAccount with the role (the "SamAccountName" in an AD entry).
This explains the failure in #1 and #2 above.
To authorize the HttpContext.User against anything but his/her Sid or SamAccountName, you'll need DirectoryServices.AccountManagement or classic LDAP.
I have an ASP.Net MVC application using Windows Authentication, and I am checking group membership for security on controller actions. Simple as it sounds, I've found no other Question that can resolve the problem I am experiencing.
Took me a lot of time to find something
http://www.c-sharpcorner.com/uploadfile/scottlysle/test-for-user-group-membership-in-Asp-Net-C-Sharp/
My code to check if user belongs to an AD group :
foreach (System.Security.Principal.IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
{
if (String.Equals(group.Translate(typeof(System.Security.Principal.NTAccount)).ToString(), #"your_domain_name\your_group_name", StringComparison.InvariantCultureIgnoreCase))
{
// the user belongs to a group
}
}
That is all I needed beside <authentication mode="Windows"/> in Web.config file

Categories