代码改变世界

Web Deploy 高级应用:自动设置 ACL

2011-01-04 19:39  Nana's Lich  阅读(6033)  评论(0编辑  收藏  举报

引言

前一篇文章中讲解了 Web Deploy 技术的简单使用,以及避免已有的 ACL 设置被清除的办法。

而这一次我将会讲解在使用 Visual Studio (Express) 进行一键发布时自动完成 ACL 设置的办法。

原理

在解决上一篇文章的问题的过程中中提到了 MSBuild 指令的使用,稍加思考的话并不难得出这样一个结论:

Web Deploy 设置 ACL 的具体方式也是通过 MSBuild 操控的。

而如果我们调查了 Microsoft.Web.Publishing.targets 的话,就会发现这样的一些指令:

<!--Make sure the by default Networkservice/AppPoolIdentity have read permissing to the fodler-->
<MsDeploySourceManifest Include="setAcl"
                        Condition="$(IncludeSetAclProviderOnDestination)">
  <Path>$(_MSDeployDirPath_FullPath)</Path>
  <setAclResourceType>Directory</setAclResourceType>
  <AdditionalProviderSettings>setAclResourceType</AdditionalProviderSettings>
</MsDeploySourceManifest>


<!--Make sure the by default anonymousAuthenticationUser have read permissing to the fodler so MIME type files will work-->
<MsDeploySourceManifest Include="setAcl"
                        Condition="$(IncludeSetAclProviderOnDestination)">
  <Path>$(_MSDeployDirPath_FullPath)</Path>
  <setAclUser>anonymousAuthenticationUser</setAclUser>
  <setAclResourceType>Directory</setAclResourceType>
  <AdditionalProviderSettings>setAclUser;setAclResourceType</AdditionalProviderSettings>
</MsDeploySourceManifest>


<!--Additionally if App_Data Need to be ReadWrite-->
<MsDeploySourceManifest Include="setAcl"
                  Condition="$(IncludeSetAclProviderOnDestination) And $(MarkApp_DataWritableOnDestination) And Exists('$(_MSDeployDirPath_FullPath)\App_Data')">
  <Path>$(_MSDeployDirPath_FullPath)\App_Data</Path>
  <setAclAccess>Write</setAclAccess>
  <setAclResourceType>Directory</setAclResourceType>
  <AdditionalProviderSettings>setAclResourceType;setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>

其中 XML 元素名称 MsDeploySourceManifest 表示了它会在 Web Deploy 的程序清单(Manifest)中产生一个对应的指令;

Include="setAcl" 指示了将要产生一个 setAcl 指令,顾名思义,它是用来设置 ACL 的;

Condition="..." 表示这条指令将在特定的条件下产生,由于其中涉及到了大量的其它 MSBuild 指令,现在就详细讲解似乎有点早,为了不让大家被一大堆概念搞得晕头转向,这部分放后面说;

<Path>$(_MSDeployDirPath_FullPath)</Path> 指示了将要向网站程序的根目录设置 ACL,$(_MSDeployDirPath_FullPath) 表示了 Web Deploy 的目标路径(它的实际含义其实是有些区别的,同样放后面);

<setAclAccess>Write</setAclAccess> 指示了赋予写操作的权限,如果只需要读操作权限,可以省略掉;

<setAclUser>anonymousAuthenticationUser</setAclUser> 指示了将要向谁赋予操作权限,anonymousAuthenticationUser 表示匿名访问者,如果希望向应用程序池的身份标识赋予操作权限,就可以省略掉;

<setAclResourceType>Directory</setAclResourceType> 表示将要向一个目录设置 ACL,可以省略掉;

<AdditionalProviderSettings>setAclResourceType</AdditionalProviderSettings> 表示启用 setAclResourceType 选项,如果其中不包含任何选项,就同样可以省略。

 

综合来说,上面所列出的这些指令说明了在默认情况下,Web Deploy 会自动赋予匿名用户及应用程序池对网站目录的读操作权限(省略时)、赋予应用程序池对数据库目录的写操作权限(看来这里不需要写成“Read,Write”,是个有意思的现象)

操练起来

如果要给应用程序池指派其它的操作权限——比方说完全控制网站目录,也需要使用像是这样的指令。

不过实际上在 *.wpp.targets 里面添加这样的 MSBuild 指令的时候是有些技术细节需要注意的,比方说这些指令应该添加在什么地方、怎么样让它们被执行。

就以这次的“完全控制”来说,首先自然要依葫芦画瓢地写一段指令,大概会是这样:

<MsDeploySourceManifest Include="setAcl">
  <Path>$(_MSDeployDirPath_FullPath)</Path>
  <setAclAccess>FullControl</setAclAccess>
  <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
</MsDeploySourceManifest>

注:由于 setAclResourceType 的默认值就是 Directory,所以可省略掉。

 

本质上说,MsDeploySourceManifest 是一种自定义的指令物件,依赖于 MSBuild 的可扩展性发挥作用。

而这种指令物件必须放置在<ItemGroup/>指令中,然后为了能在合适的时机执行这些指令,还需要把它们放进一个命名的<Target/>指令中,结果大概就是这样:

<Target Name="MySetAcl">
  <ItemGroup>
    <MsDeploySourceManifest Include="setAcl">
      <Path>$(_MSDeployDirPath_FullPath)</Path>
      <setAclAccess>FullControl</setAclAccess>
      <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
    </MsDeploySourceManifest>
  </ItemGroup>
</Target>

而指定了名称之后,还需要在适当的生成任务中调用它,才能真正地发挥作用。

如果搜索关于 Web Deploy 和 setAcl 的文章的话,可能会发现有些文章建议在 AfterAddContentPathToSourceManifest 事件中调用这个自定义生成任务,但是实际上这样做的结果是自定义的 setAcl 指令会添加到 Web Deploy 自动生成的 setAcl 指令之前,导致我们设置的 ACL 被覆盖而失去作用。

对于这个问题,国外有同样研究自动设置 ACL 的人,给出的建议是:把调用添加在 AfterAddIisSettingAndFileContentsToSourceManifest 事件中,也就是这样:

<AfterAddIisSettingAndFileContentsToSourceManifest>
  MySetAcl;
</AfterAddIisSettingAndFileContentsToSourceManifest>

到现在为止,我们的 *.wpp.targets 看起来差不多就是这样了:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AfterAddIisSettingAndFileContentsToSourceManifest>
      MySetAcl;
    </AfterAddIisSettingAndFileContentsToSourceManifest>
  </PropertyGroup>
  <Target Name="MySetAcl">
    <ItemGroup>
      <MsDeploySourceManifest Include="setAcl">
        <Path>$(_MSDeployDirPath_FullPath)</Path>
        <setAclAccess>FullControl</setAclAccess>
        <AdditionalProviderSettings>setAclAccess</AdditionalProviderSettings>
      </MsDeploySourceManifest>
    </ItemGroup>
  </Target>
</Project>

 

而到这个程度,就已经可以达到“向应用程序池赋予对应用程序目录的完全控制权限”的目的了。

在进行发布之前请注意保存 *.wpp.targets,并重新加载项目。

补充说明

有两个技术细节在这里补充一下:

  1. 如果把 IncludeSetAclProviderOnDestination 设置为 False 的话,是可以在 AfterAddContentPathToSourceManifest 事件调用我们的自定义任务的,但是这样做到话 Web Deploy 也不会自动为匿名访问者赋予读取权限,需要我们手动添加相关的指令,所以不推荐这样做。
  2. 在起草本文的过程中参考了 http://leethams.com/blog/?p=102,但是本文省略掉了 AfterAddIisSettingAndFileContentsToSourceManifest 事件的条件属性,原因是:从 *.wpp.targets 导入的指令的执行顺序比较靠前,这个时候绝大部分和 Web Deploy 有关的 MSBuild 属性(包括事件)的内容都为空,条件判断的结果一定为真,没有实际意义。

另外由于实际写出来之后文章内容比预期得要长,所以为了让大家阅读起来方便一些,关于“自定义子目录的 ACL”的部分就要放到后面的部分中了,敬请期待。