forked from i8beef/SAML2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSaml20AbstractEndpointHandler.cs
More file actions
227 lines (199 loc) · 9.09 KB
/
Saml20AbstractEndpointHandler.cs
File metadata and controls
227 lines (199 loc) · 9.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading;
using System.Web;
using SAML2.Bindings;
using SAML2.Config;
using SAML2.Utils;
namespace SAML2.Protocol
{
/// <summary>
/// Base class for all SAML20 specific endpoints.
/// </summary>
public abstract class Saml20AbstractEndpointHandler : AbstractEndpointHandler
{
/// <summary>
/// Parameter name for IDP choice
/// </summary>
public const string IdpChoiceParameterName = "cidp";
/// <summary>
/// Key used to override <c>ForceAuthn</c> setting
/// </summary>
public const string IdpForceAuthn = "IDPForceAuthn";
/// <summary>
/// Key used to override IsPassive setting
/// </summary>
public const string IdpIsPassive = "IDPIsPassive";
/// <summary>
/// Key used to save login session
/// </summary>
public const string IdpLoginSessionKey = "LoginIDPId";
/// <summary>
/// Key used to save the IDP name id in session context
/// </summary>
public const string IdpNameId = "IDPNameId";
/// <summary>
/// Used to save the name id format of the assertion
/// </summary>
public const string IdpNameIdFormat = "IDPNameIdFormat";
/// <summary>
/// Key used to save SessionId
/// </summary>
public const string IdpSessionIdKey = "IDPSessionID";
/// <summary>
/// Key used to save temporary session id
/// </summary>
public const string IdpTempSessionKey = "TempIDPId";
/// <summary>
/// Gets or sets a value indicating whether configuration has been validated
/// </summary>
public static bool Validated { get; set; }
/// <summary>
/// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
public sealed override void ProcessRequest(HttpContext context)
{
try
{
CheckConfiguration();
Handle(context);
}
catch (ThreadAbortException)
{
// This will swallow the ThreadAbortException automatically thrown by Response.Redirect, Response.End, and Server.Transfer
}
catch (Exception ex)
{
Logger.Error(ex.Message, ex);
throw;
}
}
/// <summary>
/// Handles the selection of an IDP. If only one IDP is found, the user is automatically redirected to it.
/// If several are found, and nothing indicates to which one the user should be sent, this method returns null.
/// </summary>
/// <param name="context">The context.</param>
/// <returns>The <see cref="IdentityProviderElement"/>.</returns>
public IdentityProvider RetrieveIDP(HttpContext context)
{
var config = Saml2Config.Current;
// If idpChoice is set, use it value
if (!string.IsNullOrEmpty(context.Request.Params[IdpChoiceParameterName]))
{
Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromQueryString, context.Request.Params[IdpChoiceParameterName]);
var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == context.Request.Params[IdpChoiceParameterName]);
if (endPoint != null)
{
return endPoint;
}
}
// If we have a common domain cookie, use it's value
// It must have been returned from the local common domain cookie reader endpoint.
if (!string.IsNullOrEmpty(context.Request.QueryString["_saml_idp"]))
{
var cdc = new CommonDomainCookie(context.Request.QueryString["_saml_idp"]);
if (cdc.IsSet)
{
var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == cdc.PreferredIDP);
if (endPoint != null)
{
Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromCommonDomainCookie, cdc.PreferredIDP);
return endPoint;
}
Logger.WarnFormat(ErrorMessages.CommonDomainCookieIdentityProviderInvalid, cdc.PreferredIDP);
}
}
// If there is only one configured IdentityProviderEndpointElement lets just use that
if (config.IdentityProviders.Count == 1 && config.IdentityProviders[0].Metadata != null)
{
Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, config.IdentityProviders[0].Name);
return config.IdentityProviders[0];
}
// If one of the endpoints are marked with default, use that one
var defaultIDP = config.IdentityProviders.FirstOrDefault(idp => idp.Default);
if (defaultIDP != null)
{
Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, defaultIDP.Id);
return defaultIDP;
}
// In case an IDP selection url has been configured, redirect to that one.
if (!string.IsNullOrEmpty(config.IdentityProviderSelectionUrl))
{
Logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromSelection, config.IdentityProviderSelectionUrl);
context.Response.Redirect(config.IdentityProviderSelectionUrl);
}
// If an IDPSelectionEvent handler is present, request the handler for an IDP endpoint to use.
return IdpSelectionUtil.InvokeIDPSelectionEventHandler(config.IdentityProviders);
}
/// <summary>
/// Looks through the Identity Provider configurations and
/// </summary>
/// <param name="idpId">The identity provider id.</param>
/// <returns>The <see cref="IdentityProviderElement"/>.</returns>
public IdentityProvider RetrieveIDPConfiguration(string idpId)
{
var config = Saml2Config.Current;
return config.IdentityProviders.FirstOrDefault(x => x.Id == idpId);
}
/// <summary>
/// Determine which endpoint to use based on the protocol defaults, configuration data and metadata.
/// </summary>
/// <param name="defaultBinding">The binding to use if none has been specified in the configuration and the metadata allows all bindings.</param>
/// <param name="config">The endpoint as described in the configuration. May be null.</param>
/// <param name="metadata">A list of endpoints of the given type (e.g. SSO or SLO) that the metadata contains.</param>
/// <returns>The <see cref="IdentityProviderElement"/>.</returns>
internal static IdentityProviderEndpoint DetermineEndpointConfiguration(BindingType defaultBinding, IdentityProviderEndpoint config, List<IdentityProviderEndpoint> metadata)
{
var result = new IdentityProviderEndpoint { Binding = defaultBinding };
// Determine which binding to use.
if (config != null)
{
result.Binding = config.Binding;
}
else
{
// Verify that the metadata allows the default binding.
var allowed = metadata.Exists(el => el.Binding == defaultBinding);
if (!allowed)
{
result.Binding = result.Binding == BindingType.Post
? BindingType.Redirect
: BindingType.Post;
}
}
if (config != null && !string.IsNullOrEmpty(config.Url))
{
result.Url = config.Url;
}
else
{
var endpoint = metadata.Find(el => el.Binding == result.Binding);
if (endpoint == null)
{
throw new ConfigurationErrorsException(string.Format("No IdentityProvider supporting SAML binding {0} found in metadata", result.Binding));
}
result.Url = endpoint.Url;
}
return result;
}
/// <summary>
/// Abstract handler function
/// </summary>
/// <param name="ctx">The context.</param>
protected abstract void Handle(HttpContext ctx);
/// <summary>
/// Checks the configuration elements and redirects to an error page if something is missing or wrong.
/// </summary>
private void CheckConfiguration()
{
if (Validated)
{
return;
}
Validated = BindingUtility.ValidateConfiguration();
}
}
}