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 	[ComVisible (true)]
38 	[Serializable]
39 	public sealed class PrincipalPermission : IPermission, IUnrestrictedPermission, IBuiltInPermission {
40 
41 		private const int version = 1;
42 
43 		internal class PrincipalInfo {
44 
45 			private string _name;
46 			private string _role;
47 			private bool _isAuthenticated;
48 
PrincipalInfo(string name, string role, bool isAuthenticated)49 			public PrincipalInfo (string name, string role, bool isAuthenticated)
50 			{
51 				_name = name;
52 				_role = role;
53 				_isAuthenticated = isAuthenticated;
54 			}
55 
56 			public string Name {
57 				get { return _name; }
58 			}
59 
60 			public string Role {
61 				get { return _role; }
62 			}
63 
64 			public bool IsAuthenticated {
65 				get { return _isAuthenticated; }
66 			}
67 		}
68 
69 		private ArrayList principals;
70 
71 		// Constructors
72 
PrincipalPermission(PermissionState state)73 		public PrincipalPermission (PermissionState state)
74 		{
75 			principals = new ArrayList ();
76 			if (CodeAccessPermission.CheckPermissionState (state, true) == PermissionState.Unrestricted) {
77 				PrincipalInfo pi = new PrincipalInfo (null, null, true);
78 				principals.Add (pi);
79 			}
80 		}
81 
PrincipalPermission(string name, string role)82 		public PrincipalPermission (string name, string role) : this (name, role, true)
83 		{
84 		}
85 
PrincipalPermission(string name, string role, bool isAuthenticated)86 		public PrincipalPermission (string name, string role, bool isAuthenticated)
87 		{
88 			principals = new ArrayList ();
89 			PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
90 			principals.Add (pi);
91 		}
92 
PrincipalPermission(ArrayList principals)93 		internal PrincipalPermission (ArrayList principals)
94 		{
95 			this.principals = (ArrayList) principals.Clone ();
96 		}
97 
98 		// Properties
99 
100 		// Methods
101 
Copy()102 		public IPermission Copy ()
103 		{
104 			return new PrincipalPermission (principals);
105 		}
106 
Demand()107 		public void Demand ()
108 		{
109 			IPrincipal p = Thread.CurrentPrincipal;
110 			if (p == null)
111 				throw new SecurityException ("no Principal");
112 
113 			if (principals.Count > 0) {
114 				// check restrictions
115 				bool demand = false;
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))) {
123 						demand = true;
124 						break;
125 					}
126 				}
127 
128 				if (!demand)
129 					throw new SecurityException ("Demand for principal refused.");
130 			}
131 		}
132 
FromXml(SecurityElement elem)133 		public void FromXml (SecurityElement elem)
134 		{
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)
139 
140 			principals.Clear ();
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;
150 					if (auth != null) {
151 						try {
152 							isAuthenticated = Boolean.Parse (auth);
153 						}
154 						catch {}
155 					}
156 					PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
157 					principals.Add (pi);
158 				}
159 			}
160 		}
161 
Intersect(IPermission target)162 		public IPermission Intersect (IPermission target)
163 		{
164 			PrincipalPermission pp = Cast (target);
165 			if (pp == null)
166 				return null;
167 
168 			if (IsUnrestricted ())
169 				return pp.Copy ();
170 			if (pp.IsUnrestricted ())
171 				return Copy ();
172 
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) {
177 						string name = null;
178 						if ((pi.Name == opi.Name) || (opi.Name == null))
179 							name = pi.Name;
180 						else if (pi.Name == null)
181 							name = opi.Name;
182 						string role = null;
183 						if ((pi.Role == opi.Role) || (opi.Role == null))
184 							role = pi.Role;
185 						else if (pi.Role == null)
186 							role = opi.Role;
187 						if ((name != null) || (role != null)) {
188 							PrincipalInfo ipi = new PrincipalInfo (name, role, pi.IsAuthenticated);
189 							intersect.principals.Add (ipi);
190 						}
191 					}
192 				}
193 			}
194 
195 			return ((intersect.principals.Count > 0) ? intersect : null);
196 		}
197 
IsSubsetOf(IPermission target)198 		public bool IsSubsetOf (IPermission target)
199 		{
200 			PrincipalPermission pp = Cast (target);
201 			if (pp == null)
202 				return IsEmpty ();
203 
204 			if (IsUnrestricted ())
205 				return pp.IsUnrestricted ();
206 			else if (pp.IsUnrestricted ())
207 				return true;
208 
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))
216 						thisItem = true;
217 				}
218 				if (!thisItem)
219 					return false;
220 			}
221 
222 			return true;
223 		}
224 
IsUnrestricted()225 		public bool IsUnrestricted ()
226 		{
227 			foreach (PrincipalInfo pi in principals) {
228 				if ((pi.Name == null) && (pi.Role == null) && (pi.IsAuthenticated))
229 					return true;
230 			}
231 			return false;
232 		}
233 
ToString()234 		public override string ToString ()
235 		{
236 			return ToXml ().ToString ();
237 		}
238 
ToXml()239 		public SecurityElement ToXml ()
240 		{
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 ());
245 
246 			foreach (PrincipalInfo pi in principals) {
247 				SecurityElement sec = new SecurityElement ("Identity");
248 				if (pi.Name != null)
249 					sec.AddAttribute ("ID", pi.Name);
250 				if (pi.Role != null)
251 					sec.AddAttribute ("Role", pi.Role);
252 				if (pi.IsAuthenticated)
253 					sec.AddAttribute ("Authenticated", "true");
254 				se.AddChild (sec);
255 			}
256 			return se;
257 		}
258 
Union(IPermission other)259 		public IPermission Union (IPermission other)
260 		{
261 			PrincipalPermission pp = Cast (other);
262 			if (pp == null)
263 				return Copy ();
264 
265 			if (IsUnrestricted () || pp.IsUnrestricted ())
266 				return new PrincipalPermission (PermissionState.Unrestricted);
267 
268 			PrincipalPermission union = new PrincipalPermission (principals);
269 			foreach (PrincipalInfo pi in pp.principals)
270 				union.principals.Add (pi);
271 
272 			return union;
273 		}
274 
275 		[ComVisible (false)]
Equals(object obj)276 		public override bool Equals (object obj)
277 		{
278 			if (obj == null)
279 				return false;
280 
281 			PrincipalPermission pp = (obj as PrincipalPermission);
282 			if (pp == null)
283 				return false;
284 
285 			// same number of principals ?
286 			if (principals.Count != pp.principals.Count)
287 				return false;
288 
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)) {
296 						thisItem = true;
297 						break;
298 					}
299 				}
300 				if (!thisItem)
301 					return false;
302 			}
303 			return true;
304 		}
305 
306 		// according to documentation (fx 2.0 beta 1) we can have
307 		// different hash code even if both a Equals
308 		[ComVisible (false)]
GetHashCode()309 		public override int GetHashCode ()
310 		{
311 			return base.GetHashCode ();
312 		}
313 
314 		// IBuiltInPermission
IBuiltInPermission.GetTokenIndex()315 		int IBuiltInPermission.GetTokenIndex ()
316 		{
317 			return (int) BuiltInToken.Principal;
318 		}
319 
320 		// helpers
321 
Cast(IPermission target)322 		private PrincipalPermission Cast (IPermission target)
323 		{
324 			if (target == null)
325 				return null;
326 
327 			PrincipalPermission pp = (target as PrincipalPermission);
328 			if (pp == null) {
329 				CodeAccessPermission.ThrowInvalidPermission (target, typeof (PrincipalPermission));
330 			}
331 
332 			return pp;
333 		}
334 
IsEmpty()335 		private bool IsEmpty ()
336 		{
337 			return (principals.Count == 0);
338 		}
339 
340 		// Normally permissions tags are "IPermission" but this (non-CAS) permission use "Permission"
CheckSecurityElement(SecurityElement se, string parameterName, int minimumVersion, int maximumVersion)341 		internal int CheckSecurityElement (SecurityElement se, string parameterName, int minimumVersion, int maximumVersion)
342 		{
343 			if (se == null)
344 				throw new ArgumentNullException (parameterName);
345 
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);
350 			}
351 
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
356 
357 			// we assume minimum version if no version number is supplied
358 			int version = minimumVersion;
359 			string v = se.Attribute ("version");
360 			if (v != null) {
361 				try {
362 					version = Int32.Parse (v);
363 				}
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);
368 				}
369 			}
370 
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);
375 			}
376 			return version;
377 		}
378 	}
379 }
380