I use the below snippet of code to fetch the client user name in my WCF service. On one of my servers, I am getting the wrong client name. My client is Win7 talking to Server 2008R2 in a workgroup configuration and both machines have users Dave and Dave_Admin. Both are admin on Win7 and only the later is admin on the server. Problem is I start my client as Dave and the server shows the client as Dave_Admin. I have debugged the identities on both sides of the connection as Dave on the client and Dave_Admin on the server. The claim resources also show the Dave_Admin SID.
The only two reasons I can imagine this happens are
the server somehow finds user Dave_Admin looking for Dave which I doubt, or
after setup, I may have renamed administrative user Dave to Dave_Admin and then created a new user Dave as a standard user.
I only have a vague recollection I may have done that but am not sure if I did or not. The c:\users folder looks normal. If I did do this, and this is the reason, is there anyway to correct?
Anyone have another possible explanation or means to fix if this happens after a user rename?
OperationContext lContext = OperationContext.Current;
RemoteEndpointMessageProperty mEndpointMessageProperties = lContext.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
mIdentity = lContext.ServiceSecurityContext.WindowsIdentity;
mUserName = mIdentity.Name;
mIPAddress = mEndpointMessageProperties.Address;
mPort = mEndpointMessageProperties.Port;
mConsoleID = string.Format("IP:{0}Port:{1}", mIPAddress, mPort);
mCallbackInterface = lContext.GetCallbackChannel<IConsoleCallbacks>();
mAuthority = TxWcfServer.sSelf.Authorized(mIdentity); // get the user's authority from the WcfServer when they logged on
// show client information
if (AppSupport.IsLogLevel(LogLevel.WCF))
{
// show the various security contexts
var x = lContext.ServiceSecurityContext;
AppSupport.WriteLog(LogLevel.Note, "*** WCF WindowsIdentity is '{0}'.", x.WindowsIdentity.Name);
AppSupport.WriteLog(LogLevel.Note, "*** WCF PrimaryIdentity is '{0}'.", x.PrimaryIdentity.Name);
AppSupport.WriteLog(LogLevel.Note, "*** WCF IsAnonymous is '{0}'.", x.IsAnonymous);
foreach (ClaimSet claimset in ServiceSecurityContext.Current.AuthorizationContext.ClaimSets)
{
foreach (System.IdentityModel.Claims.Claim claim in claimset)
{
// Write out each claim type, claim value, and the right. There are two
// possible values for the right: "identity" and "possessproperty".
AppSupport.WriteLog(LogLevel.Note, "*** WCF Claim Type: {0}, Resource: {1} Right: {2}",
claim.ClaimType, claim.Resource.ToString(), claim.Right);
}
}
}
You need to turn on Impersonation on your WCF service for your code to be able to get the client context, otherwise you'll be getting the service context (Which is probably why you get Dave_Admin instead of Dave, as your service is running as Dave_Admin)
This post has information on how to turn it on:
http://msdn.microsoft.com/en-us/library/ms730088.aspx
Related
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.
I'm building a .net core 3 website where I'm trying to add a user to an Active Directory security group. The below code works fine in my development environment but once it's deployed to IIS I receive:
System.DirectoryServices.DirectoryServicesCOMException (0x8007202B):
A referral was returned from the server.
The error occurs at "group.Save();"
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "ad.xxx.com:389",
"DC=ad,DC=xxx,DC=com", svcAccountUsername, svcAccountPw))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, groupName);
group.Members.Add(pc, IdentityType.SamAccountName, username);
group.Save();
}
Again, this works locally in my development environment but not once deployed to IIS. Any suggestions on how to fix?
I would suggest looking up the account that you are trying to add to the AD. Other things i can suggest is using the debugger to confirm the account / group exists in the domain that you are running this under.
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "domain" ...))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, groupName);
// Do some validation / logging to make sure there is a group returned.
var principal = Principal.FindByIdentity(pc, IdentityType.SamAccountName, username);
// Do some validation here to make sure principal is not null
group.Members.Add(principal);
group.Save();
}
Make sure the server running this script has access to the domain you are updating.
A referral means that you aren't talking to the right server, but the server knows who you should be talking to. If you look into the exception object more, you might even find which server it wants to send you to.
This can happen if the group is not on the same domain that you passed to the PrincipalContext.
I am using ReportExecutionServiceSoapClient in .Net Core i got the latest version of .net Core and tried to get a report from reporting services to work. after I've used the WCF connection service I was able to add the code with looks like bellow
// Instantiate the Soap client
ReportExecutionServiceSoap rsExec = new ReportExecutionServiceSoapClient(ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap);
// Create a network credential object with the appropriate username and password used
// to access the SSRS web service
string historyID = null;
TrustedUserHeader trustedUserHeader = new TrustedUserHeader();
ExecutionHeader execHeader = new ExecutionHeader();
// Here we call the async LoadReport() method using the "await" keyword, which means any code below this method
// will not execute until the result from the LoadReportAsync task is returned
var taskLoadReport = rsExec.LoadReportAsync(reportPath, historyID);
// By the time the LoadReportAsync task is returned successfully, its "executionInfo" property
// would have already been populated. Now the remaining code in this main thread will resume executing
string deviceInfo = null;
string format = "EXCEL";
// Now, similar to the above task, we will call the RenderAsync() method and await its result
var taskRender = await rsExec.RenderAsync(renderReq);
When it hist renderAsync all falls apart because the credentials for the service are not set anywhere. I've tried to Login async with no success. Also I've tried to set the credentials with SetExecutionCredentialsAsync but I've got and error saying "The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'." I don't know how to change that for ReportExecutionServiceSoapClient.
I have read some posts in which Microsoft guys says that the authentication with a soap is not resolved but for me it seems so close to be true. I feel like I am missing something.
Technology stack: VS 2017, .net Core web api, ssrs 2016, sql server 2016 standard
How can I authenticate the user for this call?
I know this is an old question but I had the same issue and stumbled onto the answer.
After creating the ReportExecutionServiceSoap object you can specify the username and password in the ClientCredentials. I've had success with this using the Basic client credential type. Be sure you are using HTTPS, otherwise your password is sent in plaintext to the reporting server. I also recommend storing the user/password in a secure place and not code.
BasicHttpBinding rsBinding = new BasicHttpBinding();
rsBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
rsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
EndpointAddress rsEndpointAddress = new EndpointAddress("https://servername/ReportServer/ReportExecution2005.asmx");
var rsExec = new ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress);
rsExec.ClientCredentials.UserName.UserName = "username";
rsExec.ClientCredentials.UserName.Password = "pass";
I want to authenticate user against VDS(virtual directory server) using Java.
How VDS is different from LDAP? Or VDS is also working on LDAP
protocol?
Please help with any sample Java code for authentication against VDS
A sample code to authenticate against LDAP is as below
String userName = "John P R-Asst General Manager";
String passWord = "asdfgh123";
String base ="OU=SOU,DC=example,DC=com";
String dn = "cn=" + userName + "," + base;
String ldapURL = "ldap://mdsdc3.example.com:389";
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, ldapURL);
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, dn);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
try {
DirContext authContext = new InitialDirContext(authEnv);
return true;
} catch (NamingException namEx) {
return false;
}
To authenticate against VDS, is a complete dn required. Because as per experts only username and password needs to be send to VDS. It will automatically find its DN and do the authentication.
Will be thankful if anyone provide nice reference material regarding ldap and vds
A virtual directory server is a type of server that provides a unified view of identities regardless of how they are stored. (Or you may prefer Wikipedia's definition: "a software layer that delivers a single access point for identity management applications and service platforms"
LDAP is a protocol (hence the "P") for communicating with directory servers.
There isn't a necessary link between LDAP and a VDS, but it is likely that a VDS provides and LDAP interface and, potentially, other programmatic interfaces (Kerberos in particular comes to mind). The details of how you communicate with the VDS are going to be dependent on the configuration you are trying to talk to, but LDAP is a good bet.
Regarding needing a full DN, you don't even need a full DN to authenticate against plain Active Directory. The more usual mode would be to supply something like DOMAIN\username (using the sAMAccountName) or username#dc.example.com (that is, the user principal name) as the SECURITY_PRINCIPAL. In your example, the user would need to type John P R-Asst General Manager rather than anything they are likely to regard as their "user name."
You do, however, need to work out what the VDS you are trying to communicate with requires as the user name. Does it need DOMAIN\username, something else? These are details that whoever runs the VDS you are communicating with should be able to provide you.
In code, you should wind up with something like this (assuming you can use LDAP):
String userName = "DOMAIN\johnp";
String passWord = "asdfgh123";
String ldapURL = "ldaps://vds.example.com";
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, ldapURL);
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, username);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
try {
DirContext authContext = new InitialDirContext(authEnv);
return true;
} catch (NamingException namEx) {
return false;
}
I have a windows application and it connected to a server via WCF services, database is on the server. To using this software, you have to login. So I want to show a notify to UserA, who is working on the software, if UserA's account is logged (on another computer) by another people.
Please help !
Make your logon service operation on the service return a flag which indicates if this account is being used concurrently by another user.
// Pseudocode
LogonResponse response = Service.Logon :> username, password;
if response.UserAccountIsAlreadyLoggedOn print "User logged on already!"
To do this the service would have to persist the logged-on state of all users, probably in the database.
Your logon service operation would need to query the logged-on state of the user account being presented by the operation caller.
// Pseudocode
public LogonResponse Logon (string username, string password)
{
if database.IsThisAccountAlreadyLoggedOn :> username ?
return LogonResponse :> UserAccountIsAlreadyLoggedOn = true;
otherwise
return LogonResponse :> UserAccountIsAlreadyLoggedOn = false;
}
This is the easiest way I can think of to proceed with this.