Using BDC with RdbCredentials - when you have no Domain!!

Posted on 8/18/2007 @ 7:37 AM in #SharePoint by | Feedback | 6497 views

I have blogged about BDC in the past.

The examples I showed there used PassThrough authentication, which means, the identity of the end user on the browser, is what gets transported all the way back to the LOB system (in our case a SQL Server database). That works well and fine for a development environment on a single machine, or else you will need multiple virtual machines, with atleast one of them providing the role of a PDC. Alternatively, that works well within an organization where you have Kerberos and a PDC.

PassThrough however has the following disadvantages:

  1. Lack of effective connection pooling. Every user gets his own pool - YUCK! So, 9/10 times you will end up using RdbCredential, tie it together with the SharePoint SSO and use a username password.
  2. Some databases, such as Oracle or MySQL don't understand windows authentication/kerberos tickets.

I suspect, 9/10 times you will end up using RdbCredential instead. RdbCredential works with the SSO that comes with SharePoint. You can configure SSO by first starting the Microsoft Single Sign On service, and then configuring SSO in Central Administration > Operations by specifying appropriate user ids, with the appropriate permissions. Okay too many appropriates in the last sentence. Truth be told - configuring SSO is a pain in the arse. Not because it is difficult, because the "appropriate permissions" translates to, "you need a user id with more rights than god himself". This has 2 disadvantages:

  1. You need a user-id with more rights than god himself :)
  2. You most definitely need a domain, and a domain controller. So this knocks down two scenarios a) Single machine non-farm development environment, and b) MOSS being used as WCM that uses BDC.

So the question is, how can you use BDC with RdbCredentials, when you have no domain? :-)

Answer: Simply swap the SSO mechanism with your custom mechanism.

Here is how -

MOSS comes with Microsoft SSO. You can swap it for a custom single sign on mechanism, by implementing the ISSOprovider interface. Note that at a time a farm can use only a single mechanism of SSO. Why the heck you would ever want more than a single mechanism of SSO in a single farm, or even a single organization, is beyond my comprehension. But, you can also use the same mechanism to support an alternate SSO scheme within MOSS, should your organization choose to not use AD based SSO.

So let us begin with the bare minimum BDC app I described in this blogpost, and make it work with RdbCredentials, when you have no domain. 

First, let us implement my bare minimum CustomSSO that makes my BDC. Here's the code for that (sorry it's a bit lengthy) -

using System;

using System.Collections.Generic;

using System.Text;

 

using Microsoft.SharePoint.Portal.SingleSignon;

using System.Reflection;

 

namespace Winsmarts

{

    public class CustomSSO : ISsoProvider

    {

        #region ISsoProvider Members

 

        #region Not Implemented Exception Methods

        public Application.ApplicationInfo[] GetApplicationDefinitions()

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public Application.ApplicationField[] GetApplicationFields(string AppID)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public Uri GetCredentialManagementURL(string AppID)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public SsoCredentials GetCredentialsUsingTicket(string Ticket, string AppID)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public string GetCurrentUser()

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

 

        public string GetTicket()

        {

            return "No Ticket Management";

        }

 

        public void PutIdentityOnRequest(ref System.Web.Services.Protocols.HttpWebClientProtocol request, string AppID)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public void PutIdentityOnRequestUsingTicket(ref System.Web.Services.Protocols.HttpWebClientProtocol request, string Ticket, string AppID)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        #endregion

 

        public SsoProviderInfo GetSsoProviderInfo()

        {

            SsoProviderInfo ssoProvInfo = new SsoProviderInfo();

 

            ssoProvInfo.AssemblyName = Assembly.GetExecutingAssembly().FullName;

            ssoProvInfo.Vendor = "Winsmarts";

            ssoProvInfo.Version = "1.0";

 

            return ssoProvInfo;

        }

 

        public Application.ApplicationInfo GetApplicationInfo(string AppID)

        {

            Application.ApplicationInfo applicationInfo =

                new Application.ApplicationInfo("Winsmarts.CustomSSO", "Winsmarts.CustomSSO", Application.ApplicationType.IndividualWindows, "administrator@MOSS2007");

 

            return applicationInfo;

        }

 

        public SsoCredentials GetCredentials(string AppID)

        {

            SsoCredentials creds = new SsoCredentials();

            try

            {

                switch (AppID)

                {

                    case "Northwind":

                        creds.Evidence = new System.Security.SecureString[2];

                        creds.Evidence[0] = MakeSecureString("sa");

                        creds.Evidence[1] = MakeSecureString("sa");

                        creds.UserName = creds.Evidence[0];

                        creds.Password = creds.Evidence[1];

                        break;

                    default:

                        throw new SingleSignonException(SSOReturnCodes.SSO_E_APPLICATION_NOT_FOUND);

                }

            }

            catch (SingleSignonException ex)

            {

                System.Diagnostics.EventLog.WriteEntry("Winsmarts.CustomSSO", "Caught SSO Exception: " + ex.ToString());

                throw;

            }

            catch (Exception ex)

            {

                System.Diagnostics.EventLog.WriteEntry("Winsmarts.CustomSSO", "Caught Exception: " + ex.ToString());

                throw new SingleSignonException(SSOReturnCodes.SSO_E_EXCEPTION, ex);

            }

 

            return creds;

        }

 

        public SsoCredentials GetRestrictedCredentials(string AppID)

        {

            return GetCredentials(AppID);

        }

 

        #endregion

 

        #region Helper methods

        private System.Security.SecureString MakeSecureString(string s)

        {

            if (s == null)

            {

                return null;

            }

 

            System.Security.SecureString secureString = new System.Security.SecureString();

            foreach (char ch in s)

            {

                secureString.AppendChar(ch);

            }

 

            return secureString;

        }

        #endregion

 

    }

}

 

Great! Now let us focus on the most important portion in the above code, the "GetCredentials" method. As you can see, I look for an application ID called "Northwind" and I simply supply an appropriate username and password. You could make this configurable via a .config file or whatever, but I'm trying to keep things blog-ish-simple :).

Go ahead and strong name the above assembly, and put it in the GAC.

Now the next step is to replace the MSFT's SSO with our SSO. This can be done using the following  command -

"C:\Program Files\Microsoft Office Servers\12.0\Bin\Microsoft.SharePoint.Portal.SingleSignon.ProviderAdmin.exe" "Winsmarts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=da3e71efc1a4ced0" "Winsmarts.CustomSSO"

Excellente!

Once the above command tells you that it was successful in replacing the MS-SSO with your SSO, go ahead and change your LobSystemInstance to ---

<LobSystemInstances>

  <LobSystemInstance Name="NorthWindTraders">

    <Properties>

      <Property Name="AuthenticationMode" Type="System.String">RdbCredentials</Property>

      <Property Name="DatabaseAccessProvider" Type="System.String">SqlServer</Property>

      <Property Name="RdbConnection Data Source" Type="System.String">MOSS2007</Property>

      <Property Name="RdbConnection Initial Catalog" Type="System.String">Northwind</Property>

      <Property Name="RdbConnection Pooling" Type="System.String">true</Property>

      <Property Name="SsoApplicationId" Type="System.String">Northwind</Property>

      <Property Name="SsoProviderImplementation" Type="System.String">Winsmarts.CustomSSO, Winsmarts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=da3e71efc1a4ced0</Property>

      <Property Name="WildCardCharacter" Type="System.String">%</Property>

    </Properties>

  </LobSystemInstance>

</LobSystemInstances>

 

(BTW - if you wish to look at the original LobSystemInstance, see here, or if you are confused about WTF is a LobSystemInstance, I'd recommend you brush up on BDC).

That's it!!

Now run the application, throw the BDC Data Item webpart on the page, and you should see the following results -

NEAT! BDC/RdbCredentials working without an active Domain. w00t! :-)

Sound off but keep it civil:

Older comments..


On 11/12/2007 6:20:02 PM Ofer Gal said ..
Your solution worked fine on my Domainless Laptop Thanks!

I have a problem RdbCredentials with domain!


how do you configure SsoApplicationId on the server when using the regular SsoProviderImplementation?


Its hard figure what is what!