Hi,

Is anybody else trying to CASify Sharepoint 2010?

Sharepoint 2010 uses Saml 1.1 Tokens internally (Farm).
MS Recommendation: "For new implementations of SharePoint Server 2010, 
use claims-based authentication."

Claims-based authentication is based on Windows Identity Foundation.

WS-Federation:
http://msdn.microsoft.com/en-us/library/bb608217.aspx

This 3 "Windows Identity Foundation" Modules take care of WS-Federation 
protocol in IIS 7:
ClaimsPrincipalHttpModule
WSFederationAuthenticationModule
SessionAuthenticationModule

(Sharepoint 2010 got its own versions of this 
modules:Microsoft.SharePoint.IdentityModel.SPWindowsClaimsAuthenticationHttpModule,
 
Microsoft.SharePoint.IdentityModel.SPFederationAuthenticationModule, 
Microsoft.SharePoint.IdentityModel.SPSessionAuthenticationModule)

SP 2010 Claims based authentication works with out of the box ADFS 2.0 
and Windows LiveID.
(http://www.wictorwilen.se/Post/Visual-guide-to-Windows-Live-ID-authentication-with-SharePoint-2010-part-1.aspx)

Main Difference between WS-Federation and CAS: The Application recives 
the SAML 1.1 token through hidden POST there is no direct communication 
between Identity Provider and the Relying Party.
WS-Federation Passive Requestor Profile Step 6 – Return requestor token 
(setTimeout('document.forms[0].submit() </script>)
WS-Federation Passive Requestor Profile Step 7 – POST requestor token 
(disable javascript to see the button with the form-hidden saml1.1 token)

So we need a "Transitioning" oder "Bridging" Token Service.

Proof of concept:
http://blogs.pointbridge.com/Blogs/nielsen_travis/Pages/Post.aspx?_ID=40

replace the Custom STS Login.apsx.cs with the attached one and change 
the methods in customsecuritytokenservice accordingly.

Another Example for the bridging concept from Dominick Baier and Matias 
Woloski: http://www.leastprivilege.com/StarterSTS15.aspx
Who builds a CAS bridge for this :) ?

best regards
--
Markus








-- 
You are currently subscribed to [email protected] as: 
[email protected]
To unsubscribe, change settings or access archives, see 
http://www.ja-sig.org/wiki/display/JSG/cas-user
// changes to the code in GetScope and GetOutputClaimsIdentity according the 
example from Travis Nielsen


protected override Scope GetScope( IClaimsPrincipal principal, 
RequestSecurityToken request )
    {
        ValidateAppliesTo( request.AppliesTo );

        //
        // Note: The signing certificate used by default has a Distinguished 
name of "CN=STSTestCert",
        // and is located in the Personal certificate store of the Local 
Computer. Before going into production,
        // ensure that you change this certificate to a valid CA-issued 
certificate as appropriate.
        //
        Scope scope = new Scope( request.AppliesTo.Uri.OriginalString, 
SecurityTokenServiceConfiguration.SigningCredentials );

        string encryptingCertificateName = WebConfigurationManager.AppSettings[ 
"EncryptingCertificateName" ];
        if ( !string.IsNullOrEmpty( encryptingCertificateName ) )
        {
            // Important note on setting the encrypting credentials.
            // In a production deployment, you would need to select a 
certificate that is specific to the RP that is requesting the token.
            // You can examine the 'request' to obtain information to determine 
the certificate to use.
            scope.EncryptingCredentials = new X509EncryptingCredentials( 
CertificateUtil.GetCertificate( StoreName.My, StoreLocation.LocalMachine, 
encryptingCertificateName ) );
        }
        else
        {
            // If there is no encryption certificate specified, the STS will 
not perform encryption.
            // This will succeed for tokens that are created without keys 
(BearerTokens) or asymmetric keys.  
            scope.TokenEncryptionRequired = false;            
        }

        // Set the ReplyTo address for the WS-Federation passive protocol 
(wreply). This is the address to which responses will be directed. 
        // In this template, we have chosen to set this to the AppliesToAddress.
        if (scope.AppliesToAddress.ToLower() == "urn:bbexpress:cas")
        {
            scope.ReplyToAddress = 
"https://bbexpress.hrz.tu-darmstadt.de/_trust/";;
        }
        else
        {
            scope.ReplyToAddress = scope.AppliesToAddress;
        }
        return scope;
    }


    /// <summary>
    /// This method returns the claims to be issued in the token.
    /// </summary>
    /// <param name="principal">The caller's principal.</param>
    /// <param name="request">The incoming RST, can be used to obtain addtional 
information.</param>
    /// <param name="scope">The scope information corresponding to this 
request.</param> 
    /// <exception cref="ArgumentNullException">If 'principal' parameter is 
null.</exception>
    /// <returns>The outgoing claimsIdentity to be included in the issued 
token.</returns>
    protected override IClaimsIdentity GetOutputClaimsIdentity( 
IClaimsPrincipal principal, RequestSecurityToken request, Scope scope )
    {
        if ( null == principal )
        {
            throw new ArgumentNullException( "principal" );
        }

        ClaimsIdentity outputIdentity = new ClaimsIdentity();

        // Issue custom claims.
        // TODO: Change the claims below to issue custom claims required by 
your application.
        // Update the application's configuration file too to reflect new 
claims requirement.

        var CAS20Claims = HttpContext.Current.Session["CAS20Claims"] as 
Dictionary<string, string>;
        foreach (var casClaim in CAS20Claims)
        {
            outputIdentity.Claims.Add(new Claim(casClaim.Key, casClaim.Value));
        } 


        //outputIdentity.Claims.Add( new Claim( 
System.IdentityModel.Claims.ClaimTypes.Name, principal.Identity.Name ) );
        //outputIdentity.Claims.Add( new Claim( ClaimTypes.Role, "Manager" ) );

        return outputIdentity;
    }
}
using System;
using System.Web.Security;
using System.Web;
using System.Net;
using System.IO;
using System.Collections.Generic;
using DotNetCasClient.Validation.Schema.Cas20;
using DotNetCasClient.Validation;
//using DotNetCasClient.Security;
using System.Xml.Serialization;
using System.Xml;

public partial class Login : System.Web.UI.Page
{
    public const string CASHOST = "https://sso.hrz.tu-darmstadt.de/";;
    private string ticket="";
    private string UrlToAuthenticate = 
"https://bbexpress.hrz.tu-darmstadt.de:54322/casts/Login.aspx";;
    public enum Method { GET, POST };

    protected void Page_Load( object sender, EventArgs e )
    {
        // cas20 Angepasst
        // based on custom sts by Travis Nielsen: 
http://blogs.pointbridge.com/Blogs/nielsen_travis
        //CAS20Validator casval = new CAS20Validator();
        if (Request["ticket"] == null)
        {

            if (Request.QueryString["ReturnUrl"] != null)
                HttpContext.Current.Session.Add("OriginalQueryString", 
Request.QueryString.ToString());

            string SignonUrl = CASHOST + "login?" + "service=" + 
UrlToAuthenticate + "&" + Request.QueryString.ToString();
            //string SignonUrl = CASHOST + "login?" + "service=" + 
UrlToAuthenticate;
            Response.Redirect(SignonUrl);
            
        }
        else
        {
            ticket = Request["ticket"];
            string validateurl = CASHOST + "serviceValidate?" + "service=" + 
UrlToAuthenticate + "&ticket=" + ticket;
            string retval = RetrieveResponseFromServer(validateurl, ticket);

            // ********CAS client
            ServiceResponse serviceResponse;
            try
            {
                serviceResponse = ServiceResponse.ParseResponse(retval);
            }
            catch (InvalidOperationException)
            {
                throw new TicketValidationException ("CAS Server response does 
not conform to CAS 2.0 schema");
            }
            
            AuthenticationSuccess authSuccessResponse = 
(AuthenticationSuccess)serviceResponse.Item;
            
            Dictionary<string, string> claims = GetClaims(retval);
            HttpContext.Current.Session.Add("CAS20Claims", claims);
            
            FormsAuthentication.SetAuthCookie("CAS Test", false);
            Response.Redirect("default.aspx?" + 
HttpContext.Current.Session["OriginalQueryString"]); 
            //Response.Redirect("default.aspx");             
        }
    }
    private Dictionary<string, string> GetClaims(string castoken)
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(castoken);

        XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
        nsmgr.AddNamespace("cas", "http://www.yale.edu/tp/cas";);
        
        XmlElement root = doc.DocumentElement;
                       
        Dictionary<string, string> claims = new Dictionary<string, string>();
        string mail = root.SelectSingleNode("//cas:mail", nsmgr).InnerText;
        string tuid = root.SelectSingleNode("//cas:cn", nsmgr).InnerText;
        string role = root.SelectSingleNode("//cas:__AUTHUSERCONTEXT__", 
nsmgr).InnerText;
        string sn = root.SelectSingleNode("//cas:surname", nsmgr).InnerText;
        string gn = root.SelectSingleNode("//cas:givenName", nsmgr).InnerText;
        string groups = root.SelectSingleNode("//cas:groupMembership", 
nsmgr).InnerText;
        
        if (!String.IsNullOrEmpty(tuid))
            
claims.Add(Microsoft.IdentityModel.Claims.ClaimTypes.NameIdentifier, tuid);
        
        if (!String.IsNullOrEmpty(sn))
            claims.Add(Microsoft.IdentityModel.Claims.ClaimTypes.Surname, sn);

        if (!String.IsNullOrEmpty(gn))
            claims.Add(Microsoft.IdentityModel.Claims.ClaimTypes.GivenName, gn);

        if (!String.IsNullOrEmpty(mail))
            claims.Add(Microsoft.IdentityModel.Claims.ClaimTypes.Email, mail);

     
        if (!String.IsNullOrEmpty(role))
            claims.Add(Microsoft.IdentityModel.Claims.ClaimTypes.Role, role);

        if (!String.IsNullOrEmpty(groups))
            claims.Add(Microsoft.IdentityModel.Claims.ClaimTypes.GroupSid, 
groups);
        
        return claims;
    }

    public string RetrieveResponseFromServer(string url, string ticket)
    {
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
        req.Method = "GET";
        req.ServicePoint.Expect100Continue = false;
        req.ContentType = "text/xml";
        req.UserAgent = ".NET Framework Test Client";
        req.Timeout = 20000;

        HttpWebResponse response = (HttpWebResponse)req.GetResponse();
        Stream responseStream = response.GetResponseStream();
        if (responseStream != null)
        {
            using (StreamReader responseReader = new 
StreamReader(responseStream))
            {
                return responseReader.ReadToEnd();
            }
        }
        else
        {
            throw new ApplicationException("Unable to retrieve response 
stream.");
        }
    }
}

Reply via email to