<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Web.Administration;
using static Oocx.ACME.Common.Log;

namespace Oocx.ACME.IIS
{
    // This project can output the Class library as a NuGet Package.
    // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
    public class IISChallengeService
    {
        private readonly ServerManager manager;

        public IISChallengeService()
        {
            manager = new ServerManager();
        }
        public bool CanAcceptChallengeForDomain(string domain)
        {            
            return GetSiteForDomain(domain) != null;
        }

        private Site GetSiteForDomain(string domain)
        {
            return manager.Sites.SingleOrDefault(s => s.Bindings.Any(b => string.Equals(domain, b.Host, StringComparison.OrdinalIgnoreCase)));
        }

        const string AcmeWebConfigContents =
            "<?xml version = \"1.0\" encoding=\"UTF-8\"?><configuration><system.webServer><staticContent><mimeMap fileExtension = \".\" mimeType=\"application/jose+json\" /></staticContent></system.webServer></configuration>";

        public void AcceptChallenge(string domain, string token, string challengeJson)
        {
            Info($"IISChallengeService is accepting challenge with token {token} for domain {domain}");
            var wellKnownPath = CreateWellKnownDirectory(token, challengeJson);            
            CreateIISVirtualDirectory(domain, wellKnownPath);
        }

        private static void AllowReadPermissionsForEveryone(string wellKnownPath)
        {
            //TODO instead of allowing read for everyone, only allow read for the IIS application pool account
            var accessControl = File.GetAccessControl(wellKnownPath);
            var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
            var allowRead = new FileSystemAccessRule(everyone, FileSystemRights.Read, AccessControlType.Allow);
            accessControl.AddAccessRule(allowRead);
        }

        private void CreateIISVirtualDirectory(string domain, string wellKnownPath)
        {
            var site = GetSiteForDomain(domain);
            var root = site.Applications["/"];
            var wellKnownDir = root.VirtualDirectories.FirstOrDefault(d => "/.well-known".Equals(d.Path));
            if (wellKnownDir == null)
            {
                Verbose("creating virtual directory /.well-known/acme");
                var wellKnownVdir = root.VirtualDirectories.Add("/.well-known", wellKnownPath);
                var acmeVdir = root.VirtualDirectories.Add("/.well-known/acme", Path.Combine(wellKnownPath, "acme"));
                manager.CommitChanges();
            }
        }

        private static string CreateWellKnownDirectory(string token, string challengeJson)
        {<commit_msg>set permissions for acme challenge path<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Web.Administration;
using static Oocx.ACME.Common.Log;

namespace Oocx.ACME.IIS
{
    // This project can output the Class library as a NuGet Package.
    // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
    public class IISChallengeService
    {
        private readonly ServerManager manager;

        public IISChallengeService()
        {
            manager = new ServerManager();
        }
        public bool CanAcceptChallengeForDomain(string domain)
        {            
            return GetSiteForDomain(domain) != null;
        }

        private Site GetSiteForDomain(string domain)
        {
            return manager.Sites.SingleOrDefault(s => s.Bindings.Any(b => string.Equals(domain, b.Host, StringComparison.OrdinalIgnoreCase)));
        }

        const string AcmeWebConfigContents =
            "<?xml version = \"1.0\" encoding=\"UTF-8\"?><configuration><system.webServer><staticContent><mimeMap fileExtension = \".\" mimeType=\"application/jose+json\" /></staticContent></system.webServer></configuration>";

        public void AcceptChallenge(string domain, string token, string challengeJson)
        {
            Info($"IISChallengeService is accepting challenge with token {token} for domain {domain}");
            var wellKnownPath = CreateWellKnownDirectory(token, challengeJson);            
            CreateIISVirtualDirectory(domain, wellKnownPath);
        }

        private static void AllowReadPermissionsForEveryone(string wellKnownPath)
        {
            //TODO instead of allowing read for everyone, only allow read for the IIS application pool account
            var accessControl = File.GetAccessControl(wellKnownPath);
            var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
            var allowRead = new FileSystemAccessRule(everyone, FileSystemRights.Read, AccessControlType.Allow);
            accessControl.AddAccessRule(allowRead);
            File.SetAccessControl(wellKnownPath, accessControl);
        }

        private void CreateIISVirtualDirectory(string domain, string wellKnownPath)
        {
            var site = GetSiteForDomain(domain);
            var root = site.Applications["/"];
            var wellKnownDir = root.VirtualDirectories.FirstOrDefault(d => "/.well-known".Equals(d.Path));
            if (wellKnownDir == null)
            {
                Verbose("creating virtual directory /.well-known/acme");
                var wellKnownVdir = root.VirtualDirectories.Add("/.well-known", wellKnownPath);
                var acmeVdir = root.VirtualDirectories.Add("/.well-known/acme", Path.Combine(wellKnownPath, "acme"));
                manager.CommitChanges();
            }
        }

        private static string CreateWellKnownDirectory(string token, string challengeJson)
        {
{'fraction_non_alphanumeric': 0.09140738352410124, 'fraction_numerical': 0.0009672738997259391, 'mean_word_length': 2.744115872057936, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4541790', 'n_tokens_mistral': 1597, 'n_tokens_neox': 1529, 'n_words': 395}