Merge pull request #5198 from BrzVlad/fix-binprot-stats
[mono.git] / mcs / class / corlib / System.Security.Permissions / PrincipalPermission.cs
index 172354900154f5ec256108552ca1f045c353286c..5052eef0b07e37cbdbfb7a7131c5931c07377970 100644 (file)
@@ -2,13 +2,10 @@
 // System.Security.Permissions.PrincipalPermission.cs
 //
 // Author
-//     Sebastien Pouliot  <spouliot@motus.com>
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
 // Copyright (C) 2003 Motus Technologies. http://www.motus.com
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.Collections;
+using System.Runtime.InteropServices;
 using System.Security.Principal;
-using System.Text;
 using System.Threading;
 
 namespace System.Security.Permissions {
 
+       [ComVisible (true)]
        [Serializable]
        public sealed class PrincipalPermission : IPermission, IUnrestrictedPermission, IBuiltInPermission {
 
+               private const int version = 1;
+
                internal class PrincipalInfo {
 
                        private string _name;
                        private string _role;
                        private bool _isAuthenticated;
                        
-                       public PrincipalInfo (string name, string role, bool isAuthenticated) {
+                       public PrincipalInfo (string name, string role, bool isAuthenticated)
+                       {
                                _name = name;
                                _role = role;
                                _isAuthenticated = isAuthenticated;
@@ -73,15 +73,9 @@ namespace System.Security.Permissions {
                public PrincipalPermission (PermissionState state)
                {
                        principals = new ArrayList ();
-                       switch (state) {
-                               case PermissionState.None:
-                                       break;
-                               case PermissionState.Unrestricted:
-                                       PrincipalInfo pi = new PrincipalInfo (null, null, true);
-                                       principals.Add (pi);
-                                       break;
-                               default:
-                                       throw new ArgumentException ("unknown PermissionState");
+                       if (CodeAccessPermission.CheckPermissionState (state, true) == PermissionState.Unrestricted) {
+                               PrincipalInfo pi = new PrincipalInfo (null, null, true);
+                               principals.Add (pi);
                        }
                }
 
@@ -132,30 +126,33 @@ namespace System.Security.Permissions {
                                }
 
                                if (!demand)
-                                       throw new SecurityException ("invalid Principal");
+                                       throw new SecurityException ("Demand for principal refused.");
                        }
                }
 
-               public void FromXml (SecurityElement esd
+               public void FromXml (SecurityElement elem
                {
-                       if (esd == null)
-                               throw new ArgumentNullException ("esd");
-                       if (esd.Tag != "IPermission")
-                               throw new ArgumentException ("not IPermission");
-                       if (!(esd.Attributes ["class"] as string).StartsWith ("System.Security.Permissions.PrincipalPermission"))
-                               throw new ArgumentException ("not PrincipalPermission");
-                       if ((esd.Attributes ["version"] as string) != "1")
-                               throw new ArgumentException ("wrong version");
+                       // General validation in CodeAccessPermission
+                       CheckSecurityElement (elem, "elem", version, version);
+                       // Note: we do not (yet) care about the return value 
+                       // as we only accept version 1 (min/max values)
 
                        principals.Clear ();
                        // Children is null, not empty, when no child is present
-                       if (esd.Children != null) {
-                               foreach (SecurityElement se in esd.Children) {
+                       if (elem.Children != null) {
+                               foreach (SecurityElement se in elem.Children) {
                                        if (se.Tag != "Identity")
                                                throw new ArgumentException ("not IPermission/Identity");
-                                       string name = (se.Attributes ["Name"] as string);
-                                       string role = (se.Attributes ["Role"] as string);
-                                       bool isAuthenticated = ((se.Attributes ["Authenticated"] as string) == "true");
+                                       string name = se.Attribute ("ID");
+                                       string role = se.Attribute ("Role");
+                                       string auth = se.Attribute ("Authenticated");
+                                       bool isAuthenticated = false;
+                                       if (auth != null) {
+                                               try {
+                                                       isAuthenticated = Boolean.Parse (auth);
+                                               }
+                                               catch {}
+                                       }
                                        PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
                                        principals.Add (pi);
                                }
@@ -164,27 +161,29 @@ namespace System.Security.Permissions {
 
                public IPermission Intersect (IPermission target) 
                {
-                       if (target == null)
+                       PrincipalPermission pp = Cast (target);
+                       if (pp == null)
                                return null;
-                       if (! (target is PrincipalPermission))
-                               throw new ArgumentException ("wrong type");
 
-                       PrincipalPermission o = (PrincipalPermission) target;
                        if (IsUnrestricted ())
-                               return o.Copy ();
-                       if (o.IsUnrestricted ())
+                               return pp.Copy ();
+                       if (pp.IsUnrestricted ())
                                return Copy ();
 
                        PrincipalPermission intersect = new PrincipalPermission (PermissionState.None);
                        foreach (PrincipalInfo pi in principals) {
-                               foreach (PrincipalInfo opi in o.principals) {
+                               foreach (PrincipalInfo opi in pp.principals) {
                                        if (pi.IsAuthenticated == opi.IsAuthenticated) {
                                                string name = null;
                                                if ((pi.Name == opi.Name) || (opi.Name == null))
                                                        name = pi.Name;
+                                               else if (pi.Name == null)
+                                                       name = opi.Name;
                                                string role = null;
                                                if ((pi.Role == opi.Role) || (opi.Role == null))
                                                        role = pi.Role;
+                                               else if (pi.Role == null)
+                                                       role = opi.Role;
                                                if ((name != null) || (role != null)) {
                                                        PrincipalInfo ipi = new PrincipalInfo (name, role, pi.IsAuthenticated);
                                                        intersect.principals.Add (ipi);
@@ -198,22 +197,19 @@ namespace System.Security.Permissions {
 
                public bool IsSubsetOf (IPermission target) 
                {
-                       if (target == null)
-                               return false;
+                       PrincipalPermission pp = Cast (target);
+                       if (pp == null)
+                               return IsEmpty ();
 
-                       if (! (target is PrincipalPermission))
-                               throw new ArgumentException ("wrong type");
-
-                       PrincipalPermission o = (PrincipalPermission) target;
                        if (IsUnrestricted ())
-                               return o.IsUnrestricted ();
-                       else if (o.IsUnrestricted ())
+                               return pp.IsUnrestricted ();
+                       else if (pp.IsUnrestricted ())
                                return true;
 
                        // each must be a subset of the target
                        foreach (PrincipalInfo pi in principals) {
                                bool thisItem = false;
-                               foreach (PrincipalInfo opi in o.principals) {
+                               foreach (PrincipalInfo opi in pp.principals) {
                                        if (((pi.Name == opi.Name) || (opi.Name == null)) && 
                                                ((pi.Role == opi.Role) || (opi.Role == null)) && 
                                                (pi.IsAuthenticated == opi.IsAuthenticated))
@@ -242,16 +238,15 @@ namespace System.Security.Permissions {
 
                public SecurityElement ToXml () 
                {
-                       SecurityElement se = new SecurityElement ("IPermission");
+                       SecurityElement se = new SecurityElement ("Permission");
                        Type type = this.GetType ();
-                       StringBuilder asmName = new StringBuilder (type.Assembly.ToString ());
-                       asmName.Replace ('\"', '\'');
-                       se.AddAttribute ("class", type.FullName + ", " + asmName);
-                       se.AddAttribute ("version", "1");
+                       se.AddAttribute ("class", type.FullName + ", " + type.Assembly.ToString ().Replace ('\"', '\''));
+                       se.AddAttribute ("version", version.ToString ());
+
                        foreach (PrincipalInfo pi in principals) {
                                SecurityElement sec = new SecurityElement ("Identity");
                                if (pi.Name != null)
-                                       sec.AddAttribute ("Name", pi.Name);
+                                       sec.AddAttribute ("ID", pi.Name);
                                if (pi.Role != null)
                                        sec.AddAttribute ("Role", pi.Role);
                                if (pi.IsAuthenticated)
@@ -261,28 +256,124 @@ namespace System.Security.Permissions {
                        return se;
                }
 
-               public IPermission Union (IPermission target)
+               public IPermission Union (IPermission other)
                {
-                       if (target == null)
+                       PrincipalPermission pp = Cast (other);
+                       if (pp == null)
                                return Copy ();
-                       if (! (target is PrincipalPermission))
-                               throw new ArgumentException ("wrong type");
 
-                       PrincipalPermission o = (PrincipalPermission) target;
-                       if (IsUnrestricted () || o.IsUnrestricted ())
+                       if (IsUnrestricted () || pp.IsUnrestricted ())
                                return new PrincipalPermission (PermissionState.Unrestricted);
 
                        PrincipalPermission union = new PrincipalPermission (principals);
-                       foreach (PrincipalInfo pi in o.principals)
+                       foreach (PrincipalInfo pi in pp.principals)
                                union.principals.Add (pi);
 
                        return union;
                }
 
+               [ComVisible (false)]
+               public override bool Equals (object obj)
+               {
+                       if (obj == null)
+                               return false;
+
+                       PrincipalPermission pp = (obj as PrincipalPermission);
+                       if (pp == null)
+                               return false;
+
+                       // same number of principals ?
+                       if (principals.Count != pp.principals.Count)
+                               return false;
+
+                       // then all principals in "this" should be in "pp"
+                       foreach (PrincipalInfo pi in principals) {
+                               bool thisItem = false;
+                               foreach (PrincipalInfo opi in pp.principals) {
+                                       if (((pi.Name == opi.Name) || (opi.Name == null)) && 
+                                               ((pi.Role == opi.Role) || (opi.Role == null)) && 
+                                               (pi.IsAuthenticated == opi.IsAuthenticated)) {
+                                               thisItem = true;
+                                               break;
+                                       }
+                               }
+                               if (!thisItem)
+                                       return false;
+                       }
+                       return true;
+               }
+
+               // according to documentation (fx 2.0 beta 1) we can have 
+               // different hash code even if both a Equals
+               [ComVisible (false)]
+               public override int GetHashCode ()
+               {
+                       return base.GetHashCode ();
+               }
+
                // IBuiltInPermission
                int IBuiltInPermission.GetTokenIndex ()
                {
-                       return 8;
+                       return (int) BuiltInToken.Principal;
+               }
+
+               // helpers
+
+               private PrincipalPermission Cast (IPermission target)
+               {
+                       if (target == null)
+                               return null;
+
+                       PrincipalPermission pp = (target as PrincipalPermission);
+                       if (pp == null) {
+                               CodeAccessPermission.ThrowInvalidPermission (target, typeof (PrincipalPermission));
+                       }
+
+                       return pp;
+               }
+
+               private bool IsEmpty ()
+               {
+                       return (principals.Count == 0);
+               }
+
+               // Normally permissions tags are "IPermission" but this (non-CAS) permission use "Permission"
+               internal int CheckSecurityElement (SecurityElement se, string parameterName, int minimumVersion, int maximumVersion) 
+               {
+                       if (se == null)
+                               throw new ArgumentNullException (parameterName);
+
+                       // Tag is case-sensitive
+                       if (se.Tag != "Permission") {
+                               string msg = String.Format (Locale.GetText ("Invalid tag {0}"), se.Tag);
+                               throw new ArgumentException (msg, parameterName);
+                       }
+
+                       // Note: we do not care about the class attribute at 
+                       // this stage (in fact we don't even if the class 
+                       // attribute is present or not). Anyway the object has
+                       // already be created, with success, if we're loading it
+
+                       // we assume minimum version if no version number is supplied
+                       int version = minimumVersion;
+                       string v = se.Attribute ("version");
+                       if (v != null) {
+                               try {
+                                       version = Int32.Parse (v);
+                               }
+                               catch (Exception e) {
+                                       string msg = Locale.GetText ("Couldn't parse version from '{0}'.");
+                                       msg = String.Format (msg, v);
+                                       throw new ArgumentException (msg, parameterName, e);
+                               }
+                       }
+
+                       if ((version < minimumVersion) || (version > maximumVersion)) {
+                               string msg = Locale.GetText ("Unknown version '{0}', expected versions between ['{1}','{2}'].");
+                               msg = String.Format (msg, version, minimumVersion, maximumVersion);
+                               throw new ArgumentException (msg, parameterName);
+                       }
+                       return version;
                }
        }
 }