This commit is contained in:
Peter Hsu 2019-03-11 15:05:07 -07:00
Родитель ce6f23fac8
Коммит 7b3bc789f1
7 изменённых файлов: 111 добавлений и 281 удалений

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

@ -87,23 +87,52 @@ function CleanUp() {
}
function StartTest() {
$group = GetGlobalVariable IIS_ADMIN_API_OWNERS
$member = & ([System.IO.Path]::Combine($scriptDir, "setup", "security.ps1")) CurrentAdUser
if (!(Get-LocalGroupMember -Group $group -Member $member -ErrorAction SilentlyContinue)) {
Add-LocalGroupMember -Group $group -Member $member
}
$pingEndpoint = "https://localhost:$testPort"
try {
Invoke-WebRequest $pingEndpoint | Out-Null
Invoke-WebRequest -UseDefaultCredentials -UseBasicParsing $pingEndpoint | Out-Null
} catch {
Write-Error "Failed to ping test server $pingEndpoint, did you forget to start it manually?"
Exit 1
}
## do the real test
}
function VerifyPath($path) {
if (!(Test-Path $path)) {
Write-Path "$path does not exist"
return $false
}
return $true
}
function VerifyPrecondition() {
if (!(VerifyPath [System.IO.Path]::Combine($projectRoot, "test", "appsettings.test.json")) `
-or !(VerifyPath [System.IO.Path]::Combine($projectRoot, "test", "Microsoft.IIS.Administration.Tests", "test.config.json.template"))) {
throw "Test configurations do no exist, run .\scripts\Configure-DevEnvironment.ps1 -ConfigureTestEnvironment"
}
}
function GetGlobalVariable($name) {
& ([System.IO.Path]::Combine($scriptDir, "setup", "globals.ps1")) $name
}
########################################################### Main Script ##################################################################
$scriptDir = Join-Path $PSScriptRoot "scripts"
try {
$projectRoot = git rev-parse --show-toplevel
} catch {
Write-Warning "Error looking for project root $_, using script location instead"
$projectRoot = $PSScriptRoot
}
$scriptDir = Join-Path $projectRoot "scripts"
# publish script only takes full path
$publishPath = ForceResolvePath "$publishPath"
$installPath = ForceResolvePath "$installPath"
$serviceName = & ([System.IO.Path]::Combine($scriptDir, "setup", "globals.ps1")) DEFAULT_SERVICE_NAME
$serviceName = GetGlobalVariable DEFAULT_SERVICE_NAME
Write-Host "[Build] Starting clean up..."
CleanUp

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

@ -252,9 +252,9 @@ function Set-Acls($_path) {
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow)
$administratorsModify = New-Object System.Security.AccessControl.FileSystemAccessRule(
$administratorsRead = New-Object System.Security.AccessControl.FileSystemAccessRule(
$administrators,
[System.Security.AccessControl.FileSystemRights]::Modify,
[System.Security.AccessControl.FileSystemRights]::ReadAndExecute,
[System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit",
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow)
@ -281,7 +281,7 @@ function Set-Acls($_path) {
$acl.Access | Foreach-Object { $acl.RemoveAccessRule($_) }
$acl.AddAccessRule($currentUserRead)
$acl.AddAccessRule($trustedInstallerFullControl)
$acl.AddAccessRule($administratorsModify)
$acl.AddAccessRule($administratorsRead)
$acl.AddAccessRule($systemRead)
# Update the folder to use the new ACL
Set-Acl -Path $_path -AclObject $acl
@ -293,7 +293,7 @@ function Set-Acls($_path) {
# Remove all existing access rules
$acl.Access | Foreach-Object { $acl.RemoveAccessRule($_) }
$acl.AddAccessRule($currentUserRead)
$acl.AddAccessRule($administratorsModify)
$acl.AddAccessRule($administratorsRead)
$acl.AddAccessRule($trustedInstallerFullControl)
$acl.AddAccessRule($systemFullControl)
# Update the folder to use the new ACL

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

@ -9,6 +9,7 @@
<AssemblyName>Microsoft.IIS.Administration</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Microsoft.IIS.Administration</PackageId>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
<ItemGroup>
@ -41,6 +42,7 @@
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.HttpSys" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />

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

@ -5,10 +5,10 @@
namespace Microsoft.IIS.Administration {
using AspNetCore.Builder;
using AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IIS.Administration.WindowsService;
using Serilog;
public class Program {
@ -45,8 +45,7 @@ namespace Microsoft.IIS.Administration {
//
// Run as a Service
Log.Information($"Running as service: {serviceName}");
new ServiceHelper(serviceName).Run(token => host.RunAsync(token))
.Wait();
host.RunAsService();
}
else {
//

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

@ -1,55 +0,0 @@
// 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.WindowsService {
using System;
using System.Runtime.InteropServices;
static class Interop {
private const string SERVICE_CORE_API_SET = "api-ms-win-service-core-l1-1-0";
public const int SERVICE_TYPE_WIN32_OWN_PROCESS = 0x00000010;
public const int SERVICE_TYPE_WIN32_SHARE_PROCESS = 0x00000020;
public const int SERVICE_TYPE_WIN32 = SERVICE_TYPE_WIN32_OWN_PROCESS | SERVICE_TYPE_WIN32_SHARE_PROCESS;
public const int SERVICE_CONTROL_STOP = 0x00000001;
public const int SERVICE_ACCEPT_STOP = 0x00000001;
public const int SERVICE_STOPPED = 0x00000001;
public const int SERVICE_START_PENDING = 0x00000002;
public const int SERVICE_STOP_PENDING = 0x00000003;
public const int SERVICE_RUNNING = 0x00000004;
public const int SERVICE_CONTINUE_PENDING = 0x00000005;
public const int SERVICE_PAUSE_PENDING = 0x00000006;
public const int SERVICE_PAUSED = 0x00000007;
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS {
public int serviceType;
public int currentState;
public int controlsAccepted;
public int win32ExitCode;
public int serviceSpecificExitCode;
public int checkPoint;
public int waitHint;
}
[StructLayout(LayoutKind.Sequential)]
public class SERVICE_TABLE_ENTRY {
public IntPtr name;
public Delegate callback;
}
[DllImport(SERVICE_CORE_API_SET, CharSet = CharSet.Unicode, SetLastError = true)]
public extern static IntPtr RegisterServiceCtrlHandlerExW(string serviceName, Delegate callback, IntPtr userData);
[DllImport(SERVICE_CORE_API_SET, CharSet = CharSet.Unicode, SetLastError = true)]
public extern static bool SetServiceStatus(IntPtr serviceHandle, ref SERVICE_STATUS status);
[DllImport(SERVICE_CORE_API_SET, CharSet = CharSet.Unicode, SetLastError = true)]
public extern static bool StartServiceCtrlDispatcherW(IntPtr entry);
}
}

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

@ -1,215 +0,0 @@
// 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.WindowsService {
using System;
using System.ComponentModel;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
sealed class ServiceHelper {
private const uint ERROR_MORE_DATA = 0xEA;
private const int PENDING_TIMEOUT = 4000; // Timeout in ms during starting/stopping
private delegate int SvcCtrlHandlerEx(int control, int eventType, IntPtr eventData, IntPtr eventContext);
private delegate void SvcMainHandler(int argCount, IntPtr args);
private string _serviceName;
private Task _svcInitTask;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
private IntPtr _serviceHandle = IntPtr.Zero;
SvcMainHandler _svcMainHandler;
SvcCtrlHandlerEx _svcCtrlHandlerEx;
class TaskState {
public ExceptionDispatchInfo Exception { get; set; }
}
public ServiceHelper(string serviceName) {
if (string.IsNullOrEmpty(serviceName)) {
throw new ArgumentNullException(nameof(serviceName));
}
_serviceName = serviceName;
_svcMainHandler = new SvcMainHandler(SvcMain);
_svcCtrlHandlerEx = new SvcCtrlHandlerEx(SvcCtrlHandler);
}
private bool IsService {
get {
return !string.IsNullOrEmpty(_serviceName);
}
}
public string ServiceName {
get {
return _serviceName;
}
}
public async Task Run(Action<CancellationToken> action) {
if (action == null) {
throw new ArgumentNullException(nameof(action));
}
if (!IsService) {
throw new InvalidOperationException("The process is not running as Windows Service");
}
await EnsureInit();
await Task.Run(()=> action(_cancellationToken.Token));
}
private Task EnsureInit() {
if (_svcInitTask != null) {
return _svcInitTask;
}
_svcInitTask = CreateInitTask();
//
// Start StartServiceCtrlDispatcher
Task.Run(() => {
IntPtr namePtr = Marshal.StringToHGlobalUni(ServiceName);
try {
//
// Build SERVICE_TABLE_ENTRY[2] table
IntPtr ptr = Marshal.AllocHGlobal(2 * Marshal.SizeOf<Interop.SERVICE_TABLE_ENTRY>());
Marshal.StructureToPtr(new Interop.SERVICE_TABLE_ENTRY() { callback = _svcMainHandler, name = namePtr }, ptr, true);
Marshal.StructureToPtr(new Interop.SERVICE_TABLE_ENTRY() { callback = null, name = IntPtr.Zero },
new IntPtr(ptr.ToInt64() + Marshal.SizeOf<Interop.SERVICE_TABLE_ENTRY>()), true);
//
// Blocks until the Windows Service stops
if (!Interop.StartServiceCtrlDispatcherW(ptr)) {
throw new Win32Exception();
}
}
catch (Exception e) {
CompleteInitTask(e);
throw e;
}
finally {
Marshal.FreeHGlobal(namePtr);
}
});
return _svcInitTask;
}
private void SvcMain(int argCount, IntPtr args) {
try {
//
// Register the handler function for the service
_serviceHandle = Interop.RegisterServiceCtrlHandlerExW(ServiceName, _svcCtrlHandlerEx, IntPtr.Zero);
if (_serviceHandle == IntPtr.Zero) {
throw new Win32Exception();
}
//
//
// Report Starting
SetStatus(Interop.SERVICE_START_PENDING);
//
// Do some startup logic here...
//
//
//
// Report Running
SetStatus(Interop.SERVICE_RUNNING);
//
//
CompleteInitTask(null);
//
// Wait for stop event
_cancellationToken.Token.WaitHandle.WaitOne();
}
catch (Exception e) {
CompleteInitTask(e);
throw e;
}
finally {
//
// Report Stopped
SetStatus(Interop.SERVICE_STOPPED);
}
}
private int SvcCtrlHandler(int command, int eventType, IntPtr eventData, IntPtr eventContext) {
//
// Handle service control operation
switch (command) {
case Interop.SERVICE_CONTROL_STOP:
SetStatus(Interop.SERVICE_STOP_PENDING);
//
// Signal the service to stop
_cancellationToken.Cancel();
break;
default:
break;
}
return 0;
}
private void SetStatus(int state, int error = 0) {
if (_serviceHandle == IntPtr.Zero) {
throw new ArgumentNullException(nameof(_serviceHandle));
}
var status = new Interop.SERVICE_STATUS() {
currentState = state,
win32ExitCode = error,
serviceType = Interop.SERVICE_TYPE_WIN32_OWN_PROCESS,
controlsAccepted = (state == Interop.SERVICE_START_PENDING) ? 0 : Interop.SERVICE_ACCEPT_STOP,
waitHint = (state == Interop.SERVICE_START_PENDING) || (state == Interop.SERVICE_STOP_PENDING) ? PENDING_TIMEOUT : 0
};
Interop.SetServiceStatus(_serviceHandle, ref status); // Ignore errors
}
private Task CreateInitTask() {
return new Task(s => {
var state = (TaskState)s;
if (state.Exception != null) {
state.Exception.Throw();
}
},
new TaskState());
}
private void CompleteInitTask(Exception e) {
if (_svcInitTask.Status != TaskStatus.Created) {
return; // The task has been started already
}
//
// Fail the task
if (e != null) {
((TaskState)_svcInitTask.AsyncState).Exception = ExceptionDispatchInfo.Capture(e);
}
_svcInitTask.Start();
}
}
}

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

@ -0,0 +1,70 @@
{
"host_id": "",
"host_name": "IIS Administration API",
"urls": "https://*:44326",
"security": {
"require_windows_authentication": true,
"users": {
"administrators": [
"IIS Administrators",
"IIS Administration API Owners"
],
"owners": [
"IIS Administration API Owners"
]
},
"access_policy": {
"api": {
"users": "administrators",
"access_key": true
},
"api_keys": {
"users": "administrators",
"access_key": false
},
"system": {
"users": "owners",
"access_key": true
}
}
},
"logging": {
"enabled": true,
"file_name": "iis-admin-api-test-{Date}.txt",
"min_level": "Information",
"path": "%temp%\\iis-admin-api\\"
},
"auditing": {
"enabled": true,
"file_name": "audit-{Date}.txt",
"path": null
},
"cors": {
"rules": [
{
"origin": "https://manage.iis.net",
"allow": true
}
]
},
"files": {
"locations": [
{
"alias": "inetpub",
"path": "%systemdrive%\\inetpub",
"claims": [
"read",
"write"
]
},
{
"alias": "tests",
"path": "D:\\GitHub\\Microsoft\\IIS.Administration\\.test",
"claims": [
"read",
"write"
]
}
]
}
}