438 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			438 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Reflection;
 | |
| using System.ComponentModel;
 | |
| using System.Runtime.Serialization;
 | |
| using Csla.Properties;
 | |
| 
 | |
| namespace Csla
 | |
| {
 | |
| 
 | |
|   /// <summary>
 | |
|   /// This is a base class from which readonly business classes
 | |
|   /// can be derived.
 | |
|   /// </summary>
 | |
|   /// <remarks>
 | |
|   /// This base class only supports data retrieve, not updating or
 | |
|   /// deleting. Any business classes derived from this base class
 | |
|   /// should only implement readonly properties.
 | |
|   /// </remarks>
 | |
|   /// <typeparam name="T">Type of the business object.</typeparam>
 | |
|   [Serializable()]
 | |
|   public abstract class ReadOnlyBase<T> : ICloneable, Core.IReadOnlyObject, Csla.Security.IAuthorizeReadWrite
 | |
|     where T : ReadOnlyBase<T>
 | |
|   {
 | |
|     #region Object ID Value
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Override this method to return a unique identifying
 | |
|     /// vlaue for this object.
 | |
|     /// </summary>
 | |
|     /// <remarks>
 | |
|     /// If you can not provide a unique identifying value, it
 | |
|     /// is best if you can generate such a unique value (even
 | |
|     /// temporarily). If you can not do that, then return 
 | |
|     /// <see langword="Nothing"/> and then manually override the
 | |
|     /// <see cref="Equals"/>, <see cref="GetHashCode"/> and
 | |
|     /// <see cref="ToString"/> methods in your business object.
 | |
|     /// </remarks>
 | |
|     protected abstract object GetIdValue();
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|     #region System.Object Overrides
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Compares this object for equality with another object, using
 | |
|     /// the results of <see cref="GetIdValue"/> to determine
 | |
|     /// equality.
 | |
|     /// </summary>
 | |
|     /// <param name="obj">The object to be compared.</param>
 | |
|     public override bool Equals(object obj)
 | |
|     {
 | |
|       if (obj is T)
 | |
|       {
 | |
|         object id = GetIdValue();
 | |
|         if (id == null)
 | |
|           throw new ArgumentException(Resources.GetIdValueCantBeNull);
 | |
|         return id.Equals(((T)obj).GetIdValue());
 | |
|       }
 | |
|       else
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Returns a hash code value for this object, based on
 | |
|     /// the results of <see cref="GetIdValue"/>.
 | |
|     /// </summary>
 | |
|     public override int GetHashCode()
 | |
|     {
 | |
|       object id = GetIdValue();
 | |
|       if (id == null)
 | |
|         throw new ArgumentException(Resources.GetIdValueCantBeNull);
 | |
|       return id.GetHashCode();
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Returns a text representation of this object by
 | |
|     /// returning the <see cref="GetIdValue"/> value
 | |
|     /// in text form.
 | |
|     /// </summary>
 | |
|     public override string ToString()
 | |
|     {
 | |
|       object id = GetIdValue();
 | |
|       if (id == null)
 | |
|         throw new ArgumentException(Resources.GetIdValueCantBeNull);
 | |
|       return id.ToString();
 | |
|     }
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|     #region Constructors
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Creates an instance of the object.
 | |
|     /// </summary>
 | |
|     protected ReadOnlyBase()
 | |
|     {
 | |
|       Initialize();
 | |
|       AddInstanceAuthorizationRules();
 | |
|       if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
 | |
|       {
 | |
|         lock (this.GetType())
 | |
|         {
 | |
|           if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
 | |
|             AddAuthorizationRules();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|     #region Initialize
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Override this method to set up event handlers so user
 | |
|     /// code in a partial class can respond to events raised by
 | |
|     /// generated code.
 | |
|     /// </summary>
 | |
|     protected virtual void Initialize()
 | |
|     { /* allows subclass to initialize events before any other activity occurs */ }
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|     #region Authorization
 | |
| 
 | |
|     [NotUndoable()]
 | |
|     [NonSerialized()]
 | |
|     private Dictionary<string, bool> _readResultCache;
 | |
|     [NotUndoable()]
 | |
|     [NonSerialized()]
 | |
|     private System.Security.Principal.IPrincipal _lastPrincipal;
 | |
| 
 | |
|     [NotUndoable()]
 | |
|     [NonSerialized()]
 | |
|     private Security.AuthorizationRules _authorizationRules;
 | |
| 
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Override this method to add authorization
 | |
|     /// rules for your object's properties.
 | |
|     /// </summary>
 | |
|     protected virtual void AddInstanceAuthorizationRules()
 | |
|     {
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Override this method to add per-type
 | |
|     /// authorization rules for your type's properties.
 | |
|     /// </summary>
 | |
|     /// <remarks>
 | |
|     /// AddSharedAuthorizationRules is automatically called by CSLA .NET
 | |
|     /// when your object should associate per-type authorization roles
 | |
|     /// with its properties.
 | |
|     /// </remarks>
 | |
|     protected virtual void AddAuthorizationRules()
 | |
|     {
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Provides access to the AuthorizationRules object for this
 | |
|     /// object.
 | |
|     /// </summary>
 | |
|     /// <remarks>
 | |
|     /// Use this object to add a list of allowed and denied roles for
 | |
|     /// reading and writing properties of the object. Typically these
 | |
|     /// values are added once when the business object is instantiated.
 | |
|     /// </remarks>
 | |
|     protected Security.AuthorizationRules AuthorizationRules
 | |
|     {
 | |
|       get
 | |
|       {
 | |
|         if (_authorizationRules == null)
 | |
|           _authorizationRules = new Security.AuthorizationRules(this.GetType());
 | |
|         return _authorizationRules;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Returns <see langword="true" /> if the user is allowed to read the
 | |
|     /// calling property.
 | |
|     /// </summary>
 | |
|     /// <returns><see langword="true" /> if read is allowed.</returns>
 | |
|     /// <param name="throwOnFalse">Indicates whether a negative
 | |
|     /// result should cause an exception.</param>
 | |
|     [System.Runtime.CompilerServices.MethodImpl(
 | |
|       System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
 | |
|     public bool CanReadProperty(bool throwOnFalse)
 | |
|     {
 | |
|       string propertyName =
 | |
|         new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name.Substring(4);
 | |
|       bool result = CanReadProperty(propertyName);
 | |
|       if (throwOnFalse && result == false)
 | |
|         throw new System.Security.SecurityException(
 | |
|           string.Format("{0} ({1})",
 | |
|           Resources.PropertyGetNotAllowed, propertyName));
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Returns <see langword="true" /> if the user is allowed to read the
 | |
|     /// calling property.
 | |
|     /// </summary>
 | |
|     /// <returns><see langword="true" /> if read is allowed.</returns>
 | |
|     /// <param name="propertyName">Name of the property to read.</param>
 | |
|     /// <param name="throwOnFalse">Indicates whether a negative
 | |
|     /// result should cause an exception.</param>
 | |
|     public bool CanReadProperty(string propertyName, bool throwOnFalse)
 | |
|     {
 | |
|       bool result = CanReadProperty(propertyName);
 | |
|       if (throwOnFalse && result == false)
 | |
|         throw new System.Security.SecurityException(
 | |
|           string.Format("{0} ({1})",
 | |
|           Resources.PropertyGetNotAllowed, propertyName));
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Returns <see langword="true" /> if the user is allowed to read the
 | |
|     /// calling property.
 | |
|     /// </summary>
 | |
|     /// <returns><see langword="true" /> if read is allowed.</returns>
 | |
|     public bool CanReadProperty()
 | |
|     {
 | |
|       string propertyName = 
 | |
|         new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name.Substring(4);
 | |
|       return CanReadProperty(propertyName);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Returns <see langword="true" /> if the user is allowed to read the
 | |
|     /// specified property.
 | |
|     /// </summary>
 | |
|     /// <param name="propertyName">Name of the property to read.</param>
 | |
|     /// <returns><see langword="true" /> if read is allowed.</returns>
 | |
|     /// <remarks>
 | |
|     /// <para>
 | |
|     /// If a list of allowed roles is provided then only users in those
 | |
|     /// roles can read. If no list of allowed roles is provided then
 | |
|     /// the list of denied roles is checked.
 | |
|     /// </para><para>
 | |
|     /// If a list of denied roles is provided then users in the denied
 | |
|     /// roles are denied read access. All other users are allowed.
 | |
|     /// </para><para>
 | |
|     /// If neither a list of allowed nor denied roles is provided then
 | |
|     /// all users will have read access.
 | |
|     /// </para>
 | |
|     /// </remarks>
 | |
|     [EditorBrowsable(EditorBrowsableState.Advanced)]
 | |
|     public virtual bool CanReadProperty(string propertyName)
 | |
|     {
 | |
|       bool result = true;
 | |
| 
 | |
|       VerifyAuthorizationCache();
 | |
| 
 | |
|       if (_readResultCache.ContainsKey(propertyName))
 | |
|       {
 | |
|         // cache contains value - get cached value
 | |
|         result = _readResultCache[propertyName];
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         if (AuthorizationRules.HasReadAllowedRoles(propertyName))
 | |
|         {
 | |
|           // some users are explicitly granted read access
 | |
|           // in which case all other users are denied.
 | |
|           if (!AuthorizationRules.IsReadAllowed(propertyName))
 | |
|             result = false;
 | |
|         }
 | |
|         else if (AuthorizationRules.HasReadDeniedRoles(propertyName))
 | |
|         {
 | |
|           // some users are explicitly denied read access.
 | |
|           if (AuthorizationRules.IsReadDenied(propertyName))
 | |
|             result = false;
 | |
|         }
 | |
|         // store value in cache
 | |
|         _readResultCache[propertyName] = result;
 | |
|       }
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
|     bool Csla.Security.IAuthorizeReadWrite.CanWriteProperty(string propertyName)
 | |
|     {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     private void VerifyAuthorizationCache()
 | |
|     {
 | |
|       if (_readResultCache == null)
 | |
|         _readResultCache = new Dictionary<string, bool>();
 | |
|       if (!ReferenceEquals(Csla.ApplicationContext.User, _lastPrincipal))
 | |
|       {
 | |
|         // the principal has changed - reset the cache
 | |
|         _readResultCache.Clear();
 | |
|         _lastPrincipal = Csla.ApplicationContext.User;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|     #region IClonable
 | |
| 
 | |
|     object ICloneable.Clone()
 | |
|     {
 | |
|       return GetClone();
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Creates a clone of the object.
 | |
|     /// </summary>
 | |
|     /// <returns>A new object containing the exact data of the original object.</returns>
 | |
|     [EditorBrowsable(EditorBrowsableState.Advanced)]
 | |
|     public virtual object GetClone()
 | |
|     {
 | |
|       return Core.ObjectCloner.Clone(this);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Creates a clone of the object.
 | |
|     /// </summary>
 | |
|     /// <returns>
 | |
|     /// A new object containing the exact data of the original object.
 | |
|     /// </returns>
 | |
|     public T Clone()
 | |
|     {
 | |
|       return (T)GetClone();
 | |
|     }
 | |
|     #endregion
 | |
| 
 | |
|     #region Data Access
 | |
| 
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "criteria")]
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
 | |
|     private void DataPortal_Create(object criteria)
 | |
|     {
 | |
|       throw new NotSupportedException(Resources.CreateNotSupportedException);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Override this method to allow retrieval of an existing business
 | |
|     /// object based on data in the database.
 | |
|     /// </summary>
 | |
|     /// <param name="criteria">An object containing criteria values to identify the object.</param>
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
 | |
|     protected virtual void DataPortal_Fetch(object criteria)
 | |
|     {
 | |
|       throw new NotSupportedException(Resources.FetchNotSupportedException);
 | |
|     }
 | |
| 
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
 | |
|     private void DataPortal_Update()
 | |
|     {
 | |
|       throw new NotSupportedException(Resources.UpdateNotSupportedException);
 | |
|     }
 | |
| 
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "criteria")]
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
 | |
|     private void DataPortal_Delete(object criteria)
 | |
|     {
 | |
|       throw new NotSupportedException(Resources.DeleteNotSupportedException);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Called by the server-side DataPortal prior to calling the 
 | |
|     /// requested DataPortal_xyz method.
 | |
|     /// </summary>
 | |
|     /// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
 | |
|     [EditorBrowsable(EditorBrowsableState.Advanced)]
 | |
|     protected virtual void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
 | |
|     {
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Called by the server-side DataPortal after calling the 
 | |
|     /// requested DataPortal_xyz method.
 | |
|     /// </summary>
 | |
|     /// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
 | |
|     [EditorBrowsable(EditorBrowsableState.Advanced)]
 | |
|     protected virtual void DataPortal_OnDataPortalInvokeComplete(DataPortalEventArgs e)
 | |
|     {
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Called by the server-side DataPortal if an exception
 | |
|     /// occurs during data access.
 | |
|     /// </summary>
 | |
|     /// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
 | |
|     /// <param name="ex">The Exception thrown during data access.</param>
 | |
|     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
 | |
|     [EditorBrowsable(EditorBrowsableState.Advanced)]
 | |
|     protected virtual void DataPortal_OnDataPortalException(DataPortalEventArgs e, Exception ex)
 | |
|     {
 | |
| 
 | |
|     }
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|     #region Serialization Notification
 | |
| 
 | |
|     [OnDeserialized()]
 | |
|     private void OnDeserializedHandler(StreamingContext context)
 | |
|     {
 | |
|       OnDeserialized(context);
 | |
|       AddInstanceAuthorizationRules();
 | |
|       if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
 | |
|       {
 | |
|         lock (this.GetType())
 | |
|         {
 | |
|           if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
 | |
|             AddAuthorizationRules();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// This method is called on a newly deserialized object
 | |
|     /// after deserialization is complete.
 | |
|     /// </summary>
 | |
|     /// <param name="context">Serialization context object.</param>
 | |
|     [EditorBrowsable(EditorBrowsableState.Advanced)]
 | |
|     protected virtual void OnDeserialized(StreamingContext context)
 | |
|     {
 | |
|       // do nothing - this is here so a subclass
 | |
|       // could override if needed
 | |
|     }
 | |
| 
 | |
|     #endregion
 | |
| 
 | |
|   }
 | |
| } |