/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Globalization; using System.Management.Automation.Internal; using System.Diagnostics.CodeAnalysis; using System.Management.Automation.Language; using System.Runtime.CompilerServices; namespace System.Management.Automation.Internal { /// /// Serves as the base class for Metadata attributes. /// /// /// PSSnapins may not create custom attributes derived directly from /// , /// since it has no public constructor. Only the public subclasses /// and /// /// are available. /// /// /// [AttributeUsage(AttributeTargets.All)] public abstract class CmdletMetadataAttribute : Attribute { /// /// Default constructor /// internal CmdletMetadataAttribute() { } } /// /// Serves as the base class for Metadata attributes that serve as guidance to the parser and parameter binder. /// /// /// PSSnapins may not create custom attributes derived /// , /// since it has no public constructor. Only the sealed public subclasses /// and /// /// are available. /// /// /// [AttributeUsage(AttributeTargets.All)] public abstract class ParsingBaseAttribute : CmdletMetadataAttribute { /// /// Constructor with no parameters /// internal ParsingBaseAttribute() { } } } namespace System.Management.Automation { #region Base Metadata Classes /// /// Serves as the base class for Validate attributes that validate parameter arguments. /// /// /// Argument validation attributes can be attached to /// and /// parameters to ensure that the Cmdlet or CmdletProvider will not /// be invoked with invalid values of the parameter. Existing /// validation attributes include , /// , /// , /// , /// , /// , /// , and /// . /// /// PSSnapins wishing to create custom argument validation attributes /// should derive from /// /// and override the /// /// abstract method, after which they can apply the /// attribute to their parameters. /// /// validates the argument /// as a whole. If the argument value is potentially an enumeration, /// you can derive from /// which will take care of unrolling the enumeration /// and validate each element individually. /// /// It is also recommended to override /// to return a readable string /// similar to the attribute declaration, for example /// "[ValidateRangeAttribute(5,10)]". /// /// If this attribute is applied to a string parameter, the string command argument will be validated. /// If this attribute is applied to a string[] parameter, the string[] command argument will be validated. /// /// /// /// /// /// /// /// /// /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public abstract class ValidateArgumentsAttribute : CmdletMetadataAttribute { /// /// Overridden by subclasses to implement the validation of the parameter arguments /// /// argument value to validate /// /// The engine APIs for the context under which the prerequisite is being /// evaluated. /// /// /// Validate that the value of is valid, /// and throw /// if it is invalid. /// /// should be thrown for any validation failure protected abstract void Validate(object arguments, EngineIntrinsics engineIntrinsics); /// /// Method that the command processor calls for data validate processing /// /// object to validate /// /// The engine APIs for the context under which the prerequisite is being /// evaluated. /// /// /// bool true if the validate succeeded /// /// Whenever any exception occurs during data validate. /// All the system exceptions are wrapped in ValidationMetadataException /// /// for invalid arguments internal void InternalValidate(object o, EngineIntrinsics engineIntrinsics) { Validate(o, engineIntrinsics); } /// /// Initializes a new instance of a class derived from ValidateArgumentsAttribute /// protected ValidateArgumentsAttribute() { } } /// /// A variant of which /// unrolls enumeration values and validates each element /// individually. /// /// /// is like /// , except that if /// the argument value is an enumeration, /// will unroll /// the enumeration and validate each item individually. /// /// Existing enumerated validation attributes include /// , /// , /// , and /// . /// /// PSSnapins wishing to create custom enumerated argument validation attributes /// should derive from /// /// and override the /// /// abstract method, after which they can apply the /// attribute to their parameters. /// /// It is also recommended to override /// to return a readable string /// similar to the attribute declaration, for example /// "[ValidateRangeAttribute(5,10)]". /// /// If this attribute is applied to a string parameter, the string command argument will be validated. /// If this attribute is applied to a string[] parameter, each string command argument will be validated. /// /// /// /// /// /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public abstract class ValidateEnumeratedArgumentsAttribute : ValidateArgumentsAttribute { /// /// Initializes a new instance of a class derived from ValidateEnumeratedArgumentsAttribute /// protected ValidateEnumeratedArgumentsAttribute() : base() { } /// /// Overridden by subclasses to implement the validation of each parameter argument /// /// /// Validate that the value of /// is valid, and throw /// /// if it is invalid. /// /// one of the parameter arguments /// should be thrown for any validation failure protected abstract void ValidateElement(object element); /// /// Calls ValidateElement in each element in the enumeration argument value. /// /// object to validate /// /// The engine APIs for the context under which the prerequisite is being /// evaluated. /// /// /// PSSnapins should override instead. /// /// should be thrown for any validation failure protected sealed override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { if (arguments == null || arguments == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullOrEmptyCollectionFailure); } var enumerator = _getEnumeratorSite.Target.Invoke(_getEnumeratorSite, arguments); if (enumerator == null) { ValidateElement(arguments); return; } // arguments is IEnumerator while (enumerator.MoveNext()) { ValidateElement(enumerator.Current); } enumerator.Reset(); } private readonly CallSite> _getEnumeratorSite = CallSite>.Create(PSEnumerableBinder.Get()); } #endregion Base Metadata Classes #region Misc Attributes /// /// To specify RunAs behavior for the class /// /// public enum DSCResourceRunAsCredential { /// Default is same as optional. Default, /// /// PsDscRunAsCredential can not be used for this DSC Resource /// NotSupported, /// /// PsDscRunAsCredential is mandatory for resource /// Mandatory, /// /// PsDscRunAsCredential can or can not be specified /// Optional = Default, } /// /// Indicates the class defines a DSC resource. /// [AttributeUsage(AttributeTargets.Class)] public class DscResourceAttribute : CmdletMetadataAttribute { /// /// To specify RunAs Behavior for the resource. /// public DSCResourceRunAsCredential RunAsCredential { get; set; } } /// /// When specified on a property or field of a DSC Resource, the property /// can or must be specified in a configuration, unless it is marked /// , in which case it is /// returned by the Get() method of the resource. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class DscPropertyAttribute : CmdletMetadataAttribute { /// /// Indicates the property is a required key property for a DSC resource. /// public bool Key { get; set; } /// /// Indicates the property is a required property for a DSC resource. /// public bool Mandatory { get; set; } /// /// Indicates the property is not a parameter to the DSC resource, but the /// property will contain a value after the Get() method of the resource is called. /// public bool NotConfigurable { get; set; } } /// /// Indication the configuration is for local configuration manager, also known as meta configuration. /// [AttributeUsage(AttributeTargets.Class)] public class DscLocalConfigurationManagerAttribute : CmdletMetadataAttribute { } /// /// Contains information about a cmdlet's metadata. /// [AttributeUsage(AttributeTargets.Class)] public abstract class CmdletCommonMetadataAttribute : CmdletMetadataAttribute { /// /// Gets and sets the cmdlet default parameter set /// public string DefaultParameterSetName { get; set; } /// /// Gets and sets a Boolean value that indicates the Cmdlet supports ShouldProcess. By default /// the value is false, meaning the cmdlet doesn't support ShouldProcess. /// public bool SupportsShouldProcess { get; set; } = false; /// /// Gets and sets a Boolean value that indicates the Cmdlet supports Paging. By default /// the value is false, meaning the cmdlet doesn't support Paging. /// public bool SupportsPaging { get; set; } = false; /// /// Gets and sets a Boolean value that indicates the Cmdlet supports Transactions. By default /// the value is false, meaning the cmdlet doesn't support Transactions. /// public bool SupportsTransactions { get { return _supportsTransactions; } set { #if !CORECLR _supportsTransactions = value; #else // Disable 'SupportsTransactions' in CoreCLR // No transaction supported on CSS due to the lack of System.Transactions namespace _supportsTransactions = false; #endif } } private bool _supportsTransactions = false; /// /// Gets and sets a ConfirmImpact value that indicates /// the "destructiveness" of the operation and when it /// should be confirmed. This should only be used when /// SupportsShouldProcess is specified. /// public ConfirmImpact ConfirmImpact { get; set; } = ConfirmImpact.Medium; /// /// Gets and sets a HelpUri value that indicates /// the location of online help. This is used by /// Get-Help to retrieve help content when -Online /// is specified. /// [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] public string HelpUri { get; set; } = String.Empty; /// /// Gets and sets the RemotingBehavior value that declares how this cmdlet should interact /// with ambient remoting. /// public RemotingCapability RemotingCapability { get; set; } = RemotingCapability.PowerShell; } /// /// Identifies a class as a cmdlet and specifies the verb and noun identifying this cmdlet. /// [AttributeUsage(AttributeTargets.Class)] public sealed class CmdletAttribute : CmdletCommonMetadataAttribute { /// /// Gets the cmdlet noun /// public string NounName { get; } /// /// Gets the cmdlet verb /// public string VerbName { get; } /// /// Initializes a new instance of the CmdletAttribute class /// /// verb for the command /// noun for the command /// for invalid arguments public CmdletAttribute(string verbName, string nounName) { //NounName,VerbName have to be Non-Null strings if (string.IsNullOrEmpty(nounName)) { throw PSTraceSource.NewArgumentException("nounName"); } if (string.IsNullOrEmpty(verbName)) { throw PSTraceSource.NewArgumentException("verbName"); } NounName = nounName; VerbName = verbName; } } /// /// Identifies PowerShell script code as behaving like a cmdlet and hence uses /// cmdlet parameter binding instead of script parameter binding. /// [AttributeUsage(AttributeTargets.Class)] public class CmdletBindingAttribute : CmdletCommonMetadataAttribute { /// /// When true, the script will auto-generate appropriate parameter metadata to support positional /// parameters if the script hasn't already specified multiple parameter sets or specified positions /// explicitly via the . /// public bool PositionalBinding { get; set; } = true; } /// /// OutputTypeAttribute is used to specify the type of objects output by a cmdlet /// or script. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] public sealed class OutputTypeAttribute : CmdletMetadataAttribute { /// /// Construct the attribute from a System.Type /// internal OutputTypeAttribute(Type type) { Type = new[] { new PSTypeName(type) }; } /// /// Construct the attribute from a type name. /// internal OutputTypeAttribute(string typeName) { Type = new[] { new PSTypeName(typeName) }; } /// /// Construct the attribute from an array of System.Type /// /// The types output by the cmdlet public OutputTypeAttribute(params Type[] type) { if (type != null && type.Length > 0) { Type = new PSTypeName[type.Length]; for (int i = 0; i < type.Length; i++) { Type[i] = new PSTypeName(type[i]); } } else { Type = Utils.EmptyArray(); } } /// /// Construct the attribute from an array of names of types. /// /// The types output by the cmdlet public OutputTypeAttribute(params string[] type) { if (type != null && type.Length > 0) { Type = new PSTypeName[type.Length]; for (int i = 0; i < type.Length; i++) { Type[i] = new PSTypeName(type[i]); } } else { Type = Utils.EmptyArray(); } } /// /// The types specified by the attribute. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] public PSTypeName[] Type { get; private set; } /// /// Attributes implemented by a provider can use: /// /// [OutputType(ProviderCmdlet='cmdlet', typeof(...))] /// /// To specify the provider specific objects returned for a given cmdlet. /// public string ProviderCmdlet { get; set; } /// /// The list of parameter sets this OutputType specifies. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] ParameterSetName { get { return _parameterSetName ?? (_parameterSetName = new[] { ParameterAttribute.AllParameterSets }); } set { _parameterSetName = value; } } private string[] _parameterSetName; } /// /// This attribute is used on a dynamic assembly to mark it as one that is used to implement /// a set of classes defined in a PowerShell script. /// [AttributeUsage(AttributeTargets.Assembly)] public class DynamicClassImplementationAssemblyAttribute : Attribute { } #endregion Misc Attributes #region Parsing guidelines Attributes /// /// Declares an alternative name for a parameter /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] public sealed class AliasAttribute : ParsingBaseAttribute { internal string[] aliasNames; /// /// Gets the alias names passed to the constructor /// public IList AliasNames { get { return this.aliasNames; } } /// /// Initiailizes a new instance of the AliasAttribute class /// /// The name for this alias /// for invalid arguments public AliasAttribute(params string[] aliasNames) { if (aliasNames == null) { throw PSTraceSource.NewArgumentNullException("aliasNames"); } this.aliasNames = aliasNames; } } /// /// Identifies parameters to Cmdlets /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public sealed class ParameterAttribute : ParsingBaseAttribute { /// /// ParameterSetName refering to all ParameterSets /// public const string AllParameterSets = "__AllParameterSets"; /// /// Initializes a new instance of the ParameterAttribute class /// public ParameterAttribute() { } private string _parameterSetName = ParameterAttribute.AllParameterSets; private string _helpMessage; private string _helpMessageBaseName; private string _helpMessageResourceId; /// /// Gets and sets the parameter position. If not set, the parameter is named. /// public int Position { get; set; } = int.MinValue; /// /// Gets and sets the name of the parameter set this parameter belongs to. When /// it is not specified ParameterAttribute.AllParameterSets is assumed. /// public string ParameterSetName { get { return _parameterSetName; } set { _parameterSetName = value; if (String.IsNullOrEmpty(_parameterSetName)) { _parameterSetName = ParameterAttribute.AllParameterSets; } } } /// /// Gets and sets a flag specifying if this parameter is Mandatory. When /// it is not specified, false is assumed and the parameter is considered optional. /// public bool Mandatory { get; set; } = false; /// /// Gets and sets a flag that specifies that this parameter can take values /// from the incoming pipeline object. When it is not specified, false is assumed. /// public bool ValueFromPipeline { get; set; } /// /// Gets and sets a flag that specifies that this parameter can take values from a property /// in the incoming pipeline object with the same name as the parameter. When it /// is not specified, false is assumed. /// public bool ValueFromPipelineByPropertyName { get; set; } /// /// Gets and sets a flag that specifies that the remaining command line parameters /// should be associated with this parameter in the form of an array. When it /// is not specified, false is assumed. /// public bool ValueFromRemainingArguments { get; set; } = false; /// /// Gets and sets a short description for this parameter, suitable for presentation as a tool tip. /// /// for a null or empty value when setting public string HelpMessage { get { return _helpMessage; } set { if (string.IsNullOrEmpty(value)) { throw PSTraceSource.NewArgumentException("value"); } _helpMessage = value; } } /// /// Gets and sets the base name of the resource for a help message. When this field is speicifed, /// HelpMessageResourceId must also be specified. /// /// for a null or empty value when setting public string HelpMessageBaseName { get { return _helpMessageBaseName; } set { if (string.IsNullOrEmpty(value)) { throw PSTraceSource.NewArgumentException("value"); } _helpMessageBaseName = value; } } /// /// Gets and sets the Id of the resource for a help message. When this field is speicifed, /// HelpMessageBaseName must also be specified. /// /// for a null or empty value when setting public string HelpMessageResourceId { get { return _helpMessageResourceId; } set { if (string.IsNullOrEmpty(value)) { throw PSTraceSource.NewArgumentException("value"); } _helpMessageResourceId = value; } } /// /// Indicates that this parameter should not be shown to the user in this like intellisense /// This is primarily to be used in functions that are implementing the logic for dynamic keywords. /// public bool DontShow { get; set; } } /// /// Specifies PSTypeName of a cmdlet or function parameter. /// /// /// This attribute is used to restrict the type name of the parameter, when the type goes beyond the .NET type system. /// For example one could say: [PSTypeName("System.Management.ManagementObject#root\cimv2\Win32_Process")] /// to only allow Win32_Process objects to be bound to the parameter. /// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] public class PSTypeNameAttribute : Attribute { /// /// /// public string PSTypeName { get; private set; } /// /// Creates a new PSTypeNameAttribute /// /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public PSTypeNameAttribute(string psTypeName) { if (string.IsNullOrEmpty(psTypeName)) { throw PSTraceSource.NewArgumentException("psTypeName"); } this.PSTypeName = psTypeName; } } /// /// Specifies that a parameter supports wildcards. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class SupportsWildcardsAttribute : ParsingBaseAttribute { } /// /// Specify a default value and/or help comment for a command parameter. This attribute /// does not have any semantic meaning, it is simply an aid to tools to make it simpler /// to know the true default value of a command parameter (which may or may not have /// any correlation with, e.g., the backing store of the Parameter's property or field. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class PSDefaultValueAttribute : ParsingBaseAttribute { /// /// Specify the default value of a command parameter. The PowerShell engine does not /// use this value in any way, it exists for other tools that want to reflect on cmdlets. /// public object Value { get; set; } /// /// Specify the help string for the default value of a command parameter. /// public string Help { get; set; } } /// /// Specify that the member is hidden for the purposes of cmdlets like Get-Member and /// that the member is not displayed by default by Format-* cmdlets. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Event)] public sealed class HiddenAttribute : ParsingBaseAttribute { } #endregion Parsing guidelines Attributes #region Data validate Attributes /// /// Validates that the length of each parameter argument's Length falls in the range /// specified by MinLength and MaxLength /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateLengthAttribute : ValidateEnumeratedArgumentsAttribute { /// /// Gets the attribute's minimum length /// public int MinLength { get; } /// /// Gets the attribute's maximum length /// public int MaxLength { get; } /// /// Validates that the length of each parameter argument's Length falls in the range /// specified by MinLength and MaxLength /// /// object to validate /// if is not a string /// with length between minLength and maxLength /// for invalid arguments protected override void ValidateElement(object element) { string objectString = element as string; if (objectString == null) { throw new ValidationMetadataException("ValidateLengthNotString", null, Metadata.ValidateLengthNotString); } int len = objectString.Length; if (len < MinLength) { throw new ValidationMetadataException("ValidateLengthMinLengthFailure", null, Metadata.ValidateLengthMinLengthFailure, MinLength, len); } if (len > MaxLength) { throw new ValidationMetadataException("ValidateLengthMaxLengthFailure", null, Metadata.ValidateLengthMaxLengthFailure, MaxLength, len); } } /// /// Initializes a new instance of the ValidateLengthAttribute class /// /// Minimum required length /// Maximum required length /// for invalid arguments /// if maxLength is less than minLength public ValidateLengthAttribute(int minLength, int maxLength) : base() { if (minLength < 0) { throw PSTraceSource.NewArgumentOutOfRangeException("minLength", minLength); } if (maxLength <= 0) { throw PSTraceSource.NewArgumentOutOfRangeException("maxLength", maxLength); } if (maxLength < minLength) { throw new ValidationMetadataException("ValidateLengthMaxLengthSmallerThanMinLength", null, Metadata.ValidateLengthMaxLengthSmallerThanMinLength); } MinLength = minLength; MaxLength = maxLength; } } /// /// Validates that each parameter argument falls in the range /// specified by MinRange and MaxRange /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateRangeAttribute : ValidateEnumeratedArgumentsAttribute { /// /// Gets the attribute's minimum range /// public object MinRange { get; } private IComparable _minComparable; /// /// Gets the attribute's maximum range /// public object MaxRange { get; } private IComparable _maxComparable; /// /// The range values and the value to validate will all be converted to the promoted type. /// If minRange and maxRange are the same type, /// private Type _promotedType; /// /// Validates that each parameter argument falls in the range /// specified by MinRange and MaxRange /// /// object to validate /// /// Thrown if the object to be validated does not implement IComparable, /// if the element type is not the same of MinRange, MaxRange, /// or if the element is not between MinRange and MaxRange. /// protected override void ValidateElement(object element) { if (element == null) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullFailure); } var o = element as PSObject; if (o != null) { element = o.BaseObject; } // minRange and maxRange have the same type, so we just need // to compare to one of them if (element.GetType() != _promotedType) { object resultValue; if (LanguagePrimitives.TryConvertTo(element, _promotedType, out resultValue)) { element = resultValue; } else { throw new ValidationMetadataException("ValidationRangeElementType", null, Metadata.ValidateRangeElementType, element.GetType().Name, MinRange.GetType().Name); } } // They are the same type and are all IComparable, so this should not throw if (_minComparable.CompareTo(element) > 0) { throw new ValidationMetadataException("ValidateRangeTooSmall", null, Metadata.ValidateRangeSmallerThanMinRangeFailure, element.ToString(), MinRange.ToString()); } if (_maxComparable.CompareTo(element) < 0) { throw new ValidationMetadataException("ValidateRangeTooBig", null, Metadata.ValidateRangeGreaterThanMaxRangeFailure, element.ToString(), MaxRange.ToString()); } } /// /// Initializes a new instance of the ValidateRangeAttribute class /// /// Minimum value of the range allowed. /// Maximum value of the range allowed. /// for invalid arguments /// /// if maxRange has a differnet type than minRange /// if maxRange is smaller than minRange /// if maxRange, minRange are not IComparable /// public ValidateRangeAttribute(object minRange, object maxRange) : base() { if (minRange == null) { throw PSTraceSource.NewArgumentNullException("minRange"); } if (maxRange == null) { throw PSTraceSource.NewArgumentNullException("maxRange"); } if (maxRange.GetType() != minRange.GetType()) { bool failure = true; _promotedType = GetCommonType(minRange.GetType(), maxRange.GetType()); if (_promotedType != null) { object resultValue; if (LanguagePrimitives.TryConvertTo(minRange, _promotedType, out resultValue)) { minRange = resultValue; if (LanguagePrimitives.TryConvertTo(maxRange, _promotedType, out resultValue)) { maxRange = resultValue; failure = false; } } } if (failure) { throw new ValidationMetadataException("MinRangeNotTheSameTypeOfMaxRange", null, Metadata.ValidateRangeMinRangeMaxRangeType, minRange.GetType().Name, maxRange.GetType().Name); } } else { _promotedType = minRange.GetType(); } _minComparable = minRange as IComparable; // minRange and maxRange have the same type, so we just need // to check one of them if (_minComparable == null) { throw new ValidationMetadataException("MinRangeNotIComparable", null, Metadata.ValidateRangeNotIComparable); } _maxComparable = maxRange as IComparable; Diagnostics.Assert(_maxComparable != null, "maxComparable comes from a type that is IComparable"); // Thanks to the IComparable if above this will not throw. They have the same type // and are IComparable if (_minComparable.CompareTo(maxRange) > 0) { throw new ValidationMetadataException("MaxRangeSmallerThanMinRange", null, Metadata.ValidateRangeMaxRangeSmallerThanMinRange); } MinRange = minRange; MaxRange = maxRange; } private static Type GetCommonType(Type minType, Type maxType) { Type resultType = null; TypeCode minTypeCode = LanguagePrimitives.GetTypeCode(minType); TypeCode maxTypeCode = LanguagePrimitives.GetTypeCode(maxType); TypeCode opTypeCode = (int)minTypeCode >= (int)maxTypeCode ? minTypeCode : maxTypeCode; if ((int)opTypeCode <= (int)TypeCode.Int32) { resultType = typeof(int); } else if ((int)opTypeCode <= (int)TypeCode.UInt32) { // If one of the operands is signed, we need to promote to double if the value is negative. We aren't // checking the value, so we unconditionally promote to double. resultType = LanguagePrimitives.IsSignedInteger(minTypeCode) || LanguagePrimitives.IsSignedInteger(maxTypeCode) ? typeof(double) : typeof(uint); } else if ((int)opTypeCode <= (int)TypeCode.Int64) { resultType = typeof(long); } else if ((int)opTypeCode <= (int)TypeCode.UInt64) { // If one of the operands is signed, we need to promote to double if the value is negative. We aren't // checking the value, so we unconditionally promote to double. resultType = LanguagePrimitives.IsSignedInteger(minTypeCode) || LanguagePrimitives.IsSignedInteger(maxTypeCode) ? typeof(double) : typeof(ulong); } else if (opTypeCode == TypeCode.Decimal) { resultType = typeof(decimal); } else if (opTypeCode == TypeCode.Single || opTypeCode == TypeCode.Double) { resultType = typeof(double); } return resultType; } } /// /// Validates that each parameter argument matches the RegexPattern /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidatePatternAttribute : ValidateEnumeratedArgumentsAttribute { /// /// Gets the Regex pattern to be used in the validation /// public string RegexPattern { get; } /// /// Gets or sets the Regex options to be used in the validation /// public RegexOptions Options { set; get; } = RegexOptions.IgnoreCase; /// /// Validates that each parameter argument matches the RegexPattern /// /// object to validate /// if is not a string /// that matches the pattern /// and for invalid arguments protected override void ValidateElement(object element) { if (element == null) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullFailure); } string objectString = element.ToString(); Regex regex = null; regex = new Regex(RegexPattern, Options); Match match = regex.Match(objectString); if (!match.Success) { throw new ValidationMetadataException("ValidatePatternFailure", null, Metadata.ValidatePatternFailure, objectString, RegexPattern); } } /// /// Initializes a new instance of the ValidatePatternAttribute class /// /// Pattern string to match /// for invalid arguments public ValidatePatternAttribute(string regexPattern) { if (String.IsNullOrEmpty(regexPattern)) { throw PSTraceSource.NewArgumentException("regexPattern"); } RegexPattern = regexPattern; } } /// /// Class for validating against a script block. /// public sealed class ValidateScriptAttribute : ValidateEnumeratedArgumentsAttribute { /// /// Gets the scriptblock to be used in the validation /// public ScriptBlock ScriptBlock { get; } /// /// Validates that each parameter argument matches the scriptblock /// /// object to validate /// if is invalid protected override void ValidateElement(object element) { if (element == null) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullFailure); } object result = ScriptBlock.DoInvokeReturnAsIs( useLocalScope: true, errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe, dollarUnder: LanguagePrimitives.AsPSObjectOrNull(element), input: AutomationNull.Value, scriptThis: AutomationNull.Value, args: Utils.EmptyArray()); if (!LanguagePrimitives.IsTrue(result)) { throw new ValidationMetadataException("ValidateScriptFailure", null, Metadata.ValidateScriptFailure, element, ScriptBlock); } } /// /// Initializes a new instance of the ValidateScriptBlockAttribute class /// /// Scriptblock to match /// for invalid arguments public ValidateScriptAttribute(ScriptBlock scriptBlock) { if (scriptBlock == null) { throw PSTraceSource.NewArgumentException("scriptBlock"); } ScriptBlock = scriptBlock; } } /// /// Validates that the parameter argument count is in the specified range /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateCountAttribute : ValidateArgumentsAttribute { /// /// Gets the minimum length of this attribute /// public int MinLength { get; } /// /// Gets the maximum length of this attribute /// public int MaxLength { get; } /// /// Validates that the parameter argument count is in the specified range /// /// Object to validate /// /// The engine APIs for the context under which the validation is being /// evaluated. /// /// /// if the element is none of ICollection, IEnumerable, IList, IEnumerator /// if the element's lenght is not between MinLength and MAxLEngth /// protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { UInt32 len = 0; IList il; ICollection ic; IEnumerable ie; IEnumerator ienumerator; if (arguments == null || arguments == AutomationNull.Value) { // treat a nul list the same as an empty list // with a count of zero. len = 0; } else if ((il = arguments as IList) != null) { len = (UInt32)il.Count; } else if ((ic = arguments as ICollection) != null) { len = (UInt32)ic.Count; } else if ((ie = arguments as IEnumerable) != null) { IEnumerator e = ie.GetEnumerator(); for (; e.MoveNext() == true; len++) ; } else if ((ienumerator = arguments as IEnumerator) != null) { for (; ienumerator.MoveNext() == true; len++) ; } else { // No conversion succeeded so throw and exception... throw new ValidationMetadataException("NotAnArrayParameter", null, Metadata.ValidateCountNotInArray); } if (len < MinLength) { throw new ValidationMetadataException("ValidateCountSmallerThanMin", null, Metadata.ValidateCountMinLengthFailure, MinLength, len); } if (len > MaxLength) { throw new ValidationMetadataException("ValidateCountGreaterThanMax", null, Metadata.ValidateCountMaxLengthFailure, MaxLength, len); } } /// /// Initializes a new instance of the ValidateCountAttribute class /// /// Minimum number of values required /// Maximum number of values required /// for invalid arguments /// /// if minLength is greater than maxLength /// public ValidateCountAttribute(int minLength, int maxLength) { if (minLength < 0) { throw PSTraceSource.NewArgumentOutOfRangeException("minLength", minLength); } if (maxLength <= 0) { throw PSTraceSource.NewArgumentOutOfRangeException("maxLength", maxLength); } if (maxLength < minLength) { throw new ValidationMetadataException("ValidateRangeMaxLengthSmallerThanMinLength", null, Metadata.ValidateCountMaxLengthSmallerThanMinLength); } MinLength = minLength; MaxLength = maxLength; } } /// /// Validates that each parameter argument is present in a specified set /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateSetAttribute : ValidateEnumeratedArgumentsAttribute { private string[] _validValues; /// /// Gets a flag specifying if we should ignore the case when performing string comparison. The /// default is true. /// public bool IgnoreCase { get; set; } = true; /// /// Gets the values in the set /// public IList ValidValues { get { return _validValues; } } /// /// Validates that each parameter argument is present in the specified set /// /// Object to validate /// /// if element is not in the set /// for invalid argument /// protected override void ValidateElement(object element) { if (element == null) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullFailure); } string objString = element.ToString(); for (int setIndex = 0; setIndex < _validValues.Length; setIndex++) { string setString = _validValues[setIndex]; if (CultureInfo.InvariantCulture. CompareInfo.Compare(setString, objString, IgnoreCase ? CompareOptions.IgnoreCase : CompareOptions.None) == 0) { return; } } throw new ValidationMetadataException("ValidateSetFailure", null, Metadata.ValidateSetFailure, element.ToString(), SetAsString()); } private string SetAsString() { return string.Join(CultureInfo.CurrentUICulture.TextInfo.ListSeparator, _validValues); } /// /// Initializes a new instance of the ValidateSetAttribute class /// /// list of valid values /// for null arguments /// for invalid arguments public ValidateSetAttribute(params string[] validValues) { if (validValues == null) { throw PSTraceSource.NewArgumentNullException("validValues"); } if (validValues.Length == 0) { throw PSTraceSource.NewArgumentOutOfRangeException("validValues", validValues); } _validValues = validValues; } } #region Allow /// /// Allows a NULL as the argument to a mandatory parameter /// [AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)] public sealed class AllowNullAttribute : CmdletMetadataAttribute { /// /// Initializes a new instance of the AllowNullAttribute class /// public AllowNullAttribute() { } } /// /// Allows an empty string as the argument to a mandatory string parameter /// [AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)] public sealed class AllowEmptyStringAttribute : CmdletMetadataAttribute { /// /// Initializes a new instance of the AllowEmptyStringAttribute class /// public AllowEmptyStringAttribute() { } } /// /// Allows an empty collection as the argument to a mandatory collection parameter /// [AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)] public sealed class AllowEmptyCollectionAttribute : CmdletMetadataAttribute { /// /// Initializes a new instance of the AllowEmptyCollectionAttribute class /// public AllowEmptyCollectionAttribute() { } } #endregion Allow #region Path validation attributes /// /// Validates that the path has an approved root drive /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class ValidateDriveAttribute : ValidateArgumentsAttribute { private string[] _validRootDrives; /// /// Initializes a new instance of the ValidateDrivePath class /// /// List of approved root drives for path public ValidateDriveAttribute(params string[] validRootDrives) { if (validRootDrives == null) { throw PSTraceSource.NewArgumentException("validRootDrives"); } _validRootDrives = validRootDrives; } /// /// Validates path argument /// /// Object to validate /// Engine intrinsics protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { if (arguments == null) { throw new ValidationMetadataException( "PathArgumentIsEmpty", null, Metadata.ValidateNotNullFailure); } var path = arguments as string; if (path == null) { throw new ValidationMetadataException( "PathArgumentIsNotValid", null, Metadata.ValidateDrivePathArgNotString); } ProviderInfo providerInfo; PSDriveInfo driveInfo; var resolvedPath = engineIntrinsics.SessionState.Internal.Globber.GetProviderPath( path: path, context: new CmdletProviderContext(engineIntrinsics.SessionState.Internal.ExecutionContext), isTrusted: true, provider: out providerInfo, drive: out driveInfo); string rootDrive = driveInfo.Name; if (string.IsNullOrEmpty(rootDrive)) { throw new ValidationMetadataException( "PathArgumentNoRoot", null, Metadata.ValidateDrivePathNoRoot); } bool rootFound = false; foreach (var validDrive in _validRootDrives) { if (rootDrive.Equals(validDrive, StringComparison.OrdinalIgnoreCase)) { rootFound = true; break; } } if (!rootFound) { throw new ValidationMetadataException( "PathRootInvalid", null, Metadata.ValidateDrivePathFailure, rootDrive, ValidDriveListAsString()); } } private string ValidDriveListAsString() { return string.Join(CultureInfo.CurrentUICulture.TextInfo.ListSeparator, _validRootDrives); } } /// /// Validates that the path parameter is a User drive /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateUserDriveAttribute : ValidateDriveAttribute { /// /// Initializes a new instance of the ValidateUserDrivePathAttribute class /// public ValidateUserDriveAttribute() : base(new string[] { "User" }) { } } #endregion #region NULL validation attributes /// /// Validates that the parameters's argument is not null /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateNotNullAttribute : ValidateArgumentsAttribute { /// /// Verifies the argument is not null and if it is a collection, that each /// element in the collection is not null. /// If the argument is a collection then each element is validated. /// /// /// The arguments to verify. /// /// /// The engine APIs for the context under which the validation is being /// evaluated. /// /// /// true if the argument is valid. /// /// /// if element is null or a collection with a null element /// protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { IEnumerable ienum = null; IEnumerator itor = null; if (arguments == null || arguments == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsNull", null, Metadata.ValidateNotNullFailure); } else if ((ienum = arguments as IEnumerable) != null) { foreach (object element in ienum) { if (element == null || element == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsNull", null, Metadata.ValidateNotNullCollectionFailure); } } } else if ((itor = arguments as IEnumerator) != null) { for (; itor.MoveNext() == true;) { if (itor.Current == null || itor.Current == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsNull", null, Metadata.ValidateNotNullCollectionFailure); } } } } } /// /// Validates that the parameters's argument is not null, is not /// an empty string, and is not an empty collection. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValidateNotNullOrEmptyAttribute : ValidateArgumentsAttribute { /// /// Validates that the parameters's argument is not null, is not /// an empty string, and is not an empty collection. If arguments /// is a collection, each argument is verified. /// /// /// The arguments to verify. /// /// /// The engine APIs for the context under which the validation is being /// evaluated. /// /// /// if the arguments are not valid /// protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { IEnumerable ienum = null; IEnumerator itor = null; string str = null; if (arguments == null || arguments == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsNull", null, Metadata.ValidateNotNullOrEmptyFailure); } else if ((str = arguments as String) != null) { if (String.IsNullOrEmpty(str)) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullOrEmptyFailure); } } else if ((ienum = arguments as IEnumerable) != null) { int validElements = 0; foreach (object element in ienum) { validElements++; if (element == null || element == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsNull", null, Metadata.ValidateNotNullOrEmptyCollectionFailure); } string elementAsString = element as String; if (elementAsString != null) { if (String.IsNullOrEmpty(elementAsString)) { throw new ValidationMetadataException( "ArgumentCollectionContainsEmpty", null, Metadata.ValidateNotNullOrEmptyFailure); } } } if (validElements == 0) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullOrEmptyCollectionFailure); } } else if ((itor = arguments as IEnumerator) != null) { int validElements = 0; for (; itor.MoveNext() == true;) { validElements++; if (itor.Current == null || itor.Current == AutomationNull.Value) { throw new ValidationMetadataException( "ArgumentIsNull", null, Metadata.ValidateNotNullOrEmptyCollectionFailure); } } if (validElements == 0) { throw new ValidationMetadataException( "ArgumentIsEmpty", null, Metadata.ValidateNotNullOrEmptyCollectionFailure); } } } } #endregion NULL validation attributes #endregion Data validate Attributes #region Data Generation Attributes /// /// Serves as the base class for attributes that perform argument transformation. /// /// /// Argument transformation attributes can be attached to /// and /// parameters to automatically transform the argument value in /// some fashion. The transformation might change the object, /// convert the type, or even load a file or AD object based on /// the name. /// Existing argument transformation attributes include /// . /// /// PSSnapins wishing to create custom argument transformation attributes /// should derive from /// /// and override the /// /// abstract method, after which they can apply the /// attribute to their parameters. /// /// It is also recommended to override /// to return a readable string /// similar to the attribute declaration, for example /// "[ValidateRangeAttribute(5,10)]". /// /// If multiple transformations are defined on a parameter, /// they will be invoked in series, each getting the output /// of the previous transformation. /// /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public abstract class ArgumentTransformationAttribute : CmdletMetadataAttribute { /// /// Initializes a new instance of the ArgumentTransformationAttribute class /// protected ArgumentTransformationAttribute() { } /// /// Method that will be overridden by the subclasses to transform the inputData parameter /// argument into some other object that will be used for the parameter's value. /// /// /// The engine APIs for the context under which the transformation is being /// made. /// /// parameter argument to mutate /// mutated object(s) /// /// Return the transformed value of . /// Throw /// if the value of is invalid, /// and throw /// for other recoverable errors. /// /// should be thrown for invalid arguments /// should be thrown for any problems during transformation public abstract object Transform(EngineIntrinsics engineIntrinsics, object inputData); /// /// The property is only checked when: /// a) The parameter is not mandatory /// b) The argument is null /// public virtual bool TransformNullOptionalParameters { get { return true; } } } #endregion Data Generation Attributes }