Coder Social home page Coder Social logo

dotnet-cas-client's People

Contributors

aandteogkaffi avatar cwinfrey avatar dmccallum avatar lanorkin avatar phantomtypist avatar rdev5 avatar savvasmisaghmoayyed avatar scottt732 avatar serac avatar thehokiecoder avatar wgthom avatar wilsonson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dotnet-cas-client's Issues

Implement unit testing.

I think it would be beneficial to implement unit testing in general so that we can use it to validate code and intended behavior in certain places.

The first tests I'd like us to create would be to validate #53

RequestEvaluator.GetRequestIsAppropriateForCasAuthentication() seems incomplete and appears to be causing redirect loop

We've been tracing a redirect loop issue that appears to be stemming from a seemingly incomplete implementation of GetRequestIsAppropriateForCasAuthentication()

Given the following (and only) route in RouteConfig.cs:

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}",
                defaults: new { controller = "Login", action = "Index" }
            );

Browsing our SSO application (complete rewrite of CAS-OWA) at https://example.com/app/ works just fine and loads the LoginController#Index method.

200: https://example.com/app/
302: https://example.com/app/Login/SSO
302: https://cas.example.com/login?service=https://example.com/app/Login/SSO
302: https://example.com/app/Login/SSO?ticket=...
302: https://example.com/app/Login/SSO
200: https://example.com/finalDestination
...

However, when browsing to the following, we get a much different behavior:

200: https://example.com/app (note the missing trailing slash)
302: https://cas.example.com/login?service=https://example.com/app
302: https://example.com/app?ticket=...
302: https://example.com/app
302: https://cas.example.com/login?service=https://example.com/app
302: https://example.com/app?ticket=...
302: https://example.com/app
...

What is happening here is CasAuthenticationModule.cs specifies an OnAuthenticateRequest event handler being fired on every HttpRequest. I noticed that NEC-55 did not get address/merged so I did this myself in our local copy of the project:

        private static void OnAuthenticateRequest(object sender, EventArgs e)
        {
            // Validate the ticket coming back from the CAS server
            if (!RequestEvaluator.GetRequestIsAppropriateForCasAuthentication())
            {
                logger.Debug("AuthenticateRequest bypassed for " + HttpContext.Current.Request.RawUrl);
                return;
            }

            // TEST: Prevent processing at /app (caused by route)
            if (UrlUtil.RemoveCasArtifactsFromUrl(HttpContext.Current.Request.Url.AbsoluteUri) == "https://example.com/app")
            {
                HttpContext.Current.Response.Write("This request should NOT be getting authenticated");
                HttpContext.Current.Response.End();
                return;
            }

            HttpContext context = HttpContext.Current;
            HttpRequest request = context.Request;

            // Validate the ticket coming back from the CAS server
            if (RequestEvaluator.GetRequestHasCasTicket())
            {
                logger.Info("Processing Proxy Callback request");
                CasAuthentication.ProcessTicketValidation();
            }

            logger.Debug("Starting AuthenticateRequest for " + request.RawUrl);
            CasAuthentication.ProcessRequestAuthentication();
            logger.Debug("Ending AuthenticateRequest for " + request.RawUrl);
        }

In the latter example (https://example.com/app), I don't feel that the RequestEvaluator#GetRequestIsAppropriateForCasAuthentication is completely evaluating if the current request is in fact appropriate for CasAuthentication because under these conditions (using the /app path which .NET is not getting a chance to redirect to /app/ due to the module's current behavior), DotNetCasClient deems both of the following controller methods to be "appropriate for CasAuthentication:"

public class LoginController : Controller
   ...
   // NOT appropriate for CasAuthentication but still being processed
   // Not marked as "appropriate" when browsing /app/ (correct)
   // Marked as "appropriate" when browsing /app (incorrect)
   public void Index() {}

   // Appropriate for CasAuthentication
   [Authorize]
   public void SSO()

It appears that a quick remedy is to specify bypassCasForHandlers in the casClientConfig section which I can do, but it should also be noted that such a configuration option is not documented here: https://wiki.jasig.org/display/casc/.net+cas+client

Going back to the original method in question (GetRequestIsAppropriateForCasAuthentication), I'm assuming there isn't a practical way to evaluate if the current requested class/method has an [Authorize] attribute, or is there?

NULL Proxy Granting Ticket causes Unhandled Error

The CacheProxyTicketManager class will throw an unhandled exception if a null PGT is passed in. The error is thrown because you cannot insert a NULL into the HttpRuntime cache.

In the Cas20ServiceTicketValidator class, the PGT IOU is checked for null but the PGT is not.

This can be solved easily in the CacheProxyTicketManager with one line:
public void InsertProxyGrantingTicketMapping(string proxyGrantingTicketIou, string proxyGrantingTicket)
{
if (proxyGrantingTicket != null)
HttpContext.Current.Cache.Insert(proxyGrantingTicketIou, proxyGrantingTicket, null, DateTime.Now.Add(DefaultExpiration), Cache.NoSlidingExpiration);
}

Add Documentation For SAML Ticket Validator Time Tolerance

Myself and others have been bitten by an issue (not a bug with the client) where the time clock difference between a client and server becomes greater than the 1 second default value for the SAML ticket validator (configured via the ticketTimeTolerance attribute of the casClientConfig configuration section). It has been argued that 1 second is not a realistic value, with which I agree. I'd like to help add documentation to the project that states if someone uses the SAML ticket validator they should also set a more realistic value of ticketTimeTolerance based on their preferred balance of security and convenience.

Does anyone have any suggestion for a more realistic value for the allowable time drift? As a reference, the Microsoft recommendation for maximum time drift for Kerberos tickets in a domain is a whopping 5 minutes (see Maximum tolerance for computer clock synchronization). I have heard a suggestion of somewhere around 15-30s before, and I personally think 15s is a perfectly reasonable window that doesn't allow too much time for replay attacks and doesn't allow for too much time drift between the client and server.

Another thing that would be helpful is to add the attribute to the boilerplate casClientConfig that gets injected to web.config when the NuGet package is installed in an application so that consumers of the package are more aware of its existence.

Service URL Truncated

We have a third party application that uses URLs with an encoded data parameter. These are used for registration and other purposes and following these links prior to authentication result in any URL encoded characters at the end of the URL being lost during the CAS login process. See the URLs below from the resulting login chain which were captured from the browser developer tools:

https://login.denison.edu/cas/login?service=https%3a%2f%2fevents.denison.edu%2fMasterCalendar%2fManageSubscription.aspx%3fdata%3d7J%2fMlsKOh99bxaaUpZSeJRD0avGuP%2brv%2fRo48w4%2brHTk%2bSQ628pOYLZ0zyUzj2M7TwhZdt6mrtyb7419rMmsSQ%3d%3d

https://events.denison.edu/MasterCalendar/ManageSubscription.aspx?data=7J/MlsKOh99bxaaUpZSeJRD0avGuP+rv/Ro48w4+rHTk+SQ628pOYLZ0zyUzj2M7TwhZdt6mrtyb7419rMmsSQ==&ticket=ST-1261537-bwgiAsqkfoewNA7tmuOS-login.denison.edu

https://events.denison.edu/MasterCalendar/ManageSubscription.aspx?data=7J/MlsKOh99bxaaUpZSeJRD0avGuP%20rv/Ro48w4%20rHTk%20SQ628pOYLZ0zyUzj2M7TwhZdt6mrtyb7419rMmsSQ

As you'll note when the ticket was removed from the URL so were the two equal signs (=) which resulted in the application failing to load the correct subscription.

If the user previously authenticated; clicking and following this link (which is delivered via an email) works as expected. It only appears to happen when the CAS client parses the URL to retrieve the CAS ticket.

Any help or guidance is appreciated!

Where to get the Proxy Ticket

Not sure where else to ask this, I've got a successful integration with logs showing Proxy tickets coming back and I can see them in the cache. Is there a method or location that retrieves the current users proxy ticket?

.NET Core / OWIN Support?

ASP.NET Core has a very different authentication format which is built on the Microsoft.AspnetCore.Authentication namespace (you can see their code here: https://github.com/aspnet/Security). They already provide many pre-built authentication providers, including OAuth, OIDC, JWT, Facebook, Google, etc, and other people have written extensions to bring in even more providers. As far as I can tell there are no working CAS providers for .NET Core 2.0 (the current version), and I'm wondering if this project has considered building against this new set of APIs and publishing a .NET Standard library so it can be used in .NET Standard & Core apps.

The end result would look something like this

services.AddAuthentication().AddCas(casOptions => { ... });

Thanks for your consideration!

Entire project fails to build

DotNetCasClient builds just fine by itself, but DotNetCasProxyDemoApp and ExampleWebSite throw errors when compiled separately. Also, when the entire solution builds, it throws circular file references errors. Should either build as the solution or as individuals or both.

Error 2 An error occurred while signing: Failed to sign bin/Debug/app.publish/DotNetCasProxyDemoApp.exe. SignTool Error: No certificates were found that met all the given criteria. DotNetCasProxyDemoApp
Error 65 Circular file references are not allowed. ~/dotnet-cas-client/ExampleWebSite/NotAuthorized.aspx 1 
Error 66 Circular file references are not allowed. ~/dotnet-cas-client/ExampleWebSite/CookiesRequired.aspx 1 
Error 67 Circular file references are not allowed. ~/dotnet-cas-client/ExampleWebSite/Default.aspx 1 

Signing and/or Strongly Naming the Assembly

Looking to get some feedback from the other developers and users of this package to see if we should start strongly naming the assembly. The StackOverflow answer linked below has some good points as to why it should be done. The main reason why I am for it is that a project that references the DotNetCasClient assembly cannot, itself, be strongly-named since the assembly is not strongly-named. In other words, all referenced assemblies of a strongly-named assembly must themselves also be strongly-named. Aside from that, perhaps the best benefit is one can be sure the assembly hasn't been modified outside of this project's official builds. And

There are some opinions out there that strong-naming is a waste of time or causes more headaches than it solves. So that's why I'm looking for opinions to make sure we make the best decision.

StackOverflow: Why use strong named assemblies?

Add a DSL for build tasks

Purpose

To make it easier to manage the build tasks and process. Contributors, forkers, and the build (CI) server will all run the same build script, with consistent repeatability with regards to building the project and nuget package. Building in VS is not enough nowadays because it leads to "it works on my computer" builds. By using a build script in addition to VS, you as the developer can mimic the build process that runs on any developers' computer and also the build server.

For contributors and forkers, it makes the experience of building the project easier and less cumbersome... you just run the build script.

Using a build DSL also abstracts away the joy and complexities of MSBuild XML configurations.

Popular DSL's for .NET:

CacheServiceTicketManager threading issues

For instance, when updating the sliding expiration, this is done by revoking and then reinserting the ticket into the Cache. If the thread gets interrupted while another thread is verifying the ticket, it will find the ticket is not in the cache and as such, fails and sends ASPXAUTH clear cookie to the browser, invalidating the authentication ticket.

Support CAS v3 protocol

We need a new validator, Cas30 perhaps that is able to consume attributes and other protocol-related attrs. I do have a bit of time set aside this week to review this better and submit a PR.

Prevent Override of [Authorize]

I'm trying to use the .Net Cas Client alongside my custom Asp.Net Membership implementation. To do this, I would like to bind the client's Cas authorization to it's own Data Annotation, rather than have it simply override the default.

Is this possible?

After a first redirect loop, only a restart can put the system back

From my browser:

GET https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2f net::ERR_TOO_MANY_REDIRECTS
Request URL:https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2f
Request Headers
Provisional headers are shown
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.94 Safari/537.36
Query String Parametersview sourceview URL encoded
service:http://rascol.rassystem.com.br/

Problem begins after a new application deployment, but not every application deployment. Sometimes it happens even without any environment change. A spottable difference on logs is a protocol message:

Invalid request - pgtId parameter not found

After a restart, all work fine again.

My configuration log messages follow:

DotNetCasClient.Config Information 3237 casServerUrlPrefix = https://id.rassystem.com.br/cas/
DotNetCasClient.Config Information 3237 casServerLoginUrl = https://id.rassystem.com.br/cas/login
DotNetCasClient.Config Information 3237 ticketValidatorName = Cas20
DotNetCasClient.Config Information 3237 ticketTimeTolerance = 1000
DotNetCasClient.Config Information 3237 serverName = http://rascol.rassystem.com.br/
DotNetCasClient.Config Information 3237 renew = False
DotNetCasClient.Config Information 3237 gateway = False
DotNetCasClient.Config Information 3237 gatewayStatusCookieName = cas_gateway_status
DotNetCasClient.Config Information 3237 redirectAfterValidation = True
DotNetCasClient.Config Information 3237 singleSignOut = True
DotNetCasClient.Config Information 3237 serviceTicketManagerProvider = CacheServiceTicketManager
DotNetCasClient.Config Information 3237 proxyTicketManagerProvider = CacheProxyTicketManager
DotNetCasClient.Config Information 3237 notAuthorizedUrl = ~/NotAuthorized.aspx
DotNetCasClient.Config Information 3237 cookiesRequiredUrl = ~/CookiesRequired.aspx
DotNetCasClient.Config Information 3237 gatewayParameterName = gatewayResponse
DotNetCasClient.Config Information 3237 proxyCallbackParameterName = proxyResponse
DotNetCasClient.Config Information 3237 proxyCallbackUrl =
DotNetCasClient.Config Information 3237 requireCasForMissingContentTypes = True
DotNetCasClient.Config Information 3237 requireCasForContentTypes = System.String[]
DotNetCasClient.Config Information 3237 bypassCasForHandlers = System.String[]


A sample working request log:


DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2fGeomapas%2fMapaPrincipal.aspx%3fproxyResponse%3dtrue%2chttp%3a%2f%2frascol.rassystem.com.br%2fScriptResource.axd%3fd%3d6SlcmXaK98kXPeZwLxBZEkXUfsvw3bUQl3AaTHOBR79--1nutzpvds36JdBnHM7MQby_YqGVVSw1SKnzPwTG6-f1nZdn-DhRuG4sK2V0Si4YeMhJoBzrRyvfqqqu1zqfDPwv1zPtNHEHsaQvjXzv_UuH9UXBYeNiJGLNuHT8NJj2JlR5jnRfg8i7R_5MqvnQ0%26proxyResponse%3dtrue%26t%3d7e632e9f
DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2fGeomapas%2fMapaPrincipal.aspx%3fproxyResponse%3dtrue%2chttp%3a%2f%2frascol.rassystem.com.br%2fScriptResource.axd%3fd%3d6SlcmXaK98kXPeZwLxBZEkXUfsvw3bUQl3AaTHOBR79--1nutzpvds36JdBnHM7MQby_YqGVVSw1SKnzPwTG6-f1nZdn-DhRuG4sK2V0Si4YeMhJoBzrRyvfqqqu1zqfDPwv1zPtNHEHsaQvjXzv_UuH9UXBYeNiJGLNuHT8NJj2JlR5jnRfg8i7R_5MqvnQ0%26proxyResponse%3dtrue%26t%3d7e632e9f%26pgtIou%3dPGTIOU-593342-QQeoDrfuVQkUeBbFUP3Y-cas%26pgtId%3dTGT-653161-vVrwYCWwZqbhG0XamaNj3NtccQ3b2A1I5qo7p2mIvmdW4JBuVM-cas
DotNetCasClient.Protocol Verbose 3237 Ticket validation response:

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
cas:authenticationSuccess
cas:userxxxxxxxxx/cas:user

    <cas:proxyGrantingTicket>PGTIOU-593342-QQeoDrfuVQkUeBbFUP3Y-cas</cas:proxyGrantingTicket>


</cas:authenticationSuccess>

/cas:serviceResponse
DotNetCasClient.HttpModule Information 3237 Redirecting from login callback
DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Verbose 3237 Constructed validation URL https://id.rassystem.com.br/cas/proxyValidate?service=http%3a%2f%2frascol.rassystem.com.br%2fGeomapas%2fMapaPrincipal.aspx&ticket=ST-603858-9EGOnDQuLNuYaXoc9Dyq-cas&pgtUrl=http%3a%2f%2frascol.rassystem.com.br%2fGeomapas%2fMapaPrincipal.aspx%3fproxyResponse%3dtrue,http%3a%2f%2frascol.rassystem.com.br%2fScriptResource.axd%3fd%3d6SlcmXaK98kXPeZwLxBZEkXUfsvw3bUQl3AaTHOBR79--1nutzpvds36JdBnHM7MQby_YqGVVSw1SKnzPwTG6-f1nZdn-DhRuG4sK2V0Si4YeMhJoBzrRyvfqqqu1zqfDPwv1zPtNHEHsaQvjXzv_UuH9UXBYeNiJGLNuHT8NJj2JlR5jnRfg8i7R_5MqvnQ0%26t%3d7e632e9f%26proxyResponse%3dtrue


A redirect loop request log:


DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Information 3237 Invalid request - pgtId parameter not found
DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2fOperacoes%2fRelatorioDiarioLista.aspx%3fproxyResponse%3dtrue
DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2f
DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Information 3237 Recieved proxyGrantingTicketId [TGT-653386-6kAVTNNogkkaFNx6ufzPug4wn1kdXwImFtSsKJD6YEfrjTOFxT-cas] for proxyGrantingTicketIou [PGTIOU-593563-dpvB1RjJadBG2VSgvzdJ-cas]
DotNetCasClient.Protocol Verbose 3237 Ticket validation response:

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
cas:authenticationSuccess
cas:userxxxxxxxx/cas:user

    <cas:proxyGrantingTicket>PGTIOU-593563-dpvB1RjJadBG2VSgvzdJ-cas</cas:proxyGrantingTicket>


</cas:authenticationSuccess>

/cas:serviceResponse
DotNetCasClient.Protocol Verbose 3237 Creating FormsAuthenticationTicket for ST-604084-CSETWsdpYwNC96FWG5fl-cas
DotNetCasClient.HttpModule Information 3237 Redirecting from login callback
DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Verbose 3237 Constructed validation URL https://id.rassystem.com.br/cas/proxyValidate?service=http%3a%2f%2frascol.rassystem.com.br%2f&ticket=ST-604087-t4XGhFu4S14k42EjpfSJ-cas&pgtUrl=http%3a%2f%2frascol.rassystem.com.br%2fOperacoes%2fRelatorioDiarioLista.aspx%3fproxyResponse%3dtrue
DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Information 3237 Invalid request - pgtId parameter not found
DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2fOperacoes%2fRelatorioDiarioLista.aspx%3fproxyResponse%3dtrue
DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Information 3237 Recieved proxyGrantingTicketId [TGT-653387-dF2GUNOxKenJJqvHUdzEbNbLZsAGdRzNuuW31RnqnC1kjtV3Cm-cas] for proxyGrantingTicketIou [PGTIOU-593564-LdMoQ7dF3XwuNXvdHkUU-cas]
DotNetCasClient.Protocol Verbose 3237 Ticket validation response:

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
cas:authenticationSuccess
cas:userxxxxxxxx/cas:user

    <cas:proxyGrantingTicket>PGTIOU-593564-LdMoQ7dF3XwuNXvdHkUU-cas</cas:proxyGrantingTicket>


</cas:authenticationSuccess>

/cas:serviceResponse
DotNetCasClient.Protocol Verbose 3237 Creating FormsAuthenticationTicket for ST-604087-t4XGhFu4S14k42EjpfSJ-cas
DotNetCasClient.HttpModule Information 3237 Redirecting from login callback
DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2f
DotNetCasClient.HttpModule Information 3237 Redirecting to CAS Login Page
DotNetCasClient.Protocol Information 3237 Redirecting to https://id.rassystem.com.br/cas/login?service=http%3a%2f%2frascol.rassystem.com.br%2f
DotNetCasClient.HttpModule Information 3237 Processing Proxy Callback request
DotNetCasClient.Protocol Verbose 3237 Constructed validation URL https://id.rassystem.com.br/cas/proxyValidate?service=http%3a%2f%2frascol.rassystem.com.br%2f&ticket=ST-604082-k1Jq9WqZKqMbFVxIUA9O-cas&pgtUrl=http%3a%2f%2frascol.rassystem.com.br%2fOperacoes%2fRelatorioDiarioLista.aspx%3fproxyResponse%3dtrue

Gateway with sign in button on site

We would like to log in user automatically if user already logged into one of our other site. If not, then show login button on page and perform login via redirected to cas. We have sign button on landing page which redirect user to CAS logon (without gateway flag). CAS logon works, but when cas .net client try to validate service ticket it encounter an error that Service Ticket url and site url does not match. CAS .net client assume that we are using gateway flag and add gatewayResponse=โ€trueโ€ to url which does not match CAS service ticket. Can cas client modify to add this function

Add StyleCop to project

The Why

Verbiage from the actual StyleCop

The goal is to define guidelines to enforce consistent style and formatting and help developers avoid common pitfalls and mistakes.

StyleCop contributes to this maintainability by encouraging consistency of style, which in turn makes it easier for developers to pick up existing code and work with it productively, and by encouraging plenty of documentation for future developers to read thereby improving the long term maintainability of the source.

I understand that everybody has their own style in how they code, but I think it's beneficial for open source projects like this one to settle on a set of styles and conventions with regards to code. StyleCop rules by default follow most of the standard guidelines set forth for C#. I am not opposed to adding specific rules to conform to something that already exists in this project because I understand it was a port from the Java client and there may be some vestigial styles that came from the Java world.

References:

CasSaml11Response code lines unused

I was looking at CasSaml1Response.cs and noticed that these lines are not actually used...

var xmlReaderSettings = new XmlReaderSettings();
xmlReaderSettings.ConformanceLevel = ConformanceLevel.Auto;
xmlReaderSettings.IgnoreWhitespace = true;
xmlReaderSettings.IgnoreComments = true;
xmlReaderSettings.CloseInput = true;

....

string authMethod = SamlUtils.GetAttributeValue(authenticationStmtNode.Attributes, "AuthenticationMethod");

...

IList authValues = new List();
IDictionary<string, IList> authenticationAttributes = new Dictionary<string, IList>();
authValues.Add(authMethod);
authenticationAttributes.Add("samlAuthenticationStatement::authMethod", authValues);

Is this intentional? Or have I miss understood something?

Dynamic proxyCallbackUrl not suitable for static proxy chain configuration

Hello again,

This issue just came up when we were trying to implement a post-redirect upon successful authentication via the Authorize attribute. We are essentially in the process of testing a complete rewrite to the CAS-OWA app that's currently out there (https://github.com/Unicon/cas-owa-2010) to use the current version of DotNetCasClient in .NET 4.5.

Original request:
https://www.example.com/casOwa/Login/Redirect/?url=https%3A%2F%2Fwww.example.com%2FdestinationApp%2F

Method sets URL cookie and then redirects to a method with the Authorize attribute on it:
https://www.example.com/casOwa/Login/

Web.config specifies the following proxyCallbackUrl:
https://www.example.com/casOwa/Login/ProxyCallbackUrl?proxyResponse=true

This value should not change (as it is in the server's proxy chain), but instead, it is being modified by the following line in question:
https://github.com/Jasig/dotnet-cas-client/blob/master/DotNetCasClient/Utils/UrlUtil.cs#L188

Under these conditions, the ticket validator will now fail and throw InvalidProxyChainTicketValidationException as follows:

Invalid proxy chain: [https://www.example.com/casOwa/ProxyCallbackUrl?proxyResponse=true&url=https%3A%2F%2Fwww.example.com%2FdestinationApp%2F]

Note the additional URL parameter that got added. Furthermore, there is a little bit of a sticky behavior where even after logging out at CAS and clearing browser cookies at the client application that the DotNetCasClient seems to persist this behavior (a separate issue requiring further debugging).

For now, I've commented the offending line out in UrlUtil.cs and would propose removing it altogether since dynamic proxy callback URLs are not compatible with static proxy chains configured on the server.

Could you guys validate my observations and verify if this is a change that needs to be made?

Thanks,

Matt

Push new Nuget Packages

The Nuget package seems to be missing this issue, which has been a significant problem for us - 36ca258

Are there any plans for a 1.0.3 release?

ProcessRequestAuthentication using stale Ticket

https://github.com/Jasig/dotnet-cas-client/blob/master/DotNetCasClient/CasAuthenticationModule.cs

method OnAuthenticateRequest

line 144 CasAuthentication.ProcessTicketValidation();
This method checked for a new ticket, it then places this in to the Response, The next call is

line 148 CasAuthentication.ProcessRequestAuthentication()
This method uses the ticket from the previous request, and not the new one.

Do you know why? This means that the CAS Principle is not updated on the request leaving it stale.

AssertionRoleProvider Issues

The first, and major, issue that I have encountered is that the IsUserInRole() method in AssertionRoleProvider.cs does not seem to filter for the specified role name provided to the method. It appears to me that you can specify any role that you want, and as long as the current user is a member of any role, the method will return true. Oddly enough, this hasn't reared itself as a bug in my use cases when dealing with role membership. After doing some debugging and online research, I have come to learn that the .NET identity framework apparently uses GetRolesForUser() to get an initial cache of all roles for the current user. The only time when IsUserInRole() in AssertionRoleProvider would be called is if explicitly called with a different identity from the current user's identity. But that causes this specific implementation to throw a provider exception since that use case is not supported. Thus, the method is basically useless. See this StackOverflow question for an explanation of why the overridden method does not get called for the current user. Regardless, to be complete, I think our implementation should correctly indicate whether or not the user is in the role, even if it is not possible for that kind of invocation to occur.

The second issue is that in the private method GetCurrentUserRoles() does not check for existence of the key (roleAttribute) used for role membership values in the .Attributes dictionary. If that key does not exist in the dictionary, a KeyNotFoundException is thrown. I think it would be better to first test if the key exists in the dictionary (via ContainsKey() or TryGetValue()) before attempting to access the values so that an exception is never thrown.

The third issue is that I am not a fan of how the GetAllRoles() method is being repurposed to perform the same function as GetRolesForUser(). I'd rather have GetAllRoles() return a not implemented exception (since it is not possible for the assertion role provider to know all possible roles for the application) and move its logic to the GetRolesForUser() method. This way, someone familiar with the role membership portion of .NET identity management, but unfamiliar with DotNetCasClient, would not be confused by GetAllRoles() returning different values based upon the current identity context.

Lastly, I'd like to take the opportunity to add better comments to the entire class to make its documentation complete. I just first want to get some feedback/approval regarding the first two issues.

RFC: Distributed cache provider for proxy and service ticket managers

The current implementations provided are only store the proxy and service tickets in-memory or on disk locally to the web application. The limitation of this model is that it will not support clustered, load balanced, or round-robin style configurations.

I plan on writing one, or several, distributed cache provider(s) for the proxy and service ticket managers. I'd like to hear back from the community what types of distributed cache systems you would be using in conjunction with the .NET CAS Client (e.g. Redis, Memcached, etc.)

The providers would each be in separate C# projects and therefore separately installable NuGet packages (e.g. DotNetCasClient.CacheProvider.Redis)

So if you want to use the Redis provider, you would:

  1. Install the DotNetCasClient NuGet package like you normally would.
  2. Install the proxy/service ticket manager cache provider you want (e.g DotNetCasClient.CacheProvider.Redis)
  3. In your web.config file, in the casClientConfig section, change the proxyTicketManager and serviceTicketManager values to RedisCacheProxyTicketManager and RedisCacheServiceTicketManager.

Note: Step 3 can actually be eliminated altogether by using web.config transformations in the cache provider NuGet package (i.e. when it's installed it'll change the values for you and when you uninstall it'll change them back to the default in-memory managers.)

Proposed Distributed Cache Providers for Proxy and Service Ticket Managers

  • Redis done
  • Memcached done
  • SQL Server

Dulplicated query items when redirecting from cas login page

I encounter an issue during development using .net cas client.
Here is my original url: http://example.com/?param1=a&param2=b
After redirecting back from cas login page, the url turned out to be : http://example.com?param1=a&param2=b&param2=b

I step through the code and found that there might be wrong here:
https://github.com/Jasig/dotnet-cas-client/blob/master/DotNetCasClient/Utils/EnhancedUriBuilder.cs#L273

The querystring is decoded before splitting by '&'. It caused duplicated query items.
In my case, the original querystring:

param1=a&param2=b&TARGET=http%3A%2F%2Flocalhost%3A50582%2F%3Fparam1%3Da%26param2%3Db&SAMLart=foo

Then after url decode:

param1=a&param2=b&TARGET=http://localhost:50582/?param1=a&param2=b&SAMLart=foo

query items after splitting by '&':
param1, param2, target, param2, samlart

In my opinion, the querystring should not be decoded before splitting by '&'.

Configure CAS client programmatically

I need to configure the CAS client in run-time in order to allow multiple configurations for multiple environments. As I couldn't find a way to configure the CAS client besides changing the web.config, my current solution is to rewrite the web.config upon web application start.

I was wondering if there is another way to do it or if there is an intention to add a way to configure the client programmatically.

SAML params are hardcoded: RequestID, IssueInstant

I see here https://github.com/apereo/dotnet-cas-client/blob/master/DotNetCasClient/Validation/TicketValidator/Saml11TicketValidator.cs#L190 the following:

protected string RetrieveResponseFromServer(string validationUrl, string ticket)
{
    StringBuilder messageBuilder = new StringBuilder();
    messageBuilder.AppendLine(@"<SOAP-ENV:Envelope xmlns:SOAP-ENV=""http://schemas.xmlsoap.org/soap/envelope/"">");
    messageBuilder.AppendLine(@"<SOAP-ENV:Header/><SOAP-ENV:Body>");
    messageBuilder.AppendLine(@"<samlp:Request xmlns:samlp=""urn:oasis:names:tc:SAML:1.0:protocol"" ");
    messageBuilder.AppendLine(@"MajorVersion=""1"" MinorVersion=""1"" RequestID=""_192.168.16.51.1024506224022"" ");
    messageBuilder.AppendLine(@"IssueInstant=""2002-06-19T17:03:44.022Z"">");
    messageBuilder.AppendLine(@"<samlp:AssertionArtifact>" + ticket);
    messageBuilder.AppendLine(@"</samlp:AssertionArtifact></samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>");
    string message = messageBuilder.ToString();
    ...

As you can see everything except ticket is harcoded.

Is it intended? From my understanding, parameters like RequestID / IssueInstant should be unique / regenerated for each request.

Issue with asp.net postback

Steps to Repro
Create a simple website, add a web page (webpage1.aspx) which can demonstrate a simple postback like retrieving isPostback value.
Configure the website .net CAS
Publish the website to IIS.
Set the default document to webpage1.aspx.

Workaround
As of now implemented a workaround by creating a default.aspx and redirecting to webpage1.aspx.

Ask
Is there CAS configuration where we can redirect the user to webpage1.aspx after login or Is there known issue with IIS default document module.

Bad practice to store per-request value in ThreadStatic variables in ASP.Net

In ASP.Net, there's no guarantee that a request will use the same thread for its entire duration, see here for example https://stackoverflow.com/a/4791234/2170171

As a result this logic with [ThreadStatic] works on small load, but can lead to security issues on highload:

https://github.com/apereo/dotnet-cas-client/blob/master/DotNetCasClient/CasAuthentication.cs#L107

        // Provide reliable way for arbitrary components in forms
        // authentication pipeline to access CAS principal
        [ThreadStatic]
        private static ICasPrincipal currentPrincipal;

One of possible solutions is to use HttpContext.Current.Items[...] instead of static variables, but in general providing static access to principal can be considered a bad design.

Move to Apereo org on Github

Planning to move the repo to the Apereo org here on Github by Friday. Please speak up if you see an issue with that plan.

The demo project still work?

I implemented the demo, clicked the login button redirects to the CAS login screen, returned to the demo project and still not displaying any information.

"POST" method support

While adding an additional method parameter to the casClientConfig in the webconfig a ConfigurationErrorException exception is thrown.

After looking into the source code I couldn't find the POST method support.

Is this something that is missing and is going to be delivered soon?

Is there another way rather than using the POST Login to get users extra info?

Thanks,
Daniel

Using Custom Login Page with Dotnet CAS Client

Hi,

We had a query implementing the login page redirect using DotNetCasClient 1.1.0 client.

We want to have a custom UI for the login page where the CAS login shuld be displayed in an IFRAME.

We created a page and updated the URL to <forms loginUrl="https://www.myapp.com/login">
however in the documentation - it says that it is necessary to match the URL specified in <casServerLoginUrl> and <forms> element in the config.

Please advise how we can redirect the unauthenticated requests to - "https://www.myapp.com/login" instead of the "https://sso.myapp.com/cas/login" URL (which is SSO Blue Login Screen ).

Thanks in advance.
Regards,
Kritul

Cleanup Duplicate/Unused Assembly References

Noticed today that the DotNetCasClient project is referencing a lot of .NET assemblies that are never used, more than likely from the bloated templates that Visual Studio provides out-of-the-box. To keep things lean and mean, I'd like to remove the unused assemblies (by no means difficult to add back should they need to be referenced in the future). Also, there are two groupings of references to separate .NET20/35 and .NET40/45, but they reference many of the same assemblies. I plan to merge the shared assemblies together in one item grouping in the .csproj file, leaving only the differing assemblies in the conditional groupings. The end result should be leaner resource usage with no functional changes.

Anyone against these two changes?

Too Many Redirects error

hello:
i use the .net cas client 1.1.0
project :asp.net mvc 4.0 .
web.config is ok .
after login the page is "Localhost redirected too many times." can you help me !
thank you !

Fixes proposed for preserving query string parameters for incoming SAML requests

We're working on several updates to DotNetCasClient project internally, including a Couchbase (that is, distributed caching) implementation of IServiceTicketManager and IProxyTicketManager.

Adjacent to this is work being done on a SAML IdP where the following two pull requests describe additional changes we've had to make to DotNetCasClient to better handle query string parameters.

Please see #34 and #35.

Missing legacy files

This seems pretty trivial, but annoying right off from the clone.

DotNetCasClient.csproj has references to the following legacy files:

dotnet-cas-client/DotNetCasClient/LICENSE.txt
dotnet-cas-client/DotNetCasClient/NOTICE.txt
dotnet-cas-client/DotNetCasClient/lib/log4net.dll

Static CurrentPrincipal leads to shared authentication between threads

Just got bitten by this today : once a user was authenticated on my ASP.NET website, everyone shared his/her identity!

This was due to the currentPrincipal variable being shared between requests (even though there is the ThreadStatic annotation).

I fixed it by making the following change :

public static ICasPrincipal CurrentPrincipal
    {
        //get { return currentPrincipal; }
        get { return Thread.CurrentPrincipal as ICasPrincipal; }
    }

Not being redirected

Thanks for this library!

However, my site isn't being redirected to the provided URL. I'm not seeing any valuable logging in order to diagnose, only the web.config attributes. Is there a rewrite value or something that I should be checking for that would prevent the redirection?

Logout URL

Hello ,

I am trying to set the landing page for the logout after calling CasAuthentication.SingleSignOut().

Can you let me how to set the the logout landing page ? ...It's now setting to Logout.aspx which is the page I called this function.

Support reading extra_attributes

A CAS server may pass extra_attributes (like full name) for the user being logged in. Looks like currently there is no way for this client to read that.

Is that correct?

Ticket Caching Failing With Visual Studio 2017

The TL;DR is that when running a CAS-enabled application in Visual Studio 2017 (IIS Express), the .NET cache provider (System.Web.Caching.Cache) is immediately expiring the new service tickets as soon as they are added to the cache. Thus, no service ticket is ever able to be validated. Below is how I arrived at this conclusion.

Recently my development computer at work bit the dust, prompting me to reformat and re-install Windows 10. I took that opportunity to also upgrade to Visual Studio 2017 Enterprise (from 2015 Enterprise). When I began working on one of my CAS-enabled applications in VS2017, I immediately got the problem of a redirect cycle between my application and the CAS server.

First thing I checked was that it wasn't another timing problem with the tolerance level for the SAML tickets, which it wasn't. No matter what value I set for ticketTimeTolerance, the redirects still occurred. I debugged about all I could think of in my application with no indication of why the SAML ticket validation was failing and thus redirecting back to the CAS server. Even turning on the tracing per the instructions in this project's README didn't shine light on the source of the problem.

I finally broke down and added the DotNetCasClient project to my solution so that I could step through the code as the process was happening. Eventually I noticed that the service ticket was being added to the cache, but in a subsequent call to retrieve the ticket from the cache, null was returned. I added some code for debugging to InsertTicket(). I was able to see that, immediately after inserting the ticket into the cache the cache item count increased by one. However, after a few debugging steps, the cache count decreased by one. I read in the comments for RemoveExpiredTickets() that the cache will automatically remove any expired items, so this got me thinking that the problem was with the expiration date for the cache item being inserted. By pure luck I was able to notice that the dates for the forms authentication ticket were using UTC while the expiration date for the cache item was in local time. Furthermore, I noticed the comments for the Insert() method of System.Web.Caching.Cache recommend to always use UTC dates as issues can occur with daylight savings time. The particular problem I am having wasn't related to DST, but on a whim I changed the insert statement from:

HttpContext.Current.Cache.Insert(GetTicketKey(casAuthenticationTicket.ServiceTicket), casAuthenticationTicket, null, expiration, Cache.NoSlidingExpiration);

to:

HttpContext.Current.Cache.Insert(GetTicketKey(casAuthenticationTicket.ServiceTicket), casAuthenticationTicket, null, expiration.ToUniversalTime(), Cache.NoSlidingExpiration);

Immediately my application worked again. To double-check that the fix was correct I have created another dummy web application that consumes CAS in Visual Studio 2017. Using the local date/time, the redirect loop occurs. Modify it to use UTC date/time, the application works as expected.

I created the same dummy application in a copy of Visual Studio 2015 I have, targeting the same version of the .NET framework (v4.5.2). Using the same configuration settings for the client in web.config, I did the same tests, except this time the caching of the service tickets works regardless of using local or UTC time for expiration. As best as I can tell, the only difference between the projects is Visual Studio 2015 versus 2017. They target the same framework version, have all the same NuGet packages/versions, and use the same web.config settings.

Using those same dummy projects (one in 2015, the other in 2017) I basically copied the code from ContainsTicket() to list the contents of the cache on each page load. Using a special query string parameter, I could instruct the page load code to add a simple string value as an object to the cache with a +1 minute expiration date. In VS2017, using the local date for expiration, the string would be gone from the cache on the next load, even though the second request was only a few seconds after adding the object to the cache. Using the UTC date, the string would stay in the cache for a minute, then be removed. Switching to VS2015 yielded the expected result that it did not matter which form of the date/time was used for expiration. The string value would stay in cache for 1 minute, then expire.

According to the MSDN Documentation for the Insert() method, the recommendation for the absoluteExpiration parameter value is for it to be a UTC date/time, not a local one. It is laughable, though, that the examples on the same page use DateTime.Now, against their own recommendation. In any case, I think it would be a good thing to change the expiration to a UTC date/time regardless of it being the absolute root cause of the problem I am experiencing.

I believe the change would need to be made at the point where the expiration date is set, which is when the forms authentication ticket is being created in CreateFormsAuthenticationTicket() of CasAuthentication.cs. The fromDate and toDate variables can be set as UTC dates, and the client has no problem in VS2017. I will create a pull request soon with the suggested changes along with documentation as to why UTC dates are needed. In the meantime, if anyone wants to weigh in on why this is happening between VS2015 (and earlier) and VS2017, it would be appreciated!

Implement OWIN client

Any modern ASP.NET app will use OWIN to make the login interchangeable with Twitter, Facebook, Google, etc. We need an OWIN client to be able to talk to CAS.

RFC: Support more target frameworks other than .NET 2/3.x

This issue is spurred from issue #54 and related to PR #55.

Background

When .NET 4.7 was GA'd, there was a bug introduced in the Cache.Insert method in System.Web.Caching. Ref: https://forums.asp.net/p/2123507/6145985.aspx. Now, it's debatable if this is an actual bug or not. MS may have fixed the method to implement the correct functionality, but as a result of years of people compensating for the original bug... the fix now possible caused problems in said people's code.

This functionality change, or correction if you will, was only done in .NET 4.7 (affecting the entire .NET 4.x lineage.) The original implementation and behavior expected of Cache.Insert in System.Web.Caching still exists in the .NET 2/3.x lineage.

On the ASP.NET forums link above, a commented mentioned that MS is aware of the issue and a bug report was filed, but I could not find any references to back up the said claim.

Proposal

Right now the project and nuget package are only compiled for and target the .NET 2/3.x framework. Regardless of that, the assembly will still work in applications targeting the .NET 4.x framework.

The current code relies on System.Web.Caching as the cache provider. This was the recommended cache provider for web applications back in the day targeting .NET 2/3.x and still applies for today. This has a hard dependency on System.Web.

In the .NET 4.x runtime, the recommended caching provider is System.Runtime.Caching, which has no other dependencies (e.g. System.Web.)

References for claim in above paragraph:

I am going to suggest that we keep the code in it's current state targeted and compiled for the .NET 2/3.x framework. Next, we make a target and compilation specifically for the .NET 4.x framework that has one specific change in the code so that it uses System.Runtime.Caching instead of System.Web.Caching as the cache provider. The next version of the nuget package (1.0.3) will have both of these assemblies. This fixes the issue at hand for projects targeting .NET 4.x. This will require no changes to the configuration file transformations in consuming projects and no other action on behalf of the developer consuming the nuget package other than upgrading to the newer version of the nuget package. This is more of a "behind the scenes" change.

In the end, there will still only be one solution, one project, one codebase, and one nuget package. The nuget package will contain multiple compiled assemblies of this project, each targeted for a different .NET framework version/runtime.

To give you an idea how this would be implemented, see these references as examples:

There are many other highly used/popular libraries that follow this methodology of targeting multiple framework versions from one code base. E.g. JSON.NET, log4net, Nlog, etc. (Example Ref: https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Newtonsoft.Json.csproj)

We are going to have to upgrade the solution and project files so they are opened in Visual Studio 2017 to accomplish this. I don't think that is a blocking issue for current and future contributors to this project because they could always use the Community Edition (free) of Visual Studio 2017 if they want to contribute to this project. The VS 2017 installation is siloed so that it does not affect any other versions of Visual Studio installed on a developer machine.

Going down this route will allow us to support other targets such as .NET Core, OWIN and ASP.NET Core in the future.

If everyone is on the same page, or we come to an agreement, I'll start on the work and open a PR. Time line is about one business week. This is a high priority for us as we are primarily a .NET shop and this is a blocking issue for us. Additionally, we plan to do .NET Core development in the near future and will require this project to support that runtime as a result.

@hokiecoder @scottt732 @serac @danransom @webman1 @mmoayyed @sbubaron

CacheServiceTicketManager cleanup issues

When the memory pressure is hig on the HttpContext.Current.Cache, it gets cleared by IIS. Then the user is actually "logged out" in the middle of an otherwise authenticated session.

Implementing the storage of local tickets on the HttpContext Cache seems flawed by design, since collection of items might not be totally controlled.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.