svn path=/branches/mono-1-1-9/mcs/; revision=51216
[mono.git] / mcs / class / corlib / System.Security.Permissions / PrincipalPermission.cs
1 //
2 // System.Security.Permissions.PrincipalPermission.cs
3 //
4 // Author
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2003 Motus Technologies. http://www.motus.com
8 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
9 //
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:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
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.
28 //
29
30 using System.Collections;
31 using System.Runtime.InteropServices;
32 using System.Security.Principal;
33 using System.Threading;
34
35 namespace System.Security.Permissions {
36
37 #if NET_2_0
38         [ComVisible (true)]
39 #endif
40         [Serializable]
41         public sealed class PrincipalPermission : IPermission, IUnrestrictedPermission, IBuiltInPermission {
42
43                 private const int version = 1;
44
45                 internal class PrincipalInfo {
46
47                         private string _name;
48                         private string _role;
49                         private bool _isAuthenticated;
50                         
51                         public PrincipalInfo (string name, string role, bool isAuthenticated)
52                         {
53                                 _name = name;
54                                 _role = role;
55                                 _isAuthenticated = isAuthenticated;
56                         }
57
58                         public string Name {
59                                 get { return _name; }
60                         }
61
62                         public string Role {
63                                 get { return _role; }
64                         }
65
66                         public bool IsAuthenticated {
67                                 get { return _isAuthenticated; }
68                         }
69                 }
70
71                 private ArrayList principals;
72
73                 // Constructors
74
75                 public PrincipalPermission (PermissionState state)
76                 {
77                         principals = new ArrayList ();
78                         if (CodeAccessPermission.CheckPermissionState (state, true) == PermissionState.Unrestricted) {
79                                 PrincipalInfo pi = new PrincipalInfo (null, null, true);
80                                 principals.Add (pi);
81                         }
82                 }
83
84                 public PrincipalPermission (string name, string role) : this (name, role, true)
85                 {
86                 }
87
88                 public PrincipalPermission (string name, string role, bool isAuthenticated)
89                 {
90                         principals = new ArrayList ();
91                         PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
92                         principals.Add (pi);
93                 }
94
95                 internal PrincipalPermission (ArrayList principals) 
96                 {
97                         this.principals = (ArrayList) principals.Clone ();
98                 }
99
100                 // Properties
101
102                 // Methods
103
104                 public IPermission Copy () 
105                 {
106                         return new PrincipalPermission (principals);
107                 }
108
109                 public void Demand ()
110                 {
111                         IPrincipal p = Thread.CurrentPrincipal;
112                         if (p == null)
113                                 throw new SecurityException ("no Principal");
114
115                         if (principals.Count > 0) {
116                                 // check restrictions
117                                 bool demand = false;
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))) {
125                                                 demand = true;
126                                                 break;
127                                         }
128                                 }
129
130                                 if (!demand)
131                                         throw new SecurityException ("Demand for principal refused.");
132                         }
133                 }
134
135                 public void FromXml (SecurityElement esd) 
136                 {
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)
141
142                         principals.Clear ();
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;
152                                         if (auth != null) {
153                                                 try {
154                                                         isAuthenticated = Boolean.Parse (auth);
155                                                 }
156                                                 catch {}
157                                         }
158                                         PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
159                                         principals.Add (pi);
160                                 }
161                         }
162                 }
163
164                 public IPermission Intersect (IPermission target) 
165                 {
166                         PrincipalPermission pp = Cast (target);
167                         if (pp == null)
168                                 return null;
169
170                         if (IsUnrestricted ())
171                                 return pp.Copy ();
172                         if (pp.IsUnrestricted ())
173                                 return Copy ();
174
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) {
179                                                 string name = null;
180                                                 if ((pi.Name == opi.Name) || (opi.Name == null))
181                                                         name = pi.Name;
182                                                 else if (pi.Name == null)
183                                                         name = opi.Name;
184                                                 string role = null;
185                                                 if ((pi.Role == opi.Role) || (opi.Role == null))
186                                                         role = pi.Role;
187                                                 else if (pi.Role == null)
188                                                         role = opi.Role;
189                                                 if ((name != null) || (role != null)) {
190                                                         PrincipalInfo ipi = new PrincipalInfo (name, role, pi.IsAuthenticated);
191                                                         intersect.principals.Add (ipi);
192                                                 }
193                                         }
194                                 }
195                         }
196
197                         return ((intersect.principals.Count > 0) ? intersect : null);
198                 }
199
200                 public bool IsSubsetOf (IPermission target) 
201                 {
202                         PrincipalPermission pp = Cast (target);
203                         if (pp == null)
204                                 return IsEmpty ();
205
206                         if (IsUnrestricted ())
207                                 return pp.IsUnrestricted ();
208                         else if (pp.IsUnrestricted ())
209                                 return true;
210
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))
218                                                 thisItem = true;
219                                 }
220                                 if (!thisItem)
221                                         return false;
222                         }
223
224                         return true;
225                 }
226
227                 public bool IsUnrestricted () 
228                 {
229                         foreach (PrincipalInfo pi in principals) {
230                                 if ((pi.Name == null) && (pi.Role == null) && (pi.IsAuthenticated))
231                                         return true;
232                         }
233                         return false;
234                 }
235
236                 public override string ToString () 
237                 {
238                         return ToXml ().ToString ();
239                 }
240
241                 public SecurityElement ToXml () 
242                 {
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 ());
247
248                         foreach (PrincipalInfo pi in principals) {
249                                 SecurityElement sec = new SecurityElement ("Identity");
250                                 if (pi.Name != null)
251                                         sec.AddAttribute ("ID", pi.Name);
252                                 if (pi.Role != null)
253                                         sec.AddAttribute ("Role", pi.Role);
254                                 if (pi.IsAuthenticated)
255                                         sec.AddAttribute ("Authenticated", "true");
256                                 se.AddChild (sec);
257                         }
258                         return se;
259                 }
260
261                 public IPermission Union (IPermission target)
262                 {
263                         PrincipalPermission pp = Cast (target);
264                         if (pp == null)
265                                 return Copy ();
266
267                         if (IsUnrestricted () || pp.IsUnrestricted ())
268                                 return new PrincipalPermission (PermissionState.Unrestricted);
269
270                         PrincipalPermission union = new PrincipalPermission (principals);
271                         foreach (PrincipalInfo pi in pp.principals)
272                                 union.principals.Add (pi);
273
274                         return union;
275                 }
276
277 #if NET_2_0
278                 [ComVisible (false)]
279                 public override bool Equals (object obj)
280                 {
281                         if (obj == null)
282                                 return false;
283
284                         PrincipalPermission pp = (obj as PrincipalPermission);
285                         if (pp == null)
286                                 return false;
287
288                         // same number of principals ?
289                         if (principals.Count != pp.principals.Count)
290                                 return false;
291
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)) {
299                                                 thisItem = true;
300                                                 break;
301                                         }
302                                 }
303                                 if (!thisItem)
304                                         return false;
305                         }
306                         return true;
307                 }
308
309                 // according to documentation (fx 2.0 beta 1) we can have 
310                 // different hash code even if both a Equals
311                 [ComVisible (false)]
312                 public override int GetHashCode ()
313                 {
314                         return base.GetHashCode ();
315                 }
316 #endif
317
318                 // IBuiltInPermission
319                 int IBuiltInPermission.GetTokenIndex ()
320                 {
321                         return (int) BuiltInToken.Principal;
322                 }
323
324                 // helpers
325
326                 private PrincipalPermission Cast (IPermission target)
327                 {
328                         if (target == null)
329                                 return null;
330
331                         PrincipalPermission pp = (target as PrincipalPermission);
332                         if (pp == null) {
333                                 CodeAccessPermission.ThrowInvalidPermission (target, typeof (PrincipalPermission));
334                         }
335
336                         return pp;
337                 }
338
339                 private bool IsEmpty ()
340                 {
341                         return (principals.Count == 0);
342                 }
343
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) 
346                 {
347                         if (se == null)
348                                 throw new ArgumentNullException (parameterName);
349
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);
354                         }
355
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
360
361                         // we assume minimum version if no version number is supplied
362                         int version = minimumVersion;
363                         string v = se.Attribute ("version");
364                         if (v != null) {
365                                 try {
366                                         version = Int32.Parse (v);
367                                 }
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);
372                                 }
373                         }
374
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);
379                         }
380                         return version;
381                 }
382         }
383 }