Initial commit.
This commit is contained in:
Коммит
c97d50d7b0
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
```
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"projects": [
|
||||
"src",
|
||||
"test"
|
||||
],
|
||||
|
||||
"sdk": {
|
||||
"version": "1.0.0-preview2-003121",
|
||||
"runtime": "coreclr",
|
||||
"architecture": "x64"
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче