Casifying TeamDynamix (TDNext)

We have recently implemented TeamDynamix at our campus.  Here are my crude notes on how to accomplish authentication via CAS to TeamDynamix.

From their site:

The Only PPM Solution Designed For Higher Education.

TeamDynamixHE provides the only project and portfolio management solutions specifically tailored to accommodate the needs of Higher Education. With a simple yet powerful interface, TeamDynamixHE solutions enable colleges and universities to prioritize efforts, adapt to change, manage resources and meet commitments on time and on budget.

Notes:

  • TeamDynamix has numerous web applications, at this time version 7.1 only has SSO support for the component TDNext.
  • Since TeamDynamix uses Windows form authentication, the officail cas .Net client will not work.  So we used the method explained here: ASP.NET Forms Authentication
  • We are using .Net 4
  • Cas Server 3.4.2
  • TeamDynamix caches the authentication framework, so you will need to restart IIS for DLL changes to be loaded.

Step 1 configure TdNext's web.config to use Custom SSOAuth.

TeamDynamix has a hook for customSSO, that we must configure.

  • Configure SSOReturnUrl
  • Configure SSOLogoutUrl
  • Switch the loginurl of authentication to *LoginSSO.aspx *from Login.aspx

Note, as usual with CAS, make sure to URLEncode the service URL.

web.xml
<appSettings>
....
	<!--SSOReturnUrl: If you use a single sign on provider, set the "SSOReturnUrl" to the url for the single sign on provider.  Also, set the  <authentication> node LoginUrl to "LoginSSO.aspx"-->
	<add key="SSOReturnUrl" value="https://cas.myuniversity.edu/login?service=https%3A%2F%2Fdev-dynamix.myuniversity.edu%2FTDNext%2FLoginSSO.aspx%3FReturnUrl%3D%2FTDNext%2FHome%2FDesktop%2FDesktop.aspx" />    <!--SSOLogoutUrl: This is the url you want the users browser to go after logging out of TD.  This is required for SSO implementations and optional for others-->	<add key="SSOReturnUrl" value="https://cas.myuniversity.edu/login?service=https%3A%2F%2Fdynamix.myuniversity.edu%2FTDNext%2FLoginSSO.aspx%3FReturnUrl%3D%2FTDNext%2FHome%2FDesktop%2FDesktop.aspx" />
	<!--SSOLogoutUrl: This is the url you want the users browser to go after logging out of TD.  This is required for SSO implementations and optional for others-->
	<add key="SSOLogoutUrl" value="https://cas.myuniversity.edu/logout" />
....
</appSettings>
....
<system.web> 
....
	<authentication mode="Forms">
		<forms name="TeamDynamix" loginUrl="LoginSSO.aspx" timeout="480" defaultUrl="LoginSSO.aspx" cookieless="UseCookies" slidingExpiration="true" path="/" protection="All">
 			<credentials passwordFormat="SHA1" />
		</forms>
	</authentication>
....
</system.web>

Step 2 create the .net dll (SSOAuth that overrides the TeamDynamix interface)

  • Include the TeamDynamix SDK dll as a reference.

SSO Auth
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.IO;
using System.Net;
using System.Xml;
using System.Web.Security;
using System.Net.Security;
namespace TeamDynamixCAS
{
    public class SSOAuth : TeamDynamix.AuthenticationSDK.IAuthenticationProvider
    {
        public String AuthenticationSso(System.Web.HttpRequest req)
        {
            //Set up dynamic switch for production/test
            string CASHOST = "https://dev-cas.myuniversity.edu/";
            string urlencodedService = "https%3A%2F%2Fdev-dynamix.myuniversity.edu%2FTDNext%2FLoginSSO.aspx";

            if (req.ServerVariables["HTTP_HOST"].Equals("dynamix.myuniversity.edu"))//use production cas/dynamix settings.
            {
                CASHOST = "https://cas.myuniversity.edu/";
                urlencodedService = "https%3A%2F%2Fdynamix.myuniversity.edu%2FTDNext%2FLoginSSO.aspx";
            }

            string tkt = req.QueryString["ticket"];
            if (tkt != null && tkt.Length > 0)
            {
                string service = urlencodedService + "%3FReturnUrl%3D%2FTDNext%2FHome%2FDesktop%2FDesktop.aspx";
                string validateurl = CASHOST + "serviceValidate?" + "ticket=" + tkt + "&" + "service=" + service;
                StreamReader Reader = new StreamReader(new WebClient().OpenRead(validateurl));
                string resp = Reader.ReadToEnd();
                NameTable nt = new NameTable();
                XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
                XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
                XmlTextReader reader = new XmlTextReader(resp, XmlNodeType.Element, context);
                while (reader.Read())
                {
                    if (reader.IsStartElement())
                    {
                        string tag = reader.LocalName;
                        if (tag == "user")
                            return reader.ReadString();
                    }
                }
                reader.Close();
            }
            return "";//TD expects blank, as an indicator that the user is not logged in.
        }
        public bool Authenticate(string UserName, string Password)
        {
            return false;
        }
        public bool ChangePassword(string Username, string OldPassword, string NewPassword)
        {
            return false;
        }
        public bool SupportsChangePassword { get { return false; } }
    }
}

Step 3 compile for release

  • Move the release dll to the bin directory of TDnext/bin
  • Restart IIS, since TeamDynamix caches various dlls.