I want to modify the ACL of a specific folder. To do this, I need to remove the inherited FileSystemAccessRules, and change them to ReadAndExecute permissions. To do this, I decided to use the ModifyAccessRule() method.
My problem is that I need to use the ModifyAccessRule method, and as part of the constructor, I need a FileSystemAccessRule object that would take the same input.
I am aware that this could be simply solved by not using a pipeline, but I am trying to keep it efficient and within one, hence why no external variables are being used (with the exception of the initial path).
Get-Item $folder_path |
%{$_.GetAccessControl()} |
%{$_.ModifyAccessRule(
"Add",
New-Object System.Security.AccessControl.FileSystemAccessRule(
$_.Access.IdentityReference,
[System.Security.AccessControl.FileSystemRights]::ReadAndExecute,
[System.Security.AccessControl.InheritanceFlags]::ContainerInherit,
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow
)
)}
This is my code at the moment, but it does not work, due to New-Object not being executable inside a constructor. I have searched for a possible replacement, but have not found anything. As you can see, though, all the parts necessary for this new object are available.
Is there some type of format or method I could use to create this FileSystemAccessRule object within another object's constructor?
PS: As a bonus, can anyone confirm that PropagationFlags and InheritanceFlags are at the right values for the folder to not inherit parent permissions but propagate to children? ^-^
Related
I have read in various places that Global variables are at best a code smell, and best avoided. At the moment I am working on refactoring a big function based PS script to classes, and thought to use a Singleton. The use case being a large data structure that will need to be referenced from a lot of different classes and modules.
Then I found this, which seems to suggest that Singletons are a bad idea too.
So, what IS the right way (in PS 5.1) to create a single data structure that needs to be referenced by a lot of classes, and modified by some of them? Likely pertinent is the fact that I do NOT need this to be thread safe. By definition the queue will be processed in a very linear fashion.
FWIW, I got to the referenced link looking for information on singletons and inheritance, since my singleton is simply one of a number of classes with very similar behavior, where I start with the singleton which contains collections of the next class, which each contain collections of the next class, to create a hierarchical queue. I wanted to have a base class that handled all the common queue management then extend that for the differing functionality lof each class. Which works great other than having that first extended class be a singleton. That seems to be impossible, correct?
EDIT: Alternatively, is it possible with this nested classes in a generic list property approach to be able to identify the parent from within a child? This is how I handled this is the Function based version. A global [XML] variable formed the data structure, and I could step through that structure, using .SelectNode() to populate a variable to pass to the next function down, and using .Parent to get information from higher up, and especially from the root of the data structure.
EDIT: Since I seem not to be able to paste code here right now, I have some code on GitHub. The example here of where the Singleton comes in is at line 121, where I need to verify if there are any other examples of the same task that have not yet comnepelted, so I can skip all but the last instance. This is a proof of concept for deleting common components of various Autodesk software, which is managed in a very ad hoc manner. So I want to be able to install any mix of programs (packages) and uninstall on any schedule, and ensure that the last package that has a shared component uninstall is the one that uninstalls it. So as to no break other dependent programs before that last uninstall happens. Hopefully that makes sense. Autodesk installs are a fustercluck of misery. If you don't have to deal with them, consider yourself lucky. :)
To complement Mathias R. Jessen's helpful answer - which may well be the best solution to your problem - with an answer to your original question:
So, what IS the right way (in PS 5.1) to create a single data structure that needs to be referenced by a lot of classes, and modified by some of them [without concern for thread safety]?
The main reason that global variables are to be avoided is that they are session-global, meaning that code that executes after your own sees those variables too, which can have side effects.
You cannot implement a true singleton in PowerShell, because PowerShell classes do not support access modifiers; notably, you cannot make a constructor private (non-public), you can only "hide" it with the hidden keyword, which merely makes it less discoverable while still being accessible.
You can approximate a singleton with the following technique, which itself emulates a static class (which PowerShell also doesn't support, because the static keyword is only supported on class members, not the class as a whole).
A simple example:
# NOT thread-safe
class AlmostAStaticClass {
hidden AlmostAStaticClass() { Throw "Instantiation not supported; use only static members." }
static [string] $Message # static property
static [string] DoSomething() { return ([AlmostAStaticClass]::Message + '!') }
}
[AlmostAStaticClass]::<member> (e.g., [AlmostAStaticClass]::Message = 'hi') can now be used in the scope in which AlmostAStaticClass was defined and all descendant scopes (but it is not available globally, unless the defining scope happens to be the global one).
If you need access to the class across module boundaries, you can pass it as a parameter (as a type literal); note that you still need :: to access the (invariably static) members; e.g.,
& { param($staticClass) $staticClass::DoSomething() } ([AlmostAStaticClass])
Implementing a thread-safe quasi-singleton - perhaps for use
with ForEach-Object -Parallel (v7+) or Start-ThreadJob (v6+, but installable on v5.1) - requires more work:
Note:
Methods are then required to get and set what are conceptually properties, because PowerShell doesn't support code-backed property getters and setters as of 7.0 (adding this ability is the subject of this GitHub feature request).
You still need an underlying property however, because PowerShell doesn't support fields; again the best you can do is to hide this property, but it is technically still accessible.
The following example uses System.Threading.Monitor (which C#'s lock statement is based on) to manage thread-safe access to a value; for managing concurrent adding and removing items from collections, use the thread-safe collection types from the System.Collections.Concurrent namespace.
# Thread-safe
class AlmostAStaticClass {
static hidden [string] $_message = '' # conceptually, a *field*
static hidden [object] $_syncObj = [object]::new() # sync object for [Threading.Monitor]
hidden AlmostAStaticClass() { Throw "Instantiation not supported; use only static members." }
static SetMessage([string] $text) {
Write-Verbose -vb $text
# Guard against concurrent access by multiple threads.
[Threading.Monitor]::Enter([AlmostAStaticClass]::_syncObj)
[AlmostAStaticClass]::_message = $text
[Threading.Monitor]::Exit([AlmostAStaticClass]::_syncObj)
}
static [string] GetMessage() {
# Guard against concurrent access by multiple threads.
# NOTE: This only works with [string] values and instances of *value types*
# or returning an *element from a collection* that is
# only subject to concurrency in terms of *adding and removing*
# elements.
# For all other (reference) types - entire (non-concurrent)
# collections or individual objects whose properties are
# themselves subject to concurrent access, the *calling* code
# must perform the locking.
[Threading.Monitor]::Enter([AlmostAStaticClass]::_syncObj)
$msg = [AlmostAStaticClass]::_message
[Threading.Monitor]::Exit([AlmostAStaticClass]::_syncObj)
return $msg
}
static [string] DoSomething() { return ([AlmostAStaticClass]::GetMessage() + '!') }
}
Note that, similar to crossing module boundaries, using threads too requires passing the class as a type object to other threads, which, however is more conveniently done with the $using: scope specifier; a simple (contrived) example:
# !! BROKEN AS OF v7.0
$class = [AlmostAStaticClass]
1..10 | ForEach-Object -Parallel { ($using:class)::SetMessage($_) }
Note: This cross-thread use is actually broken as of v7.0, due to classes currently being tied to the defining runspace - see this GitHub issue. It is to be seen if a solution will be provided.
As you can see, the limitations of PowerShell classes make implementing such scenarios cumbersome; using Add-Type with ad hoc-compiled C# code is worth considering as an alternative.
This GitHub meta issue is a compilation of various issues relating to PowerShell classes; while they may eventually get resolved, it is unlikely that PowerShell's classes will ever reach feature parity with C#; after all, OOP is not the focus of PowerShell's scripting language (except with respect to using preexisting objects).
As mentioned in the comments, nothing in the code you linked to requires a singleton.
If you want to retain a parent-child relationship between your ProcessQueue and related Task instance, that can be solved structurally.
Simply require injection of a ProcessQueue instance in the Task constructor:
class ProcessQueue
{
hidden [System.Collections.Generic.List[object]]$Queue = [System.Collections.Generic.List[object]]::New()
}
class Task
{
[ProcessQueue]$Parent
[string]$Id
Task([string]$id, [ProcessQueue]$parent)
{
$this.Parent = $parent
$this.Id = $id
}
}
When instantiating the object hierarchy:
$myQueue = [ProcessQueue]::new()
$myQueue.Add([Task]#{ Id = "id"; Parent = $myQueue})
... or refactor ProcessQueue.Add() to take care of constructing the task:
class ProcessQueue
{
[Task] Add([string]$Id){
$newTask = [Task]::new($Id,$this)
$Queue.Add($newTask)
return $newTask
}
}
At which point you just use ProcessQueue.Add() as a proxy for the [Task] constructor:
$newTask = $myQueue.Add($id)
$newTask.DisplayName = "Display name goes here"
Next time you need to search related tasks from a single Task instance, you just do:
$relatedTasks = $task.Parent.Find($whatever)
I am currently trying to use the AST functionality introduced in PowerShell 3.0 to modify a ScriptBlock. My requirement is that all the parameters in the parameter block of the ScriptBlock get a [Parameter(Mandatory)] attribute.
Basically the code should modify this:
Param([string]$x)
Write-Host $x
to this:
Param([Parameter(Mandatory)][string]$x)
Write-Host $x
However, I ran into a problem when adding that new attribute, since it expects an IScriptExtent and I am not sure how I should create a new IScriptExtent.
How can I create a new script extent? What values can I use for the position? Do I have to change the position of all following extents?
I tried just reusing the extent of each parameter I am modifying, but unfortunately this does not seem to yield the results it should (e.g. when I am calling ToString on the modified ScriptBlock I don't see any changes).
My implementation so far is based on the ICustomAstVisitor found here.
The most important method looks like this:
public object VisitParameter(ParameterAst parameterAst)
{
var newName = VisitElement(parameterAst.Name);
var extent = // What to do here?
var mandatoryArg = new AttributeAst(extent, new ReflectionTypeName(typeof (ParameterAttribute)),
new ExpressionAst[0],
new[] {new NamedAttributeArgumentAst(extent, "Mandatory", new ConstantExpressionAst(extent, true), true)});
var newAttributes = new[] {mandatoryArg}.Concat(VisitElements(parameterAst.Attributes));
var newDefaultValue = VisitElement(parameterAst.DefaultValue);
return new ParameterAst(parameterAst.Extent, newName, newAttributes, newDefaultValue);
}
The script extent is used primarily for error reporting, but is also used for debugging (for example, setting a line breakpoint.)
In general, the options for synthesized script (like your example) are:
reuse an existing ast, presumably near/related to the ast you're adding
use an empty ast (basically create instances of ScriptExtent and ScriptPosition with no file, empty line)
create a synthetic extent that aids in debugging somehow, maybe with some special content
In your example, any of the above are suitable. The second option is the simplest. The third option is just a variant of the second, but you would set the content to something useful, e.g.
<#Generated: [Parameter(Mandatory)] #>
Names that begin with I are typically Interfaces. They are not classes that you create an instance of, they are contracts of sorts that specify that a particular class implements a certain known set of functionality.
For example, a [hashtable] implements IEnumerable. That means that anything that knows how to work with an IEnumerable interface and operate on that class; you could create your own class that implements the interface, and code that never could have known about your class or what it does can still interact with it in the way that IEnumerable defines (which in this case is a way to iterate over it).
So, when a function declares a parameter with an interface type, it's not looking for any one specific class, it's looking for any class that implements that interface.
The next step then is to find which types implement that interface. Here's some PowerShell code I used to find those:
[System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes() | Where-Object {
[System.Management.Automation.Language.IScriptExtent].IsAssignableFrom($_)
}
From this, we can see the following:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False IScriptExtent
False False InternalScriptExtent System.Object
False False EmptyScriptExtent System.Object
True False ScriptExtent System.Object
The first listing is the interface itself. Of the other three, two of them are not public, so that just leaves ScriptExtent.
You can create one of these with New-Object but you need to supply the start and end positions as [ScriptPosition] objects. I'm not entirely sure what those should be without seeing more of your code.
I'd like to use a stateflow object's Tag property to hold persistent data relative to that object. The problem arises when the user performs a copy of the object using a right-click/drag/release of the object. Under this (and all copy operations), the Tag property is not cleared. If anyone has a solution, I'd be most interested in hearing it.
(For completeness, on the other copy operations (context menu, edit menu) I can modify sfcall.m (in private, yikes!) to clear the property).
I'm creating a jQuery Template system that recursively calls the same template as needed. Most of the examples of "recursive" templates are actually just templates calling other templates, even the ones that pass around the $item object to retain custom functions. In this case, I want to call the same template with a sub-portion of the original $data (AKA $item.data) while also passing around custom template functions passed in to the options of the original tmpl call.
// original template call
$("#someTemplate").tmpl(data, {
someFunction1: function(itemToCheck) {
return itemToCheck.p3;
},
someFunction2: function(itemToCheck) {
return itemToCheck.p1 + ", " + itemToCheck.p2;
}
}).appendTo(".results");
<!--in-template secondary call-->
{{tmpl($data.sub, $item) "#someTemplate"}}
Full code jsFiddle
It seems that passing around $item as the options parameter for the recursive call results in the data parameter being ignored, probably because $item.data contains the original object and it simply overwrites the new data parameter. As a result, at each recursive level, I am still operating at the original caller level in the object and making no further progress in the object structure (hence the stack issue).
Is there some other property of $item I need to use to pass around only the custom functions without having $item.data override the template data being passed in?
Update
You definitely cannot just pass $item as the options parameter to {{tmpl}}. After looking at the code, it sets data for the new template call and then blows it away with the original $item.data through jQuery.extend a few lines later.
While I cannot seem to find a variable on $item that wraps all the custom functions, I was able to work around this issue by passing in each custom function individually from the original $item object. Not ideal, but it works.
{{tmpl($data.sub, { someFunction1: $item.someFunction1, someFunction2: $item.someFunction2 }) "#someTemplate"}}
I am making a custom ActiveForm method, but it requires the model to have a certain custom validator attached the the attribute that is being passed through (otherwise who knows what will happen!?)
My question is simply... is there a way to run this check in the code that is reliable?
I don't want to add the validator at runtime. That would create confusion and possibly let someone use this type of field where it ought not be used.
So I want to say something like:
if( model NOT HAVE validationMethod ON property)
throw Exception;
I'm also not sure why you want to do this, but in addition to viewing the rules array you can do:
$model->getValidators($attribute)
to check which validators are active for a particular attribute (or all attributes, if the arg is null. (I'm assuming $attribute = property in your example.)
This will return all the validator objects that are active for the current scenario and you can check if a predefined or custom class exists. It also gets you a bit more info than just the rules array (i.e., the properties of the validator class).