This commit is contained in:
Jimmy Campbell 2016-08-29 13:31:27 -07:00
Коммит c97d50d7b0
573 изменённых файлов: 44697 добавлений и 0 удалений

224
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,224 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
**logs/
**/api-keys.json
**plugins/
**publishoutput/
src/*/wwwroot/lib
PublishProfiles

23
LICENSE.md Normal file
Просмотреть файл

@ -0,0 +1,23 @@
Microsoft.IIS.Administration
Copyright (c) Microsoft Corporation
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the ""Software""), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,248 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5504DA5E-BCF3-409E-A285-E59EDBBBB8B8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{361F8099-C420-4274-A6EB-45BFD232E1CE}"
ProjectSection(SolutionItems) = preProject
global.json = global.json
scripts\Publish.ps1 = scripts\Publish.ps1
README.md = README.md
scripts\setup\setup.ps1 = scripts\setup\setup.ps1
scripts\utils.ps1 = scripts\utils.ps1
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration", "src\Microsoft.IIS.Administration\Microsoft.IIS.Administration.xproj", "{9C2ED81D-E4BF-4BF1-A9A0-278654B89313}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer", "src\Microsoft.IIS.Administration.WebServer\Microsoft.IIS.Administration.WebServer.xproj", "{46906A4C-6A49-4D1D-B556-DBBD6F744976}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.RequestFiltering", "src\Microsoft.IIS.Administration.WebServer.RequestFiltering\Microsoft.IIS.Administration.WebServer.RequestFiltering.xproj", "{E4C27952-D7E9-4914-8DCE-1F55008E1F6D}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.DefaultDocuments", "src\Microsoft.IIS.Administration.WebServer.DefaultDocuments\Microsoft.IIS.Administration.WebServer.DefaultDocuments.xproj", "{3FA5663A-BA46-4E31-8EB2-BBB57BBF9BDC}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Compression", "src\Microsoft.IIS.Administration.WebServer.Compression\Microsoft.IIS.Administration.WebServer.Compression.xproj", "{88408DBC-2D46-4152-8EC0-1E9A5CA1B8BC}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.Core", "src\Microsoft.IIS.Administration.Core\Microsoft.IIS.Administration.Core.xproj", "{A3ADB83A-C602-4DE4-A8DD-085FE5DD29CD}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Modules", "src\Microsoft.IIS.Administration.WebServer.Modules\Microsoft.IIS.Administration.WebServer.Modules.xproj", "{7A888725-698A-44CC-B462-7CB50BA74BEB}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.DirectoryBrowsing", "src\Microsoft.IIS.Administration.WebServer.DirectoryBrowsing\Microsoft.IIS.Administration.WebServer.DirectoryBrowsing.xproj", "{973047BF-0432-40FA-8E63-4572BD208254}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Logging", "src\Microsoft.IIS.Administration.WebServer.Logging\Microsoft.IIS.Administration.WebServer.Logging.xproj", "{373A43D3-531C-415E-8A06-BB3ADB8B1E0C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.HttpResponseHeaders", "src\Microsoft.IIS.Administration.WebServer.HttpResponseHeaders\Microsoft.IIS.Administration.WebServer.HttpResponseHeaders.xproj", "{E48EDD4B-A971-41BF-B99E-F29C3B4265B6}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.StaticContent", "src\Microsoft.IIS.Administration.WebServer.StaticContent\Microsoft.IIS.Administration.WebServer.StaticContent.xproj", "{8C8F1B8A-05B4-4FD3-B072-3BDF8EEA3AAA}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Sites", "src\Microsoft.IIS.Administration.WebServer.Sites\Microsoft.IIS.Administration.WebServer.Sites.xproj", "{056C022B-19A4-4004-8512-4331EEEB3555}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.AppPools", "src\Microsoft.IIS.Administration.WebServer.AppPools\Microsoft.IIS.Administration.WebServer.AppPools.xproj", "{7675A545-E9EA-4941-833B-9D213D769A8F}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Applications", "src\Microsoft.IIS.Administration.WebServer.Applications\Microsoft.IIS.Administration.WebServer.Applications.xproj", "{4F45F2E0-D81A-4EF9-BA74-7BEC57516914}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.VirtualDirectories", "src\Microsoft.IIS.Administration.WebServer.VirtualDirectories\Microsoft.IIS.Administration.WebServer.VirtualDirectories.xproj", "{8C6CC5D4-DE7F-488B-BC4C-F7C34CF9BA71}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.IPRestrictions", "src\Microsoft.IIS.Administration.WebServer.IPRestrictions\Microsoft.IIS.Administration.WebServer.IPRestrictions.xproj", "{C6DEA80E-89EA-40C1-80A7-035D30454B5C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Transactions", "src\Microsoft.IIS.Administration.WebServer.Transactions\Microsoft.IIS.Administration.WebServer.Transactions.xproj", "{7E4F866D-6CAC-4ABE-A0C0-A534FEF8C200}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Delegation", "src\Microsoft.IIS.Administration.WebServer.Delegation\Microsoft.IIS.Administration.WebServer.Delegation.xproj", "{40D9A548-0A74-4BFA-A291-EFFBD3ADE426}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Handlers", "src\Microsoft.IIS.Administration.WebServer.Handlers\Microsoft.IIS.Administration.WebServer.Handlers.xproj", "{F2049AF9-0457-405F-8A50-3DEC0CB428BE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Authentication", "src\Microsoft.IIS.Administration.WebServer.Authentication\Microsoft.IIS.Administration.WebServer.Authentication.xproj", "{704450D2-5386-45A2-9475-3A9F585317BE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.Certificates", "src\Microsoft.IIS.Administration.Certificates\Microsoft.IIS.Administration.Certificates.xproj", "{3E20985C-5629-41B0-A507-67C0AF024644}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.SslSettings", "src\Microsoft.IIS.Administration.WebServer.SslSettings\Microsoft.IIS.Administration.WebServer.SslSettings.xproj", "{BC861A82-6C97-49D1-9711-D77495AFEB96}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.WorkerProcesses", "src\Microsoft.IIS.Administration.WebServer.WorkerProcesses\Microsoft.IIS.Administration.WebServer.WorkerProcesses.xproj", "{0CCE6387-131C-4834-B549-963440F948D2}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Authorization", "src\Microsoft.IIS.Administration.WebServer.Authorization\Microsoft.IIS.Administration.WebServer.Authorization.xproj", "{B82ABB45-60C0-4866-BAD3-0794852D3C66}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.RequestMonitor", "src\Microsoft.IIS.Administration.WebServer.RequestMonitor\Microsoft.IIS.Administration.WebServer.RequestMonitor.xproj", "{BD6EFE51-5B2C-4B29-A745-09CC8A081334}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.AccessManagement", "src\Microsoft.IIS.Administration.AccessManagement\Microsoft.IIS.Administration.AccessManagement.xproj", "{AF203E48-EAA7-4486-B5ED-8A5007AC9536}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Scm", "src\Microsoft.IIS.Administration.WebServer.Scm\Microsoft.IIS.Administration.WebServer.Scm.xproj", "{E38D3DCC-FA34-4EFA-A628-C924093931E8}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.Info", "src\Microsoft.IIS.Administration.WebServer.Info\Microsoft.IIS.Administration.WebServer.Info.xproj", "{463B55E9-768C-462F-88ED-6105F48AF093}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Packager", "src\Packager\Packager.xproj", "{45D76FD8-CD19-4E16-BD42-6BAD177B9B79}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.WebServer.HttpRequestTracing", "src\Microsoft.IIS.Administration.WebServer.HttpRequestTracing\Microsoft.IIS.Administration.WebServer.HttpRequestTracing.xproj", "{2B1FDC49-CABC-4A6E-9561-8B04031F6355}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.IIS.Administration.Tests", "test\Microsoft.IIS.Administration.Tests\Microsoft.IIS.Administration.Tests.xproj", "{61B68BC3-B92B-40D4-B8C0-B8C4A8C02ADA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9C2ED81D-E4BF-4BF1-A9A0-278654B89313}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C2ED81D-E4BF-4BF1-A9A0-278654B89313}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C2ED81D-E4BF-4BF1-A9A0-278654B89313}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C2ED81D-E4BF-4BF1-A9A0-278654B89313}.Release|Any CPU.Build.0 = Release|Any CPU
{46906A4C-6A49-4D1D-B556-DBBD6F744976}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46906A4C-6A49-4D1D-B556-DBBD6F744976}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46906A4C-6A49-4D1D-B556-DBBD6F744976}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46906A4C-6A49-4D1D-B556-DBBD6F744976}.Release|Any CPU.Build.0 = Release|Any CPU
{E4C27952-D7E9-4914-8DCE-1F55008E1F6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4C27952-D7E9-4914-8DCE-1F55008E1F6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4C27952-D7E9-4914-8DCE-1F55008E1F6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4C27952-D7E9-4914-8DCE-1F55008E1F6D}.Release|Any CPU.Build.0 = Release|Any CPU
{3FA5663A-BA46-4E31-8EB2-BBB57BBF9BDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3FA5663A-BA46-4E31-8EB2-BBB57BBF9BDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FA5663A-BA46-4E31-8EB2-BBB57BBF9BDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FA5663A-BA46-4E31-8EB2-BBB57BBF9BDC}.Release|Any CPU.Build.0 = Release|Any CPU
{88408DBC-2D46-4152-8EC0-1E9A5CA1B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88408DBC-2D46-4152-8EC0-1E9A5CA1B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88408DBC-2D46-4152-8EC0-1E9A5CA1B8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88408DBC-2D46-4152-8EC0-1E9A5CA1B8BC}.Release|Any CPU.Build.0 = Release|Any CPU
{A3ADB83A-C602-4DE4-A8DD-085FE5DD29CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3ADB83A-C602-4DE4-A8DD-085FE5DD29CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3ADB83A-C602-4DE4-A8DD-085FE5DD29CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3ADB83A-C602-4DE4-A8DD-085FE5DD29CD}.Release|Any CPU.Build.0 = Release|Any CPU
{7A888725-698A-44CC-B462-7CB50BA74BEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A888725-698A-44CC-B462-7CB50BA74BEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A888725-698A-44CC-B462-7CB50BA74BEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A888725-698A-44CC-B462-7CB50BA74BEB}.Release|Any CPU.Build.0 = Release|Any CPU
{973047BF-0432-40FA-8E63-4572BD208254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{973047BF-0432-40FA-8E63-4572BD208254}.Debug|Any CPU.Build.0 = Debug|Any CPU
{973047BF-0432-40FA-8E63-4572BD208254}.Release|Any CPU.ActiveCfg = Release|Any CPU
{973047BF-0432-40FA-8E63-4572BD208254}.Release|Any CPU.Build.0 = Release|Any CPU
{373A43D3-531C-415E-8A06-BB3ADB8B1E0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{373A43D3-531C-415E-8A06-BB3ADB8B1E0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{373A43D3-531C-415E-8A06-BB3ADB8B1E0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{373A43D3-531C-415E-8A06-BB3ADB8B1E0C}.Release|Any CPU.Build.0 = Release|Any CPU
{E48EDD4B-A971-41BF-B99E-F29C3B4265B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E48EDD4B-A971-41BF-B99E-F29C3B4265B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E48EDD4B-A971-41BF-B99E-F29C3B4265B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E48EDD4B-A971-41BF-B99E-F29C3B4265B6}.Release|Any CPU.Build.0 = Release|Any CPU
{8C8F1B8A-05B4-4FD3-B072-3BDF8EEA3AAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C8F1B8A-05B4-4FD3-B072-3BDF8EEA3AAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C8F1B8A-05B4-4FD3-B072-3BDF8EEA3AAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C8F1B8A-05B4-4FD3-B072-3BDF8EEA3AAA}.Release|Any CPU.Build.0 = Release|Any CPU
{056C022B-19A4-4004-8512-4331EEEB3555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{056C022B-19A4-4004-8512-4331EEEB3555}.Debug|Any CPU.Build.0 = Debug|Any CPU
{056C022B-19A4-4004-8512-4331EEEB3555}.Release|Any CPU.ActiveCfg = Release|Any CPU
{056C022B-19A4-4004-8512-4331EEEB3555}.Release|Any CPU.Build.0 = Release|Any CPU
{7675A545-E9EA-4941-833B-9D213D769A8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7675A545-E9EA-4941-833B-9D213D769A8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7675A545-E9EA-4941-833B-9D213D769A8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7675A545-E9EA-4941-833B-9D213D769A8F}.Release|Any CPU.Build.0 = Release|Any CPU
{4F45F2E0-D81A-4EF9-BA74-7BEC57516914}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F45F2E0-D81A-4EF9-BA74-7BEC57516914}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F45F2E0-D81A-4EF9-BA74-7BEC57516914}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F45F2E0-D81A-4EF9-BA74-7BEC57516914}.Release|Any CPU.Build.0 = Release|Any CPU
{8C6CC5D4-DE7F-488B-BC4C-F7C34CF9BA71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C6CC5D4-DE7F-488B-BC4C-F7C34CF9BA71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C6CC5D4-DE7F-488B-BC4C-F7C34CF9BA71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C6CC5D4-DE7F-488B-BC4C-F7C34CF9BA71}.Release|Any CPU.Build.0 = Release|Any CPU
{C6DEA80E-89EA-40C1-80A7-035D30454B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6DEA80E-89EA-40C1-80A7-035D30454B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6DEA80E-89EA-40C1-80A7-035D30454B5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6DEA80E-89EA-40C1-80A7-035D30454B5C}.Release|Any CPU.Build.0 = Release|Any CPU
{7E4F866D-6CAC-4ABE-A0C0-A534FEF8C200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E4F866D-6CAC-4ABE-A0C0-A534FEF8C200}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E4F866D-6CAC-4ABE-A0C0-A534FEF8C200}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E4F866D-6CAC-4ABE-A0C0-A534FEF8C200}.Release|Any CPU.Build.0 = Release|Any CPU
{40D9A548-0A74-4BFA-A291-EFFBD3ADE426}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40D9A548-0A74-4BFA-A291-EFFBD3ADE426}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40D9A548-0A74-4BFA-A291-EFFBD3ADE426}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40D9A548-0A74-4BFA-A291-EFFBD3ADE426}.Release|Any CPU.Build.0 = Release|Any CPU
{F2049AF9-0457-405F-8A50-3DEC0CB428BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2049AF9-0457-405F-8A50-3DEC0CB428BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2049AF9-0457-405F-8A50-3DEC0CB428BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2049AF9-0457-405F-8A50-3DEC0CB428BE}.Release|Any CPU.Build.0 = Release|Any CPU
{704450D2-5386-45A2-9475-3A9F585317BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{704450D2-5386-45A2-9475-3A9F585317BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{704450D2-5386-45A2-9475-3A9F585317BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{704450D2-5386-45A2-9475-3A9F585317BE}.Release|Any CPU.Build.0 = Release|Any CPU
{3E20985C-5629-41B0-A507-67C0AF024644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E20985C-5629-41B0-A507-67C0AF024644}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E20985C-5629-41B0-A507-67C0AF024644}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E20985C-5629-41B0-A507-67C0AF024644}.Release|Any CPU.Build.0 = Release|Any CPU
{BC861A82-6C97-49D1-9711-D77495AFEB96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC861A82-6C97-49D1-9711-D77495AFEB96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC861A82-6C97-49D1-9711-D77495AFEB96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC861A82-6C97-49D1-9711-D77495AFEB96}.Release|Any CPU.Build.0 = Release|Any CPU
{0CCE6387-131C-4834-B549-963440F948D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0CCE6387-131C-4834-B549-963440F948D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CCE6387-131C-4834-B549-963440F948D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CCE6387-131C-4834-B549-963440F948D2}.Release|Any CPU.Build.0 = Release|Any CPU
{B82ABB45-60C0-4866-BAD3-0794852D3C66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B82ABB45-60C0-4866-BAD3-0794852D3C66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B82ABB45-60C0-4866-BAD3-0794852D3C66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B82ABB45-60C0-4866-BAD3-0794852D3C66}.Release|Any CPU.Build.0 = Release|Any CPU
{BD6EFE51-5B2C-4B29-A745-09CC8A081334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD6EFE51-5B2C-4B29-A745-09CC8A081334}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD6EFE51-5B2C-4B29-A745-09CC8A081334}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD6EFE51-5B2C-4B29-A745-09CC8A081334}.Release|Any CPU.Build.0 = Release|Any CPU
{AF203E48-EAA7-4486-B5ED-8A5007AC9536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF203E48-EAA7-4486-B5ED-8A5007AC9536}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF203E48-EAA7-4486-B5ED-8A5007AC9536}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF203E48-EAA7-4486-B5ED-8A5007AC9536}.Release|Any CPU.Build.0 = Release|Any CPU
{E38D3DCC-FA34-4EFA-A628-C924093931E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E38D3DCC-FA34-4EFA-A628-C924093931E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E38D3DCC-FA34-4EFA-A628-C924093931E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E38D3DCC-FA34-4EFA-A628-C924093931E8}.Release|Any CPU.Build.0 = Release|Any CPU
{463B55E9-768C-462F-88ED-6105F48AF093}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{463B55E9-768C-462F-88ED-6105F48AF093}.Debug|Any CPU.Build.0 = Debug|Any CPU
{463B55E9-768C-462F-88ED-6105F48AF093}.Release|Any CPU.ActiveCfg = Release|Any CPU
{463B55E9-768C-462F-88ED-6105F48AF093}.Release|Any CPU.Build.0 = Release|Any CPU
{45D76FD8-CD19-4E16-BD42-6BAD177B9B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45D76FD8-CD19-4E16-BD42-6BAD177B9B79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45D76FD8-CD19-4E16-BD42-6BAD177B9B79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45D76FD8-CD19-4E16-BD42-6BAD177B9B79}.Release|Any CPU.Build.0 = Release|Any CPU
{2B1FDC49-CABC-4A6E-9561-8B04031F6355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B1FDC49-CABC-4A6E-9561-8B04031F6355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B1FDC49-CABC-4A6E-9561-8B04031F6355}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B1FDC49-CABC-4A6E-9561-8B04031F6355}.Release|Any CPU.Build.0 = Release|Any CPU
{61B68BC3-B92B-40D4-B8C0-B8C4A8C02ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61B68BC3-B92B-40D4-B8C0-B8C4A8C02ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61B68BC3-B92B-40D4-B8C0-B8C4A8C02ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61B68BC3-B92B-40D4-B8C0-B8C4A8C02ADA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9C2ED81D-E4BF-4BF1-A9A0-278654B89313} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{46906A4C-6A49-4D1D-B556-DBBD6F744976} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{E4C27952-D7E9-4914-8DCE-1F55008E1F6D} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{3FA5663A-BA46-4E31-8EB2-BBB57BBF9BDC} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{88408DBC-2D46-4152-8EC0-1E9A5CA1B8BC} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{A3ADB83A-C602-4DE4-A8DD-085FE5DD29CD} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{7A888725-698A-44CC-B462-7CB50BA74BEB} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{973047BF-0432-40FA-8E63-4572BD208254} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{373A43D3-531C-415E-8A06-BB3ADB8B1E0C} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{E48EDD4B-A971-41BF-B99E-F29C3B4265B6} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{8C8F1B8A-05B4-4FD3-B072-3BDF8EEA3AAA} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{056C022B-19A4-4004-8512-4331EEEB3555} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{7675A545-E9EA-4941-833B-9D213D769A8F} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{4F45F2E0-D81A-4EF9-BA74-7BEC57516914} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{8C6CC5D4-DE7F-488B-BC4C-F7C34CF9BA71} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{C6DEA80E-89EA-40C1-80A7-035D30454B5C} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{7E4F866D-6CAC-4ABE-A0C0-A534FEF8C200} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{40D9A548-0A74-4BFA-A291-EFFBD3ADE426} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{F2049AF9-0457-405F-8A50-3DEC0CB428BE} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{704450D2-5386-45A2-9475-3A9F585317BE} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{3E20985C-5629-41B0-A507-67C0AF024644} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{BC861A82-6C97-49D1-9711-D77495AFEB96} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{0CCE6387-131C-4834-B549-963440F948D2} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{B82ABB45-60C0-4866-BAD3-0794852D3C66} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{BD6EFE51-5B2C-4B29-A745-09CC8A081334} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{AF203E48-EAA7-4486-B5ED-8A5007AC9536} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{E38D3DCC-FA34-4EFA-A628-C924093931E8} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{463B55E9-768C-462F-88ED-6105F48AF093} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{45D76FD8-CD19-4E16-BD42-6BAD177B9B79} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{2B1FDC49-CABC-4A6E-9561-8B04031F6355} = {BC25CF2F-4139-4D3F-9DC5-279FD380D0F2}
{61B68BC3-B92B-40D4-B8C0-B8C4A8C02ADA} = {5504DA5E-BCF3-409E-A285-E59EDBBBB8B8}
EndGlobalSection
EndGlobal

67
README.md Normal file
Просмотреть файл

@ -0,0 +1,67 @@
Microsoft IIS Administration API
--------------------------------
### Requirements: ###
* IIS installed
* Windows authentication enabled
* Hostable Web Core enabled
* ASP.NET Core Module installed (https://go.microsoft.com/fwlink/?LinkId=817246)
### Running Tests: ###
* Open the project in Visual Studio as an Administrator and launch without debugging
* Open another instance of the project and run the tests located in the 'test' folder
* Tests can also be run with the CLI
### Installing: ###
* Run PowerShell as an Administrator
* Run the Publish.ps1 script located in the scripts directory
* \<OutputDirectory>\setup\setup.ps1 Install -verbose
### Using the new API ###
1. Navigate to https://manage.iisqa.net?api_url=localhost
2. Click 'Get Access Token'
3. Generate an access token and copy it to the clipboard
4. Exit the access tokens window and return to the connection screen
5. Paste the access token into the Access Token field of the connection screen
6. Click 'Connect'
#### Updating ####
Running the install script will perform an in place update and will preserve user files.
## Examples ##
### Intialize Api Client ###
```
var apiClient = new HttpClient();
// Set access token for every request
apiClient.DefaultRequestHeaders.Add("ACCESS_TOKEN", {token});
```
### Get Web Sites ###
```
var res = apiClient.GetAsync("https://localhost:55539/api/webserver/websites").Result;
if (res.StatusCode != HttpStatusCode.OK) {
HandleError(res);
}
JArray sites = JObject.Parse(res.Content.ReadAsStringAsync().Result).Value<JArray>("websites");
```
### Create a Web Site ###
```
var newSite = new {
name = "Contoso",
physical_path = @"C:\sites\Contoso",
bindings = new[] {
new {
port = 8080,
is_https = false,
ip_address = "*"
}
}
};
var res = apiClient.PostAsJsonAsync<object>("https://localhost:55539/api/webserver/websites", newSite).Result;
if (res.StatusCode != HttpStatusCode.Created) {
HandleError(res);
}
JObject site = JObject.Parse(res.Content.ReadAsStringAsync().Result);
```

Двоичные данные
assets/license_header_cs.txt Normal file

Двоичный файл не отображается.

Двоичные данные
assets/license_header_cshtml.txt Normal file

Двоичный файл не отображается.

Двоичные данные
assets/license_header_css.txt Normal file

Двоичный файл не отображается.

Двоичные данные
assets/license_header_js.txt Normal file

Двоичный файл не отображается.

Двоичные данные
assets/license_header_ps1.txt Normal file

Двоичный файл не отображается.

12
global.json Normal file
Просмотреть файл

@ -0,0 +1,12 @@
{
"projects": [
"src",
"test"
],
"sdk": {
"version": "1.0.0-preview2-003121",
"runtime": "coreclr",
"architecture": "x64"
}
}

102
scripts/Create100Sites.ps1 Normal file
Просмотреть файл

@ -0,0 +1,102 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[String]$RootDirectory,
[parameter(Mandatory=$true , Position=1)]
[String]$Port
)
$rootDir = Get-Item $RootDirectory -ErrorAction SilentlyContinue
if($rootDir -eq $null -or !($rootDir -is [System.IO.DirectoryInfo])){
Write-Host "Usage: Create100Sites.ps1 -RootDirectory <absolue path for sites home> -Port <Port running the API>"
exit
}
function Get-ApiHeadersObject() {
$apiKey = .\utils.ps1 Generate-AccessToken -url "https://localhost:55539"
Write-Host $apiKey
$reqHeaders = @{}
$reqHeaders."Access-Token" = "Bearer " + $apiKey;
$reqHeaders."Accept" = "application/hal+json"
return $reqHeaders;
}
Push-Location $rootDir
for($i = 1; $i -le 100 ; $i++){
if(-not (Test-Path ("site$i")) ) {
mkdir "site$i";
Push-Location "site$i";
for($j = 1; $j -le 5; $j++) {
mkdir "application$j";
Push-Location "application$j"
echo "Site $i Application $j" | Out-File "index.html"
Pop-Location;
}
mkdir "wwwroot";
Push-Location "wwwroot"
echo "Site $i" | Out-File "index.html"
Pop-Location;
Pop-Location;
}
}
Pop-Location
$apiHeaders = Get-ApiHeadersObject;
$jsonDir = $rootDir.fullname;
for($i = 1; $i -le 100; $i++) {
$portNumber = 40000 + $i;
$physicalPath = (Join-Path $jsonDir "site$i\wwwroot").Replace("\", "\\")
$newSite = @"
{ "name":"site$i",
"physical_path":"$physicalPath",
"bindings":
[
{
"ip_address": "*",
"port": "$portNumber",
"hostname": "",
"is_https": "false",
"certificate_hash": null,
"certificate_store_name": null
}
]
}
"@;
$response = Invoke-RestMethod "https://localhost:$Port/api/webserver/websites" -UseDefaultCredentials -Method Post -Body $newSite -ContentType "application/json" -Headers $apiHeaders;
Write-Host $response | ConvertTo-Json;
$physicalPath = (Join-Path $jsonDir "site$i\application1").Replace("\", "\\")
$newApp = @"
{
"path": "app$i",
"physical_path":"$physicalPath",
"website": {
"id": "$($response.id)"
}
}
"@;
$response = Invoke-RestMethod "https://localhost:$Port/api/webserver/webapps" -UseDefaultCredentials -Method Post -Body $newApp -ContentType "application/json" -Headers $apiHeaders;
Write-Host $response | ConvertTo-Json;
}

Просмотреть файл

@ -0,0 +1,47 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[String]$Port
)
function Get-ApiKey(){
$res = Invoke-WebRequest "https://localhost:$Port/apikeys" -UseDefaultCredentials -SessionVariable sess;
$hTok = $res.headers."XSRF-TOKEN";
$h = @{};
$h."XSRF-TOKEN" = $htok;
$res2 = Invoke-WebRequest "https://localhost:$Port/apikeys" -Headers $h -Method Put -UseDefaultCredentials -WebSession $sess;
$jObj = ConvertFrom-Json $res2.content
return $jObj.value;
}
function Get-ApiHeaders() {
$apiKey = Get-ApiKey;
$reqHeaders = @{}
$reqHeaders."Access-Token" = "Bearer " + $apiKey;
$reqHeaders."Accept" = "application/hal+json"
return $reqHeaders;
}
$apiHeaders = Get-ApiHeaders;
$sitesResponse = Invoke-RestMethod "https://localhost:$Port/api/webserver/websites" -UseDefaultCredentials -Headers $apiHeaders;
for($i = 1; $i -le 100; $i++) {
foreach($site in $sitesResponse.websites){
if($site.name.equals("site$i")){
$id = $site.id;
$response = Invoke-WebRequest "https://localhost:$Port/api/webserver/websites/$id" -Method delete -UseDefaultCredentials -Headers $apiHeaders;
Write-Host "Delete status: " $response.StatusCode;
}
}
}

242
scripts/Publish.ps1 Normal file
Просмотреть файл

@ -0,0 +1,242 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[string]
$OutputPath,
[parameter()]
[switch]
$ConfigDebug
)
$applicationName = "Microsoft.IIS.Administration"
$defaultAppSettingsContent = @"
{
"host_id": "",
"host_name": "IIS Administration API",
"site_creation_root": null,
"administrators": [
"Administrators",
"IIS Administrators",
],
"logging": {
"enabled": true,
"path": null,
"min_level": "error",
"file_name": "log-{Date}.txt"
},
"auditing": {
"enabled": true,
"path": null,
"file_name": "audit-{Date}.txt"
},
"cors": {
"rules": [
{
"origin": "https://manage.iis.net",
"allow": true
},
{
"origin": "https://manage.iisqa.net",
"allow": true
}
]
}
}
"@
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
function DeletePreExistingFiles($targetPath)
{
$items = Get-ChildItem $targetPath
$confirmation = Read-Host "Remove the contents of $targetPath ? (y/n)"
if($confirmation -ne 'y') {
return
}
foreach($item in $items) {
Remove-Item ($item.FullName) -Recurse
}
}
function DownloadAndUnzip($url, $outputName) {
if ($url -eq $null) {
throw "Url is required";
}
if ($outputName -eq $null) {
throw "outputName is required";
}
Invoke-WebRequest -Uri $url -OutFile $outputName
$shell = new-object -com shell.application
$cdir = $(Get-Item .).FullName
$nameSpace = Join-Path $cdir $outputName
$zip = $shell.NameSpace($nameSpace)
foreach($item in $zip.Items()) {
$shell.NameSpace($cdir).copyhere($item)
}
Remove-Item $outputName
}
function Get-IISAdministrationHost($destinationDirectory) {
if ($destinationDirectory -eq $null) {
throw "Destination directory is required";
}
if (-not(Test-Path $destinationDirectory)) {
New-Item -type directory -Path $destinationDirectory | out-null
}
pushd $destinationDirectory
$url = "http://gitlab/jimmyca/Microsoft.IIS.Administration.Host/repository/archive.zip?ref=master"
$folderName = "host"
$output = "host.zip"
mkdir $folderName -Force | Out-Null
cd $folderName
mkdir temp -force | Out-Null
pushd temp
DownloadAndUnzip $url $output
$wrapper = Get-ChildItem
Get-ChildItem $wrapper | foreach {Copy-Item $_.FullName .. -Recurse -Force}
popd
rmdir -Recurse -Force temp
popd
}
$ProjectPath = $(Resolve-Path $(join-path $(Get-ScriptDirectory) ../src/Microsoft.IIS.Administration)).Path
$ProjectPathExists = Test-Path $ProjectPath
if(!$ProjectPathExists) {
throw "Project could not be found"
}
if(-not(Test-Path $OutputPath)) {
New-Item -type Directory $OutputPath -ErrorAction Stop | out-null
}
$configFolderPath = Join-Path $ProjectPath "config"
$configPathExists = Test-Path $configFolderPath
if(!$configPathExists) {
throw "Config folder does not exist"
}
try {
dotnet -v | Out-Null
}
catch {
Write-Warning $_.Exception.Message
throw "Could not find dotnet tools"
}
DeletePreExistingFiles $OutputPath
$applicationPath = Join-Path $OutputPath $applicationName
New-Item -type Directory $applicationPath -ErrorAction Stop | out-null
$configuration = "Release"
if($ConfigDebug) {
$configuration = "Debug"
}
try {
$packagerPath = $(Resolve-Path $(join-path $(Get-ScriptDirectory) ../src/Packager)).Path
dotnet publish $packagerPath -o "$(Join-Path $ProjectPath plugins)" --configuration $configuration
if ($LASTEXITCODE -ne 0) {
throw "Plugin build failed"
}
}
catch {
throw "Could not build plugins for publishing"
}
try{
dotnet publish $ProjectPath --framework netcoreapp1.0 --output $applicationPath --configuration $configuration
if ($LASTEXITCODE -ne 0) {
throw "Publish failed"
}
}
catch {
Write-Warning $_.Exception.Message
throw "Publish failed"
}
$outputConfigPath = Join-Path $applicationPath "config"
$outputConfigPathExists = Test-Path $outputConfigPath
if(!$outputConfigPathExists) {
New-Item $outputConfigPath -Type directory | Out-Null
}
copy (Join-Path $configFolderPath "modules.json") $outputConfigPath -Force -ErrorAction Stop;
$defaultAppSettingsContent | Out-File (Join-Path $outputConfigPath "appsettings.json")
# Dlls required for plugins reside in the plugins folder at dev time
$pluginFolder = Join-Path $ProjectPath "plugins"
$outputPluginsFolder = Join-Path $applicationPath "plugins"
if(!(Test-Path $outputPluginsFolder)) {
New-Item $outputPluginsFolder -ItemType Directory | Out-Null
}
# Copy the plugin dlls into the plugins directory
# Only copying in assemblies that aren't already present
Get-ChildItem $pluginFolder | Copy-Item -Destination $outputPluginsFolder -Recurse -Force
# Copy setup.ps1
Copy-Item $(Join-Path $(Get-ScriptDirectory) setup) $OutputPath -Recurse -ErrorAction Stop
# Place version
$project = ConvertFrom-Json (Get-Content (Join-Path $ProjectPath project.json) -Raw)
$Project.version | Out-File (Join-Path $OutputPath "Version.txt")
# Place Admin Host
Get-IISAdministrationHost $OutputPath
# Remove all unnecessary files
Get-ChildItem $OutputPath *.pdb -Recurse | Remove-Item -Force | Out-Null
Get-ChildItem -Recurse $OutputPath "*unix" | where {$_.Name -eq "unix"} | Remove-Item -Force -Recurse
# Ensure no intersection between plugin dlls and application dlls
$mainDlls = Get-ChildItem $applicationPath *.dll
$pluginDlls = Get-ChildItem $outputPluginsFolder *.dll
foreach ($pluginDll in $pluginDlls) {
foreach ($mainDll in $mainDlls) {
if ($mainDll.Name -eq $pluginDll.Name) {
Remove-Item $pluginDll.FullName -Force | Out-Null
break
}
}
}

85
scripts/license-check.ps1 Normal file
Просмотреть файл

@ -0,0 +1,85 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter()]
[switch]
$Interactive
)
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
function LinesToString($arr) {
$s = "";
foreach($line in $arr) {
$s += $line + [System.Environment]::NewLine
}
return $s;
}
function Get-Lines($file, $lines) {
if (-not($file -is [System.IO.FileInfo])) {
throw "Expected file object"
}
$content = Get-Content $file.FullName | select -First $lines
return $(LinesToString $content)
}
function Check-Licenses($fileExtension) {
$baseDir = Get-ScriptDirectory
$headerFilePath = Join-Path $baseDir "..\assets\license_header_$fileExtension.txt"
$licenseHeaderFile = Get-Item $headerFilePath -ErrorAction Stop
$header = Get-Content $headerFilePath
$headerLength = $header.Length
$header = LinesToString $header
$files = Get-ChildItem $(Resolve-Path $(Join-Path $baseDir "..")).Path "*$fileExtension" -Recurse -File
foreach ($file in $files) {
$h = Get-Lines $file $headerLength
if ($file.FullName.Contains("\obj\") -or
$file.FullName.Contains("\lib\")) {
continue;
}
if ($h -ne $header) {
Write-Warning "$($file.FullName) does not have the license header."
if ($Interactive) {
$confirmation = Read-Host "Add license header to $($file.FullName) ? (y/n)"
if($confirmation -eq 'y') {
$content = Get-Content $($file.FullName)
$content = $header + $(LinesToString $content)
[System.IO.File]::WriteAllLines($file.FullName, $content)
Write-Host "Header added"
}
}
$exitCode = 1
}
}
}
$exitCode = 0
Check-Licenses "cs"
Check-Licenses "js"
Check-Licenses "css"
Check-Licenses "cshtml"
Check-Licenses "ps1"
exit $exitCode

Просмотреть файл

@ -0,0 +1,103 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[string]
$Subject,
[Parameter()]
[string]
$FriendlyName = "",
[Parameter()]
[string[]]
$AlternativeNames = ""
)
$subjectDn = new-object -com "X509Enrollment.CX500DistinguishedName"
$subjectDn.Encode( "CN=" + $subject, $subjectDn.X500NameFlags.X500NameFlags.XCN_CERT_NAME_STR_NONE)
$issuer = $subject
$issuerDn = new-object -com "X509Enrollment.CX500DistinguishedName"
$issuerDn.Encode("CN=" + $issuer, $subjectDn.X500NameFlags.X500NameFlags.XCN_CERT_NAME_STR_NONE)
#
# Create a new Private Key
$key = new-object -com "X509Enrollment.CX509PrivateKey"
$key.ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
# XCN_AT_SIGNATURE, The key can be used for signing
$key.KeySpec = 2
$key.Length = 2048
# MachineContext 0: Current User, 1: Local Machine
$key.MachineContext = 1
$key.Create()
$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate"
$cert.InitializeFromPrivateKey(2, $key, "")
$cert.Subject = $subjectDn
$cert.Issuer = $issuerDn
$cert.NotBefore = (get-date).AddMinutes(-10)
$cert.NotAfter = $cert.NotBefore.AddYears(2)
#Use Sha256
$hashAlgorithm = New-Object -ComObject X509Enrollment.CObjectId
$hashAlgorithm.InitializeFromAlgorithmName(1,0,0,"SHA256")
$cert.HashAlgorithm = $hashAlgorithm
#
# Extended key usage
$clientAuthOid = New-Object -ComObject "X509Enrollment.CObjectId"
$clientAuthOid.InitializeFromValue("1.3.6.1.5.5.7.3.2")
$serverAuthOid = new-object -com "X509Enrollment.CObjectId"
$serverAuthOid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
$ekuOids = new-object -com "X509Enrollment.CObjectIds.1"
$ekuOids.add($clientAuthOid)
$ekuOids.add($serverAuthOid)
$ekuExt = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage"
$ekuExt.InitializeEncode($ekuOids)
$cert.X509Extensions.Add($ekuext)
#
# Key usage
$keyUsage = New-Object -com "X509Enrollment.cx509extensionkeyusage"
# XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE
$flags = 0x20
# XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE
$flags = $flags -bor 0x80
$keyUsage.InitializeEncode($flags)
$cert.X509Extensions.Add($keyUsage)
#
# Subject alternative names
if ($AlternativeNames -ne $null) {
$names = new-object -com "X509Enrollment.CAlternativeNames"
$altNames = new-object -com "X509Enrollment.CX509ExtensionAlternativeNames"
foreach ($n in $AlternativeNames) {
$name = new-object -com "X509Enrollment.CAlternativeName"
# Dns Alternative Name
$name.InitializeFromString(3, $n)
$names.Add($name)
}
$altNames.InitializeEncode($names)
$cert.X509Extensions.Add($altNames)
}
$cert.Encode()
$locator = $(New-Object "System.Guid").ToString()
$enrollment = new-object -com "X509Enrollment.CX509Enrollment"
$enrollment.CertificateFriendlyName = $locator
$enrollment.InitializeFromRequest($cert)
$certdata = $enrollment.CreateRequest(0)
$enrollment.InstallResponse(2, $certdata, 0, "")
# Wait for certificate to be populated
$end = $(Get-Date).AddSeconds(1)
do {
$CACertificate = (Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.FriendlyName -eq $locator })
} while ($CACertificate -eq $null -and $(Get-Date) -lt $end)
$CACertificate.FriendlyName = $FriendlyName
return $CACertificate

94
scripts/setup/acl.ps1 Normal file
Просмотреть файл

@ -0,0 +1,94 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("SetAdminAcl",
"Add-SelfRights")]
[string]
$Command,
[parameter()]
[string]
$Path
)
function SetupAcl($_path) {
if ([System.String]::IsNullOrEmpty($_path)) {
throw "Path cannot be null"
}
if (-not(Test-Path $_path)) {
throw "Directory $_path does not exist."
}
# Construct an access rule that allows full control for Administrators
$sid = [System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid
# Construct an access rule that allows full control for Local System
$localSystemSid = [System.Security.Principal.WellKnownSidType]::LocalSystemSid
$idRef = New-Object System.Security.Principal.SecurityIdentifier($sid, $null)
$localSystemIdRef = New-Object System.Security.Principal.SecurityIdentifier($localSystemSid, $null)
$fullControl = [System.Security.AccessControl.FileSystemRights]::FullControl
$allow = [System.Security.AccessControl.AccessControlType]::Allow
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$idRef,
$fullControl,
"ContainerInherit,ObjectInherit",
[System.Security.AccessControl.PropagationFlags]::None,
$allow)
$localSystemRule = New-Object System.Security.AccessControl.FileSystemAccessRule($localSystemIdRef,
$fullControl,
"ContainerInherit,ObjectInherit",
[System.Security.AccessControl.PropagationFlags]::None,
$allow)
$acl = New-Object System.Security.AccessControl.DirectorySecurity
# Remove rule inheritance for acl
$acl.SetAccessRuleProtection($true, $false)
# Remove all existing access rules
$acl.Access | %{$acl.RemoveAccessRule($_)}
# Add the rule for Administrators
$acl.AddAccessRule($rule)
# Add the rule for Local System
$acl.AddAccessRule($localSystemRule)
# Update the folder to use the new ACL
Set-Acl -Path $_path -AclObject $acl
}
function Add-SelfRights($_path) {
$objUser = New-Object System.Security.Principal.NTAccount($env:USERNAME)
$idRef = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$fullControl = [System.Security.AccessControl.FileSystemRights]::FullControl
$allow = [System.Security.AccessControl.AccessControlType]::Allow
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$idRef,
$fullControl,
"ContainerInherit,ObjectInherit",
[System.Security.AccessControl.PropagationFlags]::None,
$allow)
$a = Get-Acl $_path
$a.AddAccessRule($rule)
Set-Acl -Path $_path -AclObject $a
}
switch($Command)
{
"SetAdminAcl"
{
return SetupAcl $Path
}
"Add-SelfRights"
{
return Add-SelfRights $Path
}
default
{
throw "Unknown command"
}
}

Просмотреть файл

@ -0,0 +1,158 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("AddUserToGroup",
"CreateLocalGroup",
"CurrentAdUser",
"GetLocalGroup",
"GroupEquals",
"RemoveLocalGroup")]
[string]
$Command,
[parameter()]
[string]
$Name,
[parameter()]
[string]
$Description,
[parameter()]
[string]
$AdPath,
[parameter()]
[System.Object]
$Group
)
function GetLocalAd {
$server = "$env:COMPUTERNAME"
return [ADSI]"WinNT://$server,computer"
}
function GetLocalGroup($groupName) {
if ([System.String]::IsNullOrEmpty($groupName)) {
throw "Name cannot be null"
}
$localAd = GetLocalAd
$group = $null;
try {
$group = $localAd.Children.Find($groupName, 'group')
}
catch {
#COM Exception if group doesn't exit
}
return $group;
}
function GroupEquals($group, $_name, $desc) {
return $group.Name -eq $_name -and $group.Properties["Description"].Value -eq $desc
}
function CreateLocalGroup($_name, $desc) {
if ([System.String]::IsNullOrEmpty($_name)) {
throw "Name cannot be null"
}
$localAd = GetLocalAd
$group = GetLocalGroup $_name;
if($group -ne $null) {
throw "Group $_name already exists"
}
$group = $localAd.Children.Add($_name, 'group')
$group.Properties["Description"].Value = $desc
$group.CommitChanges()
return $group
}
function RemoveLocalGroup($_name) {
if ([System.String]::IsNullOrEmpty($_name)) {
throw "Name cannot be null"
}
$localAd = GetLocalAd
$g = GetLocalGroup $_name
if($g -ne $null) {
$localAd.Children.Remove($g.Path)
}
}
function CurrentAdUser {
return 'WinNT://' + [System.Environment]::UserDomainName + '/' + [System.Environment]::UserName
}
function AddUserToGroup($userPath, $_group) {
if ([System.String]::IsNullOrEmpty($userPath)) {
throw "User path cannot be null"
}
if ($_group -eq $null) {
throw "Group cannot be null"
}
try {
$_group.Invoke('Add', @($userPath))
}
catch {
# HRESULT -2147023518
# The specified account name is already a member of the group.
if($_.Exception.InnerException -eq $null -or $_.Exception.InnerException.HResult -ne -2147023518) {
throw $_.Exception
}
}
}
switch($Command)
{
"GetLocalGroup"
{
return GetLocalGroup $Name
}
"CreateLocalGroup"
{
return CreateLocalGroup $Name $Description
}
"RemoveLocalGroup"
{
return RemoveLocalGroup $Name
}
"CurrentAdUser"
{
return CurrentAdUser
}
"AddUserToGroup"
{
return AddUserToGroup $AdPath $Group
}
"GroupEquals"
{
return GroupEquals $Group $Name $Description
}
default
{
throw "Unknown command"
}
}

87
scripts/setup/cache.ps1 Normal file
Просмотреть файл

@ -0,0 +1,87 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Store",
"Get",
"Destroy")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[string]
$Name
)
# Cache directory for backing up files
$USER_FILE_CACHE = "$env:USERPROFILE/appdata/local/IIS Administration"
function Store($_path, $_name) {
if ([System.String]::IsNullOrEmpty($_path)) {
throw "Path required"
}
if ([System.String]::IsNullOrEmpty($_name)) {
throw "Name required"
}
if (-not(Test-Path $_path)) {
throw "$_path not found."
}
$finalPath = $(Join-Path $USER_FILE_CACHE $_name)
# Ensure directory structure created
Remove-Item -Force -Recurse $finalPath -ErrorAction SilentlyContinue
New-Item -Force $finalPath -ErrorAction Stop | Out-Null
Remove-Item -Force -Recurse $finalPath
Copy-Item -Force -Recurse $_path $finalPath -ErrorAction Stop
}
function Get($_name) {
if ([System.String]::IsNullOrEmpty($_name)) {
throw "Name required"
}
$storedPath = $(Join-Path $USER_FILE_CACHE $_name)
if (-not(Test-Path $storedPath)) {
return $null
}
return Get-Item $storedPath
}
function Destroy {
Remove-Item -Force -Recurse $USER_FILE_CACHE -ErrorAction SilentlyContinue | Out-Null
}
switch ($Command)
{
"Store"
{
Store $Path $Name
}
"Get"
{
return Get $Name
}
"Destroy"
{
Destroy
}
default
{
throw "Unknown command"
}
}

129
scripts/setup/cert.ps1 Normal file
Просмотреть файл

@ -0,0 +1,129 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Get",
"Delete",
"New",
"AddToTrusted")]
[string]
$Command,
[parameter()]
[string]
$Name,
[parameter()]
[string]
$Thumbprint,
[parameter()]
[System.Security.Cryptography.X509Certificates.X509Certificate]
$Certificate
)
function GetCert($_name, $_thumbprint)
{
if ([System.String]::IsNullOrEmpty($_name) -and [System.String]::IsNullOrEmpty($_thumbprint)) {
throw "Name or Thumpbrint required"
}
if (-not([System.String]::IsNullOrEmpty($_name))) {
$certs = Get-ChildItem Cert:\LocalMachine\My | where {$_.DnsNameList -ne $null -and $_.DnsNameList.Contains("$_name")}
}
else {
$certs = Get-ChildItem Cert:\LocalMachine\My | where {$_.Thumbprint -eq $_thumbprint}
}
if ($certs.Length -gt 1) {
return $certs[0]
}
return $certs
}
function DeleteCert($_name, $_thumbprint)
{
if ([System.String]::IsNullOrEmpty($_name) -and [System.String]::IsNullOrEmpty($_thumbprint)) {
throw "Name or Thumpbrint required"
}
if (-not([System.String]::IsNullOrEmpty($_name))) {
$files = Get-ChildItem -Recurse cert:\LocalMachine | where {$_.DnsNameList -ne $null -and $_.DnsNameList.Contains("$_name")}
}
else {
$files = Get-ChildItem -Recurse Cert:\LocalMachine | where {$_.Thumbprint -eq $_thumbprint}
}
foreach ($file in $files){
remove-item $file.PSPath
}
}
function New($_name)
{
if ([System.String]::IsNullOrEmpty($_name)) {
throw "Name cannot be null"
}
$dnsNames = @()
$dnsNames += "localhost"
$dnsNames += hostname
$dnsNames += "$_name"
return .\Create-SelfSignedCertificate.ps1 -subject "localhost" -AlternativeNames $dnsNames -FriendlyName $_name -ErrorAction Stop
}
function AddToTrusted($cert)
{
if ($cert -eq $null) {
throw "Certificate cannot be null"
}
#Create a certificate store object
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root","LocalMachine"
#Open the certificate store to begin modfications.
$store.Open("ReadWrite")
#Store the certificate that we created.
$store.Add($cert)
$closeMember = $store | Get-Member -Name "Close"
$disposeMember = $store | Get-Member -Name "Dispose"
# Close gone on Nano Server
if ($closeMember -ne $null) {
#Close the reference to the certificate store.
$store.Close()
}
if ($disposeMember -ne $null) {
$store.Dispose()
}
}
switch ($Command)
{
"Get"
{
return GetCert $Name $Thumbprint
}
"Delete"
{
return DeleteCert $Name
}
"New"
{
return New $Name
}
"AddToTrusted"
{
return AddToTrusted $Certificate
}
default
{
throw "Unknown command"
}
}

Просмотреть файл

@ -0,0 +1,66 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("ONECORE",
"DEFAULT_ADMIN_ROOT_NAME",
"DEFAULT_INSTALL_PATH",
"IIS_HWC_APP_ID",
"DEFAULT_SERVICE_NAME",
"SERVICE_DESCRIPTION",
"CERT_NAME",
"IISAdministratorsGroupName",
"IISAdministratorsDescription")]
[string]
$Command
)
switch ($Command)
{
"ONECORE"
{
return [System.Environment]::OSVersion.Version.Major -ge 10
}
# Application directory name
"DEFAULT_ADMIN_ROOT_NAME"
{
return "IIS Administration"
}
"DEFAULT_INSTALL_PATH"
{
return Join-Path $env:ProgramFiles $(.\constants.ps1 DEFAULT_ADMIN_ROOT_NAME)
}
# Application id for IIS Hostable Web Core
"IIS_HWC_APP_ID"
{
return "{4dc3e181-e14b-4a21-b022-59fc669b0914}"
}
# Application service name
"DEFAULT_SERVICE_NAME"
{
return "Microsoft IIS Administration"
}
"SERVICE_DESCRIPTION"
{
return "Management service for IIS."
}
"CERT_NAME"
{
return "Microsoft IIS Administration Server Certificate"
}
# Application administrators group
"IISAdministratorsGroupName"
{
return "IIS Administrators"
}
"IISAdministratorsDescription"
{
return "Members of this group have complete and unrestricted access to all features of IIS."
}
default
{
throw "Unknown command"
}
}

Просмотреть файл

@ -0,0 +1,87 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Write",
"Read")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[System.Collections.Hashtable]
$Obj
)
function WriteHashTable($o, $filePath) {
$xml = [xml]""
$xml.AppendChild($xml.CreateXmlDeclaration("1.0", "UTF-8", $null)) | Out-Null
$root = $xml.CreateElement("root")
if ($o -ne $null) {
foreach($key in $o.keys) {
$xElem = $xml.CreateElement($key)
$xElem.InnerText = $o[$key]
$root.AppendChild($xElem) | Out-Null
}
}
$xml.AppendChild($root) | Out-Null
$xml.Save("$filePath") | Out-Null
}
function _Write($_obj, $_store) {
if ($_obj -eq $null) {
throw "Obj cannot be null"
}
if ([System.String]::IsNullOrEmpty($_store)) {
throw "Path cannot be null"
}
if (-not(Test-Path $_store)) {
New-Item -type File -Force $_store -ErrorAction Stop | Out-Null
}
$_store = $(Resolve-Path $_store).Path
WriteHashTable $_obj $_store
return _Read $_store
}
function _Read($_store) {
[xml]$xml = Get-Content $_store
$ret = @{}
foreach($property in $($xml.root | Get-Member -MemberType Property)) {
$n = $property.Name
$ret."$n" = $xml.root."$n"
}
return $ret
}
switch($Command)
{
"Write"
{
return _Write $obj $Path
}
"Read"
{
return _Read $Path
}
default
{
throw "Unknown command"
}
}

Просмотреть файл

@ -0,0 +1,183 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("IisEnabled",
"EnableIis",
"WinAuthEnabled",
"EnableWinAuth",
"HostableWebCoreEnabled",
"EnableHostableWebCore")]
[string]
$Command
)
# SKU >= Server 2012
$OptionalFeatureCommand = Get-Command "Get-WindowsOptionalFeature" -ErrorAction SilentlyContinue
#SKU == 2008 R2
$AddFeatureCommand = Get-Command "Add-WindowsFeature" -ErrorAction SilentlyContinue
function IisEnabled
{
# Server 2012 - 2016 use Get-WindowsOptionalFeature
# Server 2008 R2 uses Get-WindowsFeature
# Nano Server doesn't use either, and once IIS is installed, everything is installed
if ($OptionalFeatureCommand -ne $null) {
$webserverRole = Get-WindowsOptionalFeature -Online -FeatureName "IIS-WebServerRole" -Verbose:$false -ErrorAction SilentlyContinue
$webserver = Get-WindowsOptionalFeature -Online -FeatureName "IIS-WebServer" -Verbose:$false -ErrorAction SilentlyContinue
#
# Handle Nano Server case where IIS doesn't exist as an optional feature
if ($webserverRole -eq $null) {
$iis = Get-Service W3SVC -ErrorAction SilentlyContinue
if ($iis -eq $NULL) {
return $false
}
return $true
}
return $webserverRole.State -eq [Microsoft.Dism.Commands.FeatureState]::Enabled -and $webserver.State -eq [Microsoft.Dism.Commands.FeatureState]::Enabled
}
else {
$webserverRole = Get-WindowsFeature -Name Web-Server -Verbose:$false -ErrorAction Stop
$webserver = Get-WindowsFeature -Name Web-WebServer -Verbose:$false -ErrorAction Stop
return $webserverRole.Installed -and $webserver.Installed
}
}
function EnableIis {
if ($OptionalFeatureCommand -ne $null) {
# SKU > Server 2012
$webserverRole = Get-WindowsOptionalFeature -Online -FeatureName "IIS-WebServerRole" -Verbose:$false
#
# Handle Nano Server case where IIS doesn't exist as an optional feature
if ($webserverRole -eq $null) {
throw "Unable to enable IIS"
}
Enable-WindowsOptionalFeature -Online -FeatureName "IIS-WebServerRole" -NoRestart -ErrorAction Stop
Enable-WindowsOptionalFeature -Online -FeatureName "IIS-WebServer" -NoRestart -ErrorAction Stop
}
else {
#SKU == 2008 R2
Add-WindowsFeature -Name Web-Server -ErrorAction Stop
Add-WindowsFeature -Name Web-WebServer -ErrorAction Stop
}
}
function WinAuthEnabled
{
if ($OptionalFeatureCommand -ne $null) {
$winAuth = Get-WindowsOptionalFeature -Online -FeatureName "IIS-WindowsAuthentication" -Verbose:$false -ErrorAction SilentlyContinue
#
# Handle Nano Server case where IIS comes with all features
if ($winAuth -eq $null) {
$iis = Get-Service W3SVC -ErrorAction SilentlyContinue
if ($iis -eq $NULL) {
return $false
}
return $true
}
return $winAuth.State -eq [Microsoft.Dism.Commands.FeatureState]::Enabled
}
else {
$winAuth = Get-WindowsFeature -Name Web-Windows-Auth -Verbose:$false -ErrorAction Stop
return $winAuth.Installed
}
}
function EnableWinAuth {
if ($OptionalFeatureCommand -ne $null) {
$winAuth = Get-WindowsOptionalFeature -Online -FeatureName "IIS-WindowsAuthentication" -Verbose:$false -ErrorAction SilentlyContinue
if ($winAuth -eq $null) {
throw "Unable to enable Windows Authentication"
}
Enable-WindowsOptionalFeature -Online -FeatureName "IIS-WindowsAuthentication" -NoRestart -ErrorAction Stop
}
else {
Add-WindowsFeature -Name Web-Windows-Auth -ErrorAction Stop
}
}
function HostableWebCoreEnabled
{
if ($OptionalFeatureCommand -ne $null) {
$hwc = Get-WindowsOptionalFeature -Online -FeatureName "IIS-HostableWebCore" -Verbose:$false -ErrorAction SilentlyContinue
#
# Handle Nano Server case where IIS comes with all features
if ($hwc -eq $null) {
$iis = Get-Service W3SVC -ErrorAction SilentlyContinue
if ($iis -eq $NULL) {
return $false
}
return $true
}
return $hwc.State -eq [Microsoft.Dism.Commands.FeatureState]::Enabled
}
else {
$hwc = Get-WindowsFeature -Name Web-WHC -Verbose:$false -ErrorAction Stop
return $hwc.Installed
}
}
function EnableHostableWebCore {
if ($OptionalFeatureCommand -ne $null) {
$hwc = Get-WindowsOptionalFeature -Online -FeatureName "IIS-HostableWebCore" -Verbose:$false -ErrorAction SilentlyContinue
if ($hwc -eq $null) {
throw "Unable to enable IIS Hostable Web Core"
}
Enable-WindowsOptionalFeature -Online -FeatureName "IIS-HostableWebCore" -NoRestart -ErrorAction Stop
}
else {
Add-WindowsFeature -Name Web-WHC -ErrorAction Stop
}
}
switch ($Command)
{
"IisEnabled"
{
return IisEnabled
}
"EnableIis"
{
return EnableIis
}
"WinAuthEnabled"
{
return WinAuthEnabled
}
"EnableWinAuth"
{
return EnableWinAuth
}
"HostableWebCoreEnabled"
{
return HostableWebCoreEnabled
}
"EnableHostableWebCore"
{
return EnableHostableWebCore
}
default
{
throw "Unknown command"
}
}

481
scripts/setup/install.ps1 Normal file
Просмотреть файл

@ -0,0 +1,481 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#Requires -RunAsAdministrator
#Requires -Version 4.0
Param(
[parameter(Mandatory=$true)]
[string]
$Path,
[string]
$Version,
[parameter(Mandatory=$true)]
[string]
$ServiceName,
[parameter()]
[int]
$Port,
[parameter()]
[string]
$DistributablePath,
[parameter()]
[string]
$CertHash,
[parameter()]
[switch]
$SkipVerification,
[parameter()]
[bool]
$SkipIisAdministrators,
[parameter()]
[switch]
$DontCopy
)
$_rollbackStore = @{}
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
function CheckInstallParameters
{
if ([String]::IsNullOrEmpty($Path)){
throw "Path required."
}
if ([String]::IsNullOrEmpty($Version)){
throw "Version required."
}
if ([String]::IsNullOrEmpty($DistributablePath) -and -not($DontCopy)){
throw "Distributable path required"
}
if ([String]::IsNullOrEmpty($ServiceName)){
throw "Service name required"
}
if ($Port -eq 0) {
$script:Port = 55539
}
else {
$script:PortIsExplicit = $true
}
$distributableDirectory = Get-Item $DistributablePath -ErrorAction SilentlyContinue
if ($distributableDirectory -eq $null -or !($distributableDirectory -is [System.IO.DirectoryInfo]) -and -not($DontCopy)){
throw "Invalid DistributablePath directory: $DistributablePath"
}
}
function CheckUninstallParameters
{
if ([String]::IsNullOrEmpty($Path)){
$script:Path = $env:ProgramFiles
}
}
function InstallationPreparationCheck
{
Write-Host "Checking installation requirements"
if (!$SkipVerification) {
Write-Verbose "Verifying IIS is enabled"
$iisInstalled = .\dependencies.ps1 IisEnabled
if (!$iisInstalled) {
Write-Warning "IIS-WebServer not enabled"
Write-Host "Enabling IIS"
try {
.\dependencies.ps1 EnableIis
}
catch {
Write-Warning "Could not enable IIS"
throw $_
}
}
Write-Verbose "Ok"
Write-Verbose "Verifying Windows Authentication is Enabled"
$winAuthEnabled = .\dependencies.ps1 WinAuthEnabled
if (!$winAuthEnabled) {
Write-Warning "IIS-WindowsAuthentication not enabled"
Write-Host "Enabling IIS Windows Authentication"
try {
.\dependencies.ps1 EnableWinAuth
}
catch {
Write-Warning "Could not enable IIS Windows Authentication"
throw $_
}
}
Write-Verbose "Ok"
Write-Verbose "Verifying IIS-HostableWebCore is Enabled"
$hostableWebCoreEnabled = .\dependencies.ps1 HostableWebCoreEnabled
if (!$hostableWebCoreEnabled) {
Write-Warning "IIS-HostableWebCore not enabled"
Write-Host "Enabling IIS Hostable Web Core"
try {
.\dependencies.ps1 EnableHostableWebCore
}
catch {
Write-Warning "Could not enable IIS Hostable Web Core"
throw $_
}
}
Write-Verbose "Ok"
}
Write-Verbose "Verifying AspNet Core Module is installed"
$aspNetCoreModuleSchemaInstalled = test-path "$env:windir\system32\inetsrv\config\schema\aspnetcore_schema.xml"
if (!$aspNetCoreModuleSchemaInstalled) {
Write-Warning "AspNet Core Module not installed"
Write-Warning "Download AspNet Core module from 'https://go.microsoft.com/fwlink/?LinkId=817246'"
throw "Cannot install IIS Administration API without AspNet Core Module being installed"
}
Write-Verbose "Ok"
Write-Verbose "Checking if port `'$Port`' is available"
$available = .\network.ps1 IsAvailable -Port $Port
if ($available) {
Write-Verbose Ok
}
else {
Write-Verbose "Not available"
}
if (!$SkipIisAdministrators) {
Write-Verbose "Verifying that IIS Administrators group does not already exist"
$group = .\activedirectory.ps1 GetLocalGroup -Name $(.\constants.ps1 IISAdministratorsGroupName)
# It is okay if IIS Administrators group already exists if it is our group
if ($group -ne $null -and (-not (.\activedirectory.ps1 GroupEquals -Group $group -Name $(.\constants.ps1 IISAdministratorsGroupName) -Description $(.\constants.ps1 IISAdministratorsDescription) ))) {
throw "IIS Administrators group already exists."
}
}
Write-Host "Installation Requirements met"
}
function GetRollbackStore {
return $_rollbackStore;
}
function rollback() {
Write-Host "Rolling back"
$rollbackStore = GetRollbackStore
#
# Delete any service we created
if ($rollbackStore.createdService -ne $null) {
Write-Host "Rolling back service creation"
try {
Stop-Service $rollbackStore.createdService -ErrorAction SilentlyContinue
}
catch {
Write-Warning "Could not stop newly created service"
}
sc.exe delete "$($rollbackStore.createdService)" | Out-Null
}
#
# Restore our service we may have deleted
if ($rollbackStore.deletedSvc -ne $null) {
$name = $rollbackStore.deletedSvc.Name
$startType = $rollbackStore.deletedSvcStartType
$binaryPath = $rollbackStore.deletedSvcBinaryPath
Write-Host "Rolling back service $name"
try {
New-Service -BinaryPathName $binaryPath -StartupType $startType -DisplayName $name -Name $name -ErrorAction Stop | Out-Null
}
catch {
Write-Warning "Could not restore the $($name) service."
}
}
#
# Remove any new HTTP.Sys binding
if ($rollbackStore.newBoundCertPort -ne $null) {
Write-Host "Rolling back HTTP.Sys port binding"
.\network.ps1 DeleteSslBinding -Port $rollbackStore.newBoundCertPort
}
#
# Restore any deleted HTTP.Sys binding
if ($rollbackStore.preboundCertInfo -ne $null) {
$info = $rollbackStore.preboundCertInfo
Write-Host "Rolling back deleted SSL binding on port $($info.ipPort)"
netsh http add sslcert ipport="$($info.ipPort)" certhash="$($info.thumbprint)" appid=$($info.appId) | Out-Null
}
#
# Remove any certificate we may have created
if ($rollbackStore.createdCertThumbprint -ne $null) {
Write-Host "Rolling back SSL certificate creation"
$files = Get-ChildItem Cert:\LocalMachine -Recurse| where {$_.Thumbprint -eq $rollbackStore.createdCertThumbprint}
try {
foreach ($file in $files){
remove-item $file.PSPath
}
}
catch {
write-warning "Could not delete certificate that was created during installation."
}
}
#
# Remove IIS Administrators group if we created it
if ($rollbackStore.iisAdministratorsGroup -ne $null) {
Write-Host "Rolling back IIS Administrators group creation"
.\activedirectory.ps1 RemoveLocalGroup -Name $rollbackStore.iisAdministratorsGroup
}
#
# Restart the existing service if we stopped it
if ($rollbackStore.stoppedOldService -ne $null) {
try {
Write-Host "Restarting service $($rollbackStore.stoppedOldService)"
Start-Service $rollbackStore.stoppedOldService
}
catch {
write-warning "Could not restart service $($rollbackStore.stoppedOldService)."
}
}
#
# Remove the program folder if we created it
if ($rollbackStore.createdAdminRoot -eq $true) {
$adminRoot = $rollbackStore.adminRoot
try {
Write-Host "Rolling back installation folder creation"
Remove-Item $adminRoot -Force -Recurse
}
catch {
write-warning "Could not delete installation folder $adminRoot."
}
}
Write-Host "Finished rolling back."
}
function Install
{
$rollbackStore = GetRollbackStore
# Mark the location that the service will be copied too
$adminRoot = Join-Path $Path $Version
$rollbackStore.adminRoot = $adminRoot
$destinationDirectory = Get-Item $adminRoot -ErrorAction SilentlyContinue
if ($destinationDirectory -eq $null -and -not($DontCopy)){
Try
{
Write-Verbose "Creating installaton directory $adminRoot"
new-item -ItemType Directory $adminRoot -Force -ErrorAction Stop | Out-Null
$rollbackStore.createdAdminRoot = $true
}
Catch
{
Write-Warning "Directory creation failed"
throw "Could not create directory at $adminRoot"
}
}
elseif (-not($destinationDirectory -eq $null) -and -not($destinationDirectory -is [System.IO.DirectoryInfo])) {
throw "Install destination already exists and is not a directory"
}
# Check for a previous installation at the installation path provided
$previousInstallSettings = .\installationconfig.ps1 Get -Path $adminRoot
if ($previousInstallSettings -ne $null) {
throw "Cannot overwrite previous installation."
}
$svc = Get-Service "$ServiceName" -ErrorAction SilentlyContinue
if ($svc -ne $null) {
throw "Service with name: `'$ServiceName`' already exists"
}
if (!(.\network.ps1 IsAvailable -Port $Port)) {
# Make sure we don't scan for available port if explicit port was specified
if ($PortIsExplicit) {
Write-Warning "Port: `'$Port`' already in use"
throw "The port specified is not available"
}
try {
$Port = .\network.ps1 GetAvailable -Port $Port
}
catch {
throw $_
}
}
# Construct an access rule that allows full control for Administrators
.\acl.ps1 SetAdminAcl -Path $adminRoot
if (-not($DontCopy)) {
Write-Host "Copying files"
try {
Copy-Item -Recurse -Force (Join-Path $DistributablePath host) $adminRoot -ErrorAction Stop
Copy-Item -Recurse -Force (Join-Path $DistributablePath Microsoft.IIS.Administration) $adminRoot -ErrorAction Stop
Copy-Item -Recurse -Force (Join-Path $DistributablePath setup) $adminRoot -ErrorAction Stop
}
catch {
Write-Warning "Failed copying application files"
throw
}
}
# applicationHost.config must be configured on install for proper loading of the API
$appHostPath = Join-Path $adminRoot host\applicationHost.config
# The application path needs to be logged in the applicationHost.config
$appPath = Join-Path $adminRoot Microsoft.IIS.Administration
# Configure applicationHost.config based on install parameters
.\installationconfig.ps1 Write-AppHost -AppHostPath $appHostPath -ApplicationPath $appPath -Port $Port
if (!$SkipIisAdministrators) {
$group = .\activedirectory.ps1 GetLocalGroup -Name $(.\constants.ps1 IISAdministratorsGroupName)
if ($group -eq $null) {
$group = .\activedirectory.ps1 CreateLocalGroup -Name $(.\constants.ps1 IISAdministratorsGroupName) -Description $(.\constants.ps1 IISAdministratorsDescription)
$rollbackStore.iisAdministratorsGroup = $group
}
# Add the user running the installer to the IIS Administrators group
$currentUser = .\activedirectory.ps1 CurrentAdUser
.\activedirectory.ps1 AddUserToGroup -AdPath $currentUser -Group $group
}
# Need a cert to bind to the port the API is supposed to listen on
$cert = $null
if (![String]::IsNullOrEmpty($CertHash)) {
# User provided a certificate hash (thumbprint)
# Retrieve the cert from the hash
$cert = Get-Item "Cert:\LocalMachine\My\$CertHash" -ErrorAction SilentlyContinue
if ($cert -eq $null) {
throw "Could not find certificate with hash $CertHash in store: My"
}
Write-Verbose "Using certificate with thumbprint $CertHash"
}
else {
$cert = .\cert.ps1 Get -Name $(.\constants.ps1 CERT_NAME)
if ($cert -eq $null) {
# No valid cert exists, we must create one to enable HTTPS
Write-Verbose "Creating new IIS Administration Certificate"
$cert = .\cert.ps1 New -Name $(.\constants.ps1 CERT_NAME)
$rollbackStore.createdCertThumbprint = $cert.Thumbprint;
Write-Verbose "Adding the certificate to trusted store"
.\cert.ps1 AddToTrusted -Certificate $cert
}
else {
# There is already a Microsoft IIS Administration Certificate on the computer that we can use for the API
Write-Verbose "Using pre-existing IIS Administration Certificate"
}
}
# Create a hash table storing parameters of the installation that can be used when configuring or uninstalling
# at a later date
$installObject = @{
InstallPath = $Path
Port = $Port
ServiceName = $ServiceName
Version = $Version
Installer = $([System.Environment]::UserDomainName + '/' + [System.Environment]::UserName)
Date = date
CertificateThumbprint = $cert.thumbprint
}
.\installationconfig.ps1 Write-Config -ConfigObject $installObject -Path $adminRoot
# Get the certificate currently bound on desired installation port if any
$preboundCert = .\network.ps1 GetSslBindingInfo -Port $Port
# If a certificate is bound we delete it to bind our cert
if ( $preboundCert -ne $null) {
$rollbackStore.preboundCertInfo = $preboundCert
# Remove any preexisting HTTPS. binding on the specified port
Write-Verbose "Deleting certificate from port $Port in HTTP.Sys"
.\network.ps1 DeleteSslBinding -Port $Port | Out-Null
}
Write-Verbose "Binding Certificate to port $Port in HTTP.Sys"
.\network.ps1 BindCert -Hash $cert.thumbprint -Port $Port -AppId $(.\constants.ps1 IIS_HWC_APP_ID) | Out-Null
$rollbackStore.newBoundCertPort = $Port
$platform = "onecore"
if (!$(.\constants.ps1 ONECORE)) {
$platform = "win32"
}
# Register the Self Host exe as a service
$svcExePath = Join-Path $adminRoot "host\x64\$platform\Microsoft.IIS.Host.exe"
sc.exe create "$ServiceName" binpath= "$svcExePath -appHostConfig:\`"$appHostPath\`" -serviceName:\`"$ServiceName\`"" start= auto
$rollbackStore.createdService = $ServiceName
try {
Start-Service "$ServiceName" -ErrorAction Stop
}
catch {
throw "Could not start service"
}
$svc = Get-Service "$ServiceName" -ErrorAction SilentlyContinue
if ($svc -eq $null) {
throw "Could not install service"
}
sc.exe description "$ServiceName" "$(.\constants.ps1 SERVICE_DESCRIPTION)" | Out-Null
.\cache.ps1 Destroy
write-host Service installed, URI: https://localhost:$Port
}
try {
Push-Location $(Get-ScriptDirectory)
CheckInstallParameters
InstallationPreparationCheck
Install
exit 0
}
catch {
rollback
throw
}
finally {
Pop-Location
}

Просмотреть файл

@ -0,0 +1,170 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Get",
"Exists",
"Get-UserFileMap",
"Write-Config",
"Write-AppHost")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[object]
$ConfigObject,
[parameter()]
[string]
$AppHostPath,
[parameter()]
[string]
$ApplicationPath,
[parameter()]
[int]
$Port
)
# Name of file we place installation data in
$INSTALL_FILE = "setup.config"
function Get-UserFileMap {
return @{
"applicationHost.config" = "host/applicationHost.config"
"web.config" = "Microsoft.IIS.administration/web.config"
"modules.json" = "Microsoft.IIS.administration/config/modules.json"
"config.json" = "Microsoft.IIS.administration/config/appsettings.json"
"api-keys.json" = "Microsoft.IIS.administration/config/api-keys.json"
}
}
function Exists($_path) {
if ([string]::IsNullOrEmpty($_path)) {
throw "Path required."
}
return Test-Path (Join-Path $_path $INSTALL_FILE)
}
function Get($_path) {
if ([string]::IsNullOrEmpty($_path)) {
throw "Path required."
}
if (-not(Exists $_path)) {
return $null
}
[xml]$configXml = Get-Content (Join-Path $_path $INSTALL_FILE)
$config = $configXml.Configuration
$userFiles = Get-UserFileMap
$installConfig = @{
InstallPath = $config.InstallPath
Port = $config.Port
ServiceName = $config.ServiceName
Version = $config.Version
UserFiles = $userFiles
Installer = $config.Installer
Date = $config.Date
CertificateThumbprint = $config.CertificateThumbprint
}
return $installConfig
}
function Write-Config($obj, $_path) {
if ([string]::IsNullOrEmpty($_path)) {
throw "Path required."
}
$xml = [xml]""
$xConfig = $xml.CreateElement("Configuration")
foreach ($key in $obj.keys) {
$xElem = $xml.CreateElement($key)
$xElem.InnerText = $obj[$key]
$xConfig.AppendChild($xElem) | Out-Null
}
$xml.AppendChild($xConfig) | Out-Null
$sw = New-Object System.IO.StreamWriter -ArgumentList (Join-Path $_path $INSTALL_FILE)
$xml.Save($sw) | Out-Null
$sw.Dispose()
}
function Write-AppHost($_appHostPath, $_applicationPath, $_port) {
if ([string]::IsNullOrEmpty($_appHostPath)) {
throw "AppHostPath required."
}
if ([string]::IsNullOrEmpty($_applicationPath)) {
throw "ApplicationPath required."
}
if ($_port -eq 0) {
throw "Port required."
}
$IISAdminSiteName = "IISAdmin"
[xml]$xml = Get-Content -Path "$_appHostPath"
$sites = $xml.GetElementsByTagName("site")
$site = $null;
foreach ($s in $sites) {
if ($s.name -eq $IISAdminSiteName) {
$site = $s;
}
}
if ($site -eq $null) {
throw "Installation applicationHost.config does not contain IISAdmin site"
}
$site.application.virtualDirectory.SetAttribute("physicalPath", "$_applicationPath")
$site.bindings.binding.SetAttribute("bindingInformation", "*:$($_port):")
$sw = New-Object System.IO.StreamWriter -ArgumentList $_appHostPath
$xml.Save($sw)
$sw.Dispose()
}
switch ($Command)
{
"Get"
{
return Get $Path
}
"Exists"
{
return Exists $Path
}
"Get-UserFileMap"
{
return Get-UserFileMap
}
"Write-Config"
{
Write-Config $ConfigObject $Path
}
"Write-AppHost"
{
Write-AppHost $AppHostPath $ApplicationPath $Port
}
default
{
throw "Unknown command"
}
}

298
scripts/setup/migrate.ps1 Normal file
Просмотреть файл

@ -0,0 +1,298 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#Requires -RunAsAdministrator
#Requires -Version 4.0
Param (
[parameter(Mandatory=$true)]
[string]
$Destination,
[parameter(Mandatory=$true)]
[string]
$Source
)
$Script:migrateRollback = @{}
function Cache-UserFiles($root) {
# Copy all user files to temporary location
$userFiles = .\installationconfig.ps1 Get-UserFileMap
foreach ($fileName in $userFiles.keys) {
try {
.\cache.ps1 Store -Path $(Join-Path $root $userFiles[$fileName]) -Name $userFiles[$fileName]
}
catch {
# Not generated until first run
if ($fileName -eq 'api-keys.json') {
Write-Warning "$filename could not be found for backup"
continue;
}
Write-Warning $_.Exception.Message
Write-Warning "Could not temporarily cache application files"
throw;
}
}
}
function RestoreUserFiles($root) {
$userFiles = .\installationconfig.ps1 Get-UserFileMap
foreach ($fileName in $userFiles.keys) {
$item = .\cache.ps1 Get -Name $userFiles[$fileName]
if ($item -ne $null) {
Copy-Item -Force $item.FullName (Join-Path $root $userFiles[$fileName])
}
}
}
function Rollback {
Write-Warning "Rolling back migration."
#
# Stop new service that we started
if ($migrateRollback.startedNewService -ne $null) {
try {
Stop-Service $migrateRollback.startedNewService -ErrorAction Stop
}
catch {
Write-Warning "Could not stop newly created service $($migrateRollback.startedNewService)"
}
}
#
# Restore destination service we may have deleted
if ($migrateRollback.deletedDestinationSvc -ne $null) {
$name = $migrateRollback.deletedDestinationSvc.Name
$startType = $migrateRollback.deletedDestinationSvcStartType
$binaryPath = $migrateRollback.deletedDestinationSvcImagePath
Write-Host "Rolling back service $name"
try {
New-Service -BinaryPathName $binaryPath -StartupType $startType -DisplayName $name -Name $name -ErrorAction Stop | Out-Null
}
catch {
Write-Warning "Could not restore the $($name) service."
}
}
#
# Delete new service we created
if ($migrateRollback.createdNewService -ne $null) {
Write-Host "Rolling back service creation"
try {
sc.exe delete "$($migrateRollback.createdNewService)" | Out-Null
}
catch {
Write-Warning "Could not remove newly created service '$($migrateRollback.createdNewService)'"
}
}
#
# Restore source service we may have deleted
if ($migrateRollback.deletedSourceSvc -ne $null) {
$name = $migrateRollback.deletedSourceSvc.Name
$startType = $migrateRollback.deletedSourceSvcStartType
$binaryPath = $migrateRollback.deletedSourceSvcImagePath
Write-Host "Rolling back service $name"
try {
New-Service -BinaryPathName $binaryPath -StartupType $startType -DisplayName $name -Name $name -ErrorAction Stop | Out-Null
}
catch {
Write-Warning "Could not restore the $($name) service."
}
}
#
# Restore destination user files we cached
if ($migrateRollback.cachedUserFiles -ne $null) {
"Rolling back destination application files"
RestoreUserFiles $migrateRollback.cachedUserFiles
}
#
# Restart destination service we stopped
if ($migrateRollback.stoppedDestinationService -ne $null) {
Write-Host "Restarting the $($migrateRollback.stoppedDestinationService) service."
try {
Start-Service $migrateRollback.stoppedDestinationService -ErrorAction Stop
}
catch {
Write-Warning "Could not restart destination service"
}
}
#
# Restart source service we stopped
if ($migrateRollback.stoppedSourceService -ne $null) {
Write-Host "Restarting the $($migrateRollback.stoppedSourceService) service."
try {
Start-Service $migrateRollback.stoppedSourceService -ErrorAction Stop
}
catch {
Write-Warning "Could not restart source service"
}
}
}
function Migrate {
if ([System.String]::IsNullOrEmpty($Source)) {
throw "Source path required."
}
if ([System.String]::IsNullOrEmpty($Destination)) {
throw "Destination path required."
}
$sourceSettings = .\installationconfig.ps1 Get -Path $Source
if ($sourceSettings -eq $null) {
throw "Cannot find installation settings for source."
}
if ([string]::IsNullOrEmpty($sourceSettings.ServiceName)) {
throw "Cannot find source service name."
}
$destinationSettings = .\installationconfig.ps1 Get -Path $Destination
if ($destinationSettings -eq $null) {
throw "Cannot find installation settings for destination."
}
if ([string]::IsNullOrEmpty($destinationSettings.ServiceName)) {
throw "Cannot find destination service name."
}
$sourceSvc = Get-Service $sourceSettings.ServiceName
$destinationSvc = Get-Service $destinationSettings.ServiceName
if ($destinationSvc -eq $null) {
throw "Destination service not found"
}
if ($sourceSvc -ne $null -and $sourceSvc.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
Stop-Service $sourceSvc -ErrorAction Stop
$migrateRollback.stoppedSourceService = $sourceSvc.Name
}
if ($destinationSvc.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
Stop-Service $destinationSvc -ErrorAction Stop
$migrateRollback.stoppedDestinationService = $destinationSvc.Name
}
$userFiles = .\installationconfig.ps1 Get-UserFileMap
Cache-UserFiles $Destination
$migrateRollback.cachedUserFiles = $Destination
# Modules should be the union of destination and source modules
$oldModules = .\modules.ps1 Get-JsonContent -Path $(Join-Path $Source $userFiles["modules.json"])
$newModules = .\modules.ps1 Get-JsonContent -Path $(Join-Path $Destination $userFiles["modules.json"])
$oldModules.modules = .\modules.ps1 Add-NewModules -OldModules $oldModules.modules -NewModules $newModules.modules
foreach ($fileName in $userFiles.keys) {
Copy-Item -Force -Recurse $(Join-Path $Source $userFiles[$fileName]) $(Join-Path $Destination $userFiles[$fileName]) -ErrorAction SilentlyContinue
}
.\modules.ps1 Set-JsonContent -Path $(Join-Path $Destination $userFiles["modules.json"]) -JsonObject $oldModules
$appHostPath = Join-Path $Destination host\applicationHost.config
$appPath = Join-Path $Destination Microsoft.IIS.Administration
$Port = $sourceSettings.Port
# Configure applicationHost.config based on install parameters
.\installationconfig.ps1 Write-AppHost -AppHostPath $appHostPath -ApplicationPath $appPath -Port $Port
Start-Service $destinationSvc -ErrorAction Stop
Stop-Service $destinationSvc -ErrorAction Stop
if ($sourceSvc -ne $null) {
$svc = .\services.ps1 Get-ServiceAsWmiObject -Name $sourceSvc.Name
if ($svc -eq $null) {
throw "Could not access service information through WMI."
}
$migrateRollback.deletedSourceSvc = $sourceSvc
$migrateRollback.deletedSourceSvcStartType = $sourceSvc.StartType
$migrateRollback.deletedSourceSvcImagePath = $svc.PathName
sc.exe delete "$($sourceSvc.Name)" | Out-Null
if ($LASTEXITCODE -ne 0) {
$migrateRollback.deletedSourceSvc = $null
throw "Could not delete source service"
}
}
$platform = "onecore"
if (!$ONECORE) {
$platform = "win32"
}
# Register the Self Host exe as a service
$svcExePath = Join-Path $destination "host\x64\$platform\Microsoft.IIS.Host.exe"
sc.exe create "$($sourceSettings.ServiceName)" binpath= "$svcExePath -appHostConfig:\`"$appHostPath\`" -serviceName:\`"$($sourceSvc.Name)\`"" start= auto | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Could not create new service"
}
$migrateRollback.createdNewService = $sourceSettings.ServiceName
if ($sourceSvc -ne $null -and $destinationSvc.Name -ne $sourceSvc.Name) {
$svc = .\services.ps1 Get-ServiceAsWmiObject -Name $destinationSvc.Name
if ($svc -eq $null) {
throw "Could not access service information through WMI."
}
$migrateRollback.deletedDestinationSvc = $destinationSvc
$migrateRollback.deletedDestinationSvcStartType = $destinationSvc.StartType
$migrateRollback.deletedDestinationSvcImagePath = $svc.PathName
sc.exe delete "$($destinationSvc.Name)" | Out-Null
if ($LASTEXITCODE -ne 0) {
$migrateRollback.deletedDestinationSvc -eq $null
throw "Could not delete destination service"
}
}
$svc = Get-Service $sourceSvc.Name
Start-Service $svc -ErrorAction Stop
$migrateRollback.startedNewService = $sourceSettings.ServiceName
$installObject = @{
InstallPath = $Destination
Port = $Port
ServiceName = $sourceSvc.Name
Version = $destinationSettings.Version
Installer = $([System.Environment]::UserDomainName + '/' + [System.Environment]::UserName)
Date = Date
CertificateThumbprint = $sourceSettings.CertificateThumbprint
}
.\installationconfig.ps1 Write-Config -ConfigObject $installObject -Path $Destination
Write-Host "Migration complete, URI: https://localhost:$Port"
}
try {
Migrate
}
catch {
Rollback
throw
}

114
scripts/setup/modules.ps1 Normal file
Просмотреть файл

@ -0,0 +1,114 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Get-JsonContent",
"Add-NewModules",
"Set-JsonContent")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[System.Array]
$OldModules,
[parameter()]
[System.Array]
$NewModules,
[parameter()]
[System.Object]
$JsonObject
)
function Get-JsonContent($_path)
{
if ([System.String]::IsNullOrEmpty($_path)) {
throw "Path required"
}
if (-not(Test-Path $_path)) {
throw "$_path not found."
}
$lines = Get-Content $Path
$content = ""
foreach ($line in $lines) {
$content += $line
}
return ConvertFrom-Json $content
}
function Set-JsonContent($_path, $jsonObject) {
if ([System.String]::IsNullOrEmpty($_path)) {
throw "Path required"
}
if ($jsonObject -eq $null) {
throw "JsonObject required"
}
New-Item -Type File $_path -Force -ErrorAction Stop | Out-Null
ConvertTo-Json $jsonObject -Depth 100 | Out-File $_path -ErrorAction Stop
}
function Add-NewModules($_oldModules, $_newModules) {
if ($_oldModules -eq $null) {
throw "OldModules required"
}
if ($_newModules -eq $null) {
throw "NewModules required"
}
foreach ($module in $_newModules) {
$exists = $false
foreach ($oldModule in $_oldModules) {
if ($oldModule.name -eq $module.name) {
$exists = $true
break
}
}
if (-not($exists)) {
$_oldModules += $module
}
}
return $_oldModules
}
switch ($Command)
{
"Add-NewModules"
{
return Add-NewModules $OldModules $NewModules
}
"Get-JsonContent"
{
return Get-JsonContent $Path
}
"Set-JsonContent"
{
Set-JsonContent $Path $JsonObject
}
default
{
throw "Unknown command"
}
}

149
scripts/setup/msi-setup.ps1 Normal file
Просмотреть файл

@ -0,0 +1,149 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#Requires -RunAsAdministrator
#Requires -Version 4.0
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Install",
"Uninstall",
"Upgrade")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[string]
$Version
)
function Get-ScriptDirectory {
Split-Path $script:MyInvocation.MyCommand.Path
}
function Require-Script($name) {
$p = Join-Path $(Get-ScriptDirectory) $("$name.ps1")
if (-not(Test-Path $p)) {
throw "Could not find required script $name"
}
}
function CheckParameters() {
if ([string]::IsNullOrEmpty($Path)) {
throw "Path required."
}
if ([string]::IsNullOrEmpty($Version)) {
throw "Version required."
}
$Script:Path = $Path.Trim(' ')
$Script:Path = $Path.Trim('"')
}
function Install() {
$adminRoot = $Path
$ServiceName = .\constants.ps1 DEFAULT_SERVICE_NAME
$svc = Get-Service $ServiceName -ErrorAction SilentlyContinue
if ($svc -ne $null) {
throw "$ServiceName already exists."
}
$Port = 55539
if ($Upgrade) {
$Port = 0
}
.\install.ps1 -Path $adminRoot -Port $Port -DistributablePath $Path -Version $Version -ServiceName $ServiceName -DontCopy
}
function Upgrade() {
$adminRoot = $Path
$ServiceName = .\constants.ps1 DEFAULT_SERVICE_NAME
$latest = .\versioning.ps1 Get-Latest -Path $Path -ServiceName $ServiceName
if ($latest -eq $null) {
throw "Could not find installation to upgrade from"
}
$ver = $(Get-Item $latest).Name
if ($(.\versioning.ps1 Compare-Version -Left $Version -Right $ver) -le 0) {
throw "Cannot upgrade from $ver to $Version."
}
$svc = Get-Service $ServiceName -ErrorAction SilentlyContinue
if ($svc -ne $null) {
$ServiceName = $ServiceName + " $Version"
}
$installed = $false
try {
.\install.ps1 -Path $adminRoot -Port 0 -DistributablePath $Path -Version $Version -ServiceName $ServiceName -DontCopy
$installed = $true
.\migrate.ps1 -Source $latest -Destination $(Join-Path $adminRoot $Version)
}
catch {
if ($installed) {
.\uninstall.ps1 -Path $adminRoot -Version $Version -KeepFiles
}
throw $_
}
}
function Uninstall() {
$adminRoot = Join-Path $Path $Version
.\uninstall.ps1 -Path $adminRoot -KeepFiles
}
Require-Script "acl"
Require-Script "activedirectory"
Require-Script "cache"
Require-Script "cert"
Require-Script "constants"
Require-Script "dependencies"
Require-Script "installationconfig"
Require-Script "migrate"
Require-Script "modules"
Require-Script "network"
Require-Script "services"
Require-Script "uninstall"
$exitCode = 0
try {
Push-Location $(Get-ScriptDirectory)
switch($Command)
{
"Install"
{
CheckParameters
Install
}
"Upgrade"
{
CheckParameters
Upgrade
}
"Uninstall"
{
CheckParameters
Uninstall
}
default
{
throw "Unknown command"
}
}
}
catch {
throw
}
finally {
Pop-Location
}

165
scripts/setup/network.ps1 Normal file
Просмотреть файл

@ -0,0 +1,165 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("IsAvailable",
"GetAvailable",
"HasSslBinding",
"GetSslBindingInfo",
"BindCert",
"DeleteSslBinding")]
[string]
$Command,
[parameter()]
[int]
$Port,
[parameter()]
[string]
$Hash,
[parameter()]
[string]
$AppId
)
$MAX_PORT = 65535
$MIN_PORT = 1
function ValidatePort($portNo) {
if ($portNo -lt $MIN_PORT -or $portNo -gt $MAX_PORT) {
throw "Please specify a valid port. ($MIN_PORT - $MAX_PORT)"
}
}
function PortAvailable($portNo)
{
ValidatePort($portNo)
$tcp = New-Object System.Net.Sockets.TcpClient
Try
{
$tcp.connect('localhost', $portNo)
return $false
}
Catch
{
return $true
}
Finally
{
$tcp.Dispose()
}
}
function GetAvailablePort($startPort) {
ValidatePort($startPort)
$available = PortAvailable $startPort
while ($startPort -le 65535) {
if ($available) {
return $startPort
}
$startPort = $startPort + 1
$available = PortAvailable $startPort
}
throw "No available port found"
}
function SslBindingExists($portNo)
{
ValidatePort $portno
$httpsys_binding = netsh http show sslcert ipport=0.0.0.0:$portno | where { $_ -match "IP" }
return $httpsys_binding -ne $null
}
function GetBoundCertificateInfo($portNo) {
ValidatePort($portNo)
$s = netsh http show sslcert ipport="0.0.0.0:$portNo"
$certHashStr = 'Certificate Hash : '
$appIdStr = 'Application ID : '
$ipPortStr = 'IP:port : '
if($s[5] -eq $null -or $s[5].IndexOf($certHashStr) -eq -1) {
return $null
}
$start = $s[5].IndexOf($certHashStr) + $certHashStr.Length
$thumbprint = $s[5].Substring($start, $s[5].Length - $start)
$start = $s[6].IndexOf($appIdStr) + $appIdStr.Length
$appId = $s[6].Substring($start, $s[6].Length - $start)
$start = $s[4].IndexOf($ipPortStr) + $ipPortStr.Length
$ipPort = $s[4].Substring($start, $s[4].Length - $start)
return @{
thumbprint = $thumbprint
appId = $appId
ipPort = $ipPort
}
}
function BindCertificate($_hash, $portNo, $_appId)
{
ValidatePort($portNo)
netsh http add sslcert ipport="0.0.0.0:$portNo" certhash="$_hash" appid=$_appId
}
function DeleteHttpsBinding($portNo)
{
ValidatePort($portNo)
if(SslBindingExists $portNo) {
netsh http delete sslcert ipport="0.0.0.0:$portNo" | Out-Null
}
else {
Write-Verbose "No HTTP.Sys binding exists for port $portNo"
}
}
switch($Command)
{
"IsAvailable"
{
return PortAvailable $Port
}
"GetAvailable"
{
return GetAvailablePort $Port
}
"HasSslBinding"
{
return SslBindingExists $Port
}
"GetSslBindingInfo"
{
return GetBoundCertificateInfo $Port
}
"BindCert"
{
return BindCertificate $Hash $Port $AppId
}
"DeleteSslBinding"
{
return DeleteHttpsBinding $Port
}
default
{
throw "Unknown command"
}
}

Просмотреть файл

@ -0,0 +1,76 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Is-Owner",
"Get-ServiceAsWmiObject")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[System.ServiceProcess.ServiceController]
$Service,
[parameter()]
[string]
$Name
)
function IsOwner($_service, $_path) {
if ($_service -eq $null) {
throw "Service required."
}
if ([string]::IsNullOrEmpty($_path)) {
throw "Path required."
}
$ownsSvc = $false
$reg = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\$($_service.Name)"
$imagePath = $reg.ImagePath.Substring(0, $reg.ImagePath.IndexOf(".exe") + ".exe".Length)
$rootIndex = $imagePath.IndexOf("\host\x64")
if ($rootIndex -ne -1) {
$imageRoot = $imagePath.Substring(0, $rootIndex)
$ownsSvc = [System.IO.Path]::GetFullPath($_path) -eq [System.IO.Path]::GetFullPath($imageRoot)
}
return $ownsSvc
}
function Get-ServiceAsWmiObject($_name) {
if ([string]::IsNullOrEmpty($_name)) {
throw "Name required."
}
$query = New-Object "System.Management.WqlObjectQuery" -ArgumentList "Select * from Win32_Service where Name = '$_name'"
$searcher = New-Object "System.Management.ManagementObjectSearcher" -ArgumentList $query
$collection = $searcher.Get()
if ($collection.Count -gt 0) {
return $collection[0]
}
return $null
}
switch ($Command)
{
"Is-Owner"
{
return IsOwner $Service $Path
}
"Get-ServiceAsWmiObject"
{
return Get-ServiceAsWmiObject $Name
}
default
{
throw "Unknown command"
}
}

212
scripts/setup/setup.ps1 Normal file
Просмотреть файл

@ -0,0 +1,212 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#Requires -RunAsAdministrator
#Requires -Version 4.0
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Install",
"Uninstall",
"Upgrade")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[int]
$Port,
[parameter()]
[string]
$DistributablePath,
[parameter()]
[string]
$CertHash,
[parameter()]
[switch]
$SkipVerification,
[parameter()]
[switch]
$SkipIisAdministrators,
[parameter()]
[bool]
$DeleteCert,
[parameter()]
[bool]
$DeleteBinding
)
function Get-ScriptDirectory {
Split-Path $script:MyInvocation.MyCommand.Path
}
function Require-Script($name) {
$p = Join-Path $(Get-ScriptDirectory) $("$name.ps1")
if (-not(Test-Path $p)) {
throw "Could not find required script $name"
}
}
function CheckInstallParameters() {
if ([string]::IsNullOrEmpty($Path)) {
$Script:path = .\constants.ps1 DEFAULT_INSTALL_PATH
}
if ([String]::IsNullOrEmpty($DistributablePath)){
$script:DistributablePath = $(Resolve-Path $(Join-Path $(Get-ScriptDirectory) ..)).Path
}
$distributableDirectory = Get-Item $DistributablePath -ErrorAction SilentlyContinue
if ($distributableDirectory -eq $null -or !($distributableDirectory -is [System.IO.DirectoryInfo])){
Write-Verbose "Invalid DistributablePath directory: $DistributablePath"
}
$versionInfoPath = Join-Path $DistributablePath "Version.txt"
if(![System.IO.File]::Exists($versionInfoPath)) {
throw "Cannot find version information."
}
try {
$Script:Version = Get-Content $versionInfoPath -ErrorAction Stop
}
catch {
Write-Warning "Could not obtain version information."
throw $_
}
}
function CheckUninstallParameters() {
if ([string]::IsNullOrEmpty($Path)) {
$Script:path = .\constants.ps1 DEFAULT_INSTALL_PATH
}
}
function Install() {
$adminRoot = $Path
$latest = .\versioning.ps1 Get-Latest -Path $adminRoot
if ($latest -ne $null) {
Upgrade
}
else {
$ServiceName = .\constants.ps1 DEFAULT_SERVICE_NAME
.\install.ps1 -Path $adminRoot -Port $Port -SkipVerification:$SkipVerification -SkipIisAdministrators:$SkipIisAdministrators -DistributablePath $DistributablePath -CertHash $CertHash -Version $Version -ServiceName $ServiceName
}
}
function Upgrade() {
$adminRoot = $Path
$latest = .\versioning.ps1 Get-Latest -Path $adminRoot
if ($latest -eq $null) {
throw "Cannot find previous installation."
}
$item = Get-Item $latest
$latestVersion = $item.Name
if ($(.\versioning.ps1 Compare-Version -Left $Version -Right $latestVersion) -le 0) {
Write-Host "Application already installed"
return
}
$ServiceName = .\constants.ps1 DEFAULT_SERVICE_NAME
$svc = Get-Service $ServiceName -ErrorAction SilentlyContinue
if ($svc -ne $null) {
$ServiceName = $ServiceName + " $Version"
}
$installed = $false
try {
.\install.ps1 -Path $adminRoot -Version $Version -SkipVerification:$SkipVerification -SkipIisAdministrators:$SkipIisAdministrators -ServiceName $ServiceName -Port 0 -DistributablePath $DistributablePath -CertHash $CertHash
$installed = $true
.\migrate.ps1 -Source $latest -Destination $(Join-Path $adminRoot $Version)
}
catch {
if ($installed) {
.\uninstall.ps1 -Path $(Join-Path $adminRoot $Version)
}
throw $_
}
.\uninstall.ps1 -Path $latest
}
function Uninstall() {
$adminRoot = $Path
$children = Get-ChildItem -Directory $adminRoot
foreach ($child in $children) {
if (-not(.\installationconfig.ps1 Exists -Path $child.FullName)) {
throw "Cannot find setup.config file for uninstall. Cannot continue"
}
}
foreach ($child in $children) {
.\uninstall.ps1 -Path $child.FullName -DeleteCert:$DeleteCert -DeleteBinding:$DeleteBinding
}
$dir = Get-Item $adminRoot -ErrorAction SilentlyContinue
if ($dir -ne $null) {
Try
{
$files = Get-ChildItem $dir.FullName
Remove-Item $dir.FullName -Recurse -Force
Write-Host "Successfully removed installation folder."
}
Catch
{
Write-Warning $_.Exception.Message
Write-Warning "Could not remove installation folder"
}
}
}
Require-Script "acl"
Require-Script "activedirectory"
Require-Script "cache"
Require-Script "cert"
Require-Script "constants"
Require-Script "dependencies"
Require-Script "installationconfig"
Require-Script "migrate"
Require-Script "modules"
Require-Script "network"
Require-Script "services"
Require-Script "uninstall"
try {
Push-Location $(Get-ScriptDirectory)
switch($Command)
{
"Install"
{
CheckInstallParameters
Install
}
"Uninstall"
{
CheckUninstallParameters
Uninstall
}
default
{
throw "Unknown command"
}
}
}
finally {
Pop-Location
}

113
scripts/setup/uninstall.ps1 Normal file
Просмотреть файл

@ -0,0 +1,113 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#Requires -RunAsAdministrator
#Requires -Version 4.0
Param(
[parameter(Position=0)]
[string]
$Path,
[parameter()]
[switch]
$DeleteCert,
[parameter()]
[switch]
$DeleteBinding,
[parameter()]
[switch]
$DeleteGroup,
[parameter()]
[switch]
$KeepFiles
)
function Uninstall($_path)
{
$adminRoot = $_path
if (!(.\installationconfig.ps1 Exists -Path $adminRoot)) {
throw "Cannot find setup.config file for uninstall. Cannot continue"
}
$installedSettings = .\installationconfig.ps1 Get -Path $adminRoot
if ($Port -eq 0) {
$Port = $installedSettings.Port
}
if ([String]::IsNullOrEmpty($ServiceName)) {
$ServiceName = $installedSettings.ServiceName
}
$svc = Get-Service $ServiceName -ErrorAction SilentlyContinue
# Check if service belongs to the application being uninstalled
$ownsSvc = $false
if ($svc -ne $null) {
$ownsSvc = .\services.ps1 Is-Owner -Path $adminRoot -Service $svc
}
# Bring down the service before interacting with it in any way
if ($ownsSvc) {
Stop-Service $ServiceName
}
if ($DeleteCert) {
Write-Verbose "Deleting certificate"
.\cert.ps1 Delete -Thumbprint $installedSettings.CertificateThumbprint
}
if ($DeleteBinding) {
Write-Verbose "Deleting ssl binding"
.\network.ps1 DeleteSslBinding -Port $Port
}
if ($DeleteGroup) {
.\activedirectory.ps1 RemoveLocalGroup -Name $(.\constants.ps1 IISAdministratorsGroupName)
}
if ($ownsSvc) {
sc.exe delete $ServiceName
}
.\cache.ps1 Destroy
$InstallationDirectory = Get-Item $adminRoot -ErrorAction SilentlyContinue
if ($InstallationDirectory -ne $null -and -not($KeepFiles)) {
try {
.\acl.ps1 Add-SelfRights -Path $InstallationDirectory.FullName
}
catch {
Write-Warning "Unable to obtain full control of installation directory"
}
Try
{
$files = Get-ChildItem $InstallationDirectory.FullName
foreach ($file in $files) {
if ($file.name -ne "setup") {
Remove-Item $file.FullName -Force -Recurse -ErrorAction Stop
}
}
Remove-Item $InstallationDirectory.FullName -Recurse -Force
Write-Verbose "Successfully removed installation folder."
}
Catch
{
Write-Warning $_.Exception.Message
Write-Warning "Could not remove installation folder"
}
}
exit 0
}
Uninstall $Path

Просмотреть файл

@ -0,0 +1,135 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param (
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Get-Latest",
"Remove-Subversion",
"Get-SubVersion",
"Compare-Version")]
[string]
$Command,
[parameter()]
[string]
$Path,
[parameter()]
[string]
$ServiceName,
[parameter()]
[string]
$Version,
[parameter()]
[string]
$Left,
[parameter()]
[string]
$Right
)
function Normalize-Version($_version) {
$v = $_version.Split('.')
$newV = $v[0]
for ($i = 1; $i -lt 3 -and $i -lt $v.Length; $i++) {
$newV = $newV + "." + $v[$i]
}
$add = 3 - $v.Length
for ($i = 0; $i -lt $add; $i++) {
$newV = $newV + ".0"
}
return $newV
}
function Get-Latest($_path, $_serviceName) {
if ([string]::IsNullOrEmpty($_path)) {
throw "Path required."
}
if ([string]::IsNullOrEmpty($_serviceName)) {
$_serviceName = .\constants.ps1 DEFAULT_SERVICE_NAME
$serviceRequired = $true
}
$svc = Get-Service $_serviceName -ErrorAction SilentlyContinue
if (-not(Test-Path $_path)) {
return $null
}
$previousInstallations = Get-ChildItem -Directory $_path
# Check if there are any previous versions at the specified path
if ($previousInstallations.Length -eq 0) {
return $null
}
# Check if any of the previous versions are the current owner of the target service
if ($svc -ne $null) {
foreach ($previousInstallation in $previousInstallations) {
if (.\services.ps1 Is-Owner -Service $svc -Path $previousInstallation.FullName) {
return $previousInstallation.FullName
}
}
}
if ($serviceRequired) {
return $null
}
$vs = $previousInstallations | %{New-Object "System.Version" -ArgumentList $(Normalize-Version $_.Name)} | sort
# Default to the latest previous version that has a valid installation config specifying the target service
for ($i = $vs.Length -1; $i -ge 0; $i--) {
$installConfig = .\installationconfig.ps1 Get -Path (Join-Path $_path $vs[$i].ToString())
if ($installConfig -ne $null -and $installConfig.ServiceName -eq $_serviceName) {
return (Join-Path $_path $vs[$i].ToString())
}
}
#No valid previous version found
return $null
}
function Compare-Version($a, $b) {
if ([string]::IsNullOrEmpty($a)) {
throw "Left required."
}
if ([string]::IsNullOrEmpty($b)) {
throw "Right required."
}
$aParts = $a.Split('.')
$bParts = $b.Split('.')
for ($i = 0; $i -lt $aParts.Length; $i++) {
$val = $aParts[$i] - $bParts[$i]
if ($val -ne 0) {
return $val
}
}
return 0
}
switch ($Command)
{
"Get-Latest"
{
return Get-Latest $Path $ServiceName
}
"Compare-Version"
{
return Compare-Version $Left $Right
}
default
{
throw "Unknown command"
}
}

50
scripts/tests/upgrade.ps1 Normal file
Просмотреть файл

@ -0,0 +1,50 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
Param(
[parameter(Mandatory=$true , Position=0)]
[string]
$DistributablePath
)
function Get-ScriptDirectory {
Split-Path $script:MyInvocation.MyCommand.Path
}
function Bump-Version($path) {
$c = Get-Content $path -ErrorAction Stop
$v = New-Object "System.Version" -ArgumentList $c
$v = [System.Version]::new($v.Major, $v.Minor, $v.Build + 1)
$v.ToString() | Out-File $path
}
if (-not(Test-Path (Join-Path $DistributablePath setup\setup.ps1))) {
throw "Cannot find installation package"
}
$svc = Get-Service "Microsoft IIS Administration" -ErrorAction SilentlyContinue
if ($svc -ne $null) {
throw "Cannot run test, service already installed."
}
Write-Host "Installing fresh"
Invoke-Expression "$DistributablePath\setup\setup.ps1 Install -Verbose -DistributablePath '$DistributablePath'" -ErrorAction Stop
Write-Host "Generating API Key"
$key = Invoke-Expression "$(Get-ScriptDirectory)\..\utils.ps1 Generate-AccessToken -url 'https://localhost:55539'" -ErrorAction Stop
Write-Host "Bumping version"
Bump-Version "$DistributablePath\Version.txt"
Write-Host "Upgrading"
Invoke-Expression "$DistributablePath\setup\setup.ps1 Install -Verbose -DistributablePath '$DistributablePath'" -ErrorAction Stop
Write-Host "Testing for successful upgrade using generated key"
$response = Invoke-WebRequest -Headers @{'Access-Token' = "Bearer $key"} -Uri "https://localhost:55539/api" -UseDefaultCredentials -Method Get -ErrorAction Stop
if ($response.StatusCode -ne 200) {
throw $response
}
Write-Host "Uninstalling"
Invoke-Expression "$DistributablePath\setup\setup.ps1 Uninstall -Verbose"

62
scripts/utils.ps1 Normal file
Просмотреть файл

@ -0,0 +1,62 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#Requires -RunAsAdministrator
#Requires -Version 4.0
Param(
[parameter(Mandatory=$true , Position=0)]
[ValidateSet("Generate-AccessToken")]
[string]
$Command,
[parameter()]
[string]
$url
)
function Usage {
Write-Host "Commands:"
Write-Host "`tGenerate-AccessToken"
}
function Generate-AccessToken-Usage {
'Generate-AccessToken -url <apiUrl>'
Write-Host "-url:`t The url of the api that the key should be generated for."
}
function Generate-AccessToken-ParameterCheck {
if ([system.string]::IsNullOrEmpty($url)) {
Generate-AccessToken-Usage
Exit
}
}
function Generate-AccessToken($apiUrl) {
$res = Invoke-WebRequest "$apiUrl/security/api-keys" -UseDefaultCredentials -SessionVariable sess;
$hTok = $res.headers."XSRF-TOKEN";
$h = @{};
$h."XSRF-TOKEN" = $htok;
$res2 = Invoke-WebRequest "$apiUrl/security/api-keys" -Headers $h -Method Post -UseDefaultCredentials -ContentType "application/json" -WebSession $sess -Body '{"expires_on": ""}';
$jObj = ConvertFrom-Json ([System.Text.Encoding]::Default.GetString($res2.content))
return $jObj.access_token;
}
switch($Command)
{
"Generate-AccessToken"
{
Generate-AccessToken-ParameterCheck
$key = Generate-AccessToken $url
return $key
}
default
{
Usage
}
}

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using System;
using Core.Security;
internal static class AccessTokenHelper {
public static dynamic ToJsonModel(ApiToken token) {
if (token.Key == null) {
throw new ArgumentNullException(nameof(token.Key));
}
if (string.IsNullOrEmpty(token.Token)) {
throw new ArgumentNullException(nameof(token.Token));
}
dynamic obj = new {
id = token.Key.Id,
created_on = DateTime.UtcNow,
expires_on = (object)token.Key.ExpiresOn ?? string.Empty,
value = token.Token,
type = token.Key.TokenType,
api_key = ApiKeyHelper.ToJsonModelRef(token.Key)
};
return Core.Environment.Hal.Apply(Defines.AccessTokensResource.Guid, obj, true);
}
public static dynamic ToJsonModel(ApiKey key) {
if (key == null) {
throw new ArgumentNullException(nameof(key));
}
dynamic obj = new {
id = key.Id,
expires_on = (object)key.ExpiresOn ?? string.Empty,
type = key.TokenType,
api_key = ApiKeyHelper.ToJsonModelRef(key)
};
return Core.Environment.Hal.Apply(Defines.AccessTokensResource.Guid, obj, true);
}
public static string GetLocation(string id) {
if (string.IsNullOrEmpty(id)) {
throw new ArgumentNullException(nameof(id));
}
return $"/{Defines.ACCESSTOKENS_PATH}/{id}";
}
}
}

Просмотреть файл

@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using System;
using Core;
using Core.Security;
using Core.Utils;
internal static class ApiKeyHelper {
public static void Update(ApiKey key, dynamic model) {
if (key == null) {
throw new ArgumentNullException();
}
if (model == null) {
throw new ApiArgumentException("model");
}
//
// purpose
string purpose = DynamicHelper.Value(model.purpose);
if (purpose != null) {
key.Purpose = purpose;
}
//
// expires_on
DateTime? expiresOn = DynamicHelper.To<DateTime>(model.expires_on);
if (expiresOn != null) {
key.ExpiresOn = expiresOn;
}
//
// Set last modified
key.LastModified = DateTime.Now;
}
public static dynamic ToJsonModel(ApiToken token) {
var key = token.Key;
dynamic obj = new {
purpose = key.Purpose,
id = key.Id,
created_on = key.CreatedOn,
last_modified = key.LastModified,
expires_on = (object)key.ExpiresOn ?? string.Empty,
access_token = token.Token
};
return Core.Environment.Hal.Apply(Defines.ApiKeysResource.Guid, obj, true);
}
public static dynamic ToJsonModel(ApiKey key) {
if (key == null) {
throw new ArgumentNullException(nameof(key));
}
dynamic obj = new {
purpose = key.Purpose,
id = key.Id,
created_on = key.CreatedOn,
last_modified = key.LastModified,
expires_on = (object)key.ExpiresOn ?? string.Empty
};
return Core.Environment.Hal.Apply(Defines.ApiKeysResource.Guid, obj, true);
}
public static dynamic ToJsonModelRef(ApiKey key) {
if (key == null) {
throw new ArgumentNullException(nameof(key));
}
dynamic obj = new {
purpose = key.Purpose,
id = key.Id
};
return Core.Environment.Hal.Apply(Defines.ApiKeysResource.Guid, obj, false);
}
public static string GetLocation(string id) {
if (string.IsNullOrEmpty(id)) {
throw new ArgumentNullException(nameof(id));
}
return $"/{Defines.APIKEYS_PATH}/{id}";
}
}
}

Просмотреть файл

@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using AspNetCore.Antiforgery;
using AspNetCore.Authorization;
using AspNetCore.Cors;
using AspNetCore.Mvc;
using Core;
using Core.Security;
using Core.Utils;
using Extensions.Options;
/// <summary>
/// AccessTokensController:
///
// CORs MUST be explicitly disabled
// AntiForgery MUST be applied
/// </summary>
[Authorize]
[DisableCors]
public class AccessTokensController : ApiController {
IApiKeyProvider _keyProvider;
public AccessTokensController(IApiKeyProvider keyProvider) {
if (keyProvider == null) {
throw new ArgumentNullException(nameof(keyProvider));
}
_keyProvider = keyProvider;
}
[HttpGet]
public void Get() {
SetAntiForgeryTokens();
//
// Nothing to do here
Context.Response.StatusCode = (int)HttpStatusCode.NoContent;
}
[HttpGet]
[ResourceInfo(Name = Defines.AccessTokenName)]
public object Get(string id) {
SetAntiForgeryTokens();
ApiKey key = _keyProvider.GetKey(id);
if (key == null) {
return NotFound();
}
return AccessTokenHelper.ToJsonModel(key);
}
//[ValidateAntiForgeryToken]
[HttpPost]
[ResourceInfo(Name = Defines.AccessTokenName)]
public async Task<object> Post([FromBody] dynamic model) {
if (model == null) {
throw new ApiArgumentException("model");
}
if (model.api_key == null || model.api_key.id == null) {
throw new ApiArgumentException("api_key");
}
string apikeyId = DynamicHelper.Value(model.api_key.id);
ApiKey key = _keyProvider.GetKey(apikeyId);
if (key == null) {
return NotFound();
}
// Renew the token
ApiToken token = await _keyProvider.RenewToken(key);
//
// Create response
dynamic obj = AccessTokenHelper.ToJsonModel(token);
return Created(AccessTokenHelper.GetLocation(key.Id), obj);
}
private AntiforgeryTokenSet SetAntiForgeryTokens() {
IAntiforgery antiforgery = (IAntiforgery) Context.RequestServices.GetService(typeof(IAntiforgery));
IOptions<AntiforgeryOptions> options = (IOptions<AntiforgeryOptions>)Context.RequestServices.GetService(typeof(IOptions<AntiforgeryOptions>));
// Save cookie
AntiforgeryTokenSet tokens = antiforgery.GetAndStoreTokens(Context);
// Set response header
Context.Response.Headers[options.Value.FormFieldName] = tokens.RequestToken;
return tokens;
}
}
}

Просмотреть файл

@ -0,0 +1,146 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using AspNetCore.Antiforgery;
using AspNetCore.Authorization;
using AspNetCore.Cors;
using AspNetCore.Mvc;
using Core;
using Core.Http;
using Core.Security;
using Core.Utils;
using Extensions.Options;
/// <summary>
/// ApiKeysController:
///
// CORs MUST be explicitly disabled
// AntiForgery MUST be applied
/// </summary>
[Authorize]
[DisableCors]
public class ApiKeysController : ApiBaseController {
IApiKeyProvider _keyProvider;
public ApiKeysController(IApiKeyProvider keyProvider) {
if (keyProvider == null) {
throw new ArgumentNullException(nameof(keyProvider));
}
_keyProvider = keyProvider;
}
[HttpGet]
[ResourceInfo(Name = Defines.ApiKeysName)]
public object Get() {
SetAntiForgeryTokens();
IEnumerable<ApiKey> keys = _keyProvider.GetAllKeys();
// Set HTTP header for total count
Context.Response.SetItemsCount(keys.Count());
return new {
api_keys = keys.Select(k => ApiKeyHelper.ToJsonModel(k))
};
}
[HttpGet]
[ResourceInfo(Name = Defines.ApiKeyName)]
public object Get(string id) {
ApiKey key = _keyProvider.GetKey(id);
if (key == null) {
return NotFound();
}
SetAntiForgeryTokens();
return ApiKeyHelper.ToJsonModel(key);
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResourceInfo(Name = Defines.ApiKeyName)]
public async Task<object> Post([FromBody] dynamic model) {
if (model == null) {
throw new ApiArgumentException("model");
}
if (model.expires_on == null) {
throw new ApiArgumentException("expires_on");
}
string purpose = DynamicHelper.Value(model.purpose) ?? string.Empty;
DateTime? expiresOn = DynamicHelper.Value(model.expires_on) != String.Empty ? DynamicHelper.To<DateTime>(model.expires_on) : null;
ApiToken token = _keyProvider.GenerateKey(purpose);
token.Key.ExpiresOn = expiresOn;
await _keyProvider.SaveKey(token.Key);
//
// Create response
dynamic key = ApiKeyHelper.ToJsonModel(token);
return Created(ApiKeyHelper.GetLocation(key.id), key);
}
[ValidateAntiForgeryToken]
[HttpPatch]
[ResourceInfo(Name = Defines.ApiKeyName)]
public async Task<object> Patch(string id, [FromBody] dynamic model) {
ApiKey key = _keyProvider.GetKey(id);
if (key == null) {
return NotFound();
}
ApiKeyHelper.Update(key, model);
await _keyProvider.SaveKey(key);
return ApiKeyHelper.ToJsonModel(key);
}
[ValidateAntiForgeryToken]
[HttpDelete]
public async Task Delete(string id) {
ApiKey key = _keyProvider.GetKey(id);
if (key != null) {
await _keyProvider.DeleteKey(key);
}
//
// Success
Context.Response.StatusCode = (int)HttpStatusCode.NoContent;
}
private AntiforgeryTokenSet SetAntiForgeryTokens() {
IAntiforgery antiforgery = (IAntiforgery) Context.RequestServices.GetService(typeof(IAntiforgery));
IOptions<AntiforgeryOptions> options = (IOptions<AntiforgeryOptions>)Context.RequestServices.GetService(typeof(IOptions<AntiforgeryOptions>));
// Save cookie
AntiforgeryTokenSet tokens = antiforgery.GetAndStoreTokens(Context);
// Set response header
Context.Response.Headers[options.Value.FormFieldName] = tokens.RequestToken;
return tokens;
}
}
}

Просмотреть файл

@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNetCore.Mvc;
using Core;
using Core.Http;
using Core.Security;
public class MyTokenController : ApiBaseController {
IApiKeyProvider _keyProvider;
public MyTokenController(IApiKeyProvider keyProvider) {
if (keyProvider == null) {
throw new ArgumentNullException(nameof(keyProvider));
}
_keyProvider = keyProvider;
}
[HttpGet]
[ResourceInfo(Name = Defines.AccessTokenName)]
public object Get() {
ApiKey key = GetCurrentApiKey();
if (key == null) {
return NotFound();
}
return AccessTokenHelper.ToJsonModel(key);
}
[HttpPost]
[ResourceInfo(Name = Defines.AccessTokenName)]
public async Task<object> Post() {
ApiKey key = GetCurrentApiKey();
if (key == null) {
return NotFound();
}
// Renew the key
ApiToken newKey = await _keyProvider.RenewToken(key);
return Created(Request.RequestUri.PathAndQuery,
AccessTokenHelper.ToJsonModel(newKey));
}
private ApiKey GetCurrentApiKey() {
var principal = Context.User as ClaimsPrincipal;
if (principal == null) {
return null;
}
Claim tokenClaim = principal.Claims.Where(c => c.Type == Core.Security.ClaimTypes.AccessToken).FirstOrDefault();
if (tokenClaim == null) {
return null;
}
return _keyProvider.FindKey(tokenClaim.Value);
}
protected override string GetId() {
ApiKey key = GetCurrentApiKey();
if (key == null) {
throw new NotFoundException("Access Token");
}
return key.Id;
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using Core;
using System;
public class Defines
{
public const string AccessTokenName = "Microsoft.WebServer.AccessToken";
public const string ApiKeysName = "Microsoft.WebServer.ApiKeys";
public const string ApiKeyName = "Microsoft.WebServer.ApiKey";
//
// /api/access-token
private const string MYTOKEN_ENDPOINT = "access-token";
public static readonly string MYTOKEN_PATH = $"{Globals.API_PATH}/{MYTOKEN_ENDPOINT}";
public static readonly ResDef MyTokenResource = new ResDef("access_token", new Guid("5677562C-F938-4D81-A706-890FB43FB0C0"), MYTOKEN_ENDPOINT);
//
// /security/api-keys
private const string APIKEYS_ENDPOINT = "api-keys";
public static readonly string APIKEYS_PATH = $"{Globals.SECURITY_PATH}/{APIKEYS_ENDPOINT}";
public static readonly ResDef ApiKeysResource = new ResDef("api_keys", new Guid("2AECA7E1-581B-419D-95F0-320FB34B908F"), APIKEYS_ENDPOINT);
//
// /security/access-tokens
private const string ACCESSTOKENS_ENDPOINT = "access-tokens";
public static readonly string ACCESSTOKENS_PATH = $"{Globals.SECURITY_PATH}/{ACCESSTOKENS_ENDPOINT}";
public static readonly ResDef AccessTokensResource = new ResDef("access_tokens", new Guid("FAB2EA1E-49CE-49D7-868B-E93E8BE0B538"), ACCESSTOKENS_ENDPOINT);
}
}

Просмотреть файл

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>af203e48-eaa7-4486-b5ed-8a5007ac9536</ProjectGuid>
<RootNamespace>Microsoft.IIS.Administration.AccessManagement</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.AccessManagement {
using AspNetCore.Builder;
using Core;
using Core.Http;
public class Startup : BaseModule {
public Startup() {
}
public override void Start() {
ConfigureMyToken();
ConfigureApiKeys();
ConfigureAccessTokens();
}
private void ConfigureMyToken() {
Environment.Host.RouteBuilder.MapWebApiRoute(Defines.MyTokenResource.Guid, $"{Defines.MYTOKEN_PATH}", new { controller = "MyToken" });
Environment.Hal.ProvideLink(Defines.MyTokenResource.Guid, "self", _ => new { href = $"/{Defines.MYTOKEN_PATH}" });
Environment.Hal.ProvideLink(Globals.ApiResource.Guid, Defines.MyTokenResource.Name, _ => new { href = $"/{Defines.MYTOKEN_PATH}" });
}
private void ConfigureApiKeys() {
Environment.Host.RouteBuilder.MapWebApiRoute(Defines.ApiKeysResource.Guid, $"{Defines.APIKEYS_PATH}/{{id?}}", new { controller = "ApiKeys" });
Environment.Hal.ProvideLink(Defines.ApiKeysResource.Guid, "self", t => new { href = ApiKeyHelper.GetLocation(t.id) });
}
private void ConfigureAccessTokens() {
Environment.Host.RouteBuilder.MapWebApiRoute(Defines.AccessTokensResource.Guid, $"{Defines.ACCESSTOKENS_PATH}/{{id?}}", new { controller = "AccessTokens" });
Environment.Hal.ProvideLink(Defines.AccessTokensResource.Guid, "self", t => new { href = AccessTokenHelper.GetLocation(t.id) });
Environment.Hal.ProvideLink(Defines.ApiKeysResource.Guid, "access_token", k => new { href = AccessTokenHelper.GetLocation(k.id) });
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
{
"version": "1.0.0",
"description": "Microsoft.IIS.Administration.AccessManagement",
"authors": [ "Microsoft" ],
"frameworks": {
"netstandard1.6": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
]
}
},
"dependencies": {
"Microsoft.IIS.Administration.Core": "1.0.0"
}
}

Просмотреть файл

@ -0,0 +1,148 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Certificates
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using Core;
using System.Security.Cryptography;
public static class CertificateHelper
{
public const StoreName STORE_NAME = StoreName.My;
public const StoreLocation STORE_LOCATION = StoreLocation.LocalMachine;
// Contains IDisposables
public static IEnumerable<X509Certificate2> GetCertificates(StoreName storeName, StoreLocation storeLocation)
{
List<X509Certificate2> certs = new List<X509Certificate2>();
using (X509Store myStore = new X509Store(storeName, storeLocation)) {
myStore.Open(OpenFlags.OpenExistingOnly);
foreach (X509Certificate2 cert in myStore.Certificates) {
// Add certificates to a list which is easier to work with
certs.Add(cert);
}
}
return certs;
}
public static X509Certificate2 GetCert(string thumbprint, StoreName storeName, StoreLocation storeLocation)
{
X509Certificate2 targetCert = null;
using (X509Store store = new X509Store(storeName, storeLocation))
{
store.Open(OpenFlags.OpenExistingOnly);
foreach (X509Certificate2 cert in store.Certificates)
{
if (cert.Thumbprint == thumbprint)
{
targetCert = cert;
}
else
{
cert.Dispose();
}
}
}
return targetCert;
}
public static object ToJsonModelRef(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation)
{
if (cert == null) {
return null;
}
CertificateId id = new CertificateId(cert.Thumbprint, storeName, storeLocation);
var obj = new {
name = GetCertName(cert),
id = id.Uuid,
issued_by = cert.Issuer,
valid_to = cert.NotAfter,
thumbprint = cert.Thumbprint
};
return Core.Environment.Hal.Apply(Defines.Resource.Guid, obj, false);
}
public static object ToJsonModel(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation)
{
if (cert == null) {
return null;
}
CertificateId id = new CertificateId(cert.Thumbprint, storeName, storeLocation);
var obj = new {
name = GetCertName(cert),
friendly_name = cert.FriendlyName,
id = id.Uuid,
dns_name = cert.GetNameInfo(X509NameType.DnsName, false),
simple_name = cert.GetNameInfo(X509NameType.SimpleName, false),
issued_by = cert.Issuer,
subject = cert.Subject,
thumbprint = cert.Thumbprint,
hash_algorithm = cert.SignatureAlgorithm.FriendlyName,
valid_from = cert.NotBefore.ToUniversalTime(),
valid_to = cert.NotAfter.ToUniversalTime(),
version = cert.Version.ToString(),
intended_purposes = GetEnhancedUsages(cert),
store = new {
name = Enum.GetName(typeof(StoreName), storeName),
location = Enum.GetName(typeof(StoreLocation), storeLocation)
}
};
return Core.Environment.Hal.Apply(Defines.Resource.Guid, obj);
}
public static IEnumerable<string> GetEnhancedUsages(X509Certificate2 cert)
{
List<string> usages = new List<string>();
foreach (X509Extension extension in cert.Extensions) {
if (extension.Oid.FriendlyName == "Enhanced Key Usage") {
X509EnhancedKeyUsageExtension ext = (X509EnhancedKeyUsageExtension)extension;
OidCollection oids = ext.EnhancedKeyUsages;
foreach (Oid oid in oids) {
usages.Add(oid.FriendlyName);
}
}
}
return usages;
}
private static string GetCertName(X509Certificate2 cert)
{
if(!string.IsNullOrEmpty(cert.FriendlyName)) {
return cert.FriendlyName;
}
string dnsName = cert.GetNameInfo(X509NameType.DnsName, false);
if (!string.IsNullOrEmpty(dnsName)) {
return dnsName;
}
string simpleName = cert.GetNameInfo(X509NameType.SimpleName, false);
if (!string.IsNullOrEmpty(simpleName)) {
return simpleName;
}
return cert.Thumbprint;
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Certificates
{
using Core.Utils;
using System;
using System.Security.Cryptography.X509Certificates;
public class CertificateId
{
private const string PURPOSE = "Certificates";
private const char DELIMITER = '\n';
private const uint THUMBPRINT_INDEX = 0;
private const uint STORE_NAME_INDEX = 1;
private const uint STORE_LOCATION_INDEX = 2;
public string Thumbprint { get; private set; }
public StoreName StoreName { get; private set; }
public StoreLocation StoreLocation { get; private set; }
public string Uuid { get; private set; }
public CertificateId(string uuid)
{
if (string.IsNullOrEmpty(uuid)) {
throw new ArgumentNullException(uuid);
}
var info = Core.Utils.Uuid.Decode(uuid, PURPOSE).Split(DELIMITER);
this.Thumbprint = info[THUMBPRINT_INDEX];
this.StoreName = (StoreName) int.Parse(info[STORE_NAME_INDEX]);
this.StoreLocation = (StoreLocation) int.Parse(info[STORE_LOCATION_INDEX]);
//this.Id = long.Parse(Core.Utils.Uuid.Decode(uuid, PURPOSE));
this.Uuid = uuid;
}
public CertificateId(string thumbprint, StoreName storeName, StoreLocation storeLocation)
{
this.Thumbprint = thumbprint;
this.StoreName = storeName;
this.StoreLocation = storeLocation;
this.Uuid = Core.Utils.Uuid.Encode($"{ this.Thumbprint }{ DELIMITER }{ (int)this.StoreName }{ DELIMITER }{ (int)this.StoreLocation }", PURPOSE);
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Certificates
{
using AspNetCore.Mvc;
using System;
using System.Security.Cryptography.X509Certificates;
using System.Collections.Generic;
using System.Linq;
using Core.Http;
public class CertificatesController : ApiBaseController
{
[HttpGet]
public object Get()
{
List<object> refs = new List<object>();
// Filter for selecting certificates with specific purpose.
string intended_purpose = Context.Request.Query["intended_purpose"];
var certs = CertificateHelper.GetCertificates(CertificateHelper.STORE_NAME, CertificateHelper.STORE_LOCATION);
if (intended_purpose != null) {
// Filter based on intended purpose, select only the certificates that contain a matching usage
certs = certs.Where(cert => {
return CertificateHelper.GetEnhancedUsages(cert).Any(s => s.Equals(intended_purpose, StringComparison.OrdinalIgnoreCase));
}).ToList();
}
// Build references in the scope of the store because references have dependence on store name and location
foreach (X509Certificate2 cert in certs) {
refs.Add(CertificateHelper.ToJsonModelRef(cert, CertificateHelper.STORE_NAME, CertificateHelper.STORE_LOCATION));
cert.Dispose();
}
// All certs disposed.
certs = null;
// Set HTTP header for total count
this.Context.Response.SetItemsCount(refs.Count());
return new {
certificates = refs
};
}
[HttpGet]
public object Get(string id)
{
CertificateId certId = new CertificateId(id);
using (X509Certificate2 cert = CertificateHelper.GetCert(certId.Thumbprint, certId.StoreName, certId.StoreLocation)) {
if (cert == null) {
return NotFound();
}
return CertificateHelper.ToJsonModel(cert, certId.StoreName, certId.StoreLocation);
}
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Certificates
{
using Core;
using System;
public class Defines
{
private const string ENDPOINT = "certificates";
public const string Identifier = "cert.id";
public const string CertificatesName = "Microsoft.Certificates";
public const string CertificateName = "Microsoft.Certificate";
public static readonly string PATH = $"{Globals.API_PATH}/{ENDPOINT}";
public static readonly ResDef Resource = new ResDef("certificates", new Guid("C4C10AFC-3CDC-484D-9791-6D52E9E28B76"), ENDPOINT);
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>3e20985c-5629-41b0-a507-67c0af024644</ProjectGuid>
<RootNamespace>Microsoft.IIS.Administration.Certificates</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Certificates
{
using AspNetCore.Builder;
using Core;
using Core.Http;
public class Startup : BaseModule
{
public Startup() { }
public override void Start()
{
Environment.Host.RouteBuilder.MapWebApiRoute(Defines.Resource.Guid, $"{Defines.PATH}/{{id?}}", new { controller = "certificates" });
Environment.Hal.ProvideLink(Defines.Resource.Guid, "self", cert => new { href = $"/{Defines.PATH}/{cert.id}" });
Environment.Hal.ProvideLink(Globals.ApiResource.Guid, Defines.Resource.Name, _ => new { href = $"/{Defines.PATH}" });
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
{
"version": "1.0.0",
"description": "Microsoft.IIS.Administration.Certificates Class Library",
"authors": [ "Microsoft" ],
"frameworks": {
"netstandard1.6": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
]
}
},
"dependencies": {
"Microsoft.IIS.Administration.Core": "1.0.0",
"System.Security.Cryptography.X509Certificates": "4.1.0"
}
}

Просмотреть файл

@ -0,0 +1,402 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using AspNetCore.Http;
using AspNetCore.Mvc.Filters;
using Newtonsoft.Json.Linq;
using Security;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
public class AuditAttribute : ActionFilterAttribute
{
private const string AUDIT_TARGET = "IIS.ADMIN.AUDIT";
private const string HIDDEN_VALUE = "{HIDDEN}";
// 2D array because we store fields as arrays of segments, i.e. model.identity.password -> ["model", "identity", "password"]
private string[][] _enabledFields;
private string[][] _hiddenFields;
private bool _allFieldsEnabled = false;
public const string ALL = "*";
public static ILogger Logger { get; set; }
public AuditAttribute(string fields = "*", string hiddenFields = null)
{
_enabledFields = MakeFields(fields);
_hiddenFields = MakeFields(hiddenFields);
// Remove fields that are present in hidden fields to avoid unnecessary processing
_enabledFields = RemoveIntersection(_enabledFields, _hiddenFields);
// Check if user wants to enable auditing of all fields
foreach(string[] field in _enabledFields)
{
if(field[0].Equals(ALL))
{
_allFieldsEnabled = true;
}
}
}
public override void OnActionExecuted(ActionExecutedContext context)
{
if(IsSuccess(context.HttpContext.Response.StatusCode) && GetArgs(context.HttpContext).Count > 0)
{
string apiKeyId = GetRequestApiKeyId(context.HttpContext);
var sb = StartAudit(apiKeyId, context.HttpContext.Request.Method, context.HttpContext.Request.Path);
// Iterate over the action arguments we stored in the Http Context
foreach(var kvp in GetArgs(context.HttpContext))
{
// Clear any necessary hidden field values for the argument
var cachedState = ClearHiddenFields(kvp.Key, kvp.Value);
AppendArg(kvp.Key, kvp.Value, sb);
if (cachedState != null)
{
// Restore any hidden fields to their original state
RestoreHiddenFields(kvp.Key, kvp.Value, cachedState);
}
}
FinishAudit(sb);
}
ClearArgs(context.HttpContext);
base.OnActionExecuted(context);
}
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
// Store action arguments in the http context to be accessed later and logged if the request was successful
foreach (var arg in actionContext.ActionArguments.Keys)
{
foreach(string[] hf in _hiddenFields)
{
if(hf.Length == 1 && hf[0].Equals(arg, StringComparison.OrdinalIgnoreCase))
{
// Don't add hidden action argument
continue;
}
}
if(!_allFieldsEnabled)
{
foreach (string[] field in _enabledFields)
{
if (field[0].Equals(arg, StringComparison.OrdinalIgnoreCase))
{
if(field.Length > 1)
{
// Handle adding field with multiple segments like model.identity.cpu
var target = actionContext.ActionArguments[arg] as JToken;
if(target == null)
{
// Adding granular properties is only supported for JObject
break;
}
// Navigate through the object to the field that is targetted for auditing
for(int i = 1; i < field.Length; i++)
{
target = target[field[i]];
}
AddArg(actionContext.HttpContext, string.Join(".", field), target);
}
else
{
AddArg(actionContext.HttpContext, arg, actionContext.ActionArguments[arg]);
}
}
}
}
else
{
AddArg(actionContext.HttpContext, arg, actionContext.ActionArguments[arg]);
}
}
base.OnActionExecuting(actionContext);
}
private IList<KeyValuePair<string[], JToken>> ClearHiddenFields(string argName, dynamic value)
{
IList<KeyValuePair<string[], JToken>> cachedState = new List<KeyValuePair<string[], JToken>>();
// Granular hidden fields only supported for JObject
JObject v = value as JObject;
if(v == null)
{
return null;
}
// Always remove _links if applicable, only for the root object
var t = RemoveLinks(v);
if(t != null)
{
cachedState.Add(new KeyValuePair<string[], JToken>($"{argName}._links".Split('.'), t));
}
string[] argNameSegs = argName.Split('.');
// Check if hidden field is our field name and more i.e. field: model.identity | hidden field: model.identity.password
foreach(string[] hf in _hiddenFields)
{
bool match = hf.Length > argNameSegs.Length;
if(!match)
{
continue;
}
int i;
for(i = 0; i < argNameSegs.Length; i++)
{
if(!hf[i].Equals(argNameSegs[i], StringComparison.OrdinalIgnoreCase))
{
match = false;
break;
}
}
if(!match)
{
continue;
}
JToken seeker = v;
// i already at argNameSegs.length - 1;
for(; i < hf.Length - 1; i++)
{
if(seeker == null)
{
break;
}
seeker = seeker[hf[i]];
}
if(seeker == null)
{
continue;
}
cachedState.Add(new KeyValuePair<string[], JToken>(hf, seeker[hf[hf.Length - 1]]));
seeker[hf[hf.Length - 1]] = HIDDEN_VALUE;
}
return cachedState;
}
private void RestoreHiddenFields(string argName, dynamic value, IList<KeyValuePair<string[], JToken>> cachedState)
{
// Granular hidden fields only supported for JObject
JObject v = value as JObject;
if (v == null)
{
return;
}
string[] argNameSegs = argName.Split('.');
foreach (var kvp in cachedState)
{
JToken seeker = v;
string[] fieldSegs = kvp.Key;
// Position the seeker to the property we are trying to restore.
// i.e. to restore identity.data.password set seeker to the 'data' object
for (int i = argNameSegs.Length; i < fieldSegs.Length - 1; i++)
{
if(seeker == null)
{
break;
}
seeker = seeker[fieldSegs[i]];
}
// Index into the seeker to restore the cached value
if (seeker != null)
{
seeker[fieldSegs[fieldSegs.Length - 1]] = kvp.Value;
}
}
}
private StringBuilder StartAudit(string apiKeyId, string method, string path)
{
StringBuilder sb = new StringBuilder();
var nl = System.Environment.NewLine;
sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff zzz")}{nl}API Key ID: {apiKeyId}{nl}Method: {method}{nl}Path: {path}{nl}");
return sb;
}
private void AppendArg(string argName, dynamic argValue, StringBuilder sb)
{
if (sb == null)
{
throw new ArgumentNullException(nameof(sb));
}
sb.Append($"{argName}: {argValue.ToString()}{System.Environment.NewLine}");
}
private void FinishAudit(StringBuilder sb)
{
sb.Append(System.Environment.NewLine);
if(Logger != null)
{
Task.Run(() =>
{
Logger.Information(sb.ToString());
});
}
}
private bool IsSuccess(int statusCode)
{
return (statusCode >= 200 && statusCode < 400);
}
//
// The arguments that are targetted by auditing are stored in the Http Context Items store between the action executing and when the action finishes executing
// These are helpers to interact with where we put the arguments
//
private void AddArg(HttpContext context, string argName, object val)
{
if (!context.Items.ContainsKey(AUDIT_TARGET))
{
context.Items[AUDIT_TARGET] = new Dictionary<string, object>();
}
((Dictionary<string,object>) context.Items[AUDIT_TARGET])[argName] = val;
}
private Dictionary<string,object> GetArgs(HttpContext context)
{
if (!context.Items.ContainsKey(AUDIT_TARGET))
{
context.Items[AUDIT_TARGET] = new Dictionary<string, object>();
}
return ((Dictionary<string, object>)context.Items[AUDIT_TARGET]);
}
private void ClearArgs(HttpContext context)
{
context.Items[AUDIT_TARGET] = null;
}
private string[][] MakeFields(string input)
{
string[][] fields;
if (input != null)
{
var withPeriods = input.Split(',');
fields = new string[withPeriods.Length][];
for (int i = 0; i < withPeriods.Length; i++)
{
withPeriods[i] = withPeriods[i].Trim();
fields[i] = withPeriods[i].Split('.');
}
}
else
{
fields = new string[0][];
}
return fields;
}
// enabledFields minus hiddenFields
private string[][] RemoveIntersection(string[][] enabledFields, string[][] hiddenFields)
{
List<string[]> dissection = new List<string[]>();
foreach (string[] field in _enabledFields)
{
// Keep track of whether we want to keep this enabled field
bool addBack = true;
foreach (string[] hiddenField in _hiddenFields)
{
if(hiddenField.Length != field.Length)
{
continue;
}
int i = 0;
for (i = 0; i < hiddenField.Length; i++)
{
if (!hiddenField[i].Equals(field[i], StringComparison.OrdinalIgnoreCase))
{
break;
}
}
// If for loop checking equivalence of the field array got to the end addBack is false
addBack = i != hiddenField.Length;
}
if (addBack)
{
dissection.Add(field);
}
}
return dissection.ToArray();
}
private JToken RemoveLinks(JObject o)
{
JToken jToken = null;
if(o["_links"] != null)
{
jToken = o["_links"];
o["_links"] = null;
}
return jToken;
}
private string GetRequestApiKeyId(HttpContext context)
{
var principal = context.User as ClaimsPrincipal;
if (principal == null)
{
return null;
}
Claim tokenClaim = principal.Claims.Where(c => c.Type == Core.Security.ClaimTypes.AccessToken).FirstOrDefault();
IApiKeyProvider keyProvider = (IApiKeyProvider)context.RequestServices.GetService(typeof(IApiKeyProvider));
if (tokenClaim == null || keyProvider == null)
{
return null;
}
var requestKey = keyProvider.FindKey(tokenClaim.Value);
return requestKey == null ? null : requestKey.Id;
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using System.Diagnostics;
using System.Reflection;
public abstract class BaseModule : IModule
{
private string _version;
public abstract void Start();
public virtual void Stop() { }
public virtual string Version {
get {
if(this._version == null) {
this._version = FileVersionInfo.GetVersionInfo(this.GetType().GetTypeInfo().Assembly.Location).ProductVersion;
}
return this._version;
}
set {
this._version = value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using Newtonsoft.Json;
using System;
using System.IO;
using Utils;
public static class ConfigurationHelper
{
public static Guid HostId{ get; private set;}
public static Config.IConfiguration Config { get; set; }
public static void Initialize(string filePath)
{
dynamic configObject;
using (FileStream fs = new FileStream(filePath, FileMode.Open))
using (StreamReader sr = new StreamReader(fs)) {
configObject = JsonConvert.DeserializeObject(sr.ReadToEnd());
}
string id = DynamicHelper.Value(configObject.host_id);
if (string.IsNullOrEmpty(id)) {
Guid guid = Guid.NewGuid();
configObject.host_id = guid.ToString();
using (FileStream fs = new FileStream(filePath, FileMode.Truncate))
using (StreamWriter sw = new StreamWriter(fs)) {
sw.Write(JsonConvert.SerializeObject(configObject, Formatting.Indented));
sw.Flush();
}
HostId = guid;
}
else {
HostId = Guid.Parse(id);
}
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Config
{
using Cors;
using Logging;
using System;
using System.Collections.Generic;
public interface IConfiguration
{
Guid HostId { get; }
string HostName { get; }
IEnumerable<string> Administrators { get; }
string SiteCreationRoot { get; }
ILoggingConfiguration Logging { get; }
ILoggingConfiguration Auditing { get; }
ICorsConfiguration Cors { get; }
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Cors
{
using System.Collections.Generic;
public interface ICorsConfiguration
{
IEnumerable<Rule> Rules { get; }
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Logging
{
using Extensions.Logging;
using Serilog.Events;
public interface ILoggingConfiguration
{
bool Enabled { get; set; }
string LogsRoot { get; set; }
LogLevel MinLevel { get; set; }
string FileName { get; set; }
LogEventLevel ToLogEventLevel(LogLevel logLevel);
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Cors
{
public class Rule
{
public string Origin { get; set; }
public bool Allow { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using System;
public static class Environment
{
public static IAdminHost Host
{
get;
set;
}
public static IHalService Hal
{
get;
set;
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
using System;
public class AlreadyExistsException : ApiArgumentException {
public AlreadyExistsException(string paramName, Exception innerException = null) : base(paramName, innerException) {
}
public override dynamic GetApiError() {
return Http.ErrorHelper.AlreadyExistsError(ParamName);
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
using System;
public class ApiArgumentException : ArgumentException, IError {
public const string EXPECTED_ARRAY = "Expected array";
public const string EXPECTED_OBJECT = "Expected object";
private string _message;
public override string Message {
get {
return _message;
}
}
public ApiArgumentException(string paramName, Exception innerException = null) : base(null, paramName, innerException) {
this._message = null;
}
public ApiArgumentException(string paramName, string message, Exception innerException = null) : base(message, paramName, innerException) {
this._message = message;
}
public virtual dynamic GetApiError() {
return Http.ErrorHelper.ArgumentError(ParamName, Message);
}
}
public class ApiArgumentOutOfRangeException : ApiArgumentException {
private long _min;
private long _max;
public ApiArgumentOutOfRangeException(string paramName, long min, long max, Exception innerException = null)
: base (paramName, $"Value must be between {min} and {max} inclusive.", innerException) {
_min = min;
_max = max;
}
public ApiArgumentOutOfRangeException(string paramName, long min, long max, string message, Exception innerException = null)
: base(paramName, message, innerException)
{
_min = min;
_max = max;
}
public override dynamic GetApiError() {
return Http.ErrorHelper.ArgumentOutOfRangeError(ParamName, _min, _max, Message);
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
using System;
public class ApiException : Exception, IError {
public ApiException(string message, Exception innerException) : base(message, innerException) {
}
public virtual dynamic GetApiError() {
return Http.ErrorHelper.Error(Message);
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
using System;
public class LockedException : Exception, IError {
public string Name { get; private set; }
public LockedException(string name, Exception innerException = null) : base(name, innerException) {
Name = name;
}
public dynamic GetApiError() {
return Http.ErrorHelper.LockedError(Name);
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using System;
public class NotFoundException : ApiArgumentException
{
public NotFoundException(string paramName, Exception innerException = null) : base(paramName, innerException) {
}
public override dynamic GetApiError() {
return Http.ErrorHelper.NotFoundError(ParamName);
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
using System;
public class UnauthorizedArgumentException : ApiArgumentException {
public UnauthorizedArgumentException(string paramName, Exception innerException = null) : base(paramName, innerException) {
}
public override dynamic GetApiError() {
return Http.ErrorHelper.UnauthorizedArgumentError(ParamName);
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
using System;
public static class Globals {
// Root of all api paths
// Access Token IS available
public static readonly string API_PATH = "api";
// Root of all SECURITY areas
// The Host MUST maintain integrated security (Windows Auth, Client Cert, etc.)
// Access Token IS NOT available!
// CORs MUST be explicitly disabled
// AntiForgery MUST be applied
public static readonly string SECURITY_PATH = "security";
public static readonly ResDef ApiResource = new ResDef("api", new Guid("76049EA6-FFB7-415E-9E3D-FC6F03414663"), API_PATH);
public static readonly string PING_PATH = $"{API_PATH}/ping";
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using System;
using System.Collections.Generic;
public interface IHalService
{
void ProvideLink(Guid resourceId, string name, Func<dynamic, dynamic> func);
object Apply(Guid resourceId, object obj, bool all = true);
IDictionary<string, dynamic> Get(Guid resourceId, object obj, bool all = true);
}
}

Просмотреть файл

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
using System;
using System.Web.Http;
using AspNetCore.Authorization;
using AspNetCore.Http;
using AspNetCore.Mvc;
using Utils;
[Authorize]
public abstract class ApiBaseController : ApiController {
[HttpGet]
public object Edge(string edge, string id = "") {
edge = edge.ToLower();
if (edge == "self") {
return NotFound();
}
Guid guid = (Guid) ActionContext.RouteData.Values["resourceId"];
object obj = new {
id = !string.IsNullOrEmpty(id) ? id : GetId()
};
var links = Core.Environment.Hal.Get(guid, obj.ToExpando());
string redirectUrl = null;
object link;
if (links.TryGetValue(edge, out link)) {
redirectUrl = ((dynamic)link.ToExpando()).href;
}
if (redirectUrl == null || Context.Request.Path.StartsWithSegments(redirectUrl)) {
return NotFound();
}
//
// Append QueryString
var uri = new Uri(redirectUrl, UriKind.RelativeOrAbsolute);
// Make absolute Uri
if (!uri.IsAbsoluteUri) {
var baseUrl = new Uri(Request.RequestUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped), UriKind.Absolute);
uri = new Uri(baseUrl, redirectUrl);
}
var qs = QueryString.FromUriComponent(uri) + Context.Request.QueryString;
string location = uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped) + qs.ToUriComponent();
return Redirect(location);
}
public virtual LocationChangedResult LocationChanged(string location, object content) {
return new LocationChangedResult(location, content);
}
protected virtual string GetId() {
return string.Empty;
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
using System.Net;
public static class ErrorHelper {
public static dynamic Error(string message) {
return new {
title = "Server error",
detail = message ?? "",
status = (int)HttpStatusCode.InternalServerError
};
}
public static dynamic ArgumentError(string paramName, string message = null) {
if (paramName == "model") {
return new {
title = "Invalid JSON request object",
status = (int)HttpStatusCode.UnsupportedMediaType
};
}
if (string.IsNullOrEmpty(message)) {
return new {
title = "Invalid parameter",
name = paramName,
status = (int)HttpStatusCode.BadRequest
};
}
return new {
title = "Invalid parameter",
name = paramName,
detail = message,
status = (int)HttpStatusCode.BadRequest
};
}
public static dynamic ArgumentOutOfRangeError(string paramName, long min, long max, string message) {
return new
{
title = "Out of range",
name = paramName,
min_value = min,
max_value = max,
detail = message,
status = (int)HttpStatusCode.BadRequest
};
}
public static dynamic NotFoundError(string paramName) {
if (string.IsNullOrEmpty(paramName)) {
return new {
title = "Not found",
status = (int)HttpStatusCode.NotFound
};
}
return new {
title = "Not found",
name = paramName,
status = (int)HttpStatusCode.NotFound
};
}
public static dynamic AlreadyExistsError(string paramName) {
return new {
title = "Conflict",
detail = "Already exists",
name = paramName,
status = (int)HttpStatusCode.Conflict
};
}
public static dynamic LockedError(string name) {
return new {
title = "Object is locked",
name = name,
status = (int)HttpStatusCode.Forbidden
};
}
public static dynamic UnauthorizedArgumentError(string paramName) {
return new {
title = "Unauthorized",
name = paramName,
status = (int)HttpStatusCode.Unauthorized
};
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http
{
using AspNetCore.Http;
using System;
public static class HttpHelper
{
static IHttpContextAccessor _contextAccessor;
public static HttpContext Current
{
get
{
return _contextAccessor.HttpContext;
}
}
public static IHttpContextAccessor HttpContextAccessor
{
set
{
if (value == null) {
throw new ArgumentNullException("HttpContextAccessor");
}
_contextAccessor = value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
public static class HeaderNames {
public const string AcceptPatch = "Accept-Patch";
public const string CorsPrefix = "Access-Control";
public const string ContentLength = "Content-Length";
public const string ContentType = "Content-Type";
public const string Total_Count = "X-Total-Count";
public const string Access_Token = "Access-Token";
public const string X_Forwarded_Proto = "X-Forwarded-Proto";
public const string XSRF_TOKEN = "XSRF-TOKEN";
public const string Origin = "Origin";
}
public static class HeaderValues
{
public const string FormEncoded = "application/x-www-form-urlencoded";
public const string Hal = "application/hal+json";
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http
{
using AspNetCore.Http;
using Utils;
public static class HttpRequestxtensions
{
public static Fields GetFields(this HttpRequest request) {
string fieldsQuery = request.Query["fields"];
return new Fields(fieldsQuery != null ? fieldsQuery.Split(',') : null);
}
public static Filter GetFilter(this HttpRequest request)
{
return new Filter(request.Query);
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
using AspNetCore.Http;
public static class HttpResponseExtensions {
public static void SetItemsCount(this HttpResponse response, int count) {
response.Headers[HeaderNames.Total_Count] = count.ToString();
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
using AspNetCore.Routing;
using AspNetCore.Builder;
using System;
using Utils;
public static class IRouteBuilderExtensions {
public static IRouteBuilder MapWebApiRoute(this IRouteBuilder builder, Guid resourceId, string template, object defaults, bool skipEdge = false) {
//
// Store the resource guid
// After Module.Start it will be used to register Edge routes
dynamic def = (dynamic)defaults.ToExpando();
if (!skipEdge) {
def.resourceId = resourceId;
}
//
// Add Route
return builder.MapWebApiRoute(resourceId.ToString(), template, (object) def);
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
public static class JsonProblem {
public static readonly string CONTENT_TYPE = "application/problem+json";
public static readonly string CONTENT_LANG = "en";
public static readonly string JSON_CONTENT_TYPE = "application/json;charset=utf-8";
}
}

Просмотреть файл

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Http {
using System;
using AspNetCore.Mvc;
public class LocationChangedResult : ObjectResult {
public LocationChangedResult(string location, object value)
: base(value)
{
if (location == null) {
throw new ArgumentNullException(nameof(location));
}
//
// The 209 (Contents of Related) status code indicates that the server is redirecting the user agent to
// a different resource, as indicated by a URI in the Location header field, that is intended to provide
// an indirect response to the original request.
// http://www.w3.org/2014/02/2xx/draft-prudhommeaux-http-status-209
//
Location = location;
StatusCode = 209; // HTTP 209 Contents of Related
}
public string Location { get; set; }
public override void OnFormatting(ActionContext context) {
if (context == null) {
throw new ArgumentNullException(nameof(context));
}
base.OnFormatting(context);
if (Location != null) {
context.HttpContext.Response.Headers[Net.Http.Headers.HeaderNames.Location] = Location;
}
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using AspNetCore.Routing;
using AspNetCore.Builder;
/// <summary>
/// The IAdminHost is the interface that is exposed to modules loaded into the IIS Management Service. Any Action that a module needs to
/// delegate to the top level management service application is done through the methods defined in this interface. Module's should be able to
/// store a reference to the Management's Service implementation of the IAdminHost.
/// </summary>
public interface IAdminHost {
IApplicationBuilder ApplicationBuilder { get; }
IModule GetModuleByAssemblyName(string assemblyName);
IRouteBuilder RouteBuilder { get; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core {
public interface IError {
// HTTP APIs
// application/problem+json
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-02
dynamic GetApiError();
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
public interface IModule
{
void Start();
void Stop();
string Version { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a3adb83a-c602-4de4-a8dd-085fe5dd29cd</ProjectGuid>
<RootNamespace>Microsoft.IIS.Administration.Core</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using System;
public class ResDef
{
public string Name;
public Guid Guid;
public string Endpoint;
public ResDef(string resourceName, Guid resourceGuid, string endpoint)
{
this.Name = resourceName;
this.Guid = resourceGuid;
this.Endpoint = endpoint;
}
//Check if two ResDefs have any common fields.
public bool Overlaps(ResDef r)
{
return this.Guid.Equals(r.Guid)
|| String.Equals(this.Name, r.Name, StringComparison.OrdinalIgnoreCase)
|| String.Equals(this.Endpoint, r.Endpoint, StringComparison.OrdinalIgnoreCase);
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Security {
public static class ClaimTypes {
public static readonly string AccessToken = "http://iis.net/claims/accesstoken";
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Security {
using System;
public struct ApiToken {
public string Token;
public ApiKey Key;
}
public class ApiKey {
public ApiKey(string tokenHash, string tokenType) {
if (string.IsNullOrEmpty(tokenHash)) {
throw new ArgumentNullException(nameof(tokenHash));
}
TokenHash = tokenHash;
TokenType = tokenType ?? string.Empty;
}
public string Id { get; set; }
public string Purpose { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ExpiresOn { get; set; }
public DateTime LastModified { get; set; }
public string TokenHash { get; private set; }
public string TokenType { get; private set; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Security {
public class ApiKeyOptions {
public int KeySize { get; set; } = 32; // (in bytes)
public int SaltSize { get; set; } = 8; // (in bytes)
public int HashSize { get; set; } = 32; // (in bytes)
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Security {
using System.Collections.Generic;
using System.Threading.Tasks;
public interface IApiKeyProvider {
ApiToken GenerateKey(string purpose);
Task<ApiToken> RenewToken(ApiKey key);
ApiKey FindKey(string token);
IEnumerable<ApiKey> GetAllKeys();
ApiKey GetKey(string id);
Task SaveKey(ApiKey key);
Task DeleteKey(ApiKey key);
}
}

Просмотреть файл

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils
{
using System;
using System.Text;
public static class Base64
{
public static string Encode(byte[] buff)
{
StringBuilder sb = new StringBuilder(Convert.ToBase64String(buff));
// Escape
int paddingIndex = sb.Length;
for (int i = 0; i < sb.Length; ++i)
{
if (sb[i] == '=' && paddingIndex == sb.Length)
{
paddingIndex = i;
}
switch (sb[i])
{
case '+':
sb[i] = '-';
break;
case '/':
sb[i] = '_';
break;
}
}
// Remove padding
return sb.ToString(0, paddingIndex);
}
public static byte[] Decode(string s)
{
StringBuilder sb = new StringBuilder(s);
// Unescape
for (int i = 0; i < sb.Length; ++i)
{
switch (sb[i])
{
case '-':
sb[i] = '+';
break;
case '_':
sb[i] = '/';
break;
}
}
// Add padding
if (sb.Length % 4 > 0)
{
sb.Append(new string('=', 4 - sb.Length % 4));
}
return Convert.FromBase64String(sb.ToString());
}
}
}

Просмотреть файл

@ -0,0 +1,209 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils {
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
/// <summary>
/// Helper class to aid in the conversion from simple dynamic objects (ex: JSON representations) to desired primitive types
/// </summary>
public static class DynamicHelper {
public static Nullable<T> To<T>(dynamic value) where T : struct {
if(value == null) {
return null;
}
if (value is JValue) {
try {
return To<T>(value.Value);
}
catch (FormatException e) {
throw new ApiArgumentException(value.Path, e);
}
catch (ArgumentException e) {
throw new ApiArgumentException(value.Path, e);
}
}
if (value is T) {
Nullable<T> t = value;
return t;
}
string str = value as string;
if (value is long) {
// For conversion of JSON primitive preferred type (long) to (int) and (byte)
str = value.ToString();
}
if(str == null) {
return null;
}
Nullable<T> returnValue;
if (typeof(T).GetTypeInfo().IsEnum) {
returnValue = (Nullable<T>)Enum.Parse(typeof(T), str, true);
}
else if (typeof(T) == typeof(TimeSpan)) {
returnValue = (Nullable<T>)((object)TimeSpan.Parse(str));
}
else if (typeof(T) == typeof(DateTime) && string.IsNullOrEmpty(str)) {
return null;
}
else {
returnValue = (Nullable<T>)Convert.ChangeType(str, typeof(T));
}
return returnValue;
}
public static long? To(dynamic value, long min, long max)
{
long? v = DynamicHelper.To<long>(value);
if (v != null)
{
Validator.WithinRange(min, max, v.Value, value is JValue ? ((JValue)value).Path : string.Empty);
}
return v;
}
public static List<T> ToList<T>(IEnumerable<dynamic> values) where T : struct
{
var result = new List<T>();
foreach (dynamic d in values) {
T? t = To<T>(d);
if (t == null) {
throw new ArgumentNullException("element");
}
result.Add(t.Value);
}
return result;
}
public static List<string> ToList(IEnumerable<dynamic> values)
{
var result = new List<string>();
foreach (dynamic d in values) {
string v = Value(d);
result.Add(v);
}
return result;
}
public static string Value(dynamic member) {
if (member == null) {
return null;
}
if (member is JValue) {
return Value(member.Value);
}
return member as string;
}
public static long? ToLong(dynamic value, int fromBase, long min = long.MinValue, long max = long.MaxValue) {
string val = DynamicHelper.Value(value);
if (val == null) {
return null;
}
try {
var v = Convert.ToInt64(val, fromBase);
Validator.WithinRange(min, max, v, value is JValue ? ((JValue)value).Path : string.Empty);
return v;
}
catch (FormatException e) {
if (value is JValue) {
throw new ApiArgumentException(value.Path, e);
}
throw;
}
}
public static T? If<T>(dynamic model, Action<T> then) where T : struct
{
T? value = DynamicHelper.To<T>(model);
if (value != null) {
try {
then(value.Value);
}
catch (FileLoadException e) {
throw new LockedException(model.Path, e);
}
catch (Exception e) {
if (model is JValue) {
throw new ApiArgumentException(model.Path, e);
}
throw;
}
}
return value;
}
public static long? If(dynamic model, long min, long max, Action<long> then)
{
long? value = DynamicHelper.To(model, min, max);
if (value != null) {
try {
then(value.Value);
}
catch (FileLoadException e) {
throw new LockedException(model.Path, e);
}
catch (Exception e) {
if (model is JValue) {
throw new ApiArgumentException(model.Path, e);
}
throw;
}
}
return value;
}
public static string If(dynamic model, Action<string> then)
{
string value = DynamicHelper.Value(model);
if (value != null) {
try {
then(value);
}
catch (FileLoadException e) {
throw new LockedException(model.Path, e);
}
catch (Exception e) {
if (model is JValue) {
throw new ApiArgumentException(model.Path, e);
}
throw;
}
}
return value;
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils
{
using System;
using System.Collections.Generic;
using System.Reflection;
public static class EnumHelper
{
public static IEnumerable<Enum> GetFlags(this Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value) && Convert.ToInt32(value) != 0)
yield return value;
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils {
using System.Collections.Generic;
using System.Dynamic;
using AspNetCore.Routing;
public static class Expando {
public static ExpandoObject ToExpando(this object anonymousObject) {
IDictionary<string, object> dict = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in dict) {
expando.Add(item);
}
return (ExpandoObject)expando;
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils {
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class Fields
{
private SortedSet<string> _fields;
private bool _allFields;
public static readonly Fields All = new Fields("*");
public bool HasFields { get; private set; }
public Fields(params string[] fields)
{
if (fields == null) {
_allFields = true;
return;
}
HasFields = true;
var f = new SortedSet<string>();
foreach (string s in fields) {
var str = s.Trim();
if (str.Equals("*")) {
_allFields = true;
return;
}
f.Add(str);
}
// Never leave out id
f.Add("id");
_fields = f;
}
public bool Exists(string field)
{
return _allFields || _fields.Any(f => f.Equals(field, StringComparison.OrdinalIgnoreCase));
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils {
using System;
using System.Reflection;
using AspNetCore.Http;
public sealed class Filter {
private IQueryCollection _values;
public Filter(IQueryCollection values) {
_values = values;
}
public Nullable<T> Get<T>(string key) where T : struct {
if (string.IsNullOrEmpty(key)) {
return null;
}
Nullable<T> returnValue;
string val = _values[key];
if (val == null) {
return null;
}
try {
if (typeof(T).GetTypeInfo().IsEnum) {
returnValue = (T)Enum.Parse(typeof(T), val, true);
}
else if (typeof(T) == typeof(TimeSpan)) {
returnValue = (T)((object)TimeSpan.Parse(val));
}
else {
returnValue = (T)Convert.ChangeType(val, typeof(T));
}
}
catch (OverflowException e) {
throw new ApiArgumentException(key, e);
}
catch (FormatException e) {
throw new ApiArgumentException(key, e);
}
catch (ArgumentException e) {
throw new ApiArgumentException(key, e);
}
return returnValue;
}
public string Get(string key) {
return _values[key];
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using AspNetCore.Hosting;
using System.IO;
public static class HostingEnvironmentExtensions
{
private const string DEV_PLUGINS_FOLDER_NAME = "plugins";
private static readonly string PROD_PLUGINS_FOLDER_NAME = "plugins";
public static string ConfigRootPath(this IHostingEnvironment env)
{
return Path.GetFullPath(Path.Combine(env.WebRootPath, "..", "config"));
}
public static string GetPluginsFolderName(this IHostingEnvironment env)
{
return env.IsDevelopment() ? DEV_PLUGINS_FOLDER_NAME : PROD_PLUGINS_FOLDER_NAME;
}
}
}

Просмотреть файл

@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils
{
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
//
// Produce stable UUID
// The cryptography here is NOT intended for security purposes, but for better entropy
//
public static class Uuid
{
private static readonly byte[] Key = ConfigurationHelper.HostId.ToByteArray();
public static string Encode(string value, string purpose)
{
using (var aes = Aes.Create())
{
aes.Key = Key;
aes.IV = CreateIV(purpose);
var encryptor = aes.CreateEncryptor();
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (var bw = new BinaryWriter(cs))
{
var bytes = Encoding.UTF8.GetBytes(value);
bw.Write(bytes);
}
return Base64.Encode(ms.ToArray());
}
}
}
}
public static string Decode(string uuid, string purpose)
{
try {
var data2 = Base64.Decode(uuid);
using (var aes = Aes.Create()) {
aes.Key = Key;
aes.IV = CreateIV(purpose);
var decryptor = aes.CreateDecryptor();
// Create the streams used for decryption.
using (var ms = new MemoryStream(data2))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (var buff = new MemoryStream()) {
cs.CopyTo(buff);
return Encoding.UTF8.GetString(buff.ToArray());
}
}
}
catch (FormatException e) {
throw new NotFoundException(null, e);
}
catch (CryptographicException e) {
throw new NotFoundException(null, e);
}
}
private static byte[] CreateIV(string purpose)
{
// Need stable (non-random) IV
// to produce same UUID for the same value/purpose pair
//
byte[] iv = new byte[16];
using (var hmac = new HMACSHA256(Key))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(purpose));
Buffer.BlockCopy(hash, 0, iv, 0, iv.Length);
}
return iv;
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core.Utils
{
public class Validator
{
public static long WithinRange(long min, long max, long value, string name)
{
if (value < min || value > max)
{
throw new ApiArgumentOutOfRangeException(name, min, max);
}
return value;
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.Core
{
using AspNetCore.Mvc.Filters;
using Http;
using System;
using System.Reflection;
using System.Text;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class ResourceInfoAttribute : ActionFilterAttribute
{
public string Name { get; set; }
public string Version { get; set; }
public override void OnActionExecuted(ActionExecutedContext context)
{
string prevtype = "";
if (context.HttpContext.Response.Headers.ContainsKey(HeaderNames.ContentType)) {
prevtype = context.HttpContext.Response.Headers[HeaderNames.ContentType];
}
if (this.Version == null) {
IModule mod = Environment.Host.GetModuleByAssemblyName(context.Controller.GetType().GetTypeInfo().Assembly.FullName);
this.Version = mod != null ? mod.Version : this.Version;
}
context.HttpContext.Response.Headers[HeaderNames.ContentType] = ContentType(this.Name, this.Version, prevtype);
}
public static string ContentType(string name, string version, string previous = null)
{
StringBuilder type = new StringBuilder();
type.Append("application/vnd.");
type.Append(name);
type.Append(".");
type.Append(version);
if (previous == null || !previous.Contains("+json")) {
type.Append("+json");
}
else {
type.Append(".");
type.Append(previous);
}
return type.ToString();
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
{
"version": "1.0.0",
"description": "Microsoft.IIS.Administration.Core Class Library",
"authors": [ "Microsoft" ],
"frameworks": {
"netstandard1.6": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
]
}
},
"dependencies": {
"NETStandard.Library": "1.6.0",
"Serilog.Extensions.Logging": "1.0.0",
"Serilog": "2.0.0",
"Serilog.Sinks.RollingFile": "2.1.0",
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Authorization": "1.0.0",
"Microsoft.AspNetCore.Mvc.WebApiCompatShim": "1.0.0",
"System.Diagnostics.FileVersionInfo": "4.0.0",
"Microsoft.Extensions.Configuration.Binder": "1.0.0"
}
}

Просмотреть файл

@ -0,0 +1,483 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.WebServer.AppPools
{
using Core;
using Core.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using Web.Administration;
public static class AppPoolHelper
{
private static readonly Fields RefFields = new Fields("name", "id", "status");
private const string IdleTimeoutActionAttribute = "idleTimeoutAction";
public static ApplicationPool CreateAppPool(dynamic model)
{
if (model == null) {
throw new ApiArgumentException("model");
}
string name = DynamicHelper.Value(model.name);
if (String.IsNullOrEmpty(name)) {
throw new ApiArgumentException("name");
}
if (GetAppPool(name) != null) {
throw new AlreadyExistsException("name");
}
var sm = ManagementUnit.ServerManager;
ApplicationPool appPool = sm.ApplicationPools.CreateElement();
SetToDefaults(appPool, sm.ApplicationPoolDefaults);
SetAppPool(appPool, model);
return appPool;
}
public static ApplicationPool GetAppPool(string name)
{
ApplicationPool pool = ManagementUnit.ServerManager.ApplicationPools.Where(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
return pool;
}
public static ApplicationPool UpdateAppPool(string name, dynamic model)
{
if(model == null) {
throw new ApiArgumentException("model");
}
else if(String.IsNullOrEmpty(name)) {
throw new ApiArgumentException("name");
}
ApplicationPool appPool = GetAppPool(name);
if(appPool != null) {
SetAppPool(appPool, model);
}
return appPool;
}
public static void DeleteAppPool(ApplicationPool pool)
{
ManagementUnit.ServerManager.ApplicationPools.Remove(pool);
}
public static object ToJsonModel(ApplicationPool pool, Fields fields = null)
{
if(pool == null) {
return null;
}
bool full = fields == null || !fields.HasFields;
if (fields == null) {
fields = Fields.All;
}
dynamic obj = new ExpandoObject();
//
// name
if (fields.Exists("name")) {
obj.name = pool.Name;
}
//
// id
obj.id = AppPoolId.CreateFromName(pool.Name).Uuid;
//
// status
if (fields.Exists("status")) {
// Prepare state
Status state = Status.Unknown;
try {
state = StatusExtensions.FromObjectState(pool.State);
}
catch (COMException) {
// Problem getting state of app pool. Possible reasons:
// 1. App pool's application pool was deleted.
// 2. App pool was just created and the status is not accessible yet.
}
obj.status = Enum.GetName(typeof(Status), state).ToLower();
}
//
// auto_start
if (fields.Exists("auto_start")) {
obj.auto_start = pool.AutoStart;
}
//
// pipeline_mode
if (fields.Exists("pipeline_mode")) {
obj.pipeline_mode = Enum.GetName(typeof(ManagedPipelineMode), pool.ManagedPipelineMode).ToLower();
}
//
// managed_runtime_version
if (fields.Exists("managed_runtime_version")) {
obj.managed_runtime_version = pool.ManagedRuntimeVersion;
}
//
// enable_32bit_win64
if (fields.Exists("enable_32bit_win64")) {
obj.enable_32bit_win64 = pool.Enable32BitAppOnWin64;
}
//
// queue_length
if (fields.Exists("queue_length")) {
obj.queue_length = pool.QueueLength;
}
//
// cpu
if (fields.Exists("cpu")) {
obj.cpu = new {
limit = pool.Cpu.Limit,
limit_interval = pool.Cpu.ResetInterval.TotalMinutes,
action = Enum.GetName(typeof(ProcessorAction), pool.Cpu.Action),
processor_affinity_enabled = pool.Cpu.SmpAffinitized,
processor_affinity_mask32 = "0x" + pool.Cpu.SmpProcessorAffinityMask.ToString("X"),
processor_affinity_mask64 = "0x" + pool.Cpu.SmpProcessorAffinityMask2.ToString("X")
};
}
//
// process_model
if (fields.Exists("process_model")) {
dynamic processModel = new ExpandoObject();
processModel.idle_timeout = pool.ProcessModel.IdleTimeout.TotalMinutes;
processModel.max_processes = pool.ProcessModel.MaxProcesses;
processModel.pinging_enabled = pool.ProcessModel.PingingEnabled;
processModel.ping_interval = pool.ProcessModel.PingInterval.TotalSeconds;
processModel.ping_response_time = pool.ProcessModel.PingResponseTime.TotalSeconds;
processModel.shutdown_time_limit = pool.ProcessModel.ShutdownTimeLimit.TotalSeconds;
processModel.startup_time_limit = pool.ProcessModel.StartupTimeLimit.TotalSeconds;
if (pool.ProcessModel.Schema.HasAttribute(IdleTimeoutActionAttribute)) {
processModel.idle_timeout_action = Enum.GetName(typeof(IdleTimeoutAction), pool.ProcessModel.IdleTimeoutAction);
}
obj.process_model = processModel;
}
//
// identity
if (fields.Exists("identity")) {
obj.identity = new {
// Not changing the casing or adding '_' on the identity type enum because they represent identities and therefore spelling and casing are important
identity_type = Enum.GetName(typeof(ProcessModelIdentityType), pool.ProcessModel.IdentityType),
username = pool.ProcessModel.UserName,
load_user_profile = pool.ProcessModel.LoadUserProfile
};
}
//
// recycling
if (fields.Exists("recycling")) {
RecyclingLogEventOnRecycle logEvent = pool.Recycling.LogEventOnRecycle;
Dictionary<string, bool> logEvents = new Dictionary<string, bool>();
logEvents.Add("time", logEvent.HasFlag(RecyclingLogEventOnRecycle.Time));
logEvents.Add("requests", logEvent.HasFlag(RecyclingLogEventOnRecycle.Requests));
logEvents.Add("schedule", logEvent.HasFlag(RecyclingLogEventOnRecycle.Schedule));
logEvents.Add("memory", logEvent.HasFlag(RecyclingLogEventOnRecycle.Memory));
logEvents.Add("isapi_unhealthy", logEvent.HasFlag(RecyclingLogEventOnRecycle.IsapiUnhealthy));
logEvents.Add("on_demand", logEvent.HasFlag(RecyclingLogEventOnRecycle.OnDemand));
logEvents.Add("config_change", logEvent.HasFlag(RecyclingLogEventOnRecycle.ConfigChange));
logEvents.Add("private_memory", logEvent.HasFlag(RecyclingLogEventOnRecycle.PrivateMemory));
obj.recycling = new {
disable_overlapped_recycle = pool.Recycling.DisallowOverlappingRotation,
disable_recycle_on_config_change = pool.Recycling.DisallowRotationOnConfigChange,
log_events = logEvents,
periodic_restart = new {
time_interval = pool.Recycling.PeriodicRestart.Time.TotalMinutes,
private_memory = pool.Recycling.PeriodicRestart.PrivateMemory,
request_limit = pool.Recycling.PeriodicRestart.Requests,
virtual_memory = pool.Recycling.PeriodicRestart.Memory,
schedule = pool.Recycling.PeriodicRestart.Schedule.Select(s => s.Time.ToString(@"hh\:mm"))
}
};
}
//
// rapid_fail_protection
if (fields.Exists("rapid_fail_protection")) {
obj.rapid_fail_protection = new {
enabled = pool.Failure.RapidFailProtection,
load_balancer_capabilities = Enum.GetName(typeof(LoadBalancerCapabilities), pool.Failure.LoadBalancerCapabilities),
interval = pool.Failure.RapidFailProtectionInterval.TotalMinutes,
max_crashes = pool.Failure.RapidFailProtectionMaxCrashes,
auto_shutdown_exe = pool.Failure.AutoShutdownExe,
auto_shutdown_params = pool.Failure.AutoShutdownParams
};
}
//
// process_orphaning
if (fields.Exists("process_orphaning")) {
obj.process_orphaning = new {
enabled = pool.Failure.OrphanWorkerProcess,
orphan_action_exe = pool.Failure.OrphanActionExe,
orphan_action_params = pool.Failure.OrphanActionParams,
};
}
return Core.Environment.Hal.Apply(Defines.Resource.Guid, obj, full);
}
public static object ToJsonModelRef(ApplicationPool pool)
{
return ToJsonModel(pool, RefFields);
}
public static string GetLocation(string id) {
if (string.IsNullOrEmpty(id)) {
throw new ArgumentNullException(nameof(id));
}
return $"/{Defines.PATH}/{id}";
}
private static void SetToDefaults(ApplicationPool pool, ApplicationPoolDefaults defaults)
{
pool.ManagedPipelineMode = defaults.ManagedPipelineMode;
pool.ManagedRuntimeVersion = defaults.ManagedRuntimeVersion;
pool.Enable32BitAppOnWin64 = defaults.Enable32BitAppOnWin64;
pool.QueueLength = defaults.QueueLength;
pool.AutoStart = defaults.AutoStart;
pool.Cpu.Limit = defaults.Cpu.Limit;
pool.Cpu.ResetInterval = defaults.Cpu.ResetInterval;
pool.Cpu.Action = defaults.Cpu.Action;
pool.Cpu.SmpAffinitized = defaults.Cpu.SmpAffinitized;
pool.Cpu.SmpProcessorAffinityMask = defaults.Cpu.SmpProcessorAffinityMask;
pool.Cpu.SmpProcessorAffinityMask2 = defaults.Cpu.SmpProcessorAffinityMask2;
if (pool.ProcessModel.Schema.HasAttribute(IdleTimeoutActionAttribute)) {
pool.ProcessModel.IdleTimeoutAction = defaults.ProcessModel.IdleTimeoutAction;
}
pool.ProcessModel.MaxProcesses = defaults.ProcessModel.MaxProcesses;
pool.ProcessModel.PingingEnabled = defaults.ProcessModel.PingingEnabled;
pool.ProcessModel.IdleTimeout = defaults.ProcessModel.IdleTimeout;
pool.ProcessModel.PingInterval = defaults.ProcessModel.PingInterval;
pool.ProcessModel.PingResponseTime = defaults.ProcessModel.PingResponseTime;
pool.ProcessModel.ShutdownTimeLimit = defaults.ProcessModel.ShutdownTimeLimit;
pool.ProcessModel.StartupTimeLimit = defaults.ProcessModel.StartupTimeLimit;
pool.Recycling.LogEventOnRecycle = defaults.Recycling.LogEventOnRecycle;
pool.Recycling.DisallowOverlappingRotation = defaults.Recycling.DisallowOverlappingRotation;
pool.Recycling.DisallowRotationOnConfigChange = defaults.Recycling.DisallowRotationOnConfigChange;
pool.Recycling.PeriodicRestart.PrivateMemory = defaults.Recycling.PeriodicRestart.PrivateMemory;
pool.Recycling.PeriodicRestart.Memory = defaults.Recycling.PeriodicRestart.Memory;
pool.Recycling.PeriodicRestart.Requests = defaults.Recycling.PeriodicRestart.Requests;
pool.Recycling.PeriodicRestart.Time = defaults.Recycling.PeriodicRestart.Time;
}
private static void SetAppPool(ApplicationPool appPool, dynamic model)
{
Debug.Assert(appPool != null);
Debug.Assert((bool)(model != null));
DynamicHelper.If((object)model.name, v => { appPool.Name = v; });
appPool.ManagedPipelineMode = DynamicHelper.To<ManagedPipelineMode>(model.pipeline_mode) ?? appPool.ManagedPipelineMode;
appPool.ManagedRuntimeVersion = DynamicHelper.Value(model.managed_runtime_version) ?? appPool.ManagedRuntimeVersion;
appPool.Enable32BitAppOnWin64 = DynamicHelper.To<bool>(model.enable_32bit_win64) ?? appPool.Enable32BitAppOnWin64;
appPool.QueueLength = DynamicHelper.To(model.queue_length, 10, 65535) ?? appPool.QueueLength;
appPool.AutoStart = DynamicHelper.To<bool>(model.auto_start) ?? appPool.AutoStart;
// CPU
if (model.cpu != null) {
dynamic cpu = model.cpu;
appPool.Cpu.Limit = DynamicHelper.To(cpu.limit, 0, 100000) ?? appPool.Cpu.Limit;
appPool.Cpu.SmpAffinitized = DynamicHelper.To<bool>(cpu.processor_affinity_enabled) ?? appPool.Cpu.SmpAffinitized;
appPool.Cpu.SmpProcessorAffinityMask = DynamicHelper.ToLong(cpu.processor_affinity_mask32, 16, 0, 4294967295) ?? appPool.Cpu.SmpProcessorAffinityMask;
appPool.Cpu.SmpProcessorAffinityMask2 = DynamicHelper.ToLong(cpu.processor_affinity_mask64, 16, 0, 4294967295) ?? appPool.Cpu.SmpProcessorAffinityMask2;
try {
appPool.Cpu.Action = DynamicHelper.To<ProcessorAction>(cpu.action) ?? appPool.Cpu.Action;
}
catch (COMException e) {
throw new ApiArgumentException("cpu.action", e);
}
long? resetInterval = DynamicHelper.To(cpu.limit_interval, 0, 1440);
appPool.Cpu.ResetInterval = (resetInterval != null) ? TimeSpan.FromMinutes(resetInterval.Value) : appPool.Cpu.ResetInterval;
}
// Process Model
if (model.process_model != null) {
dynamic processModel = model.process_model;
if (appPool.ProcessModel.Schema.HasAttribute(IdleTimeoutActionAttribute)) {
appPool.ProcessModel.IdleTimeoutAction = DynamicHelper.To<IdleTimeoutAction>(processModel.idle_timeout_action) ?? appPool.ProcessModel.IdleTimeoutAction;
}
appPool.ProcessModel.MaxProcesses = DynamicHelper.To(processModel.max_processes, 0, 2147483647) ?? appPool.ProcessModel.MaxProcesses;
appPool.ProcessModel.PingingEnabled = DynamicHelper.To<bool>(processModel.pinging_enabled) ?? appPool.ProcessModel.PingingEnabled;
long? idleTimeout = DynamicHelper.To(processModel.idle_timeout, 0, 43200);
appPool.ProcessModel.IdleTimeout = (idleTimeout != null) ? TimeSpan.FromMinutes(idleTimeout.Value) : appPool.ProcessModel.IdleTimeout;
long? pingInterval = DynamicHelper.To(processModel.ping_interval, 1, 4294967);
appPool.ProcessModel.PingInterval = (pingInterval != null) ? TimeSpan.FromSeconds(pingInterval.Value) : appPool.ProcessModel.PingInterval;
long? pingResponseTime = DynamicHelper.To(processModel.ping_response_time, 1, 4294967);
appPool.ProcessModel.PingResponseTime = (pingResponseTime != null) ? TimeSpan.FromSeconds(pingResponseTime.Value) : appPool.ProcessModel.PingResponseTime;
long? shutDownTimeLimit = DynamicHelper.To(processModel.shutdown_time_limit, 1, 4294967);
appPool.ProcessModel.ShutdownTimeLimit = (shutDownTimeLimit != null) ? TimeSpan.FromSeconds(shutDownTimeLimit.Value) : appPool.ProcessModel.ShutdownTimeLimit;
long? startupTimeLimit = DynamicHelper.To(processModel.startup_time_limit, 1, 4294967);
appPool.ProcessModel.StartupTimeLimit = (startupTimeLimit != null) ? TimeSpan.FromSeconds(startupTimeLimit.Value): appPool.ProcessModel.StartupTimeLimit;
}
// Identity
if(model.identity != null) {
dynamic identity = model.identity;
appPool.ProcessModel.IdentityType = DynamicHelper.To<ProcessModelIdentityType>(identity.identity_type) ?? appPool.ProcessModel.IdentityType;
appPool.ProcessModel.LoadUserProfile = DynamicHelper.To<bool>(identity.load_user_profile) ?? appPool.ProcessModel.LoadUserProfile;
appPool.ProcessModel.UserName = DynamicHelper.Value(identity.username) ?? appPool.ProcessModel.UserName;
DynamicHelper.If((object)identity.password, v => appPool.ProcessModel.Password = v);
}
// Recycling
if (model.recycling != null) {
dynamic recycling = model.recycling;
appPool.Recycling.DisallowOverlappingRotation = DynamicHelper.To<bool>(recycling.disable_overlapped_recycle) ?? appPool.Recycling.DisallowOverlappingRotation;
appPool.Recycling.DisallowRotationOnConfigChange = DynamicHelper.To<bool>(recycling.disable_recycle_on_config_change) ?? appPool.Recycling.DisallowRotationOnConfigChange;
// Check if log event collection provided
if(recycling.log_events != null) {
try {
// Convert the log_events dynamic into a string and then deserialize it into a Dictionary<string,bool>, from there we turn it into a flags enum
Dictionary<string, bool> logEvents = JsonConvert.DeserializeObject<Dictionary<string, bool>>(recycling.log_events.ToString());
var flags = appPool.Recycling.LogEventOnRecycle;
if (logEvents == null) {
throw new ApiArgumentException("recycling.log_events");
}
Dictionary<string, RecyclingLogEventOnRecycle> flagPairs = new Dictionary<string, RecyclingLogEventOnRecycle>
{
{ "time", RecyclingLogEventOnRecycle.Time },
{ "requests", RecyclingLogEventOnRecycle.Requests },
{ "schedule", RecyclingLogEventOnRecycle.Schedule },
{ "memory", RecyclingLogEventOnRecycle.Memory },
{ "isapi_unhealthy", RecyclingLogEventOnRecycle.IsapiUnhealthy },
{ "on_demand", RecyclingLogEventOnRecycle.OnDemand },
{ "config_change", RecyclingLogEventOnRecycle.ConfigChange },
{ "private_memory", RecyclingLogEventOnRecycle.PrivateMemory }
};
foreach (var key in flagPairs.Keys) {
if (logEvents.ContainsKey(key)) {
if (logEvents[key]) {
flags |= flagPairs[key];
}
else {
flags &= ~flagPairs[key];
}
}
}
appPool.Recycling.LogEventOnRecycle = flags;
}
catch(JsonSerializationException e) {
throw new ApiArgumentException("recycling.log_events", e);
}
}
// Periodic Restart
if(recycling.periodic_restart != null) {
dynamic periodicRestart = recycling.periodic_restart;
appPool.Recycling.PeriodicRestart.PrivateMemory = DynamicHelper.To(periodicRestart.private_memory, 0, 4294967295) ?? appPool.Recycling.PeriodicRestart.PrivateMemory;
appPool.Recycling.PeriodicRestart.Requests = DynamicHelper.To(periodicRestart.request_limit, 0, 4294967295) ?? appPool.Recycling.PeriodicRestart.Requests;
appPool.Recycling.PeriodicRestart.Memory = DynamicHelper.To(periodicRestart.virtual_memory, 0, 4294967295) ?? appPool.Recycling.PeriodicRestart.Memory;
long? timeInterval = DynamicHelper.To(periodicRestart.time_interval, 0, 432000);
appPool.Recycling.PeriodicRestart.Time = timeInterval != null ? TimeSpan.FromMinutes(timeInterval.Value) : appPool.Recycling.PeriodicRestart.Time;
// Check if schedule provided
if (periodicRestart.schedule != null) {
if (!(periodicRestart.schedule is JArray)) {
throw new ApiArgumentException("recyclying.periodic_restart.schedule", ApiArgumentException.EXPECTED_ARRAY);
}
// Clear the old time spans in the schedule
appPool.Recycling.PeriodicRestart.Schedule.Clear();
IEnumerable<dynamic> schedule = periodicRestart.schedule;
// Add the time spans
foreach (var d in schedule) {
var value = DynamicHelper.Value(d);
DateTime dt = default(DateTime);
if (value == null || !DateTime.TryParseExact(value, "HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt)) {
throw new ApiArgumentException("recyclying.periodic_restart.schedule.item", "Expected hh:mm");
}
appPool.Recycling.PeriodicRestart.Schedule.Add(dt.TimeOfDay);
}
}
}
}
// Rapid Fail Protection
if(model.rapid_fail_protection != null) {
var protection = model.rapid_fail_protection;
appPool.Failure.RapidFailProtection = DynamicHelper.To<bool>(protection.enabled) ?? appPool.Failure.RapidFailProtection;
appPool.Failure.LoadBalancerCapabilities = DynamicHelper.To<LoadBalancerCapabilities>(protection.load_balancer_capabilities) ?? appPool.Failure.LoadBalancerCapabilities;
appPool.Failure.RapidFailProtectionMaxCrashes = DynamicHelper.To(protection.max_crashes, 1, 2147483647) ?? appPool.Failure.RapidFailProtectionMaxCrashes;
appPool.Failure.AutoShutdownExe = DynamicHelper.Value(protection.auto_shutdown_exe) ?? appPool.Failure.AutoShutdownExe;
appPool.Failure.AutoShutdownParams = DynamicHelper.Value(protection.auto_shutdown_params) ?? appPool.Failure.AutoShutdownParams;
long? protectionInterval = DynamicHelper.To(protection.interval, 1, 144400);
appPool.Failure.RapidFailProtectionInterval = (protectionInterval != null) ? TimeSpan.FromMinutes(protectionInterval.Value) : appPool.Failure.RapidFailProtectionInterval;
}
// Process Orphaning
if(model.process_orphaning != null) {
var orphaning = model.process_orphaning;
appPool.Failure.OrphanWorkerProcess = DynamicHelper.To<bool>(orphaning.enabled) ?? appPool.Failure.OrphanWorkerProcess;
appPool.Failure.OrphanActionExe = DynamicHelper.Value(orphaning.orphan_action_exe) ?? appPool.Failure.OrphanActionExe;
appPool.Failure.OrphanActionParams = DynamicHelper.Value(orphaning.orphan_action_params) ?? appPool.Failure.OrphanActionParams;
}
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.WebServer.AppPools
{
public class AppPoolId
{
private const string PURPOSE = "WebServer.AppPools";
public string Name { get; private set; }
public string Uuid { get; private set; }
private AppPoolId() { }
public static AppPoolId CreateFromName(string name)
{
string uuid = Core.Utils.Uuid.Encode(name, PURPOSE);
return new AppPoolId() {
Name = name,
Uuid = uuid
};
}
public static AppPoolId CreateFromUuid(string uuid)
{
string name = Core.Utils.Uuid.Decode(uuid, PURPOSE);
return new AppPoolId() {
Uuid = uuid,
Name = name
};
}
}
}

Просмотреть файл

@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.WebServer.AppPools
{
using AspNetCore.Mvc;
using Core;
using Core.Utils;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using Web.Administration;
using Core.Http;
public class AppPoolsController : ApiBaseController {
private const string HIDDEN_FIELDS = "model.identity.password";
[HttpGet]
[ResourceInfo(Name = Defines.AppPoolsName)]
public object Get() {
Fields fields = Context.Request.GetFields();
// Get reference models for app pool collection
var pools = ManagementUnit.ServerManager.ApplicationPools.Select(pool =>
fields.HasFields ? AppPoolHelper.ToJsonModel(pool, fields) : AppPoolHelper.ToJsonModelRef(pool)).ToList();
// Set HTTP header for total count
this.Context.Response.SetItemsCount(pools.Count());
// Return the app pool reference model collection
return new {
app_pools = pools
};
}
[HttpGet]
[ResourceInfo(Name = Defines.AppPoolName)]
public object Get(string id) {
// Extract the name of the target app pool from the uuid specified in the request
string name = AppPoolId.CreateFromUuid(id).Name;
ApplicationPool pool = AppPoolHelper.GetAppPool(name);
if (pool == null) {
return NotFound();
}
return AppPoolHelper.ToJsonModel(pool, Context.Request.GetFields());
}
[HttpPost]
[Audit(AuditAttribute.ALL, HIDDEN_FIELDS)]
[ResourceInfo(Name = Defines.AppPoolName)]
public object Post([FromBody]dynamic model)
{
// Create AppPool
ApplicationPool pool = AppPoolHelper.CreateAppPool(model);
EnsureAppPoolIdentityAllowed(pool);
// Save it
ManagementUnit.ServerManager.ApplicationPools.Add(pool);
ManagementUnit.Current.Commit();
// Refresh
pool = AppPoolHelper.GetAppPool(pool.Name);
WaitForPoolStatusResolve(pool);
//
// Create response
dynamic appPool = (dynamic) AppPoolHelper.ToJsonModel(pool);
return Created((string)AppPoolHelper.GetLocation(appPool.id), appPool);
}
[HttpDelete]
[Audit]
public void Delete(string id)
{
string name = AppPoolId.CreateFromUuid(id).Name;
ApplicationPool pool = AppPoolHelper.GetAppPool(name);
if (pool != null) {
AppPoolHelper.DeleteAppPool(pool);
ManagementUnit.Current.Commit();
}
// Sucess
Context.Response.StatusCode = (int)HttpStatusCode.NoContent;
}
[HttpPatch]
[Audit(AuditAttribute.ALL, HIDDEN_FIELDS)]
[ResourceInfo(Name = Defines.AppPoolName)]
public object Patch(string id, [FromBody] dynamic model)
{
// Cut off the notion of uuid from beginning of request
string name = AppPoolId.CreateFromUuid(id).Name;
// Set settings
ApplicationPool appPool = AppPoolHelper.UpdateAppPool(name, model);
if(appPool == null) {
return NotFound();
}
if (model.identity != null) {
EnsureAppPoolIdentityAllowed(appPool);
}
// Start/Stop
if (model.status != null) {
Status status = DynamicHelper.To<Status>(model.status);
try {
switch (status) {
case Status.Stopped:
appPool.Stop();
break;
case Status.Started:
appPool.Start();
break;
}
}
catch(COMException e) {
// If pool is fresh and status is still unknown then COMException will be thrown when manipulating status
throw new ApiException("Error setting application pool status", e);
}
}
// Update changes
ManagementUnit.Current.Commit();
// Refresh data
appPool = ManagementUnit.ServerManager.ApplicationPools[appPool.Name];
//
// Create response
dynamic pool = AppPoolHelper.ToJsonModel(appPool);
// The Id could change by changing apppool name
if (pool.id != id) {
return LocationChanged(AppPoolHelper.GetLocation(pool.id), pool);
}
return pool;
}
private void WaitForPoolStatusResolve(ApplicationPool pool)
{
// Delay to get proper status of newly created pool
int n = 10;
for (int i = 0; i < n; i++) {
try {
StatusExtensions.FromObjectState(pool.State);
break;
}
catch (COMException) {
if (i < n - 1) {
Thread.Sleep(10 / n);
}
}
}
}
private void EnsureAppPoolIdentityAllowed(ApplicationPool pool) {
if (pool.ProcessModel.IdentityType != ProcessModelIdentityType.LocalSystem) {
return;
}
//
// Only admins can set up LocalSystem AppPool identity
if (!User.IsInRole("Administrators")) {
throw new UnauthorizedArgumentException("identity.identity_type");
}
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.IIS.Administration.WebServer.AppPools
{
using Core;
using System;
public class Defines
{
private const string ENDPOINT = "application-pools";
public const string IDENTIFIER = "application_pool.id";
public const string AppPoolsName = "Microsoft.WebServer.AppPools";
public const string AppPoolName = "Microsoft.WebServer.AppPool";
public static readonly string PATH = $"{WebServer.Defines.PATH}/{ENDPOINT}";
public static readonly ResDef Resource = new ResDef("app_pools", new Guid("E67CD594-0E06-4531-BC0D-45428EB9E151"), ENDPOINT);
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>7675a545-e9ea-4941-833b-9d213d769a8f</ProjectGuid>
<RootNamespace>Microsoft.IIS.Administration.WebServer.AppPools</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше