Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / mscorlib / system / security / claims / ClaimsIdentity.cs
blob115a5b663b019e4fd035d8e8a2c8af16e69baa6a
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 //
9 //
10 // ClaimsIdentity.cs
13 namespace System.Security.Claims
15 using System.Collections.Generic;
16 using System.Collections.ObjectModel;
17 using System.Diagnostics.Contracts;
18 using System.IO;
19 using System.Runtime.CompilerServices;
20 using System.Runtime.InteropServices;
21 using System.Runtime.Serialization;
22 using System.Runtime.Serialization.Formatters.Binary;
23 using System.Security.Permissions;
24 using System.Security.Principal;
26 /// <summary>
27 /// An Identity that is represented by a set of claims.
28 /// </summary>
29 [Serializable]
30 [ComVisible(true)]
31 public class ClaimsIdentity : IIdentity
33 private enum SerializationMask
35 None = 0,
36 AuthenticationType = 1,
37 BootstrapConext = 2,
38 NameClaimType = 4,
39 RoleClaimType = 8,
40 HasClaims = 16,
41 HasLabel = 32,
42 Actor = 64,
43 UserData = 128,
46 [NonSerialized]
47 private byte[] m_userSerializationData;
49 [NonSerialized]
50 const string PreFix = "System.Security.ClaimsIdentity.";
51 [NonSerialized]
52 const string ActorKey = PreFix + "actor";
53 [NonSerialized]
54 const string AuthenticationTypeKey = PreFix + "authenticationType";
55 [NonSerialized]
56 const string BootstrapContextKey = PreFix + "bootstrapContext";
57 [NonSerialized]
58 const string ClaimsKey = PreFix + "claims";
59 [NonSerialized]
60 const string LabelKey = PreFix + "label";
61 [NonSerialized]
62 const string NameClaimTypeKey = PreFix + "nameClaimType";
63 [NonSerialized]
64 const string RoleClaimTypeKey = PreFix + "roleClaimType";
65 [NonSerialized]
66 const string VersionKey = PreFix + "version";
67 [NonSerialized]
68 public const string DefaultIssuer = @"LOCAL AUTHORITY";
69 [NonSerialized]
70 public const string DefaultNameClaimType = ClaimTypes.Name;
71 [NonSerialized]
72 public const string DefaultRoleClaimType = ClaimTypes.Role;
73 // === Important
75 // adding claims to this list will affect the Authorization for this Identity
76 // originally marked this as SecurityCritical, however because enumerators access it
77 // we would need to extend SecuritySafeCritical to the enumerator methods AND the constructors.
78 // In the end, this requires additional [SecuritySafeCritical] attributes. So if any additional access
79 // is added to 'm_instanceClaims' then this must be carefully monitored. This is equivalent to adding sids to the
80 // NTToken and will be used up the stack to make Authorization decisions.
83 // these are claims that are added by using the AddClaim, AddClaims methods or passed in the constructor.
84 [NonSerialized]
85 List<Claim> m_instanceClaims = new List<Claim>();
87 // These are claims that are external to the identity. .Net runtime attaches roles owned by principals GenericPrincpal and RolePrincipal here.
88 // They are not serialized OR remembered when cloned. Access through public method: ClaimProviders.
89 [NonSerialized]
90 Collection<IEnumerable<Claim>> m_externalClaims = new Collection<IEnumerable<Claim>>();
92 [NonSerialized]
93 string m_nameType = DefaultNameClaimType;
95 [NonSerialized]
96 string m_roleType = DefaultRoleClaimType;
98 [OptionalField(VersionAdded=2)]
99 string m_version = "1.0";
101 [OptionalField(VersionAdded = 2)]
102 ClaimsIdentity m_actor;
104 [OptionalField(VersionAdded = 2)]
105 string m_authenticationType;
107 [OptionalField(VersionAdded = 2)]
108 object m_bootstrapContext;
110 [OptionalField(VersionAdded = 2)]
111 string m_label;
113 [OptionalField(VersionAdded = 2)]
114 string m_serializedNameType;
116 [OptionalField(VersionAdded = 2)]
117 string m_serializedRoleType;
119 [OptionalField(VersionAdded = 2)]
120 string m_serializedClaims;
122 #region ClaimsIdentity Constructors
124 /// <summary>
125 /// Initializes an instance of <see cref="ClaimsIdentity"/> with an empty claims collection.
126 /// </summary>
127 /// <remarks>
128 /// <see cref="Identity.AuthenticationType"/> is set to null.
129 /// </remarks>
130 public ClaimsIdentity()
131 : this((Claim[])null)
135 /// <summary>
136 /// Initializes an instance of <see cref="ClaimsIdentity"/> using the name and authentication type from
137 /// an <see cref="IIdentity"/> instance.
138 /// </summary>
139 /// <param name="identity"><see cref="IIdentity"/> to draw the name and authentication type from.</param>
140 /// <exception cref="ArgumentNullException"> if <paramref name="identity"/> is null.</exception>
141 public ClaimsIdentity(IIdentity identity)
142 : this(identity, (IEnumerable<Claim>)null)
146 /// <summary>
147 /// Initializes an instance of <see cref="Identity"/> using an enumerated collection of
148 /// <see cref="Claim"/> objects.
149 /// </summary>
150 /// <param name="claims">
151 /// The collection of <see cref="Claim"/> objects to populate <see cref="Identity.Claims"/> with.
152 /// </param>
153 /// <remarks>
154 /// <see cref="Identity.AuthenticationType"/> is set to null.
155 /// </remarks>
156 public ClaimsIdentity(IEnumerable<Claim> claims)
157 : this((IIdentity) null, claims, null, null, null)
161 /// <summary>
162 /// Initializes an instance of <see cref="Identity"/> with an empty <see cref="Claim"/> collection
163 /// and the specified authentication type.
164 /// </summary>
165 /// <param name="authenticationType">The type of authentication used.</param>
166 public ClaimsIdentity(string authenticationType)
167 : this((IIdentity) null, (IEnumerable<Claim>)null, authenticationType, (string)null, (string)null)
171 /// <summary>
172 /// Initializes an instance of <see cref="Identity"/> using an enumerated collection of
173 /// <see cref="Claim"/> objects.
174 /// </summary>
175 /// <param name="claims">
176 /// The collection of <see cref="Claim"/> objects to populate <see cref="Identity.Claims"/> with.
177 /// </param>
178 /// <param name="authenticationType">The type of authentication used.</param>
179 /// <remarks>
180 /// <see cref="Identity.AuthenticationType"/> is set to null.
181 /// </remarks>
182 public ClaimsIdentity(IEnumerable<Claim> claims, string authenticationType)
183 : this((IIdentity)null, claims, authenticationType, null, null)
187 /// <summary>
188 /// Initializes an instance of <see cref="ClaimsIdentity"/> using the name and authentication type from
189 /// an <see cref="IIdentity"/> instance.
190 /// </summary>
191 /// <param name="identity"><see cref="IIdentity"/> to draw the name and authentication type from.</param>
192 /// <exception cref="ArgumentNullException"> if <paramref name="identity"/> is null.</exception>
193 public ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims)
194 : this(identity, claims, (string)null, (string)null, (string)null)
198 /// <summary>
199 /// Initializes an instance of <see cref="Identity"/> with an empty <see cref="Claim"/> collection,
200 /// the specified authentication type, name claim type, and role claim type.
201 /// </summary>
202 /// <param name="authenticationType">The type of authentication used.</param>
203 /// <param name="nameType">The claim type to use for <see cref="Identity.Name"/>.</param>
204 /// <param name="roleType">The claim type to use for IClaimsPrincipal.IsInRole(string).</param>
205 public ClaimsIdentity(string authenticationType, string nameType, string roleType )
206 : this((IIdentity) null, (IEnumerable<Claim>)null, authenticationType, nameType, roleType)
210 /// <summary>
211 /// Initializes an instance of <see cref="ClaimsIdentity"/> using an enumeration of type
212 /// <see cref="Claim"/>, authentication type, name claim type, role claim type, and bootstrapContext.
213 /// </summary>
214 /// <param name="claims">An enumeration of type <see cref="Claim"/> to initialize this identity</param>
215 /// <param name="authenticationType">The type of authentication used.</param>
216 /// <param name="nameType">The claim type to identify NameClaims.</param>
217 /// <param name="roleType">The claim type to identify RoleClaims.</param>
218 public ClaimsIdentity(IEnumerable<Claim> claims, string authenticationType, string nameType, string roleType)
219 : this((IIdentity)null, claims, authenticationType, nameType, roleType)
223 /// <summary>
224 /// Initializes an instance of <see cref="ClaimsIdentity"/> using an enumeration of type
225 /// <see cref="Claim"/>, authentication type, name claim type, role claim type, and bootstrapContext.
226 /// </summary>
227 /// <param name="identity">The initial identity to base this identity from.</param>
228 /// <param name="claims">An enumeration of type <see cref="Claim"/> to initialize this identity.</param>
229 /// <param name="authenticationType">The type of authentication used.</param>
230 /// <param name="nameType">The claim type to identify NameClaims.</param>
231 /// <param name="roleType">The claim type to identify RoleClaims.</param>
232 public ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims, string authenticationType, string nameType, string roleType)
233 : this(identity, claims, authenticationType, nameType, roleType, true)
238 /// <summary>
239 /// This constructor was added so that the WindowsIdentity could control if the authenticationType should be checked. For WindowsIdentities this
240 /// leads to a priviledged call and will fail where the caller has low priviledge.
241 /// </summary>
242 /// <param name="identity">The initial identity to base this identity from.</param>
243 /// <param name="claims">An enumeration of type <see cref="Claim"/> to initialize this identity.</param>
244 /// <param name="authenticationType">The type of authentication used.</param>
245 /// <param name="nameType">The claim type to identify NameClaims.</param>
246 /// <param name="roleType">The claim type to identify RoleClaims.</param>
247 /// <param name="checkAuthType">This boolean flag controls if we blindly set the authenticationType, since call WindowsIdentity.AuthenticationType is a priviledged call.</param>
248 internal ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims, string authenticationType, string nameType, string roleType, bool checkAuthType)
250 bool nameTypeSet = false;
251 bool roleTypeSet = false;
253 // move the authtype, nameType and roleType over from the identity ONLY if they weren't specifically set.
254 if(checkAuthType && null != identity && string.IsNullOrEmpty(authenticationType))
256 // can safely ignore UnauthorizedAccessException from WindowsIdentity,
257 // LSA didn't allow the call and WindowsIdentity throws if property is never accessed, no reason to fail.
258 if (identity is WindowsIdentity)
262 m_authenticationType = identity.AuthenticationType;
264 catch (UnauthorizedAccessException)
266 m_authenticationType = null;
269 else
271 m_authenticationType = identity.AuthenticationType;
274 else
276 m_authenticationType = authenticationType;
279 if(!string.IsNullOrEmpty(nameType))
281 m_nameType = nameType;
282 nameTypeSet = true;
285 if(!string.IsNullOrEmpty(roleType))
287 m_roleType = roleType;
288 roleTypeSet = true;
291 ClaimsIdentity claimsIdentity = identity as ClaimsIdentity;
293 if (claimsIdentity != null)
295 m_label = claimsIdentity.m_label;
297 // give preference to parameters
298 if (!nameTypeSet)
300 m_nameType = claimsIdentity.m_nameType;
303 if (!roleTypeSet)
305 m_roleType = claimsIdentity.m_roleType;
308 m_bootstrapContext = claimsIdentity.m_bootstrapContext;
310 if (claimsIdentity.Actor != null)
313 // Check if the Actor is circular before copying. That check is done while setting
314 // the Actor property and so not really needed here. But checking just for sanity sake
316 if(!IsCircular(claimsIdentity.Actor))
318 if (!AppContextSwitches.SetActorAsReferenceWhenCopyingClaimsIdentity)
320 m_actor = claimsIdentity.Actor.Clone();
322 else
324 m_actor = claimsIdentity.Actor;
327 else
329 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
333 // We can only copy over the claims we own, it is up to the derived
334 // to copy over claims they own.
335 // BUT we need to special case WindowsIdentity as it keeps its own claims.
336 // In the case where we are not a windowsIdentity and the claimsIdentity is
337 // we need to copy the claims
339 if ((claimsIdentity is WindowsIdentity) && (!(this is WindowsIdentity)))
340 SafeAddClaims(claimsIdentity.Claims);
341 else
342 SafeAddClaims(claimsIdentity.m_instanceClaims);
344 if (claimsIdentity.m_userSerializationData != null)
346 m_userSerializationData = claimsIdentity.m_userSerializationData.Clone() as byte[];
349 else
351 if (identity != null && !string.IsNullOrEmpty(identity.Name))
353 SafeAddClaim(new Claim(m_nameType, identity.Name, ClaimValueTypes.String, DefaultIssuer, DefaultIssuer, this));
357 if (claims != null)
359 SafeAddClaims(claims);
363 /// Initializes an instance of <see cref="ClaimsIdentity"/> using a <see cref="BinaryReader"/>.
364 /// Normally the reader is constructed from the bytes returned from <see cref="WriteTo"/>
365 /// </summary>
366 /// <param name="reader">a <see cref="BinaryReader"/> pointing to a <see cref="ClaimsIdentity"/>.</param>
367 /// <exception cref="ArgumentNullException">if 'reader' is null.</exception>
368 public ClaimsIdentity(BinaryReader reader)
370 if (reader == null)
371 throw new ArgumentNullException("reader");
373 Initialize(reader);
376 /// <summary>
377 /// Copy constructor.
378 /// </summary>
379 /// <param name="other"><see cref="ClaimsIdentity"/> to copy.</param>
380 /// <exception cref="ArgumentNullException">if 'other' is null.</exception>
381 protected ClaimsIdentity(ClaimsIdentity other)
383 if (other == null)
385 throw new ArgumentNullException("other");
388 if (other.m_actor != null)
390 m_actor = other.m_actor.Clone();
393 m_authenticationType = other.m_authenticationType;
394 m_bootstrapContext = other.m_bootstrapContext;
395 m_label = other.m_label;
396 m_nameType = other.m_nameType;
397 m_roleType = other.m_roleType;
398 if (other.m_userSerializationData != null)
400 m_userSerializationData = other.m_userSerializationData.Clone() as byte[];
403 SafeAddClaims(other.m_instanceClaims);
406 /// <summary>
407 /// Initializes an instance of <see cref="Identity"/> from a serialized stream created via
408 /// <see cref="ISerializable"/>.
409 /// </summary>
410 /// <param name="info">
411 /// The <see cref="SerializationInfo"/> to read from.
412 /// </param>
413 /// <param name="context">The <see cref="StreamingContext"/> for serialization. Can be null.</param>
414 /// <exception cref="ArgumentNullException">Thrown is the <paramref name="info"/> is null.</exception>
415 [SecurityCritical]
416 protected ClaimsIdentity(SerializationInfo info, StreamingContext context)
418 if (null == info)
420 throw new ArgumentNullException("info");
423 Deserialize(info, context, true);
426 /// <summary>
427 /// Initializes an instance of <see cref="Identity"/> from a serialized stream created via
428 /// <see cref="ISerializable"/>.
429 /// </summary>
430 /// <param name="info">
431 /// The <see cref="SerializationInfo"/> to read from.
432 /// </param>
433 /// <exception cref="ArgumentNullException">Thrown is the <paramref name="info"/> is null.</exception>
434 [SecurityCritical]
435 protected ClaimsIdentity(SerializationInfo info)
437 if (null == info)
439 throw new ArgumentNullException("info");
442 StreamingContext sc = new StreamingContext();
443 Deserialize(info, sc, false);
446 #endregion
448 /// <summary>
449 /// Gets the authentication type.
450 /// </summary>
451 public virtual string AuthenticationType
453 get { return m_authenticationType; }
456 /// <summary>
457 /// Gets a value that indicates whether the user has been authenticated.
458 /// </summary>
459 public virtual bool IsAuthenticated
461 get { return !string.IsNullOrEmpty(m_authenticationType); }
464 /// <summary>
465 /// Gets or sets a <see cref="ClaimsIdentity"/> that was granted delegation rights.
466 /// </summary>
467 public ClaimsIdentity Actor
469 get { return m_actor; }
472 if(value != null)
474 if(IsCircular(value))
476 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
479 m_actor = value;
483 /// <summary>
484 /// Gets or sets a context that was used to create this <see cref="ClaimsIdentity"/>.
485 /// </summary>
486 public object BootstrapContext
488 get { return m_bootstrapContext; }
490 [SecurityCritical]
491 set { m_bootstrapContext = value; }
494 /// <summary>
495 /// Gets the claims as <see cref="IEnumerable{Claim}"/>, associated with this <see cref="ClaimsIdentity"/>.
496 /// </summary>
497 /// <remarks>May contain nulls.</remarks>
498 public virtual IEnumerable<Claim> Claims
502 for (int i = 0; i < m_instanceClaims.Count; i++)
504 yield return m_instanceClaims[i];
507 if (m_externalClaims != null)
509 for (int j = 0; j < m_externalClaims.Count; j++)
511 if (m_externalClaims[j] != null)
513 foreach (Claim claim in m_externalClaims[j])
515 yield return claim;
523 /// <summary>
524 /// Contains any additional data provided by a derived type, typically set when calling <see cref="WriteTo(BinaryWriter, byte[])"/>.</param>
525 /// </summary>
526 protected virtual byte[] CustomSerializationData
530 return m_userSerializationData;
534 /// <summary>
535 /// Allow the association of claims with this instance of <see cref="ClaimsIdentity"/>.
536 /// The claims will not be serialized or added in Clone(). They will be included in searches, finds and returned from the call to Claims.
537 /// It is recommended the creator of the claims ensures the subject of the claims reflects this <see cref="ClaimsIdentity"/>.
538 /// </summary>
539 internal Collection<IEnumerable<Claim>> ExternalClaims
541 [FriendAccessAllowed]
542 get { return m_externalClaims; }
545 /// <summary>
546 /// Gets or sets the label for this <see cref="Identity"/>
547 /// </summary>
548 public string Label
550 get { return m_label; }
551 set { m_label = value; }
554 /// <summary>
555 /// Gets the value of the first claim that has a type of NameClaimType. If no claim is found, null is returned.
556 /// </summary>
557 public virtual string Name
559 // just an accessor for getting the name claim
562 Claim claim = FindFirst(m_nameType);
563 if (claim != null)
565 return claim.Value;
568 return null;
572 /// <summary>
573 /// Gets the claim type used to distinguish claims that refer to the name.
574 /// </summary>
575 public string NameClaimType
577 get { return m_nameType; }
580 /// <summary>
581 /// Gets the claim type used to distinguish claims that refer to Roles.
582 /// </summary>
583 public string RoleClaimType
585 get { return m_roleType; }
588 /// <summary>
589 /// Returns a new instance of <see cref="ClaimsIdentity"/> with values copied from this object.
590 /// </summary>
591 /// <returns>A new <see cref="Identity"/> object copied from this object</returns>
592 public virtual ClaimsIdentity Clone()
594 ClaimsIdentity newIdentity = new ClaimsIdentity(m_instanceClaims);
596 newIdentity.m_authenticationType = this.m_authenticationType;
597 newIdentity.m_bootstrapContext = this.m_bootstrapContext;
598 newIdentity.m_label = this.m_label;
599 newIdentity.m_nameType = this.m_nameType;
600 newIdentity.m_roleType = this.m_roleType;
602 if(this.Actor != null)
604 // Check if the Actor is circular before copying. That check is done while setting
605 // the Actor property and so not really needed here. But checking just for sanity sake
606 if(!IsCircular(this.Actor))
608 if (!AppContextSwitches.SetActorAsReferenceWhenCopyingClaimsIdentity)
610 newIdentity.Actor = this.Actor.Clone();
612 else
614 newIdentity.Actor = this.Actor;
617 else
619 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
623 return newIdentity;
626 /// <summary>
627 /// Adds a single claim to this ClaimsIdentity. The claim is examined and if the subject != this, then a new claim is
628 /// created by calling claim.Clone(this). This creates a new claim, with the correct subject.
629 /// </summary>
630 /// <param name="claims">Enumeration of claims to add.</param>
631 /// This is SecurityCritical as we need to control who can add claims to the Identity. Futher down the pipe
632 /// Authorization decisions will be made based on the claims found in this collection.
633 [SecurityCritical]
634 public virtual void AddClaim(Claim claim)
636 if (claim == null)
638 throw new ArgumentNullException("claim");
641 Contract.EndContractBlock();
643 if(object.ReferenceEquals(claim.Subject, this))
645 m_instanceClaims.Add(claim);
647 else
649 m_instanceClaims.Add(claim.Clone(this));
653 /// <summary>
654 /// Adds a list of claims to this Claims Identity. Each claim is examined and if the subject != this, then a new claim is
655 /// created by calling claim.Clone(this). This creates a new claim, with the correct subject.
656 /// </summary>
657 /// <param name="claims">Enumeration of claims to add.</param>
658 /// This is SecurityCritical as we need to control who can add claims to the Identity. Futher down the pipe
659 /// Authorization decisions will be made based on the claims found in this collection.
660 [SecurityCritical]
661 public virtual void AddClaims(IEnumerable<Claim> claims)
663 if (claims == null)
665 throw new ArgumentNullException("claims");
668 Contract.EndContractBlock();
670 foreach (Claim claim in claims)
672 if (claim == null)
674 continue;
677 AddClaim(claim);
681 /// <summary>
682 /// Attempts to remove a claim from the identity. It is possible that the claim cannot be removed since it is not owned
683 /// by the identity. This would be the case for role claims that are owned by the Principal.
684 /// Matches by object reference.
685 /// <summary/>
686 [SecurityCritical]
687 public virtual bool TryRemoveClaim(Claim claim)
689 bool removed = false;
691 for (int i = 0; i < m_instanceClaims.Count; i++)
693 if (object.ReferenceEquals(m_instanceClaims[i], claim))
695 m_instanceClaims.RemoveAt(i);
696 removed = true;
697 break;
700 return removed;
703 [SecurityCritical]
704 public virtual void RemoveClaim(Claim claim)
706 if (!TryRemoveClaim(claim))
708 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ClaimCannotBeRemoved", claim));
712 /// <summary>
713 /// Called from constructor, isolated for easy review
714 /// This is called from the constructor, this implies that the base class has
715 /// ownership of holding onto the claims. We can't call AddClaim as that is a virtual and the
716 /// Derived class may not be constructed yet.
717 /// </summary>
718 /// <param name="claims"></param>
719 [SecuritySafeCritical]
720 void SafeAddClaims(IEnumerable<Claim> claims)
722 foreach (Claim claim in claims)
724 if (object.ReferenceEquals(claim.Subject, this))
726 m_instanceClaims.Add(claim);
728 else
730 m_instanceClaims.Add(claim.Clone(this));
735 /// <summary>
736 /// Called from constructor, isolated for easy review.
737 /// This is called from the constructor, this implies that the base class has
738 /// ownership of holding onto the claims. We can't call AddClaim as that is a virtual and the
739 /// Derived class may not be constructed yet.
740 /// </summary>
741 /// <param name="claim"></param>
742 [SecuritySafeCritical]
743 void SafeAddClaim(Claim claim)
745 if (object.ReferenceEquals(claim.Subject, this))
747 m_instanceClaims.Add(claim);
749 else
751 m_instanceClaims.Add(claim.Clone(this));
755 /// <summary>
756 /// Retrieves a <see cref="IEnumerable{Claim}"/> where each claim is matched by <param name="match"/>.
757 /// </summary>
758 /// <param name="match">The function that performs the matching logic.</param>
759 /// <returns>A <see cref="IEnumerable{Claim}"/> of matched claims.</returns>
760 public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match)
762 if (match == null)
764 throw new ArgumentNullException("match");
767 Contract.EndContractBlock();
769 List<Claim> claims = new List<Claim>();
771 foreach (Claim claim in Claims)
773 if (match(claim))
775 claims.Add(claim);
779 return claims.AsReadOnly();
782 /// <summary>
783 /// Retrieves a <see cref="IEnumerable{Claim}"/> where each Claim.Type equals <paramref name="type"/>.
784 /// </summary>
785 /// <param name="type">The type of the claim to match.</param>
786 /// <returns>A <see cref="IEnumerable{Claim}"/> of matched claims.</returns>
787 /// <remarks>Comparison is made using Ordinal case in-sensitive on type.<</remarks>
788 public virtual IEnumerable<Claim> FindAll(string type)
790 if (type == null)
792 throw new ArgumentNullException("type");
795 Contract.EndContractBlock();
797 List<Claim> claims = new List<Claim>();
799 foreach (Claim claim in Claims)
801 if (claim != null)
803 if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
805 claims.Add(claim);
810 return claims.AsReadOnly();
813 /// <summary>
814 /// Determines if a claim is contained within this ClaimsIdentity.
815 /// </summary>
816 /// <param name="match">The function that performs the matching logic.</param>
817 /// <returns>true if a claim is found, false otherwise.</returns>
818 public virtual bool HasClaim(Predicate<Claim> match)
820 if (match == null)
822 throw new ArgumentNullException("match");
825 Contract.EndContractBlock();
827 foreach (Claim claim in Claims)
829 if (match(claim))
831 return true;
835 return false;
838 /// <summary>
839 /// Determines if a claim with type AND value is contained in the claims within this ClaimsIdentity.
840 /// </summary>
841 /// <param name="type"> the type of the claim to match.</param>
842 /// <param name="value"> the value of the claim to match.</param>
843 /// <returns>true if a claim is matched, false otherwise.</returns>
844 /// <remarks>Does not check Issuer or OriginalIssuer. Comparison is made using Ordinal, case sensitive on value, case in-sensitive on type.</remarks>
845 public virtual bool HasClaim(string type, string value)
847 if (type == null)
849 throw new ArgumentNullException("type");
852 if (value == null)
854 throw new ArgumentNullException("value");
857 Contract.EndContractBlock();
859 foreach (Claim claim in Claims)
861 if (claim != null)
863 if (claim != null
864 && string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase)
865 && string.Equals(claim.Value, value, StringComparison.Ordinal))
867 return true;
872 return false;
875 /// <summary>
876 /// Retrieves the first <see cref="Claim"/> that is matched by <param name="match"/>.
877 /// </summary>
878 /// <param name="match">The function that performs the matching logic.</param>
879 /// <returns>A <see cref="Claim"/>, null if nothing matches.</returns>
880 /// <remarks>Comparison is made using Ordinal, case in-sensitive.</remarks>
881 public virtual Claim FindFirst(Predicate<Claim> match)
883 if (match == null)
885 throw new ArgumentNullException("match");
888 Contract.EndContractBlock();
890 foreach (Claim claim in Claims)
892 if (match(claim))
894 return claim;
898 return null;
901 /// <summary>
902 /// Retrieves the first <see cref="Claim"/> where Claim.Type equals <paramref name="type"/>.
903 /// </summary>
904 /// <param name="type">The type of the claim to match.</param>
905 /// <returns>A <see cref="Claim"/>, null if nothing matches.</returns>
906 /// <remarks>Comparison is made using Ordinal, case in-sensitive.</remarks>
907 public virtual Claim FindFirst(string type)
909 if (type == null)
911 throw new ArgumentNullException("type");
914 Contract.EndContractBlock();
916 foreach (Claim claim in Claims)
918 if (claim != null)
920 if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
922 return claim;
927 return null;
930 [OnSerializing()]
931 [SecurityCritical]
932 private void OnSerializingMethod(StreamingContext context)
934 if (this is ISerializable)
935 return;
937 m_serializedClaims = SerializeClaims();
938 m_serializedNameType = m_nameType;
939 m_serializedRoleType = m_roleType;
942 [OnDeserialized()]
943 [SecurityCritical]
944 private void OnDeserializedMethod(StreamingContext context)
946 if (this is ISerializable)
947 return;
949 if (!String.IsNullOrEmpty(m_serializedClaims))
951 DeserializeClaims(m_serializedClaims);
952 m_serializedClaims = null;
955 m_nameType = string.IsNullOrEmpty(m_serializedNameType) ? DefaultNameClaimType : m_serializedNameType;
956 m_roleType = string.IsNullOrEmpty(m_serializedRoleType) ? DefaultRoleClaimType : m_serializedRoleType;
959 [OnDeserializing()]
960 private void OnDeserializingMethod(StreamingContext context)
962 if (this is ISerializable)
963 return;
965 m_instanceClaims = new List<Claim>();
967 m_externalClaims = new Collection<IEnumerable<Claim>>();
970 /// <summary>
971 /// Populates the specified <see cref="SerializationInfo"/> with the serialization data for the ClaimsIdentity
972 /// </summary>
973 /// <param name="info">The serialization information stream to write to. Satisfies ISerializable contract.</param>
974 /// <param name="context">Context for serialization. Can be null.</param>
975 /// <exception cref="ArgumentNullException">Thrown if the info parameter is null.</exception>
976 [SecurityCritical]
977 [SecurityPermission(SecurityAction.Assert, SerializationFormatter = true)]
978 protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
980 if (null == info)
982 throw new ArgumentNullException("info");
984 Contract.EndContractBlock();
986 BinaryFormatter formatter = new BinaryFormatter();
988 info.AddValue(VersionKey, m_version);
989 if (!string.IsNullOrEmpty(m_authenticationType))
991 info.AddValue(AuthenticationTypeKey, m_authenticationType);
994 info.AddValue(NameClaimTypeKey, m_nameType);
995 info.AddValue(RoleClaimTypeKey, m_roleType);
997 if (!string.IsNullOrEmpty(m_label))
999 info.AddValue(LabelKey, m_label);
1003 // actor
1005 if (m_actor != null)
1007 using (MemoryStream ms = new MemoryStream())
1009 formatter.Serialize(ms, m_actor, null, false);
1010 info.AddValue(ActorKey, Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length));
1015 // claims
1017 info.AddValue(ClaimsKey, SerializeClaims());
1020 // bootstrapContext
1022 if (m_bootstrapContext != null)
1024 using (MemoryStream ms = new MemoryStream())
1026 formatter.Serialize(ms, m_bootstrapContext, null, false);
1027 info.AddValue(BootstrapContextKey, Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length));
1032 [SecurityCritical]
1033 private void DeserializeClaims(string serializedClaims)
1035 if (!string.IsNullOrEmpty(serializedClaims))
1037 using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(serializedClaims)))
1039 m_instanceClaims = (List<Claim>)(new BinaryFormatter()).Deserialize(stream, null, false);
1040 for (int i = 0; i < m_instanceClaims.Count; i++)
1042 m_instanceClaims[i].Subject = this;
1047 if (m_instanceClaims == null)
1049 m_instanceClaims = new List<Claim>();
1053 [SecurityCritical]
1054 private string SerializeClaims()
1056 using (MemoryStream ms = new MemoryStream())
1058 (new BinaryFormatter()).Serialize(ms, m_instanceClaims, null, false);
1059 return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
1063 /// <summary>
1064 /// Checks if a circular reference exists to 'this'
1065 /// </summary>
1066 /// <param name="subject"></param>
1067 /// <returns></returns>
1068 bool IsCircular(ClaimsIdentity subject)
1070 if(ReferenceEquals(this, subject))
1072 return true;
1075 ClaimsIdentity currSubject = subject;
1077 while(currSubject.Actor != null)
1079 if(ReferenceEquals(this, currSubject.Actor))
1081 return true;
1084 currSubject = currSubject.Actor;
1087 return false;
1090 /// <summary>
1091 /// Initializes from a <see cref="BinaryReader"/>. Normally the reader is initialized in the same as the one passed to <see cref="Serialize(BinaryWriter)"/>
1092 /// </summary>
1093 /// <param name="reader">a <see cref="BinaryReader"/> pointing to a <see cref="ClaimsIdentity"/>.</param>
1094 /// <exception cref="ArgumentNullException">if 'reader' is null.</exception>
1095 private void Initialize(BinaryReader reader)
1097 if (reader == null)
1099 throw new ArgumentNullException("reader");
1103 SerializationMask mask = (SerializationMask)reader.ReadInt32();
1105 if ((mask & SerializationMask.AuthenticationType) == SerializationMask.AuthenticationType)
1107 m_authenticationType = reader.ReadString();
1110 if ((mask & SerializationMask.BootstrapConext) == SerializationMask.BootstrapConext)
1112 m_bootstrapContext = reader.ReadString();
1115 if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
1117 m_nameType = reader.ReadString();
1119 else
1121 m_nameType = ClaimsIdentity.DefaultNameClaimType;
1124 if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
1126 m_roleType = reader.ReadString();
1128 else
1130 m_roleType = ClaimsIdentity.DefaultRoleClaimType;
1133 if ((mask & SerializationMask.HasClaims) == SerializationMask.HasClaims)
1136 int numberOfClaims = reader.ReadInt32();
1137 for (int index = 0; index < numberOfClaims; ++index)
1139 Claim claim = new Claim(reader, this);
1140 m_instanceClaims.Add(claim);
1145 /// <summary>
1146 /// Provides and extensibility point for derived types to create a custom <see cref="Claim"/>.
1147 /// </summary>
1148 /// <param name="reader">the <see cref="BinaryReader"/>that points at the claim.</param>
1149 /// <returns>a new <see cref="Claim"/>.</returns>
1150 protected virtual Claim CreateClaim(BinaryReader reader)
1152 if (reader == null)
1154 throw new ArgumentNullException("reader");
1157 return new Claim(reader, this);
1160 /// <summary>
1161 /// Serializes using a <see cref="BinaryWriter"/>
1162 /// </summary>
1163 /// <param name="writer">the <see cref="BinaryWriter"/> to use for data storage.</param>
1164 /// <exception cref="ArgumentNullException">if 'writer' is null.</exception>
1165 public virtual void WriteTo(BinaryWriter writer)
1167 WriteTo(writer, null);
1170 /// <summary>
1171 /// Serializes using a <see cref="BinaryWriter"/>
1172 /// </summary>
1173 /// <param name="writer">the <see cref="BinaryWriter"/> to use for data storage.</param>
1174 /// <param name="userData">additional data provided by derived type.</param>
1175 /// <exception cref="ArgumentNullException">if 'writer' is null.</exception>
1176 protected virtual void WriteTo(BinaryWriter writer, byte[] userData)
1178 if (writer == null)
1180 throw new ArgumentNullException("writer");
1183 int numberOfPropertiesWritten = 0;
1184 var mask = SerializationMask.None;
1185 if (m_authenticationType != null)
1187 mask |= SerializationMask.AuthenticationType;
1188 numberOfPropertiesWritten++;
1191 if (m_bootstrapContext != null)
1193 string rawData = m_bootstrapContext as string;
1194 if (rawData != null)
1196 mask |= SerializationMask.BootstrapConext;
1197 numberOfPropertiesWritten++;
1201 if (!string.Equals(m_nameType, ClaimsIdentity.DefaultNameClaimType, StringComparison.Ordinal))
1203 mask |= SerializationMask.NameClaimType;
1204 numberOfPropertiesWritten++;
1207 if (!string.Equals(m_roleType, ClaimsIdentity.DefaultRoleClaimType, StringComparison.Ordinal))
1209 mask |= SerializationMask.RoleClaimType;
1210 numberOfPropertiesWritten++;
1213 if (!string.IsNullOrWhiteSpace(m_label))
1215 mask |= SerializationMask.HasLabel;
1216 numberOfPropertiesWritten++;
1219 if (m_instanceClaims.Count > 0)
1221 mask |= SerializationMask.HasClaims;
1222 numberOfPropertiesWritten++;
1225 if (m_actor != null)
1227 mask |= SerializationMask.Actor;
1228 numberOfPropertiesWritten++;
1231 if (userData != null && userData.Length > 0)
1233 numberOfPropertiesWritten++;
1234 mask |= SerializationMask.UserData;
1237 writer.Write((Int32)mask);
1238 writer.Write((Int32)numberOfPropertiesWritten);
1239 if ((mask & SerializationMask.AuthenticationType) == SerializationMask.AuthenticationType)
1241 writer.Write(m_authenticationType);
1244 if ((mask & SerializationMask.BootstrapConext) == SerializationMask.BootstrapConext)
1246 writer.Write(m_bootstrapContext as string);
1249 if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
1251 writer.Write(m_nameType);
1254 if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
1256 writer.Write(m_roleType);
1259 if ((mask & SerializationMask.HasLabel) == SerializationMask.HasLabel)
1261 writer.Write(m_label);
1264 if ((mask & SerializationMask.HasClaims) == SerializationMask.HasClaims)
1266 writer.Write((Int32)m_instanceClaims.Count);
1267 foreach (var claim in m_instanceClaims)
1269 claim.WriteTo(writer);
1273 if ((mask & SerializationMask.Actor) == SerializationMask.Actor)
1275 m_actor.WriteTo(writer);
1278 if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
1280 writer.Write((Int32)userData.Length);
1281 writer.Write(userData);
1284 writer.Flush();
1287 // <param name="useContext"></param> The reason for this param is due to WindowsIdentity deciding to have an
1288 // api that doesn't pass the context to its internal constructor.
1289 [SecurityCritical]
1290 [SecurityPermission(SecurityAction.Assert, SerializationFormatter = true)]
1291 private void Deserialize(SerializationInfo info, StreamingContext context, bool useContext)
1294 if (null == info)
1296 throw new ArgumentNullException("info");
1299 BinaryFormatter bf;
1301 if (useContext)
1302 bf = new BinaryFormatter(null, context);
1303 else
1304 bf = new BinaryFormatter();
1307 SerializationInfoEnumerator enumerator = info.GetEnumerator();
1308 while (enumerator.MoveNext())
1310 switch (enumerator.Name)
1312 case VersionKey:
1313 info.GetString(VersionKey);
1314 break;
1316 case AuthenticationTypeKey:
1317 m_authenticationType = info.GetString(AuthenticationTypeKey);
1318 break;
1320 case NameClaimTypeKey:
1321 m_nameType = info.GetString(NameClaimTypeKey);
1322 break;
1324 case RoleClaimTypeKey:
1325 m_roleType = info.GetString(RoleClaimTypeKey);
1326 break;
1328 case LabelKey:
1329 m_label = info.GetString(LabelKey);
1330 break;
1332 case ActorKey:
1333 using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(info.GetString(ActorKey))))
1335 m_actor = (ClaimsIdentity)bf.Deserialize(stream, null, false);
1337 break;
1339 case ClaimsKey:
1340 DeserializeClaims(info.GetString(ClaimsKey));
1341 break;
1343 case BootstrapContextKey:
1344 using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(info.GetString(BootstrapContextKey))))
1346 m_bootstrapContext = bf.Deserialize(ms, null, false);
1348 break;
1350 default:
1351 // Ignore other fields for forward compatability.
1352 break;