Act on Behalf of different user / XrmServiceContext.CallerID - asp.net-mvc-4

I have created a XrmServiceContext using svcutil.exe for my CRM 2013 database, this is working great and I can retrieve data from CRM in my MVC4 application.
My website is running SSO using ADFS2 and I can retrieve the accessing users identity using:
Microsoft.IdentityModel.Claims.IClaimsIdentity ci = Thread.CurrentPrincipal.Identity as Microsoft.IdentityModel.Claims.IClaimsIdentity;
var accountNameClaim = ci.Claims.Where(x => x.ClaimType.ToLower().EndsWith("windowsaccountname")).FirstOrDefault();
this gives me something along the lines of
string accountNameClaim = "firstname.lastname#domain.com"
Using this I can retrieve the user form CRM 2013 XrmServiceContext
var user = _serviceContext.SystemUserSet
.Where( x=> x.DomainName == accountNameClaim)
.Select(s => new UserInformationProxy()
{
Id = s.Id, // this is probably needed for impersonation
FullName = s.FullName,
DomainName = s.DomainName
})
.FirstOrDefault();
Now I'm wondering how I act as / impersonate this user for all my subsequent queries to CRM using my XRMServiceContext.
This page http://msdn.microsoft.com/en-us/library/gg309629.aspx has a guide which suggests I need to set a variable called CallerID in OrganizationServiceContext which I'm guessing is contained somewhere inside my XRMServiceContext.. But I cannot find it.

The CallerId property is not on the OrganizationServiceContext but on the OrganizationServiceProxy that is used by the context:
When you are constructing the context, you are passing in an organization service intance. Prior to that, you need to set the CallerId:
organizationService.CallerId = user.Id;
var _serviceContext = new OrganizationServiceContext(organizationService);
Please note that the CallerId is only available on the OrganizationServiceProxy type, not on the interface IOrganiaztionService. I can't see how you obtain the organization service, but make sure it's an OrganizationServiceProxy.

Related

Microsoft Graph works with one program, but not another

I am attempting to create a c# Windows service that periodically captures information from Microsoft Graph. This always fails with a "AADSTS700016: Application not found in the directory of our Microsoft 365 account."
Prior to trying to write this service, I created a test program to do same access. I set up an application in Azure Active Directory with a secret. When I run the exact same code in the this test program with the correct tenant ID, client ID and the secret, the program works fine from whatever computer I run it from.
However, the c# service always fails with the error noted above and detailed below. Can't be an issue with permissions as access IDs and secret are the same. I have even tried creating a separate application in AAD but get the same error.
Common c# statements:
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientID, clientSecret, options); ;
graphClient = new GraphServiceClient(clientSecretCredential, scopes);
if (graphClient == null) throw new Exception("Unable able to obtain a GraphClient for this pass");
var groups = await graphClient.Groups.Request().Select(x => new { x.Id, x.DisplayName }).GetAsync();
Any help appreciated. I am sure it is something simple, but clueless at the moment.
Inner Exception 1:
AuthenticationFailedException: ClientSecretCredential authentication failed: AADSTS700016: Application with identifier 'c62d4eb9-587d-4b7f-a4d8-0640747f0958' was not found in the directory xxxx. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
Trace ID: c8bfac15-c9d6-407e-89e7-36f21fb18300
Correlation ID: 9c8d25ad-c275-43c0-93c1-d295608e9f92
Timestamp: 2022-08-13 15:52:36Z
And just like that, I found the error. Good old global vs local variable name. I hope no one spent too much time on this.

Update logged Identity claims when add new claims

I have a .net core web api application with angular. I use AspNetCore Identity. I have a claim table with userId and value colums. The createnewuser method call from angular site, and they call to register user method. There no any problem here.
I wanna add new claim into httpcontext current user claims list.
I'd tried but it is not help me.
Added a claim to an existing HttpContext User
But I can see added new claim into _httpContext.HttpContext.User.Claims when re-authenticate with new token. How can I see add new claim into current identity (_httpContext.HttpContext.User.Claims)?
And I got current user Id value from httpontext.
var parentId = _httpContext.HttpContext.User.Claims?.FirstOrDefault(p => p.Type == "uid");
if (parentId != null && parentId.Value != null)
{
await _IdentityService.AddClaimAsync(new RegisterRequest()
{
"newclaim" = "new-claim-key",
userId = parentId.Value
});
I think best wasy get claims from db who logged used like that:
var claimList = userManager.
GetClaimsAsync(await_userManager.GetUserAsync(_httpContext.HttpContext.User))
Although this is not a very accurate method, it is the most reliable method. This way you can get the latest claim list in db

.net core 3.1 & identity issue (claims nameidentifier has an id that doesn't match any user, while claims name is right)

I'm having an issue in an area of my website where i need to retrieve the user Id, i tried both by using the HttpContext.User and the injected IHttpContextAccessor, both give me an id that 1) doesn't match the user and 2) doesn't even exist in my database!
I also tried injecting a UserManager and calling GetUserId on it and that too gives me the wrong id (once again, no clue where from, it's not in the database). Calling GetUserAsync on it returns null.
I'm not using anything special nor fancy, the default page included with idendity core to log in, just a context that inherits from IdentityDbContext, and the login part works just fine as those pages are behind an Authorize tag and do force me to log in. If i was getting an error to begin with i could dig but i'm just getting an Id that seems to come from nowhere and am at a loss at where to look.
Here's what the claims look like when calling
HttpContext.User.Claims.ToList()
[0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: f478bf7a-1734-494c-aad6-0882ab24007f} <-- this id is not present in AspNetUsers table
[1]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: EDITED OUT FOR PRIVACY} <-- my correct username (my email)
[2]: {AspNet.Identity.SecurityStamp: EDITED OUT}
[3]: {http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Administrator} <-- correctly finds my role too
You can use the following code to get the UserId
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
var claimsIdentity = (ClaimsIdentity)this.User.Identity;
var claim = claimsIdentity.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
var userId = claim.Value;
I had that problem for using this in ExternalLoginCallback:
var user = new SmileAppUser { UserName = email, Email = email };
await _signInManager.SignInAsync(user, isPersistent: false);
Try to retrieve the user from the database to include the id in the claims with SignInAsync.

How can I search for ldap fields when using ActiveDirectoryRealm in Apache Shiro?

We use Apache Shiro to authenticate and authorize users using our active directory.
Authenticating the user and mapping groups works just fine using the following config:
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.searchBase = "OU=MYORGANIZATION,DC=MYDOMAIN,DC=COM"
adRealm.groupRolesMap = "CN=SOMEREADGROUP":"read","CN=SOMEMODIFYGROUP":"modify","CN=SOMEADMINGROUP":"admin"
adRealm.url = ldaps://my.ad.url:636
adRealm.systemUsername= systemuser
adRealm.systemPassword= secret
adRealm.principalSuffix= #myorganization.mydomain.com
I can authenticate in Shiro using the following lines:
String user = "someuser";
String password = "somepassword";
Subject currentUser = SecurityUtils.getSubject ();
if (!currentUser.isAuthenticated ()){
UsernamePasswordToken token = new UsernamePasswordToken (user,
password);
token.setRememberMe (true);
currentUser.login (token);
}
We now want to get more user information from our ActiveDirectory. How can I do that using Apache Shiro? I was not able to find anything about it in the documentation.
In the source code of ActiveDirectoryRealm I found this line:
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchCtls);
So the first part of the answer is clear: use the ldapContext to search something in it. But how can I retrieve the LdapContext?
It depends on what you are trying to do. Are you just trying to reuse the context to run a query for something other then authentication or authorization? Or are you trying to change the behavior of the query in the AD realm?
If the latter, you would need to extend the ActiveDirectoryRealm and override the queryForAuthorizationInfo() method.
Are you implementing something that is custom for your environment?
(updated)
A couple things:
The realm has access to the LdapContext in the two touch points: queryForAuthenticationInfo() and queryForAuthorizationInfo(), so if you extend the AD realm or AbstractLdapRealm you should already have it. You could change the query to return other info and add the extra info to your Principal. Then you have access to that info directly from your Subject object.
Your realms, are not required to be singletons.
If you want to do some other sort of user management (email all users with a given role, create a user, etc). Then you could create a LdapContextFactory in your shiro.ini, and use the same instance for multiple objects.
[main]
...
ldapContextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory
ldapContextFactory.systemUsername = foobar
ldapContextFactory.systemPassword = barfoo
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.ldapContextFactory = $ldapContextFactory
...
myObject = com.biz.myco.MyObject
myObject.ldapContextFactory = $ldapContextFactory
This would work well if myObject is interacting with other Shiro components, (responding to events, etc), but less so if you need access to it from another framework. You could work around this by some sort of static initialization that builds creates the ldapContextFactory, but in my opinion, this is where the sweet spot of using the shiro.ini ends, and where using Guice or Spring shines.

AspNet Identity UserManager Find with Select (optimal sql query)

I have wcf fulRest service with AspNet Identity. My android app use this web service to communicate with database - I use ssl to make connection secure (my app is a mini game so it doesn't contain so important data, I belive that ssl is enough protection in this case).
I have function LoginUser(string userName, string unHashedPassword), if user exsists it returns user's id. In all other functions this id is used if action need information of curUser - for example I have function addComment(string userId, string msg) (this kind of methods still use ssl to protection of unwanted handle userId).
In LoginUser I get id (and a little more information like e-mail, gameLogin) by use:
using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(_context)))
{
ApplicationUser user = userManager.Find(userName, password);
if (user != null)
{
result = new LogInBaseData()
{
Id = user.Id,
Email = user.Email,
Login = user.ApplicationLogin
};
}
}
But function Find generate huge query with select many unneeded data for me. Is any way to optimalizace it? I prefer do it by context.User.Where().Select() but I can't hash user's password.