Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Alternative Code for using APASP.NET Forms Authentication with a Role the addition of a .NET 2.0 Role Provider.

You should view ASP.NET Forms Authentication before reading this this page, since the information in this page was developed from it.

...

This tells ASP.NET to require authentication for all access to the directory and to internally reroute every unauthenticated user to the SSO.aspx page. Additionaly the user must be also be in the role LDAP-Admins or LDAP-WindowsTeam as determined by your the role provider., in this case an LDAP Role Provider. You can read the Microsoft documentation if you want a more limited scope of protection.

If you wish to create your own Role Provider Interface in Visual Studio 2005 then you actually only need to implement :

Code Block
xml
xml

public override string[] GetRolesForUser(string username)

and

Code Block
xml
xml

public override bool IsUserInRole(string username, string roleName)

for the role provider to work. IsUserInRole is optional and is only used in code:

Code Block
xml
xml
Label1.Text = User.IsInRole("LDAP-WindowsTeam").ToString();

Now in Visual Studio create a login.aspx page with CS C# as the language. I suggest dragging  Drag a Label object to it from the Toolbox, which will become Label1 and rename it SignonMessages and will be used for an error message if something goes wrong. There is nothing more on the "login web page". The rest is done with code.

...

Code Block
xml
xml
using System;
using System.Web.Security;
using System.IO;
using System.Net;
using System.Xml;
// using System.Configuration;

public partial class SSO : System.Web.UI.Page
{
    private const string CASHOST = "https://secure.its.yale.edu/cas/";
   

    // Or you could use the appsettings
    // private const string CASHOST = ConfigurationManager.AppSettings["CASHOST"];

    protected void Page_Load(object sender, EventArgs e)
    {
        // Look for the "ticket=" after the "?" in the URL
        string AuthorisationTicket = Request.QueryString["ticket"];

        // This page is the CAS service=, but discard any query string residue
        string UrlToAuthenticate = Request.Url.GetLeftPart(UriPartial.Path);

        // Look for a Session object called CASId
        if (Session["CASId"] != null)
        {
            // If the user gets here then either their session has timed out,
             // they have possibly logged out or the role provider has denied
            // them access to the page they have requested

            // Users will always return here if they do not have role
            // permission to view a page. The downside is, it will sign
            // them out of pages they DO have access to and they'll have to
            // start again.

            // Double check they are signed out
            string SignoutUrl = CASHOST + "logout";
            StreamReader SignoutHttpReader = new StreamReader(new WebClient().OpenRead(SignoutUrl));
            string SignOutResponse = SignoutHttpReader.ReadToEnd();

            // Sign the user out of the .NET site and Give them a message,
             // they'll never see it though...
            SignonMessages.Text = "No Permission";
            Session.Abandon();
            Session.Clear();
            FormsAuthentication.SignOut();

            // ...because they get redirected and forced to renew their
            // CAS authentication ticket.
            string RenewSignonUrl = CASHOST + "login?" + "service=" + UrlToAuthenticate + "&renew=true";
            Response.Redirect(RenewSignonUrl);
            return;
        }
        else
        {

            // First time through there is no ticket=, so redirect to CAS login
            if (AuthorisationTicket == null || AuthorisationTicket.Length == 0)
            {
                string SignonUrl = CASHOST + "login?" + "service=" + UrlToAuthenticate;
                Response.Redirect(SignonUrl);
                return;
            }

            // Second time (back from CAS) there is a ticket= to validate
            string ValidateSignonUrl = CASHOST + "serviceValidate?" + "ticket=" + AuthorisationTicket + "&" + "service=" + UrlToAuthenticate;
            StreamReader ValidateSignonHttpReader = new StreamReader(new WebClient().OpenRead(ValidateSignonUrl));

            // I like to have the text in memory for debugging rather than parsing the stream
            string ValidateSignonResponse = ValidateSignonHttpReader.ReadToEnd();

            // Some boilerplate to set up the parse.
            NameTable XmlNT = new NameTable();
            XmlNamespaceManager XmlNSManager = new XmlNamespaceManager(XmlNT);
            XmlParserContext XmlParser = new XmlParserContext(null, XmlNSManager, null, XmlSpace.None);
            XmlTextReader XmlResponseReader = new XmlTextReader(ValidateSignonResponse, XmlNodeType.Element, XmlParser);

            string SSOUsername = null;

            // A very dumb use of XML. Just scan for the "user". If it isn't there, its an error.
            while (XmlResponseReader.Read())
            {
                if (XmlResponseReader.IsStartElement())
                {
                    string XmlTag = XmlResponseReader.LocalName;
                    if (XmlTag == "user")
                    {
                        SSOUsername = XmlResponseReader.ReadString();
                    }
                }
            }
            // if you want to parse the proxy chain, just add the logic above
            XmlResponseReader.Close();

            // If there was a problem, leave the message on the screen. Otherwise, return to original page.
            if (SSOUsername == null)
            {
                SignonMessages.Text = "CAS returned to this application, but then refused to validate your identity.";
            }
            else
            {
                // Create a session for recording the fact that we are logged on
                // and for allowing us to use .NET role management.

                // Was going to store AuthorisationTicket here but not sure if that
                // is good practice or not.
                Session["CASId"] = SSOUsername;

                // set SSOUsername in ASP.NET blocks
                FormsAuthentication.RedirectFromLoginPage(SSOUsername, false);
            }

            // Note: if you use the asp:LoginStatus control, remember the LogoutPageUrl
            // should be set to the CAS logout page and you should set the LogoutAction to Redirect
        }
    }
}