At the moment I'm working with a WCF service that is used by an Asp.net MVC application.
For security reasons I'm using a guid that represents a username and pasword. ( When the user logs in, WCF checks the credentials in active directory and creates a record in a databasetable that connects the guid with the username and pasword)
When the user uses another service, I send this guid along in the header.
I want to impersonate the user in wcf using this guid.
I've tried the folowing (using authorizationManager) but this doesn't work.
public class MyAuthBehaviour :ServiceAuthorizationManager
{
public override bool CheckAccess(OperationContext operationContext, ref Message message)
{
var index = operationContext.IncomingMessageHeaders.FindHeader("identifier", "");
if (index >= 0)
{
string identifier = operationContext.IncomingMessageHeaders.GetHeader<string>(index);
AddWindowsID(identifier);
}
return true;
}
private void AddWindowsID(string identifier)
{
WindowsIdentity wid = AccountBL.GetWindowsIdentity(identifier);
wid.Impersonate();
}
}
I do get the WindowsIdentity but I can't Impersonate.
Is there a way to do this?
To say it short: I want to impersonate a user within WCF before it gets to the actual service method using the guid in the header.
When wid.Impersonate() hits, the client throws this exception:
The server was unable to process the request due to an internal error.
For more information about the error, either turn on
IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute
or from the configuration behavior) on the server in
order to send the exception information back to the client, or turn on
tracing as per the Microsoft .NET Framework SDK documentation and
inspect the server trace logs.
WCF keeps on running.(although other exceptions where trown in the WCF services)
The exception isn't thrown when wid.Impersonate() is executed, it occurs when we're leaving the MyAuthBehaviour class
Related
I have a WCF which is configured on Windows Authentication. This means that only authenticated Windows users will be able to access. I have a method which does not return anything but spins up a thread that does some long running task under the called windows user impersonation.
My code is below:
public void SampleTask();
{
Task.Factory.StartNew(this.Activity);
}
private void Activity()
{
WindowsIdentity identity = ServiceSecurityContext.Current.WindowsIdentity;
using (WindowsImpersonationContext ctx = identity.Impersonate())
{
// Log status in a log database as "In Progress"
// Do long running task (accessing database as current user)
// retreive the results and save in a file
// Update the log database as "Complete"
}
}
My question is will the task still complete of retrieving the results and saving it and put the status as it should. Or will the impersonation will not work as there will be no open session. Or am I mistaken
Regards,
Girija Shankar
The session should remain open as long as the method is executing. Even if the method is returning void, the request that started the execution of the method will be replied to.
If you don't need a reply from the service, you can use the IsOneWay = true attribute on the method, which will tell the service to not send a reply to the client. Since this is a long-running method that is not returning data to the client, that makes it a good candidate for being a one-way method. In this case I'm not sure if the session would remain open or not, but it doesn't matter because the impersonation context will be scoped to the service, and have no dependency on the client.
In your method, you can see this because of the declaration:
WindowsIdentity identity = ServiceSecurityContext.Current.WindowsIdentity;
The variable identity is scoped to the Activity method.
using (WindowsImpersonationContect ctx = identity.Impersonate())
The variable ctx is likewise scoped to the using block within the Activity method.
The only time you would run into a problem that I can think of is if the service throws an exception and crashes - then of course the method wouldn't finish.
To sum up, since the impersonation is based on the identity the service is running under, and you're not returning any data to the client, session should have no impact on both the method running to completion or the identity the service is running under.
WCF function
public void SetSession(string name)
{
HttpContext.Current.Session["abc"]=name;
}
public string GetSession(string name)
{
return HttpContext.Current.Session["abc"].ToString();
}
Proxy
using (ServiceReference1.BlackjackClient proxy = new ServiceReference1.BlackjackClient())
{
proxy.SetSession("Hello");
}
my problem is when multiple clients are accessing the service then last set session is accessed by the each client. Session are not browser request based and not recognizing the client. Which client has sent which request. What should i do to make them specific to each client. means each client must have his own session.
Please help
The service can not know which client is calling the service. Regular asp.net use of Session uses a cookie, that identifies each request and makes some internal voodoo to map the request to the correct session.
In your case, you would have to either use login from the clients to ensure that the service could identify requests, but this would not in it self solve the problem.
Since you have access to the service implementation the simplest solution would probably be to store a session identifier (a Guid) in the client, and then send this along each request to the web service thus altering
public void SetSession(string name)
{
HttpContext.Current.Session["abc"]=name;
}
public string GetSession(string name)
{
return HttpContext.Current.Session["abc"].ToString();
}
to something like
public void SetSession(string name, Guid sessionId)
{
HttpContext.Current.Session[sessionId + "_abc"]=name;
}
public string GetSession(string name, Guid sessionId)
{
return HttpContext.Current.Session[sessionId + "_abc"].ToString();
}
Modifying the method signature like this is rather ugly though, but the idea would be, that the client aids the server in identifying the caller and thus the session.
It would be cleaner to use the transport protocol to identify the caller, so if you are creating a HTTP service, you could use some http header (perhaps authorization) to contain the session identifier. If you are using SOAP the message header could contain identical information.
The session identifier could also be created at the service by a new method named something like Guid CreateSession(). But a Guid could as well be created in the client.
But again: You will need to store some unique session id or user credentials in the client and communicate them to the server in each request.
I have the following scenario:
I have workflow application object running a workflow activity. In the workflow activity logic, I call a WCF service in which the service operations is also generated as a set activities.
The problem is: How can I pass the credentials and impersonate other user in this case as its regularly done like this:
MyServiceClient client = new MyServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = domain;
client.ClientCredentials.Windows.ClientCredential.UserName = username;
client.ClientCredentials.Windows.ClientCredential.Password = password;
I tried to impersonate the code block that runs the the workflow application object, but in that case, I get some kind of exception saying "Could not load file or assembly .... Access is denied". As I understand, the reason of this exception is that the workflow application is trying to call assembly which is under the original logged on user.
Any help please?
Windows authentication using the calling process's credentials is the best supported authentication method, but there are activities for handling username/password authentication in the WF Security Pack CTP 1 on the WF CodePlex site (https://wf.codeplex.com/releases/view/48114). See TokenFlowScope and GetUserNameSecurityToken. This article goes through it in detail: http://msdn.microsoft.com/en-us/magazine/gg598919.aspx. I haven't tried this, and would avoid it if at all possible.
For your second problem, there's a bit more to making workflows running under WorkflowApplication impersonate. From the same article:
The security context of a calling thread isn’t copied to the workflow thread, so even if the workflow client is impersonating, the WF thread—which is executing the activities—won’t be impersonating. The security context of the caller can be flown to the WF thread using a custom Synchronization context that would forward the call on the same incoming Async thread, similar to the Synchronization context used by the WorkflowInvoker
public class MySyncContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}
I created a Ria services endpoint. Silverlight passes data into the RIA Services. The end point then writes/updates this data to the file system. We are experiencing the following exception:
System.ServiceModel.DomainServices.Client.DomainOperationException:
Invoke operation
'ModifyLogiDashletXmlFile' failed.
Exception of type
'System.ServiceModel.DomainServices.Client.DomainOperationException'
was thrown. at
Phoenix.UI.SL.Infrastructure.Services.LogiReportService.<>c_DisplayClass8.<ModifyLogiDashletXmlFile>b_7(InvokeOperation
operation) at
System.ServiceModel.DomainServices.Client.InvokeOperation.<>c_DisplayClass41.<Create>b__0(InvokeOperation1 arg) at
System.ServiceModel.DomainServices.Client.InvokeOperation`1.InvokeCompleteAction()
at
System.ServiceModel.DomainServices.Client.OperationBase.Complete(Exception
error) at
System.ServiceModel.DomainServices.Client.InvokeOperation.Complete(Exception
error) at
System.ServiceModel.DomainServices.Client.DomainContext.CompleteInvoke(IAsyncResult
asyncResult) at
System.ServiceModel.DomainServices.Client.DomainContext.<>c_DisplayClass38.<InvokeOperation>b__34(Object )
the attributes on the class:
[EnableClientAccess(RequiresSecureEndpoint = true)]
[LinqToEntitiesDomainServiceDescriptionProvider(typeof(PhoenixEntities))] // IMPORTANT: Must have this because we are returning/passing EF Entities from Phoenix Context
[RequiresAuthentication]
public class LogiReportService : DomainService
{
...
}
The entry method:
[Invoke]
public void ModifyLogiDashletXmlFile(IEnumerable<ParameterNameValuePair> paramNameValuePairs, Guid clientId, string dashletInstanceId)
{
// Validate Client is has a valid relationship to the User (i.e. no disabled)
if (this.PhoenixUser.MembershipClientIds.Contains(clientId))
{
ModifyLogiXmlFile(PhoenixUser.UserId, clientId.ToString(), dashletInstanceId, paramNameValuePairs);
}
}
What's baffling is other endpoints work fine. The differences between this endpoint and the other is this one writes and reads files from the file-system. The app-domain user has full-rights and the error returned does not appear to be a permissions issue.
All idea's are welcomed. I'm hitting up against a wall with this issue.
This issue turned out to be a permissions issue on the server. A file was being written to the system.
RIA services was swallowing the error and presenting a generic error in it's stead. I put a try catch around the entry method and logged the error to the database. Then I was able to discover the real issue.
Yesterday I began learning WCF by porting an existing ASP.NET Web Service.
Creating the WCF service was very easy in itself. Approximately an hour after I created my first WCF Service Library project ever, I was already successfully testing my new WCF service in the WCF Test Client.
Now I would like to implement a simple authentication system, but still do not know how. For the sake of simplicity, say my Web Service has three operations: logging in, getting the length of the user's name, and logging out. How do I complete the TODOs in the following code?
[ServiceContract]
public class MyService
{
[OperationContract(IsInitiating = true, IsTerminating = false)]
public bool Login(string userName, string password)
{
/* I have already implemented the function that validades
whether the user name and password are correct. */
if (ValidateLogin(userName, password))
{
/* TODO: Initiate a session */
return true;
}
else
return false;
}
[OperationContract(IsInitiating = false, IsTerminating = false)]
public int GetUserNameLength()
{
/*
TODO: How to validate whether the user has logged in?
How to obtain the name of the user that has logged in?
*/
int userNameLength = 42;
return userNameLength;
}
[OperationContract(IsInitiating = false, IsTerminating = true)]
public void Logout()
{
/* TODO: How to logout? */
}
}
NOTE: I am the enemy number one of gross hacks. Please lead me towards conceptually "clean" solutions, regardless of their complexity.
The approach you're following may not be correct with WCF. Based on your approach above, the user is already authenticated as it's able to invoke Login operation. Typically, User shouldn't be allowed to invoke any operation until he/she is auhenticated, but in your approach that's not the case.
Also, the sessions in WCF are client initiated, not server initiated. However, based on your approach they seems to be server initiated.
Here're some resources which sheds more light on WCF Security,
http://msdn.microsoft.com/en-us/library/ms731925.aspx
Improve wcf security guidance - http://wcfsecurityguide.codeplex.com/
If you want to use Custom UserNamePassword validator, here is the link,
http://msdn.microsoft.com/en-us/library/aa702565.aspx
HTH,
Amit
It looks like you are trying to handle authentication at the application level. If you have a particular business need to do this then go right ahead but if you just trying to ensure an authenticated user is calling your service then use the build-in WCF authentication mechanisms.
Also, the service contract you are showing is missing this setting in the ServiceContract:
[ServiceContract(SessionMode=SessionMode.Required)]
to make the IsInitiating and IsTerminating actually work. Creating session-based WCF services is pretty limiting because you are forcing all the methods in your service to be occur between he Login ... Logout sequence of calls. If you develop multiple services for your application then trying to orchestrate the interaction with each service in its own session can be very error prone.