欢迎光临 Enjoy IT (ITECN.NET) 登录 | 注册 | 帮助

Windows PowerShell每周提示(41):与安全描述符共事

对很多人来说,安全描述符总是象征了系统管理脚本的圣地:被神秘和神话所覆盖,安全描述符是每个人所向往但又不期待亲自去理解的。

这很有趣,因为使用脚本来管理安全描述符并非是不可能。毕竟,WMI已经拥有这个能力好几年了,在Windows PowerShell最早的发布版本中也引入了名为Set-ACLcmdlet。处理安全描述符有什么大不了的?

好的,问题是事实上有些理论上的可能性并不具备实际操作性。你能用WMI管理安全描述符么?当然,尽管这不是为心理软弱之人所准备的。好的,你能使用Set-ACL配置安全描述符么?当然,但是默认情况下,这只适合于你手动配置了另一个对象(比如,一个文件)的安全设置然后将这些设置复制给新的对象。下面是从PowerShell帮助文件中摘取的一个例子:

$DogACL = get-acl c:\dog.txt

set-acl -path C:\cat.txt -AclObject $DogACL

你从Dog.txt文件中得到安全描述符然后将它们复制给Cat.txt。这很有用,但是这和很多人想的不同。

那么有没有什么方法能使你使用脚本来简单的配置安全描述符?好的,事实证明,有的,只要你愿意使用Windows PowerShell,只要你愿意一头扎进.NET Framework中:

$colRights = [System.Security.AccessControl.FileSystemRights]"Read, Write"

 

$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None

$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None

 

$objType =[System.Security.AccessControl.AccessControlType]::Allow

 

$objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer")

 

$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `

    ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)

 

$objACL = Get-ACL "C:\Scripts\Test.ps1"

$objACL.AddAccessRule($objACE)

 

Set-ACL "C:\Scripts\Test.ps1" $objAC

就算第一眼看上去这不是一个使你能从脚本中简单配置安全描述符的解决方法。但是请先多多包涵,你也许改变对这个脚本的想法,尤其是在我们解释了这个脚本是如何工作的,及这脚本中的多少代码是你用来管理安全描述符是的标准代码。

再次重申,也许你不会改变的你的想法。但是过一会我们就能发现的。

让我们先从脚本的起始处开始……等等,让我们暂时先不要讨论脚本。在我们深入之前,需要注意的是,脚本专家在处理文件及文件夹的安全描述符上所花的时间并不多。那么能否保证该示例脚本在所有情况都能工作?不能。能否保证该示例脚本不会将已有文件或文件夹的安全权限弄得乱七八糟?不能。能否保证该示例脚本不会在时空连续体中撕裂出一个缺口并将你吸入黑洞之中?不能。我们向你展示的示例脚本对我们来说运行正常,但是这也是我们所能说的全部内容。该示例脚本所带来的潜在风险将由你自己承担,并且希望你能明智地使用它。(比如,在测试机上使用,直到你确信它不会给你带来任何问题)。这将让你的心情放松,不是么?

好的,让我们从头开始。在代码的第一行,我们创建了一个文件系统权限的数组,这是[System.Security.AccessControl.FileSystemRights] (一个.NET Framework文件系统枚举)结构所代表的含义。在示例脚本中,我们将两个权限(读和写)赋值给名为$colRights的变量,当我们实际创建新的访问控制项(ACEaccess control entry)时将会使用这个变量。

$colRights = [System.Security.AccessControl.FileSystemRights]"Read, Write"

读和写是唯一能在脚本中指定的两个文件系统权限么?不。理论上(之所以说“理论上”是因为我们没有尝试所有的可能性)你可以指定以下权限:

AppendData

附加数据

ChangePermissions

更改权限

CreateDirectories

创建文件夹

CreateFiles

创建文件

Delete

删除

DeleteSubdirectoriesAndFiles

删除子文件夹及文件

 

ExecuteFile

执行文件

FullControl

完全控制

ListDirectory

列出文件夹

Modify

修改

Read

读取

ReadAndExecute

读取并执行

ReadAttributes

读取属性

ReadData

读取数据

ReadExtendedAttributes

读取扩展属性

ReadPermissions

读取权限

Synchronize

同步

TakeOwnership

取得所有权

Traverse

遍历

Write

写入

WriteAttributes

写入属性

WriteData

写入数据

WriteExtendedAttributes

写入扩展属性

明白了?长话短说,在代码的第一行我们只是指定了想要指定的文件系统权限。

在下面两行代码里,我们指出这些权限如何被继承和传播至子项。(比起文件来更关注的是文件夹。)在两个示例中我们将此值设为无。此外我们将InheritanceFlag设为以下的任一值:

l  ContainerInherit(访问控制项被子容器继承,比如子文件夹)

l  ObjectInherit(访问控制项被子对象基础,比如文件)

l  None

在同一行,我们可以将PropagtionFlag设置为以下的任一值:

l  InheritOnly(访问控制项传播到所有子对象)

l  NoInheritOnly(访问控制项不会传播到所有子对象)

l  None

 

你怎么知道所有这些值到底代表什么含义?我们的建议是创建测试文件夹,并将测试文件放入其中,按后尝试多种InheritFlagsPropagateFlags组合来确认将会发生什么。

接下来我们声明访问控制项的类型,访问控制项对对象来说可以是AllowDeny两者之一。在例子中我们打算使用允许访问:

$objType =[System.Security.AccessControl.AccessControlType]::Allow

明白了?这不坏,不是么?下面一行代码也是,该代码创建将被授予这些权利的一个新的用户对象($objUser)

$objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer")

此时我们已经准备好在内存中创建新的访问控制项。通过创建System.Security.AccessControl.FileSystemAccessRule类的新实例,将所有新变量以参数形式小心传递给New-Object cmdlet

$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `

    ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)

所有这些都是可重复利用的代码,只要你将正确的值赋给独立的变量那么这行代码就如同呈现的那样处理任何安全描述符。

但是等一下,别离开,我们还没有完成所有的事。目前为止我们只是在内存中创建了新的访问控制项,实际上并没有将它添加到文件或文件夹中。为了完成此事,我们首先需要从该文件或者文件夹中提取安全描述符,也就是以下这行代码所完成的:

$objACL = Get-ACL "C:\Scripts\Test.ps1"

对,这很简单,不是么?我们所做的是使用Get-ACL cmdlet来从文件C:\Scripts\Test.ps1中提取安全描述符,并将此储存在名为$objACL的变量中。

一旦我们有了Test.ps1安全描述符的对象引用,那么就能使用AddAccessRule方法来添加新的访问控制项:

$objACL.AddAccessRule($objACE)

现在剩下的就是使用Set-ACL cmdlet来将修改过的安全描述符赋值回Test.ps1并且以列表的方式呈现它:

Get-ACL "C:\Scripts\Test.ps1" | Format-List

现在然我们看下用户fabrikam\kenmyer是否出现在安全描述符中的任意位置:

Path   : Microsoft.PowerShell.Core\FileSystem::C:\scripts\test.ps1

Owner  : FABRIKAM\pilarackerman

Group  : FABRIKAM\Domain Users

Access : FABRIKAM\kenmyer Allow  Write, Read, Synchronize

         BUILTIN\Administrators Allow  FullControl

         NT AUTHORITY\SYSTEM Allow  FullControl

         FABRIKAM\ pilarackerman Allow  FullControl

         BUILTIN\Users Allow  ReadAndExecute, Synchronize

好的,你知道了:

Access : FABRIKAM\kenmyer Allow  Write, Read, Synchronize

像我们所说的,看上去一切工作正常。但是,如同我们开始所说的,在你开始在真实机上合并安全描述符之前,最好在测试机上测试该脚本。

现在,你的注意发生了变化并决定摆脱某个访问控制项?好的,有两种方法来完成。首先,你能使用RemoveAccessRule方法来移除单一权限。例如,下面的脚本从kenmyer访问控制项中移除了读取权限:

$colRights = [System.Security.AccessControl.FileSystemRights]"Write"

 

$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None

$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None

 

$objType =[System.Security.AccessControl.AccessControlType]::Allow

 

$objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer")

 

$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `

    ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)

 

$objACL = Get-ACL "c:\scripts\test.ps1"

$objACL.RemoveAccessRule($objACE)

 

Set-ACL "C:\Scripts\Test.ps1" $objACL

如你所见,方法几乎是一样。事实上,只有两处不同。首先,我们只将读取权限赋值给我们的文件系统权限列表。这是因为这是我们唯一项处理的权限:

$colRights = [System.Security.AccessControl.FileSystemRights]"Write"

其次,我们使用RemoveAccessRule方法来从安全描述符中移除这项权限:

$objACL.RemoveAccessRule($objACE)

此外,我们可以使用RemoveAccessRuleAll方法来从安全描述符中完整移除用户fabrikam\kenmyer

$colRights = [System.Security.AccessControl.FileSystemRights]"Read"

 

$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None

$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None

 

$objType =[System.Security.AccessControl.AccessControlType]::Allow

 

$objUser = New-Object System.Security.Principal.NTAccount("wingroup\kenmyer")

 

$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `

    ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)

 

$objACL = Get-ACL "c:\scripts\test.ps1"

$objACL.RemoveAccessRuleAll($objACE)

 

Set-ACL "C:\Scripts\Test.ps1" $objACL

这次,我们只添加了读取这个单一权限至文件系统权限集合。我们尽可能地告诉你不得不需要将某些东西作为文件系统权限列出,尽管这不会有什么太大影响。如果访问控制项包含了所有权限也没什么太大关系,只需向要完成的任务集合中添加一项权限。

此外,我们也在脚本中使用了不同的方法,RemoveAccessRuleAll

$objACL.RemoveAccessRuleAll($objACE)

该方法将实现些什么?你知道:它将移除整个访问控制项。再次运行Get-ACL并看下Test1.ps1的安全描述符:

Path   : Microsoft.PowerShell.Core\FileSystem::C:\scripts\test.ps1

Owner  : FABRIKAM\pilarackerman

Group  : FABRIKAM\Domain Users

Access : BUILTIN\Administrators Allow  FullControl

         NT AUTHORITY\SYSTEM Allow  FullControl

         FABRIKAM\ pilarackerman Allow  FullControl

         BUILTIN\Users Allow  ReadAndExecute, Synchronize

还可以。

无可否认的是,有点草率的介绍了如何处理安全描述符,但是这就是本周的所有内容。试一试(对,在一台测试机上),同时,脚本专家将会继续和这个而话题打交道。在我们两者之间,最后发现了系统管理脚本的圣地:一个程序化管理安全描述符的方法。

下周见。

英文原文

http://www.microsoft.com/technet/scriptcenter/resources/pstips/may08/pstip0516.mspx

发表于 作者 ghjconan | 0 评论

Windows PowerShell每周提示(40):多选列表框及更多的技巧

诸位,好消息:你现在能放松了,因为重要的日子终于到来了。

上周的提示中,我们解释了如何通过Windows PowerShell.NET Framework来创建可以让用户从列表框中进行选择的对话框。在介绍了创建列表框的基本方法后,我们承诺在本周的提示中,我们将展示如何对这个列表框进行一些装饰工作。此外,我们还承诺将向你展示如何创建多选列表框。这有可能是个错误,毕竟,毫无疑问,当我们承诺向你展示如何装饰列表框时,你们中的很多人在过去的一周中可能寝食难安,焦急等待着这一刻的到来。好的,现在你能放松了:现在是离开基本的列表框的时候了。然我们开始向你展示如何创建多选列表框的代码(换句话说,一个能让你一次选择多个项目的列表框)。我们今天不打算详细解释其中的大部分代码,因为这些在上周的提示中已经解释了。但是不要担心:我们将解释标准列表框和多选列表框之间有差异的代码。

如果我们无法完成此事的话,我们也会寝食难安的。

但是先提重要的事:

$x = @()

 

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

 

$objForm = New-Object System.Windows.Forms.Form

$objForm.Text = "Data Entry Form"

$objForm.Size = New-Object System.Drawing.Size(300,200)

$objForm.StartPosition = "CenterScreen"

 

$objForm.KeyPreview = $True

 

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")

    {

        foreach ($objItem in $objListbox.SelectedItems)

            {$x += $objItem}

        $objForm.Close()

    }

    })

 

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")

    {$objForm.Close()}})

 

$OKButton = New-Object System.Windows.Forms.Button

$OKButton.Location = New-Object System.Drawing.Size(75,120)

$OKButton.Size = New-Object System.Drawing.Size(75,23)

$OKButton.Text = "OK"

 

$OKButton.Add_Click(

   {

        foreach ($objItem in $objListbox.SelectedItems)

            {$x += $objItem}

        $objForm.Close()

   })

 

$objForm.Controls.Add($OKButton)

 

$CancelButton = New-Object System.Windows.Forms.Button

$CancelButton.Location = New-Object System.Drawing.Size(150,120)

$CancelButton.Size = New-Object System.Drawing.Size(75,23)

$CancelButton.Text = "Cancel"

$CancelButton.Add_Click({$objForm.Close()})

$objForm.Controls.Add($CancelButton)

 

$objLabel = New-Object System.Windows.Forms.Label

$objLabel.Location = New-Object System.Drawing.Size(10,20)

$objLabel.Size = New-Object System.Drawing.Size(280,20)

$objLabel.Text = "Please make a selection from the list below:"

$objForm.Controls.Add($objLabel)

 

$objListbox = New-Object System.Windows.Forms.Listbox

$objListbox.Location = New-Object System.Drawing.Size(10,40)

$objListbox.Size = New-Object System.Drawing.Size(260,20)

 

$objListbox.SelectionMode = "MultiExtended"

 

[void] $objListbox.Items.Add("Item 1")

[void] $objListbox.Items.Add("Item 2")

[void] $objListbox.Items.Add("Item 3")

[void] $objListbox.Items.Add("Item 4")

[void] $objListbox.Items.Add("Item 5")

 

$objListbox.Height = 70

$objForm.Controls.Add($objListbox)

$objForm.Topmost = $True

 

$objForm.Add_Shown({$objForm.Activate()})

[void] $objForm.ShowDialog()

 

$x

很有趣,标准列表框很多选列表框之间只有两个真正不同的地方:1) 在一个多选列表框中你必须向SelectionMode属性指定一个值。2) 在多选列表框中你必须以数组方式来处理选择的项而不是以单一选择项处理。无论相信与否,这就是将标准列表转换为多选列表框的所有内容。

让我们首先从向SelectionMode属性指定一个值开始:

$objListbox.SelectionMode = "MultiExtended"

如你所见,和你曾写过的代码相比这并不复杂。在本例中我们将SelectionMode设置为MultiExtended。这意味着什么?好的,首先它说明你能在列表框中多选列表项。你要做的是单击某个列表项,然后按住Ctrl键然后单击另一项。(可以再选择另外一个列表项……)另外,你能单击某一列表项,然后按住Shift键然后单击第二个列表项,这将使得在这些项之间的列表项都被选中。例如,假设我们有以下列表框:

现在,在MultiExtended模式下,假设你单击Item 1,然后按住Shift键,随后单击Item 4,会发生些什么呢?如图所示:

很漂亮吧?

或者,你可以设置SelectionModeMultiSimple。在这种情况下,按住Shift键将不会选择一个范围内列表项。反之,在此模式下Shift键的功能和Ctrl键的功能类似:它允许选择一个额外的项目。下图是我们在MultiSimple模式下当我们单击Item 1然后按住Shift键然后选择Item 4所发生的:

这是好还是坏?都不是,真的,这仅仅是一个不同点。

注意. 如果你完全不想要多选列表框的话,如果你只想要一个简单古老的单选列表框的话,那么很简单,那就不要为SelectionMode感到烦恼。

顺便说一句,在MultiExtended模式下,你也能使用方向键(同时使用或者不使用Shift键)来在列表框中选择列表项。还请你自己尝试下。

现在,确定在列表框中所选项目的代码是什么?上周,当我们讨论标准列表框时,抓取所选项的值并且关闭对话框的代码如下所示:

$OKButton.Add_Click({$x=$objListbox.SelectedItem;$objForm.Close()})

这很简单,因为我们不需要考虑如何处理列表框中多于一个选中项。然而,对多选列表框而言,我们可以多选一个以上的列表项。我们如何处理这个问题?好的,下面是一种方法:

$OKButton.Add_Click(

   {

        foreach ($objItem in $objListbox.SelectedItems)

            {$x += $objItem}

        $objForm.Close()

   })

在我们解释这个代码块如何工作时,需要指出,在脚本最开始的一行,我们创建了一个名为$x的空数组:

$x = @()

我们为什么这么做?因为我们需要它来存放我们在列表框中选择的项,而数组则正适合此项要求。