I am trying to implement Adyen recurring payment to my web application (C# .Net 4) but being relatively new to web services I am not sure I am doing it the right way.
In short the payment provider exposes a WSDL url for that purpose (https://pal-test.adyen.com/pal/Recurring.wsdl) and I added it in Visual Studio 2010 as a Service Reference (i.e. Add Service Reference > Advanced > Add Web Reference)
I then went on and created a test page, to make sure the connection was operational (see code below) and retrieve the details of a test subscription that I created previously. However I am getting an exception when executing the 'listRecurringDetails' action with the error message is 'Object reference not set to an instance of an object." and I cannot figure where I am going wrong.
Any feedback would be welcome.
#
public partial class Store_ServiceTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Recurring proxy = new Recurring();
ICredentials usrCreds = new NetworkCredential("[username]", "[password]");
proxy.Credentials = usrCreds;
try
{
RecurringDetailsRequest thisUserDetail = new RecurringDetailsRequest();
thisUserDetail.merchantAccount = "[some reference]";
thisUserDetail.shopperReference = "[some reference]";
thisUserDetail.recurring.contract = "RECURRING";
RecurringDetailsResult recContractDetails = proxy.listRecurringDetails(thisUserDetail);
string createDate = recContractDetails.creationDate.ToString();
}
catch (Exception ex)
{
string err = ex.Message;
}
finally
{
proxy.Dispose();
}
}
}
Call Stack
App_Web_4h0noljo.dll!Store_ServiceTest.Page_Load(object sender, System.EventArgs e) Line 38 C#
Output window
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code
A first chance exception of type 'System.NullReferenceException' occurred in App_Web_4h0noljo.dll
The thread '' (0x15d0) has exited with code 0 (0x0).
Your code looks good. The key is to add the Recurring service as a
Service Reference instead of as a Web Reference. It should work if the
app configuration file contains:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="AdyenHttpBinding">
<security mode="Transport">
<message clientCredentialType="UserName"/>
<transport clientCredentialType="Basic" realm="Adyen PAL Service Authentication"> <!--Adyen PAL Service Authentication-->
<extendedProtectionPolicy policyEnforcement="Never"/>
</transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://pal-test.adyen.com/pal/servlet/soap/Payment" binding="basicHttpBinding" bindingConfiguration="AdyenHttpBinding" contract="Adyen.Payment.PaymentPortType" name="PaymentHttpPort"/>
<endpoint address="https://pal-test.adyen.com/pal/servlet/soap/Recurring" binding="basicHttpBinding" bindingConfiguration="AdyenHttpBinding" contract="Adyen.Recurring.RecurringPortType" name="RecurringHttpPort"/>
</client>
</system.serviceModel>
Kind Regards,
Sander Rasker (Adyen)
Related
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
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, which is hosted inside of an ASP.NET MVC application (as described in http://msdn.microsoft.com/en-us/library/aa702682.aspx). Part of the MVC actions and WCF service operations are protected, and I use ASP.NET Forms Authentication for both:
// protected MVC action
[Authorize]
public ActionResult ProtectedMvcAction(string args)
// protected WCF operation
[PrincipalPermission(SecurityAction.Demand, Role = "User")]
public void ProtectedWcfOperation(string args)
My WCF client makes sure that the Forms Authentication .ASPXAUTH cookie gets transmitted to the server on every WCF call.
This worked very well for a long time. Now I'm adding HTTPS encryption to my server using an SSL certificate. This required me to make the following changes to the Web.config`:
<basicHttpBinding>
<binding name="ApiServiceBinding">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
The service gets activated and the client can invoke the server operations. However, the [PrincipalPermission] attribute in front of the protected server operations suddenly blocks all service calls. I found out the following:
In the HTTP case (without <security mode="Transport">), both Thread.CurrentPrincipal and HttpContext.Current.User are set to a RolePrincipal instance, with a FormsIdentity instance in the RolePrincipal.Identity property. In this case, everything works fine.
In the HTTPS case (with <security mode="Transport"> in the web.config), the property HttpContext.Current.User is still set to the RolePrincipal/FormsIdentity combination. But, the property Thread.CurrentPrincipal is suddenly set to WindowsPrincipal/WindowsIdentity instances, which makes the [PrincipalPermission] attribute throw an exception.
I tried the following:
Changed the AppDomain.CurrentDomain.SetPrincipalPolicy to every possible value (in Global.asax's Application_Start), but that did not change anything.
Set the property Thread.CurrentPrincipal in Application_PostAuthenticate, but between Application_PostAuthenticate and the actual service invoke, the Thread's CurrentPrincipal is changed to a WindowsPrincipal again.
Any hints? What am I doing wrong?
This solves it:
http://www.codeproject.com/Articles/304877/WCF-REST-4-0-Authorization-with-From-Based-authent
I mofied this code to cover Windows and Forms endpoints and the same service - which also works -
public bool Evaluate( EvaluationContext evaluationContext, ref object state )
{
bool ret = false;
// get the authenticated client identity
HttpCookie formsAuth = HttpContext.Current.Request.Cookies[ ".MyFormsCookie" ];
if( null != formsAuth )
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt( formsAuth.Value );
if( null != ticket )
{
GenericIdentity client = new GenericIdentity( ticket.Name, "Forms" );
// set the custom principal
CustomPrincipal p = new CustomPrincipal( client );
p.RoleManagerProvider = "Internet";
evaluationContext.Properties[ "Principal" ] = p;
ret = true;
}
}
else
{
CustomPrincipal p = new CustomPrincipal( HttpContext.Current.User.Identity );
p.RoleManagerProvider = "Intranet";
evaluationContext.Properties[ "Principal" ] = p;
// assume windows auth
ret = true;
}
return ret;
}
Which looks for the forms auth cookie and tries to use windows authentication if its not there. I also "flip" the role provider for internal and external
This allows me to propogate the users credentials from an internet (by forwarding the cookie) and intranet (using windows constrained delegation) to the same internal service.
I did the config in config (rather than code as per sample) and it seems fine.
For the behaviour its something like:
<behavior name="FormsPaymentsBehavior">
<serviceAuthorization principalPermissionMode="Custom" >
<authorizationPolicies>
<add policyType="FormsPolicy.AuthorizationPolicy,FormsPolicy" />
</authorizationPolicies>
</serviceAuthorization>
This is used for both endpoints as the FormsPolicy (above) handle both and you cannot specify different behaviours for different endpoints.
The bindings enforce the windows credentials handshake on the appropriate endpoint:
<basicHttpBinding>
<binding name="WindowsHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
<binding name="FormsHttpBinding" allowCookies="true">
<security mode="None">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
The transport mode can be changed to
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
For https and it works fine.
For your custom principal I found I had to explicitly call the role manager
...
public bool IsInRole( string role )
{
RoleProvider p = Roles.Providers[ RoleManagerProvider ];
return p.IsUserInRole( Identity.Name, role );
}
public String RoleManagerProvider { get; set; }
...
This is, I guess, because I was no longer using any of the aspnet compat stuff. Since I am flipping role manager depending on my authentication type then ho-hum.
I have experienced this issue too and there is another report (and mine) here. http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/8f424d4f-2f47-4f85-a6b0-00f7e58871f1/
This thread points to the correct solution to be to create a custom Authorisation Policy (http://msdn.microsoft.com/en-us/library/ms729794.aspx) and this code project article (http://www.codeproject.com/Articles/304877/WCF-REST-4-0-Authorization-with-From-Based-authent) seems to explain exactly how to do this for FormsAuth - setting the evaluationContext.Properties["Principal"] = new CustomPrincipal(client) as per MS comments.
I have not yet implemented this - my "quick fix" was to simply revert to a plain old asmx service - but I will be "giving it a go" some time!
If you find another solution - please let me know.
I have created a REST server under ASP.NET and I can't figure out the url to bring up the service. I am running under VS 2010 using it's built in web server. I believe it is actually running (VS 2010 starts up fine). But every combination I can think of for a url doesn't bring it up.
Update: Please take a look at the file http://www.windward.net/temp/RestUnderAspDotNet.zip - I have two solutions in there. The one at src\update runs fine as a REST server. I have pretty much the same code at inetpub\wwwroot\update and while it runs, I can't find a url that talks to it. I tried every variation of http://localhost:56469/update/App_Code/RestServiceImpl.svc/test I could think of and get either 403 or 404.
Any idea why? (I do not want any security on this - anyone will be able to hit it once it's up.)
App_Code\IRestServiceImpl.cs:
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(UriTemplate = "/version", Method = "POST")]
XmlElement GetVersion(XmlElement stats);
[OperationContract]
[WebInvoke(UriTemplate = "/test", Method = "GET")]
string GetTest();
}
App_Code\RestServiceImpl.svc:
<%# ServiceHost Language="C#" Debug="true" Service="RestServiceImpl" CodeBehind="RestServiceImpl.svc.cs" %>
App_Code\RestServiceImpl.cs:
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Xml;
[AspNetCompatibilityRequirements
(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RestServiceImpl : IRestServiceImpl
{
public XmlElement GetVersion(XmlElement stats)
{
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("update");
root.SetAttribute("version", "11.0.13.0");
doc.AppendChild(root);
return doc.DocumentElement;
}
public string GetTest()
{
return "update server is running";
}
}
Relevant part of web.config:
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="RestServiceImpl">
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address ="" binding="webHttpBinding" contract="IRestServiceImpl" behaviorConfiguration="webBinding">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="RestServiceImpl">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="webBinding">
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
Any idea what the url is to bring up the /test GET?
thanks - dave
You can try something like:
http://localhost:whateverportVSgivesyou/RestServiceImpl.svc/test
If the VS webserver is running you should see a system tray icon for it, and if you hover over it you'll see the port it's running on...
I finally figured this out. I have this also on my blog at Windward Wrocks with screenshots. Here's the solution w/o screenshots:
Install the WCF REST Service Template 40(CS) (requires .NET 4.0).
Create a WCF service. This is a New “Project…” not a “Web Site…”. And it is under the general “Visual C#” templates, not “WCF”!