I created a simple workflow service with custom instance store(to support oracle).
The custom instance store, i follow the demo from MSDN: [http://msdn.microsoft.com/en-us/library/ee829481.aspx][1]
But when i invoke the service api, raise the exception:
A value of the wrong type was retrieved from the instance store. A
value of type {/Default Web Site/}OrderService.svc was expected, but
null was encountered instead.
I try to use SqlWorkflowInstanceStore, it's OK. No problem for service.
I use custom instance store again and debug, i found LoadWorkflowCommand be executed before SaveWorkflowCommand. I think it's an issue. Need your help!
The following is my code snippet:
1. Web Config:
<extensions>
<behaviorExtensions>
<add name="databaseWorkflowInstanceStore" type="Practices.Framework.Workflow.Configuration.DatabaseInstanceStoreElement, Practices.Framework" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<!--<sqlWorkflowInstanceStore connectionStringName="practicesDatabase" instanceCompletionAction="DeleteAll" instanceEncodingOption="GZip" />-->
<databaseWorkflowInstanceStore database="practicesDatabase" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
2. DatabaseInstanceStore
public class DatabaseInstanceStore : InstanceStore
{
protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
{
if (command is CreateWorkflowOwnerCommand)
{
var instanceOwnerId = Guid.NewGuid();
context.BindInstanceOwner(instanceOwnerId, instanceOwnerId);
}
else if (command is LoadWorkflowCommand)
{
var instanceData = this.LoadInstanceData();
context.LoadedInstance(InstanceState.Initialized, instanceData, null, null, null);
}
else if (command is SaveWorkflowCommand)
{
var saveCommand = (SaveWorkflowCommand)command;
var instanceData = saveCommand.InstanceData;
this.SaveInstanceData(instanceData);
}
return new CompletedAsyncResult<bool>(true, callback, state);
}
......
}
The MSDN article only shows the bare minimum of commands you need to implement. It sounds like you need to support more command so I would check which commands are dispatched that you don't support yet. See here for a list of commands.
The BeginTryCommand is called with the following commands executed in this order:
1. CreateWorkflowOwnerCommand
2. LoadWorkflowCommand
3. SaveWorkflowCommand
4. SaveWorkflowCommand
So for LoadWorkflowCommand:
I need Create Instance before Load
http://social.msdn.microsoft.com/Forums/en/dublin/thread/e51d7b18-1e27-4335-8ad0-4ce76b9f8b91
Related
In my WCF service implementation I used username/password for authentication, with ASP.NET Identity 2.0 and with the following codes.
public class IdentityValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Debug.WriteLine("Validate "+userName);
using (var context = new ApplicationDbContext())
using (var userStore = new UserStore<ApplicationUser>(context))
using (var userManager = new UserManager<ApplicationUser>(userStore))
{
var user = userManager.Find(userName, password);
if (user == null)
{
var msg = String.Format("Unknown Username {0} or incorrect password {1}", userName, password);
Trace.TraceWarning(msg);
throw new FaultException(msg);//the client actually will receive MessageSecurityException. But if I throw MessageSecurityException, the runtime will give FaultException to client without clear message.
}
}
Debug.WriteLine("End validate " + userName);
}
}
and the config is like
<behavior name="authBehavior">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="IdentityValidator,Security" />
</serviceCredentials>
<serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="RoleAuthorizationManager,Security"></serviceAuthorization>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
The functionality of authentication is so far so good. And the client is typically a Windows desktop app that will persist the user name and password all the time.
I understand the WCF authentication is per message. That means each call will run though some SQL queries against the database that stores the user name and password hash. So far in my low traffic test, the performance is good enough, say the authentication could be done within 1 second.
However, under heavy traffic, I wonder if it is a good idea to cache the result of querying the database, for example, before running userManager.Find, check if the userName and password pair had been authenticated in MemoryCache with 10 minutes expiry.
Is this something micro performance optimization? or in your experiences, did you need to be concerned by the performance of authentication of WCF services, built-in or custom?
I would like to host a REST-full WCF 4.0 service on an already-created IIS 7.5 website based on ASP.NET v4.0 and secured by forms authentication. So, I tried to configure my WCF stack using mixed mode authentication (aspNetCompatibilityEnabled="false") and configured the service host to use no security at all but So long, all my efforts was completely unsuccessful. When I try to call my service from the browser after a while the connection to the host is closed without a response and my browser raises an error indicating the connection to the target webstie is closed without any response.
However, if I write a dummy code in Application_BeginRequest to authenticate a dummy user in forms authentication module using FormsAuthentication.Authenticate or call the service in an authenticated browser session everything works fine and the service is called successfully.
I tried to find the problem causing this strange behavior using WCF tracing. What I have found from the resulting svclog file is this exception:
Message: Object reference not set to an instance of an object.
StackTrace:
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.get_LogonUserIdentity()
System.ServiceModel.Channels.HttpChannelListener.ValidateAuthentication(IHttpAuthenticationContext authenticationContext)
System.ServiceModel.Channels.HttpRequestContext.ProcessAuthentication()
System.ServiceModel.Channels.HttpChannelListener`1.HttpContextReceived(HttpRequestContext context, Action callback)
Any idea about the problem?
UPDATE: I even set the authentication mode of the website to "None" and authorized anonymous users. Still the same results. Nothing changed. The question is that can I use unauthenticated WCF RESTfull services with aspNetCompatibilityEnabled="false" on an ASP.NET website at all???
To be more specific, What I have tried to do is:
Implemented my WCF service in the form of a .svc file
Configured WCF in my web.config file as the following (note AspNetCompatibilityEnabled="false"):
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="false" />
</system.serviceModel>
Created and used my own ServiceHostFactory like this:
public class MyServiceHostFactory : ServiceHostFactoryBase
{
#region Methods
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
var type = Type.GetType(constructorString);
var host = new WebServiceHost(type, baseAddresses);
var serviceBehavior = host.Description.Behaviors.OfType<ServiceBehaviorAttribute>().Single();
serviceBehavior.ConcurrencyMode = ConcurrencyMode.Multiple;
serviceBehavior.MaxItemsInObjectGraph = int.MaxValue;
var metadataBehavior = host.Description.Behaviors.OfType<ServiceMetadataBehavior>().SingleOrDefault();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(metadataBehavior);
}
var debugBehavior = host.Description.Behaviors.OfType<ServiceDebugBehavior>().SingleOrDefault();
if (debugBehavior == null)
{
debugBehavior = new ServiceDebugBehavior();
host.Description.Behaviors.Add(debugBehavior);
}
metadataBehavior.HttpGetEnabled = true;
debugBehavior.IncludeExceptionDetailInFaults = true;
var binding = new WebHttpBinding { MaxBufferPoolSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue };
binding.Security.Mode = WebHttpSecurityMode.None;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
WebHttpBehavior webHttpBehavior = new WebHttpBehavior { HelpEnabled = true };
foreach (var contract in type.GetInterfaces().Where(i => i.GetCustomAttributes(typeof(ServiceContractAttribute), true).Length > 0))
{
var endpoint = host.AddServiceEndpoint(contract, binding, "");
endpoint.Behaviors.Add(webHttpBehavior);
}
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
return host;
}
#endregion
}
I am attempting to implement authentication for a REST service implemented in WCF and hosted on Azure. I am using HttpModule to handle the AuthenticationRequest, PostAuthenticationRequest and EndRequest events. If the Authorization header is missing or if the token contained therein is invalid, during EndRequest I am setting the StatusCode on the Response to 401. However, I have determined that EndRequest is called twice, and on the second call the response has already had headers set, causing the code which sets the StatusCode to throw an exception.
I added locks to Init() to ensure that the handler wasn't being registered twice; still ran twice. Init() also ran twice, indicating that two instances of the HttpModule were being created. However, using Set Object ID in the VS debugger seems to indicate that the requests are actually different requests. I've verified in Fiddler that there is only one request being issued to my service from the browser.
If I switch to using global.asax routing instead of depending on the WCF service host configuration, the handler is only called once and everything works fine.
If I add configuration to the system.web configuration section as well as the system.webServer configuration section in Web.config, the handler is only called once and everything works fine.
So I have mitigations, but I really dislike behavior I don't understand. Why does the handler get called twice?
Here is a minimal repro of the problem:
Web.config:
<system.web>
<compilation debug="true" targetFramework="4.0" />
<!--<httpModules>
<add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/>
</httpModules>-->
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
<service name="TestWCFRole.Service1">
<endpoint binding="webHttpBinding" name="RestEndpoint" contract="TestWCFRole.IService1" bindingConfiguration="HttpSecurityBinding" behaviorConfiguration="WebBehavior"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/" />
</baseAddresses>
</host>
</service>
</services>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
<bindings>
<webHttpBinding>
<binding name="HttpSecurityBinding" >
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/>
</modules>
<directoryBrowse enabled="true"/>
</system.webServer>
Http module:
using System;
using System.Web;
namespace TestWCFRole
{
public class AuthModule : IHttpModule
{
/// <summary>
/// You will need to configure this module in the web.config file of your
/// web and register it with IIS before being able to use it. For more information
/// see the following link: http://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
// Below is an example of how you can handle LogRequest event and provide
// custom logging implementation for it
context.EndRequest += new EventHandler(OnEndRequest);
}
#endregion
public void OnEndRequest(Object source, EventArgs e)
{
HttpContext.Current.Response.StatusCode = 401;
}
}
}
When an ASP.net application starts up, to maximize performance the ASP.NET Worker process will instantiate as many HttpApplication objects as it needs. Each HttpApplication object, will also instantiate one copy of each IHttpModule that is registered and call the Init method! That's really an internal design of the ASP.NET process running under IIS (or cassini which is VS built in webserver). Might be because your ASPX page has links to other resources which your browser will try to download, an external resource, and iframe, a css file, or maybe ASP.NET Worker Process behavior.
Luckily it's not the case for Global.asax:
Here's from MSDN:
The Application_Start and Application_End methods are special methods
that do not represent HttpApplication events. ASP.NET calls them once
for the lifetime of the application domain, not for each
HttpApplication instance.
However HTTPModule's init method is called once for every instance of the HttpApplication class after all modules have been created
The first time an ASP.NET page or process is requested in an
application, a new instance of HttpApplication is created. However, to
maximize performance, HttpApplication instances might be reused for
multiple requests.
And illustrated by the following diagram:
If you want code that's guaranteed to run just once, you can either use Application_Start of the Global.asax or set a flag and lock it in the underlying module which is don't think is a good practice for the sake of Authentication!
Sorry no clue to why it could be called twice, however EndRequest can end up being called for multiple reasons. request finished, request was aborted, some error happened. So i wouldn't put my trust in assuming that if you get there, you actually have a 401, it could be for other reasons.
I'd just keep my logic in the AuthenticateRequest pipeline:
public class AuthenticationModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.AuthenticateRequest += Authenticate;
}
public static void Authenticate(object sender, EventArgs e)
{
// authentication logic here
//.............
if (authenticated) {
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(myUser, myRoles);
}
// failure logic here
//.............
}
}
I have a WCF Service defined as:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Service
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public string HelloWorld()
{
return "Hello World";
}
}
My Web.Config file:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true"/>
</webHttpBinding>
</bindings>
<services>
<service name="Service">
<endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingWithJsonP" contract="Service" behaviorConfiguration="webHttpBehavior"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
I want to be able to access ASP .Net session variables in my WCF service, and I want the WCF service to be returning JSONP data, however even with this simple service, browsing to ../Service.svc/HelloWorld I am getting a 400 Bad Request error.
Can someone point me in the right direction?
Looks like the combination of JSONP, ASP.NET Compatibility and an authenticated user is not supported per this Microsoft forum.
According to the moderator of the forum, you need to disable one of the three.
Probably not the answer you were hoping for, but the moderator's explanation is pretty good and offers a few suggestions.
Hope this helps. Good luck!
I realise this has already been answered, but it's possible (though I'm unsure if recommended from a security perspective) to 'de-authenticate' a request early enough to pass the check being made by the webHttpBinding.
The gist is to set HttpContext.Current.User to be a new GenericPrincipal built on a GenericIdentity with no name or type mimicking what you'd see if an unauthenticated user had just hit your service - by the time the webHttpBinding performs its 'no authenticated JSONP calls' check the request is taking place in the context of an unauthenticated user.
Note: I'm unsure if there are security implications of this - one off the top of my head is that if you have an authenticated user their session state will still be available to your service which may be a bad thing, depending on what you're doing.
You can do this in a couple of places
By hooking the Application.AuthenticateRequest event, filtering by request URL
With a custom WCF message inspector
Example message inspector and behavior element (same class, very much use at own risk):
using System;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading;
using System.Web;
namespace MyNamespace
{
public class ForceAnonymousEndpointBehavior : BehaviorExtensionElement, IDispatchMessageInspector, IEndpointBehavior
{
public override Type BehaviorType
{
get { return typeof(ForceAnonymousEndpointBehavior); }
}
protected override object CreateBehavior()
{
return new ForceAnonymousEndpointBehavior();
}
object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpContext.Current.User = Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("", ""), null);
return null;
}
void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
{
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new ForceAnonymousEndpointBehavior());
}
void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
{
}
}
}
Then in web.config register the behavior extension (in the system.serviceModel element):
<extensions>
<behaviorExtensions>
<add name="ForceAnonymous" type="MyNamespace.ForceAnonymousEndpointBehavior, MyAssembly" />
</behaviorExtensions>
</extensions>
Adding the behavior to the endpointBehavior in question (again under system.serviceModel):
<behaviors>
<endpointBehaviors>
<behavior name="jsonpBehavior">
<ForceAnonymous />
</behavior>
</endpointBehaviors>
</behaviors>
...and making sure the endpoint behavior is called out in your service's endpoint declaration by setting the behaviorConfiguration attribute to match the behavior name you used above.
I am using WSO2-EI 6.4.0. I have tried this development with link. It work for me. But I need to get user name and password from other back end service. In this example was showed the hard corded user and password. I have added that code for your reference. Please help me to get those user name and password from property file.
public boolean processSecurity(String credentials) {
String decodedCredentials = new String(new Base64().decode(credentials.getBytes()));
String usernName = decodedCredentials.split(":")[0];
String password = decodedCredentials.split(":")[1];
if ("admin".equals(username) && "admin".equals(password)) {
return true;
} else {
return false;
}
}
I have added WSO2 EI handler like following. I need to pass the value from back service or call other sequence and load.
<api context="/test">
<resource methods="POST">
<inSequence>
................
</inSequence>
<outSequence>
................
</outSequence>
</resource>
<handlers>
<handler class="rezg.ride.common.BasicAuthHandler">
<property name="cm_password" value="admin"/>
<property name="cm_userName" value="admin"/>
</handler>
</handlers>
</api>
When we run the above API, handlers are running first and then running in and out sequences. So I need to get user name and password calling Sequence or any other method before run this BasicAuthHandler.
If you need to read the property file from the class mediator it's just straight forward java property file reading. Please refer the following call sample of reading a property file. In this scenario, Just read the carbon.properties file exists in the conf directory.
public boolean mediate(MessageContext context) {
String passwordFileLocation = System.getProperty("conf.location")+"/carbon.properties";
try (FileInputStream input = new FileInputStream(passwordFileLocation)) {
Properties prop = new Properties();
// load a properties file
prop.load(input);
log.info("------org.wso2.CipherTransformation : " + prop.getProperty("org.wso2.CipherTransformation"));
} catch (IOException ex) {
ex.printStackTrace();
}
return true;
}
To get the server location and the conf locating, There are JAVA system properties are set at the time wso2 server starts. Following are some of the useful System system properties.
carbon.local.ip
carbon.home
conf.location