2 // System.Security.Permissions.PrincipalPermission.cs
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // Copyright (C) 2003 Motus Technologies. http://www.motus.com
8 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
31 using System.Runtime.InteropServices;
32 using System.Security.Principal;
33 using System.Threading;
35 namespace System.Security.Permissions {
41 public sealed class PrincipalPermission : IPermission, IUnrestrictedPermission, IBuiltInPermission {
43 private const int version = 1;
45 internal class PrincipalInfo {
49 private bool _isAuthenticated;
51 public PrincipalInfo (string name, string role, bool isAuthenticated)
55 _isAuthenticated = isAuthenticated;
66 public bool IsAuthenticated {
67 get { return _isAuthenticated; }
71 private ArrayList principals;
75 public PrincipalPermission (PermissionState state)
77 principals = new ArrayList ();
78 if (CodeAccessPermission.CheckPermissionState (state, true) == PermissionState.Unrestricted) {
79 PrincipalInfo pi = new PrincipalInfo (null, null, true);
84 public PrincipalPermission (string name, string role) : this (name, role, true)
88 public PrincipalPermission (string name, string role, bool isAuthenticated)
90 principals = new ArrayList ();
91 PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
95 internal PrincipalPermission (ArrayList principals)
97 this.principals = (ArrayList) principals.Clone ();
104 public IPermission Copy ()
106 return new PrincipalPermission (principals);
109 public void Demand ()
111 IPrincipal p = Thread.CurrentPrincipal;
113 throw new SecurityException ("no Principal");
115 if (principals.Count > 0) {
116 // check restrictions
118 foreach (PrincipalInfo pi in principals) {
119 // if a name is present then it must be equal
120 // if a role is present then the identity must be a member of this role
121 // if authentication is required then the identity must be authenticated
122 if (((pi.Name == null) || (pi.Name == p.Identity.Name)) &&
123 ((pi.Role == null) || (p.IsInRole (pi.Role))) &&
124 ((pi.IsAuthenticated && p.Identity.IsAuthenticated) || (!pi.IsAuthenticated))) {
131 throw new SecurityException ("Demand for principal refused.");
135 public void FromXml (SecurityElement esd)
137 // General validation in CodeAccessPermission
138 CheckSecurityElement (esd, "esd", version, version);
139 // Note: we do not (yet) care about the return value
140 // as we only accept version 1 (min/max values)
143 // Children is null, not empty, when no child is present
144 if (esd.Children != null) {
145 foreach (SecurityElement se in esd.Children) {
146 if (se.Tag != "Identity")
147 throw new ArgumentException ("not IPermission/Identity");
148 string name = se.Attribute ("ID");
149 string role = se.Attribute ("Role");
150 string auth = se.Attribute ("Authenticated");
151 bool isAuthenticated = false;
154 isAuthenticated = Boolean.Parse (auth);
158 PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
164 public IPermission Intersect (IPermission target)
166 PrincipalPermission pp = Cast (target);
170 if (IsUnrestricted ())
172 if (pp.IsUnrestricted ())
175 PrincipalPermission intersect = new PrincipalPermission (PermissionState.None);
176 foreach (PrincipalInfo pi in principals) {
177 foreach (PrincipalInfo opi in pp.principals) {
178 if (pi.IsAuthenticated == opi.IsAuthenticated) {
180 if ((pi.Name == opi.Name) || (opi.Name == null))
182 else if (pi.Name == null)
185 if ((pi.Role == opi.Role) || (opi.Role == null))
187 else if (pi.Role == null)
189 if ((name != null) || (role != null)) {
190 PrincipalInfo ipi = new PrincipalInfo (name, role, pi.IsAuthenticated);
191 intersect.principals.Add (ipi);
197 return ((intersect.principals.Count > 0) ? intersect : null);
200 public bool IsSubsetOf (IPermission target)
202 PrincipalPermission pp = Cast (target);
206 if (IsUnrestricted ())
207 return pp.IsUnrestricted ();
208 else if (pp.IsUnrestricted ())
211 // each must be a subset of the target
212 foreach (PrincipalInfo pi in principals) {
213 bool thisItem = false;
214 foreach (PrincipalInfo opi in pp.principals) {
215 if (((pi.Name == opi.Name) || (opi.Name == null)) &&
216 ((pi.Role == opi.Role) || (opi.Role == null)) &&
217 (pi.IsAuthenticated == opi.IsAuthenticated))
227 public bool IsUnrestricted ()
229 foreach (PrincipalInfo pi in principals) {
230 if ((pi.Name == null) && (pi.Role == null) && (pi.IsAuthenticated))
236 public override string ToString ()
238 return ToXml ().ToString ();
241 public SecurityElement ToXml ()
243 SecurityElement se = new SecurityElement ("Permission");
244 Type type = this.GetType ();
245 se.AddAttribute ("class", type.FullName + ", " + type.Assembly.ToString ().Replace ('\"', '\''));
246 se.AddAttribute ("version", version.ToString ());
248 foreach (PrincipalInfo pi in principals) {
249 SecurityElement sec = new SecurityElement ("Identity");
251 sec.AddAttribute ("ID", pi.Name);
253 sec.AddAttribute ("Role", pi.Role);
254 if (pi.IsAuthenticated)
255 sec.AddAttribute ("Authenticated", "true");
261 public IPermission Union (IPermission target)
263 PrincipalPermission pp = Cast (target);
267 if (IsUnrestricted () || pp.IsUnrestricted ())
268 return new PrincipalPermission (PermissionState.Unrestricted);
270 PrincipalPermission union = new PrincipalPermission (principals);
271 foreach (PrincipalInfo pi in pp.principals)
272 union.principals.Add (pi);
279 public override bool Equals (object obj)
284 PrincipalPermission pp = (obj as PrincipalPermission);
288 // same number of principals ?
289 if (principals.Count != pp.principals.Count)
292 // then all principals in "this" should be in "pp"
293 foreach (PrincipalInfo pi in principals) {
294 bool thisItem = false;
295 foreach (PrincipalInfo opi in pp.principals) {
296 if (((pi.Name == opi.Name) || (opi.Name == null)) &&
297 ((pi.Role == opi.Role) || (opi.Role == null)) &&
298 (pi.IsAuthenticated == opi.IsAuthenticated)) {
309 // according to documentation (fx 2.0 beta 1) we can have
310 // different hash code even if both a Equals
312 public override int GetHashCode ()
314 return base.GetHashCode ();
318 // IBuiltInPermission
319 int IBuiltInPermission.GetTokenIndex ()
321 return (int) BuiltInToken.Principal;
326 private PrincipalPermission Cast (IPermission target)
331 PrincipalPermission pp = (target as PrincipalPermission);
333 CodeAccessPermission.ThrowInvalidPermission (target, typeof (PrincipalPermission));
339 private bool IsEmpty ()
341 return (principals.Count == 0);
344 // Normally permissions tags are "IPermission" but this (non-CAS) permission use "Permission"
345 internal int CheckSecurityElement (SecurityElement se, string parameterName, int minimumVersion, int maximumVersion)
348 throw new ArgumentNullException (parameterName);
350 // Tag is case-sensitive
351 if (se.Tag != "Permission") {
352 string msg = String.Format (Locale.GetText ("Invalid tag {0}"), se.Tag);
353 throw new ArgumentException (msg, parameterName);
356 // Note: we do not care about the class attribute at
357 // this stage (in fact we don't even if the class
358 // attribute is present or not). Anyway the object has
359 // already be created, with success, if we're loading it
361 // we assume minimum version if no version number is supplied
362 int version = minimumVersion;
363 string v = se.Attribute ("version");
366 version = Int32.Parse (v);
368 catch (Exception e) {
369 string msg = Locale.GetText ("Couldn't parse version from '{0}'.");
370 msg = String.Format (msg, v);
371 throw new ArgumentException (msg, parameterName, e);
375 if ((version < minimumVersion) || (version > maximumVersion)) {
376 string msg = Locale.GetText ("Unknown version '{0}', expected versions between ['{1}','{2}'].");
377 msg = String.Format (msg, version, minimumVersion, maximumVersion);
378 throw new ArgumentException (msg, parameterName);