Tuesday, July 7, 2015

Service Bus authentication and permissions

1)      Clients can only connect to the Service Bus, if they have the SB certificates in their Certificate Store. The ServiceBus certificates, if autogenerated, can be found using the cmdlet: Get-SBAutoGeneratedCA.
2)      Second we either need to use Shared Access Key or Windows STS Uri. If ServiceBus is installed in a domain and the client (Windows 7) is not connected to the domain (example – Corpnet), then its better to use Shared Access Key, which can be retrieved by the cmdlet: Get-SBAuthorizationRule. There are mainly 3 rights –Listen, Send, Manage.

3)      Third – To create Topics, Queues and other admin related task, the user must be a part of ManageUsers collection of the ServiceBus Namespace, which can be retrieved by the cmdlet: Get-SBNamespace.

Create a WCF listener for Service Bus

0)      Add nuget package reference to ServiceBus.v1_1 (at the time of writing this).

1)      Your ServiceContract should look this this –
[ServiceContract]
    public interface IService1
    {

        [OperationContract(IsOneWay = true, Action = "*"), ReceiveContextEnabled(ManualControl = true)]
        void AccountingReader(Message message);

        // TODO: Add your service operations here
    }
ManualControl is true means that when the message is received from the ServiceBus, We have to manually invoke receiveContext.Complete(); to remove it from the queue or topic.

2)      Your Web config should look this this –
a)      One key point - When working in Windows 7 (non server machine, or When not in corpnet), use SAS to authentication as windows STS does not seem to work.
b)      Second , in case of any issue, use – WCF tracing.

<system.serviceModel>  
    <extensions>
      <!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
      <behaviorExtensions>
        <add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </behaviorExtensions>
      <bindingElementExtensions>
        <add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingElementExtensions>
      <bindingExtensions>
        <add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
    </extensions>
    <bindings>     
      <customBinding>
        <binding name="messagingBinding" closeTimeout="00:03:00" openTimeout="00:03:00" receiveTimeout="00:03:00" sendTimeout="00:03:00">
          <textMessageEncoding messageVersion="None">
            <readerQuotas maxStringContentLength="2147483647" />
          </textMessageEncoding>
          <!--<binaryMessageEncoding/>-->
          <netMessagingTransport />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="securityBehavior">
          <transportClientEndpointBehavior>
            <tokenProvider>
              <!--VV Imp. When working in Windows 7 (non server machine), use SAS to authentication as windows STS does not seem to work-->
              <sharedAccessSignature keyName="RootManageSharedAccessKey" key="gqlUNxI0+lNzJopR0gaJOt8LLOn3jELsRepjBSij7T4=" />
              <!--<windowsAuthentication>
                <stsUris>
                  <stsUri value="https://VM-WEB-AZURE/ServiceBusDefaultNamespace"/>               
                </stsUris>
              </windowsAuthentication>-->
            </tokenProvider>
          </transportClientEndpointBehavior>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <!--To avoid disclosing metadata information, set the values below to false before deployment-->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="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>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
      <add binding="netMessagingBinding" scheme="sb" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" minFreeMemoryPercentageToActivateService="0" multipleSiteBindingsEnabled="true" />
    <services>
      <service name="ReceiveFromWSSB.Service1">

        <endpoint name="myEndPoint" listenUri="sb://VM-WEB-AZURE/ServiceBusDefaultNamespace/as400/subscriptions/AllAS400"
                  address="sb://VM-WEB-AZURE/ServiceBusDefaultNamespace/as400" binding="customBinding"
                  bindingConfiguration="messagingBinding" contract="ReceiveFromWSSB.IService1" behaviorConfiguration="securityBehavior"/>

     
      </service>
    </services>


  </system.serviceModel>
3)      Your Service implementation should look like this  -

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
    public class Service1 : IService1
    {
         public void AccountingReader(Message message)
        {


4)  Push some messages to the Service Bus Topic or Queue through code or through SB Explorer as shown below – 


WCF Sessions (and its dependency on bindings) demo


To find the instance id of the WCF instance (for monitoring purpose) use the following code –snippet

public class Service1 : IService1
    {
        private string instanceId = "";

        public Service1()
        {
            instanceId = Guid.NewGuid().ToString();
        }
        public string GetData(int value)
        {
Trace.WriteLine(String.Concat("Current WCF Instance Id is : ", instanceId));
File.AppendAllText(@"C:\Temp\logs.txt", "Current WCF Instance Id is : " + instanceId);
            return string.Format("You entered: {0}", value);
        }

You will get a different GUID printed each time if the call is per-call
By default, its per-session, but you might get different GUIDs if the binding does not support session (example basicHttpBinding). Test it by opening the WCfTestClient tool.
If you try to enforce session in the contract by specifying  -
[ServiceContract(SessionMode = SessionMode.Required)]
    public interface IService1

You will get an error when you try to add a reference to the service (if the binding is still basicHttpBinding).
The above method is a good way to test the service instance creation and its dependency on binding.