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
{
39 public sealed class PrincipalPermission
: IPermission
, IUnrestrictedPermission
, IBuiltInPermission
{
41 private const int version
= 1;
43 internal class PrincipalInfo
{
47 private bool _isAuthenticated
;
49 public PrincipalInfo (string name
, string role
, bool isAuthenticated
)
53 _isAuthenticated
= isAuthenticated
;
64 public bool IsAuthenticated
{
65 get { return _isAuthenticated; }
69 private ArrayList principals
;
73 public PrincipalPermission (PermissionState state
)
75 principals
= new ArrayList ();
76 if (CodeAccessPermission
.CheckPermissionState (state
, true) == PermissionState
.Unrestricted
) {
77 PrincipalInfo pi
= new PrincipalInfo (null, null, true);
82 public PrincipalPermission (string name
, string role
) : this (name
, role
, true)
86 public PrincipalPermission (string name
, string role
, bool isAuthenticated
)
88 principals
= new ArrayList ();
89 PrincipalInfo pi
= new PrincipalInfo (name
, role
, isAuthenticated
);
93 internal PrincipalPermission (ArrayList principals
)
95 this.principals
= (ArrayList
) principals
.Clone ();
102 public IPermission
Copy ()
104 return new PrincipalPermission (principals
);
107 public void Demand ()
109 IPrincipal p
= Thread
.CurrentPrincipal
;
111 throw new SecurityException ("no Principal");
113 if (principals
.Count
> 0) {
114 // check restrictions
116 foreach (PrincipalInfo pi
in principals
) {
117 // if a name is present then it must be equal
118 // if a role is present then the identity must be a member of this role
119 // if authentication is required then the identity must be authenticated
120 if (((pi
.Name
== null) || (pi
.Name
== p
.Identity
.Name
)) &&
121 ((pi
.Role
== null) || (p
.IsInRole (pi
.Role
))) &&
122 ((pi
.IsAuthenticated
&& p
.Identity
.IsAuthenticated
) || (!pi
.IsAuthenticated
))) {
129 throw new SecurityException ("Demand for principal refused.");
133 public void FromXml (SecurityElement elem
)
135 // General validation in CodeAccessPermission
136 CheckSecurityElement (elem
, "elem", version
, version
);
137 // Note: we do not (yet) care about the return value
138 // as we only accept version 1 (min/max values)
141 // Children is null, not empty, when no child is present
142 if (elem
.Children
!= null) {
143 foreach (SecurityElement se
in elem
.Children
) {
144 if (se
.Tag
!= "Identity")
145 throw new ArgumentException ("not IPermission/Identity");
146 string name
= se
.Attribute ("ID");
147 string role
= se
.Attribute ("Role");
148 string auth
= se
.Attribute ("Authenticated");
149 bool isAuthenticated
= false;
152 isAuthenticated
= Boolean
.Parse (auth
);
156 PrincipalInfo pi
= new PrincipalInfo (name
, role
, isAuthenticated
);
162 public IPermission
Intersect (IPermission target
)
164 PrincipalPermission pp
= Cast (target
);
168 if (IsUnrestricted ())
170 if (pp
.IsUnrestricted ())
173 PrincipalPermission intersect
= new PrincipalPermission (PermissionState
.None
);
174 foreach (PrincipalInfo pi
in principals
) {
175 foreach (PrincipalInfo opi
in pp
.principals
) {
176 if (pi
.IsAuthenticated
== opi
.IsAuthenticated
) {
178 if ((pi
.Name
== opi
.Name
) || (opi
.Name
== null))
180 else if (pi
.Name
== null)
183 if ((pi
.Role
== opi
.Role
) || (opi
.Role
== null))
185 else if (pi
.Role
== null)
187 if ((name
!= null) || (role
!= null)) {
188 PrincipalInfo ipi
= new PrincipalInfo (name
, role
, pi
.IsAuthenticated
);
189 intersect
.principals
.Add (ipi
);
195 return ((intersect
.principals
.Count
> 0) ? intersect
: null);
198 public bool IsSubsetOf (IPermission target
)
200 PrincipalPermission pp
= Cast (target
);
204 if (IsUnrestricted ())
205 return pp
.IsUnrestricted ();
206 else if (pp
.IsUnrestricted ())
209 // each must be a subset of the target
210 foreach (PrincipalInfo pi
in principals
) {
211 bool thisItem
= false;
212 foreach (PrincipalInfo opi
in pp
.principals
) {
213 if (((pi
.Name
== opi
.Name
) || (opi
.Name
== null)) &&
214 ((pi
.Role
== opi
.Role
) || (opi
.Role
== null)) &&
215 (pi
.IsAuthenticated
== opi
.IsAuthenticated
))
225 public bool IsUnrestricted ()
227 foreach (PrincipalInfo pi
in principals
) {
228 if ((pi
.Name
== null) && (pi
.Role
== null) && (pi
.IsAuthenticated
))
234 public override string ToString ()
236 return ToXml ().ToString ();
239 public SecurityElement
ToXml ()
241 SecurityElement se
= new SecurityElement ("Permission");
242 Type type
= this.GetType ();
243 se
.AddAttribute ("class", type
.FullName
+ ", " + type
.Assembly
.ToString ().Replace ('\"', '\''));
244 se
.AddAttribute ("version", version
.ToString ());
246 foreach (PrincipalInfo pi
in principals
) {
247 SecurityElement sec
= new SecurityElement ("Identity");
249 sec
.AddAttribute ("ID", pi
.Name
);
251 sec
.AddAttribute ("Role", pi
.Role
);
252 if (pi
.IsAuthenticated
)
253 sec
.AddAttribute ("Authenticated", "true");
259 public IPermission
Union (IPermission other
)
261 PrincipalPermission pp
= Cast (other
);
265 if (IsUnrestricted () || pp
.IsUnrestricted ())
266 return new PrincipalPermission (PermissionState
.Unrestricted
);
268 PrincipalPermission union
= new PrincipalPermission (principals
);
269 foreach (PrincipalInfo pi
in pp
.principals
)
270 union
.principals
.Add (pi
);
276 public override bool Equals (object obj
)
281 PrincipalPermission pp
= (obj
as PrincipalPermission
);
285 // same number of principals ?
286 if (principals
.Count
!= pp
.principals
.Count
)
289 // then all principals in "this" should be in "pp"
290 foreach (PrincipalInfo pi
in principals
) {
291 bool thisItem
= false;
292 foreach (PrincipalInfo opi
in pp
.principals
) {
293 if (((pi
.Name
== opi
.Name
) || (opi
.Name
== null)) &&
294 ((pi
.Role
== opi
.Role
) || (opi
.Role
== null)) &&
295 (pi
.IsAuthenticated
== opi
.IsAuthenticated
)) {
306 // according to documentation (fx 2.0 beta 1) we can have
307 // different hash code even if both a Equals
309 public override int GetHashCode ()
311 return base.GetHashCode ();
314 // IBuiltInPermission
315 int IBuiltInPermission
.GetTokenIndex ()
317 return (int) BuiltInToken
.Principal
;
322 private PrincipalPermission
Cast (IPermission target
)
327 PrincipalPermission pp
= (target
as PrincipalPermission
);
329 CodeAccessPermission
.ThrowInvalidPermission (target
, typeof (PrincipalPermission
));
335 private bool IsEmpty ()
337 return (principals
.Count
== 0);
340 // Normally permissions tags are "IPermission" but this (non-CAS) permission use "Permission"
341 internal int CheckSecurityElement (SecurityElement se
, string parameterName
, int minimumVersion
, int maximumVersion
)
344 throw new ArgumentNullException (parameterName
);
346 // Tag is case-sensitive
347 if (se
.Tag
!= "Permission") {
348 string msg
= String
.Format (Locale
.GetText ("Invalid tag {0}"), se
.Tag
);
349 throw new ArgumentException (msg
, parameterName
);
352 // Note: we do not care about the class attribute at
353 // this stage (in fact we don't even if the class
354 // attribute is present or not). Anyway the object has
355 // already be created, with success, if we're loading it
357 // we assume minimum version if no version number is supplied
358 int version
= minimumVersion
;
359 string v
= se
.Attribute ("version");
362 version
= Int32
.Parse (v
);
364 catch (Exception e
) {
365 string msg
= Locale
.GetText ("Couldn't parse version from '{0}'.");
366 msg
= String
.Format (msg
, v
);
367 throw new ArgumentException (msg
, parameterName
, e
);
371 if ((version
< minimumVersion
) || (version
> maximumVersion
)) {
372 string msg
= Locale
.GetText ("Unknown version '{0}', expected versions between ['{1}','{2}'].");
373 msg
= String
.Format (msg
, version
, minimumVersion
, maximumVersion
);
374 throw new ArgumentException (msg
, parameterName
);