a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
380 lines
10 KiB
C#
380 lines
10 KiB
C#
//
|
|
// System.Security.Permissions.PrincipalPermission.cs
|
|
//
|
|
// Author
|
|
// Sebastien Pouliot <sebastien@ximian.com>
|
|
//
|
|
// Copyright (C) 2003 Motus Technologies. http://www.motus.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
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System.Collections;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Principal;
|
|
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)
|
|
{
|
|
_name = name;
|
|
_role = role;
|
|
_isAuthenticated = isAuthenticated;
|
|
}
|
|
|
|
public string Name {
|
|
get { return _name; }
|
|
}
|
|
|
|
public string Role {
|
|
get { return _role; }
|
|
}
|
|
|
|
public bool IsAuthenticated {
|
|
get { return _isAuthenticated; }
|
|
}
|
|
}
|
|
|
|
private ArrayList principals;
|
|
|
|
// Constructors
|
|
|
|
public PrincipalPermission (PermissionState state)
|
|
{
|
|
principals = new ArrayList ();
|
|
if (CodeAccessPermission.CheckPermissionState (state, true) == PermissionState.Unrestricted) {
|
|
PrincipalInfo pi = new PrincipalInfo (null, null, true);
|
|
principals.Add (pi);
|
|
}
|
|
}
|
|
|
|
public PrincipalPermission (string name, string role) : this (name, role, true)
|
|
{
|
|
}
|
|
|
|
public PrincipalPermission (string name, string role, bool isAuthenticated)
|
|
{
|
|
principals = new ArrayList ();
|
|
PrincipalInfo pi = new PrincipalInfo (name, role, isAuthenticated);
|
|
principals.Add (pi);
|
|
}
|
|
|
|
internal PrincipalPermission (ArrayList principals)
|
|
{
|
|
this.principals = (ArrayList) principals.Clone ();
|
|
}
|
|
|
|
// Properties
|
|
|
|
// Methods
|
|
|
|
public IPermission Copy ()
|
|
{
|
|
return new PrincipalPermission (principals);
|
|
}
|
|
|
|
public void Demand ()
|
|
{
|
|
IPrincipal p = Thread.CurrentPrincipal;
|
|
if (p == null)
|
|
throw new SecurityException ("no Principal");
|
|
|
|
if (principals.Count > 0) {
|
|
// check restrictions
|
|
bool demand = false;
|
|
foreach (PrincipalInfo pi in principals) {
|
|
// if a name is present then it must be equal
|
|
// if a role is present then the identity must be a member of this role
|
|
// if authentication is required then the identity must be authenticated
|
|
if (((pi.Name == null) || (pi.Name == p.Identity.Name)) &&
|
|
((pi.Role == null) || (p.IsInRole (pi.Role))) &&
|
|
((pi.IsAuthenticated && p.Identity.IsAuthenticated) || (!pi.IsAuthenticated))) {
|
|
demand = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!demand)
|
|
throw new SecurityException ("Demand for principal refused.");
|
|
}
|
|
}
|
|
|
|
public void FromXml (SecurityElement elem)
|
|
{
|
|
// 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 (elem.Children != null) {
|
|
foreach (SecurityElement se in elem.Children) {
|
|
if (se.Tag != "Identity")
|
|
throw new ArgumentException ("not IPermission/Identity");
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
public IPermission Intersect (IPermission target)
|
|
{
|
|
PrincipalPermission pp = Cast (target);
|
|
if (pp == null)
|
|
return null;
|
|
|
|
if (IsUnrestricted ())
|
|
return pp.Copy ();
|
|
if (pp.IsUnrestricted ())
|
|
return Copy ();
|
|
|
|
PrincipalPermission intersect = new PrincipalPermission (PermissionState.None);
|
|
foreach (PrincipalInfo pi in 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ((intersect.principals.Count > 0) ? intersect : null);
|
|
}
|
|
|
|
public bool IsSubsetOf (IPermission target)
|
|
{
|
|
PrincipalPermission pp = Cast (target);
|
|
if (pp == null)
|
|
return IsEmpty ();
|
|
|
|
if (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 pp.principals) {
|
|
if (((pi.Name == opi.Name) || (opi.Name == null)) &&
|
|
((pi.Role == opi.Role) || (opi.Role == null)) &&
|
|
(pi.IsAuthenticated == opi.IsAuthenticated))
|
|
thisItem = true;
|
|
}
|
|
if (!thisItem)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool IsUnrestricted ()
|
|
{
|
|
foreach (PrincipalInfo pi in principals) {
|
|
if ((pi.Name == null) && (pi.Role == null) && (pi.IsAuthenticated))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return ToXml ().ToString ();
|
|
}
|
|
|
|
public SecurityElement ToXml ()
|
|
{
|
|
SecurityElement se = new SecurityElement ("Permission");
|
|
Type type = this.GetType ();
|
|
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 ("ID", pi.Name);
|
|
if (pi.Role != null)
|
|
sec.AddAttribute ("Role", pi.Role);
|
|
if (pi.IsAuthenticated)
|
|
sec.AddAttribute ("Authenticated", "true");
|
|
se.AddChild (sec);
|
|
}
|
|
return se;
|
|
}
|
|
|
|
public IPermission Union (IPermission other)
|
|
{
|
|
PrincipalPermission pp = Cast (other);
|
|
if (pp == null)
|
|
return Copy ();
|
|
|
|
if (IsUnrestricted () || pp.IsUnrestricted ())
|
|
return new PrincipalPermission (PermissionState.Unrestricted);
|
|
|
|
PrincipalPermission union = new PrincipalPermission (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 (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;
|
|
}
|
|
}
|
|
}
|