Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restrict WCF Web Service functionality based on User Group

Tags:

wcf

service

I have a WCF Web Service which is consuming by C# client application. I’m also having 4 groups stored in Active Directory. Client application is passing user credentials to connect this web service.

Web service exposing multiple APIs or Methods to be accessed by Client application as follows:

    [OperationContract]
    bool Read();


    [OperationContract]
    bool Write();

Read() method should be accessible for all clients

Write() method should be accessible by only users those belongs to specifc windows user group maintained by Active Directory.

Question: How can we filter or restrict an exposed interface or method by client based on its user group maintain in AD?


jrista, Thanks for your reply. I tried the same directives as PrincipalPermission as follows:

[PrincipalPermission(SecurityAction.Demand, Role = "Readers")]
[OperationContract]
bool Read();

[PrincipalPermission(SecurityAction.Demand, Role = "Writers")]
[OperationContract]
bool Write();

But it does not work. Read group user is also able to call the Writer() method and Writer group user is also able to call the Write() method.

One thing I want to tell you is that I'm using BasicHttpBind in my web.config file as follows:

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBind">
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows" proxyCredentialType="Windows" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service name="DXDirectory.DXDirectoryService" behaviorConfiguration="DXDirectory.Service1Behavior">
        <!-- Service Endpoints -->
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="BasicHttpBind"
                  name="BasicBinding" contract="DXDirectory.IDXDirectoryService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DXDirectory.Service1Behavior">
          <!-- 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="false" />
          <serviceAuthorization principalPermissionMode="UseWindowsGroups"/>          
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Is it required to implement wsHttpBinding for this functionality? If yes, then how can I implement wsHttpBinding in my Web Service?

like image 791
Lalit Sharma Avatar asked Aug 27 '09 18:08

Lalit Sharma


2 Answers

I am not sure off the top of my head how to integrate AD credentials into the normal .NET security framework. However, it is possible (I'll see if I can find some links), and once you do, you should be able to use the standard security attribute to check for a "role", which would correspond to your AD group:

[OperationContract]
bool Read();

[PrincipalPermission(SecurityAction.Demand, Role = "Writers")]
[OperationContract]
bool Write();

To utilize AD groups, configure a service behavior:

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <adServiceBehavior>
        <serviceAuthorization principalPermissionMode="UseWindowsGroups" />
      </adServiceBehavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Had another thought. Sometimes the desire is to not even have the Write() method on the interface at all. With WCF, you can implement multiple service contract interfaces on a single service class. An ideal solution might be to create two service contract interfaces, one with Read() and Write(), one with just Read(). Depending on the user logged into the client, you could use the Read() interface for those who only have read access, and the Read()/Write() interface for those with access to both. This would also allow you to expose the safest service contract to clients that shouldn't have write access, while utilizing the read/write contract internally for administrative purposes. You never expose code that could be potentially exploited this way.

like image 165
jrista Avatar answered Nov 19 '22 02:11

jrista


jrista is right - you can use the built-in Windows authorization services including the "PrincipalPermission" attribute to limit access.

BUT: before you can authorize, you need to authenticate. First you need to know who's knocking on your service's door before deciding whether to let him (or her) in or not.

In order to do that, you need to make sure to use Windows credentials on your message exchange, and client and server must be in the same domain (or in domains with a mutual trust relationship). Also, you'll need to use a binding like wsHttp or netTcp that allows and supports Windows credentials by default, and you need to make sure to use and configure a binding security configuration that transports the Windows credentials across from the client to the server.

You'll need to have something like:

<system.serviceModel>
  <bindings>
    <netTcpBinding>
      <binding name="Secured">
        <security mode="Transport">
          <transport clientCredentialType="Windows" />
        </security>
      </binding>
    </netTcpBinding>
  </bindings>
</system.serviceModel>

and then you'll need to reference that binding configuration from your client and server endpoints.

WsHttpBinding and NetTcpBinding both default to using Windows client credentials, so out of the box, unless you've turned security off completely, you should get Windows credentials support in those two bindings.

Marc

PS:
As jrista shows (and I did in a previous answer to almost the same question you had), you really only need to add that PrincipalPermission attribute to the methods you want to limit to users who belong to a certain group - no manual messing around with AD group memberships etc. needed.

If you really must get the groups the user calling your service belongs to, you can check out the ".Groups" property of the WindowsIdentity calling:

WindowsIdentity winCaller = ServiceSecurityContext.Current.WindowsIdentity;
foreach(var group in winCaller.Groups)
{
   Console.WriteLine(group.Value);
}

If you need the name of the user calling in, use winCaller.Name. If you need the SID for the user calling, use winCaller.User. It's all right there - no messing, no complicated code - just use it! :-)

like image 4
marc_s Avatar answered Nov 19 '22 01:11

marc_s