将 Chromedriver 与开发的扩展和强制安装组策略一起使用

Using Chromedriver with Developed Extension and Force Install Group Policy

我正在开发具有以下环境设置的 Web 应用程序:

  1. 该网站设计用于 Chrome
  2. 该网站依赖 Chrome 开发的扩展 in-house 并托管在 Chrome Play 商店
  3. 为了更轻松地获取扩展,我们的系统团队设置了 ExtensionInstallForcelist 组策略(更多信息请点击此处 http://dev.chromium.org/administrators/policy-list-3#ExtensionInstallForcelist)。这使得 Chrome 可以获取扩展并根据需要更新它,而无需手动用户交互
  4. 这都是在镜像设备上设置的(所以单独的设备,有单独的浏览器和组策略,每个设备都有自己的扩展)

我遇到的问题是尝试将 Chromedriver 实例与此组策略一起使用时。

启用该策略后,我在 运行 我们的测试时收到 "Failed to load extension from: . (extension ID ) is blocked by the administrator." 错误消息。

这似乎是因为我们通过 Chrome 选项(添加扩展名)将扩展名添加到我们的 Chromedriver。

虽然我可以手动关闭映像设备上的组策略来解决这个问题,但我想知道是否有任何设置可以添加到组策略中以允许在 Chromedriver?

我已经尝试了很多方法来解决这个问题,否则似乎不起作用:

  1. 命令行更改由组策略设置的注册表值 - 不起作用,因为虽然更改注册表值有效,但我仍然在 运行 测试
  2. 上收到错误
  3. PowerShell 更改组策略 - 将不起作用,因为它需要额外安装获取组策略 cmdlet
  4. 使用 GPMC C# 库 - 这些库在这一点上似乎已经过时了(有些只使用 .NET 2.0)。我能够设置一些代码来使用 类,但是我收到了创建 GPODomain object 的 "reference missing" 异常,即使我在我的解决方案中有引用

提前感谢大家提出有关让 Chromedriver 使用强制安装组策略的任何建议。

在网上看了很多不同的答案后,我发现这个解决方案有效。

创建一个 GroupPolicy class 来处理与之交互:

using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Automation.Core
{
[ComImport, Guid("EA502722-A23D-11d1-A7D3-0000F87571E3")]
internal class GPClass
{
}

[ComImport, Guid("EA502723-A23D-11d1-A7D3-0000F87571E3"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IGroupPolicyObject
{
    uint New([MarshalAs(UnmanagedType.LPWStr)] string domainName, [MarshalAs(UnmanagedType.LPWStr)] string displayName, uint flags);

    uint OpenDSGPO([MarshalAs(UnmanagedType.LPWStr)] string path, uint flags);

    uint OpenLocalMachineGPO(uint flags);

    uint OpenRemoteMachineGPO([MarshalAs(UnmanagedType.LPWStr)] string computerName, uint flags);

    uint Save([MarshalAs(UnmanagedType.Bool)] bool machine, [MarshalAs(UnmanagedType.Bool)] bool add, [MarshalAs(UnmanagedType.LPStruct)] Guid extension, [MarshalAs(UnmanagedType.LPStruct)] Guid app);

    uint Delete();

    uint GetName([MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxLength);

    uint GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxLength);

    uint SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name);

    uint GetPath([MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, int maxPath);

    uint GetDSPath(uint section, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, int maxPath);

    uint GetFileSysPath(uint section, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, int maxPath);

    uint GetRegistryKey(uint section, out IntPtr key);

    uint GetOptions(out uint options);

    uint SetOptions(uint options, uint mask);

    uint GetType(out IntPtr gpoType);

    uint GetMachineName([MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxLength);

    uint GetPropertySheetPages(out IntPtr pages);
}


public enum GroupPolicySection
{
    Root = 0,
    User = 1,
    Machine = 2,
}

public abstract class GroupPolicyObject
{
    protected const int MaxLength = 1024;

    /// <summary>
    /// The snap-in that processes .pol files
    /// </summary>
    private static readonly Guid RegistryExtension = new Guid(0x35378EAC, 0x683F, 0x11D2, 0xA8, 0x9A, 0x00, 0xC0, 0x4F, 0xBB, 0xCF, 0xA2);

    /// <summary>
    /// This application
    /// </summary>
    private static readonly Guid LocalGuid = new Guid(GetAssemblyAttribute<GuidAttribute>(Assembly.GetExecutingAssembly()).Value);

    protected IGroupPolicyObject Instance = (IGroupPolicyObject) new GPClass();

    static T GetAssemblyAttribute<T>(ICustomAttributeProvider assembly) where T : Attribute
    {
        object[] attributes = assembly.GetCustomAttributes(typeof(T), true);
        if (attributes.Length == 0)
            return null;

        return (T)attributes[0];
    }

    public void Save()
    {
        var result = Instance.Save(true, true, RegistryExtension, LocalGuid);
        if (result != 0)
        {
            throw new Exception("Error saving machine settings");
        }

        result = Instance.Save(false, true, RegistryExtension, LocalGuid);
        if (result != 0)
        {
            throw new Exception("Error saving user settings");
        }
    }

    public RegistryKey GetRootRegistryKey(GroupPolicySection section)
    {
        IntPtr key;
        var result = Instance.GetRegistryKey((uint)section, out key);
        if (result != 0)
        {
            throw new Exception(string.Format("Unable to get section '{0}'", Enum.GetName(typeof(GroupPolicySection), section)));
        }

        var handle = new SafeRegistryHandle(key, true);
        return RegistryKey.FromHandle(handle, RegistryView.Default);
    }
}

public class GroupPolicyObjectSettings
{
    public readonly bool LoadRegistryInformation;
    public readonly bool Readonly;

    public GroupPolicyObjectSettings(bool loadRegistryInfo = true, bool readOnly = false)
    {
        LoadRegistryInformation = loadRegistryInfo;
        Readonly = readOnly;
    }

    private const uint RegistryFlag = 0x00000001;
    private const uint ReadonlyFlag = 0x00000002;

    internal uint Flag
    {
        get
        {
            uint flag = 0x00000000;
            if (LoadRegistryInformation)
            {
                flag |= RegistryFlag;
            }

            if (Readonly)
            {
                flag |= ReadonlyFlag;
            }

            return flag;
        }
    }
}

public class ComputerGroupPolicyObject : GroupPolicyObject
{
    public readonly bool IsLocal;

    public ComputerGroupPolicyObject(GroupPolicyObjectSettings options = null)
    {
        options = options ?? new GroupPolicyObjectSettings();
        var result = Instance.OpenLocalMachineGPO(options.Flag);
        if (result != 0)
        {
            throw new Exception("Unable to open local machine GPO");
        }
        IsLocal = true;
    }

    public static void SetPolicySetting(string registryInformation, string settingValue, RegistryValueKind registryValueKind)
    {
        string valueName;
        GroupPolicySection section;
        string key = Key(registryInformation, out valueName, out section);

        // Thread must be STA
        Exception exception = null;
        var t = new Thread(() =>
        {
            try
            {
                var gpo = new ComputerGroupPolicyObject();
                using (RegistryKey rootRegistryKey = gpo.GetRootRegistryKey(section))
                {
                    // Data can't be null so we can use this value to indicate key must be delete
                    if (settingValue == null)
                    {
                        using (RegistryKey subKey = rootRegistryKey.OpenSubKey(key, true))
                        {
                            if (subKey != null)
                            {
                                subKey.DeleteValue(valueName);
                            }
                        }
                    }
                    else
                    {
                        using (RegistryKey subKey = rootRegistryKey.CreateSubKey(key))
                        {
                            subKey.SetValue(valueName, settingValue, registryValueKind);
                        }
                    }
                }

                gpo.Save();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        });
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();

        if (exception != null)
            throw exception;
    }

    public static object GetPolicySetting(string registryInformation)
    {
        string valueName;
        GroupPolicySection section;
        string key = Key(registryInformation, out valueName, out section);

        // Thread must be STA
        object result = null;
        var t = new Thread(() =>
        {
            var gpo = new ComputerGroupPolicyObject();
            using (RegistryKey rootRegistryKey = gpo.GetRootRegistryKey(section))
            {
                // Data can't be null so we can use this value to indicate key must be delete
                using (RegistryKey subKey = rootRegistryKey.OpenSubKey(key, true))
                {
                    if (subKey == null)
                    {
                        result = null;
                    }
                    else
                    {
                        result = subKey.GetValue(valueName);
                    }
                }
            }
        });
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();

        return result;
    }

    public static string Key(string registryInformation, out string value, out GroupPolicySection section)
    {
        // Parse parameter of format HKCU\Software\Policies\Microsoft\Windows\Personalization!NoChangingSoundScheme
        string[] split = registryInformation.Split('!');
        string key = split[0];
        string hive = key.Substring(0, key.IndexOf('\'));
        key = key.Substring(key.IndexOf('\') + 1);

        value = split[1];

        if (hive.Equals(@"HKLM", StringComparison.OrdinalIgnoreCase)
            || hive.Equals(@"HKEY_LOCAL_MACHINE", StringComparison.OrdinalIgnoreCase))
        {
            section = GroupPolicySection.Machine;
        }
        else
        {
            section = GroupPolicySection.User;
        }
        return key;
    }
}

}

然后通过如下调用关闭强制安装 Chrome 扩展组策略:

[STAThread]
    private static bool DisabledChromeExtensionGPO()
    {
        var PolicyExists = ComputerGroupPolicyObject.GetPolicySetting(@"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist!1");

        if (PolicyExists != null)
        {

            ComputerGroupPolicyObject.SetPolicySetting(@"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist!1", "null", RegistryValueKind.String);
        }

        return true;
    }

虽然该策略仍会在注册表编辑器中列出其键,但该键的值将设置为空。

将值设置为 null 允许 Chrome 驱动程序在此时加载我们的本地 Chrome 扩展文件而不会出现问题。