Initial commit

This commit is contained in:
João Moreno 2022-05-18 09:28:30 +02:00 committed by GitHub
parent a36186ecb3
commit c42e9ee946
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2579 additions and 22 deletions

20
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: CI
on:
push:
branches: ["**"]
tags: [v*]
pull_request:
branches: [main]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/node_modules
/build

19
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/node_modules/node-addon-api",
"${default}"
],
"defines": ["_DEBUG"],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

72
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,72 @@
{
"files.associations": {
"system_error": "cpp",
"thread": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"format": "cpp",
"forward_list": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
}
}

24
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"group": "build",
"problemMatcher": [],
"label": "npm: build",
"detail": "node-gyp build"
},
{
"type": "npm",
"script": "rebuild",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"label": "npm: rebuild",
"detail": "node-gyp rebuild"
}
]
}

View File

@ -1,14 +1,19 @@
# Project # vscode-policy-watcher
> This repo has been populated by an initial template to help get you started. Please Example usage:
> make sure to update the content to build a great experience for community-building.
As the maintainer of this project, please make a few updates: ```js
const createWatcher = require("vscode-policy-watcher");
- Improving this README.MD file to provide a great experience createWatcher(
- Updating SUPPORT.MD with content about this project's support experience "CodeOSS",
- Understanding the security reporting process in SECURITY.MD {
- Remove this section from the README UpdateMode: { type: "string" },
SCMInputFontSize: { type: "number" },
},
(update) => console.log(update)
);
```
## Contributing ## Contributing

View File

@ -1,13 +1,3 @@
# TODO: The maintainer of this repo has not yet edited this file
**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
- **No CSS support:** Fill out this template with information about how to file issues and get help.
- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps.
- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide.
*Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
# Support # Support
## How to file issues and get help ## How to file issues and get help
@ -22,4 +12,4 @@ CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
## Microsoft Support Policy ## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above. Support for this project is limited to the resources listed above.

44
binding.gyp Normal file
View File

@ -0,0 +1,44 @@
{
"targets": [
{
"target_name": "vscode-policy-watcher",
"sources": [
"src/main.cc",
"src/StringPolicy.cc",
"src/NumberPolicy.cc",
"src/PolicyWatcher.cc"
],
"include_dirs": [
"<!(node -p \"require('node-addon-api').include_dir\")"
],
'cflags!': ['-fno-exceptions'],
'cflags_cc!': ['-fno-exceptions'],
'conditions': [
["OS=='win'", {
"defines": [
"_HAS_EXCEPTIONS=1"
],
"libraries": [
"userenv.lib"
],
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1,
'AdditionalOptions': [
'/W3',
'/Qspectre',
'/guard:cf',
'-std:c++17'
]
},
'VCLinkerTool': {
'AdditionalOptions': [
'/guard:cf'
]
}
},
}],
],
}
]
}

31
index.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
interface Watcher {
dispose(): void;
}
type StringPolicy = { type: "string" };
type NumberPolicy = { type: "number" };
export interface Policies {
[policyName: string]: StringPolicy | NumberPolicy;
}
export type PolicyUpdate<T extends Policies> = {
[K in keyof T]:
| undefined
| (T[K] extends StringPolicy
? string
: T[K] extends NumberPolicy
? number
: never);
};
export function createWatcher<T extends Policies>(
productName: string,
policies: T,
onDidChange: (update: PolicyUpdate<T>) => void
): Watcher;

17
index.js Normal file
View File

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
exports.createWatcher = require('bindings')('vscode-policy-watcher');
if (require.main === module) {
exports.createWatcher(
'CodeOSS',
{
UpdateMode: { type: 'string' },
SCMInputFontSize: { type: 'number' },
},
msg => console.log(msg)
);
}

1904
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "vscode-policy-watcher",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "node-gyp build",
"rebuild": "node-gyp rebuild"
},
"author": "João Moreno",
"license": "MIT",
"os": [
"win32"
],
"types": "index.d.ts",
"dependencies": {
"bindings": "^1.5.0",
"node-addon-api": "*"
},
"devDependencies": {
"node-gyp": "^9.0.0"
}
}

21
src/NumberPolicy.cc Normal file
View File

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include "NumberPolicy.hh"
using namespace Napi;
NumberPolicy::NumberPolicy(const std::string name, const std::string &productName)
: RegistryPolicy(name, productName, REG_QWORD) {}
long long NumberPolicy::parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const
{
return *reinterpret_cast<long long *>(buffer);
}
Value NumberPolicy::getJSValue(Env env, long long value) const
{
return Number::New(env, value);
}

25
src/NumberPolicy.hh Normal file
View File

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef NUMBER_POLICY_H
#define NUMBER_POLICY_H
#include <napi.h>
#include <windows.h>
#include "RegistryPolicy.hh"
using namespace Napi;
class NumberPolicy : public RegistryPolicy<long long>
{
public:
NumberPolicy(const std::string name, const std::string &productName);
protected:
long long parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const;
Value getJSValue(Env env, long long value) const;
};
#endif

24
src/Policy.hh Normal file
View File

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef POLICY_H
#define POLICY_H
#include <napi.h>
using namespace Napi;
class Policy
{
public:
virtual bool refresh() = 0;
virtual Value getValue(Env env) const = 0;
const std::string name;
Policy(const std::string name)
: name(name) {}
};
#endif

89
src/PolicyWatcher.cc Normal file
View File

@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include "PolicyWatcher.hh"
using namespace Napi;
PolicyWatcher::PolicyWatcher(Function &okCallback, std::vector<std::unique_ptr<Policy>> _policies)
: AsyncProgressQueueWorker(okCallback),
policies(std::move(_policies))
{
}
PolicyWatcher::~PolicyWatcher()
{
UnregisterGPNotification(handles[2]);
UnregisterGPNotification(handles[3]);
CloseHandle(handles[0]);
CloseHandle(handles[1]);
CloseHandle(handles[2]);
CloseHandle(handles[3]);
}
void PolicyWatcher::OnExecute(Napi::Env env)
{
if ((handles[0] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create exit event");
if ((handles[1] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create dispose event");
if ((handles[2] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create machine GP event");
if ((handles[3] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create user GP event");
if (!RegisterGPNotification(handles[2], TRUE))
return SetError("Failed to register machine GP event");
if (!RegisterGPNotification(handles[3], FALSE))
return SetError("Failed to register user GP event");
AsyncProgressQueueWorker::OnExecute(env);
}
void PolicyWatcher::Execute(const ExecutionProgress &progress)
{
bool first = true;
while (TRUE)
{
std::vector<const Policy *> updatedPolicies;
for (auto &policy : policies)
{
if (policy->refresh())
{
updatedPolicies.push_back(policy.get());
}
}
if (first || updatedPolicies.size() > 0)
progress.Send(&updatedPolicies[0], updatedPolicies.size());
first = false;
auto dwResult = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
if (dwResult == WAIT_FAILED || (dwResult - WAIT_OBJECT_0) == 0 || (dwResult - WAIT_OBJECT_0) == 1 /* someone called dispose() */)
break;
}
}
void PolicyWatcher::OnOK() {}
void PolicyWatcher::OnError() {}
void PolicyWatcher::OnProgress(const Policy *const *policies, size_t count)
{
HandleScope scope(Env());
auto result = Object::New(Env());
for (size_t i = 0; i < count; i++)
result.Set(policies[i]->name, policies[i]->getValue(Env()));
Callback().Call(Receiver().Value(), {result});
}
void PolicyWatcher::Dispose()
{
SetEvent(handles[1]);
}

35
src/PolicyWatcher.hh Normal file
View File

@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef POLICY_WATCHER_H
#define POLICY_WATCHER_H
#include <napi.h>
#include <windows.h>
#include <userenv.h>
#include <vector>
#include "Policy.hh"
using namespace Napi;
class PolicyWatcher : public AsyncProgressQueueWorker<const Policy *>
{
public:
PolicyWatcher(Function &okCallback, std::vector<std::unique_ptr<Policy>> _policies);
~PolicyWatcher();
void OnExecute(Napi::Env env);
void Execute(const ExecutionProgress &progress);
void OnOK();
void OnError();
void OnProgress(const Policy *const *policies, size_t count);
void Dispose();
private:
std::vector<std::unique_ptr<Policy>> policies;
HANDLE handles[4];
};
#endif

89
src/RegistryPolicy.hh Normal file
View File

@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef REGISTRY_POLICY_H
#define REGISTRY_POLICY_H
#include <napi.h>
#include <windows.h>
#include <optional>
#include "Policy.hh"
using namespace Napi;
template <typename T>
class RegistryPolicy : public Policy
{
public:
RegistryPolicy(const std::string name, const std::string &productName, const DWORD regType)
: Policy(name),
registryKey("Software\\Policies\\Microsoft\\" + productName),
regType(regType) {}
bool refresh()
{
auto machine = read(HKEY_LOCAL_MACHINE);
if (machine.has_value())
{
if (value != machine)
{
value = machine;
return true;
}
return false;
}
auto user = read(HKEY_CURRENT_USER);
if (value != user)
{
value = user;
return true;
}
return false;
}
Value getValue(Env env) const
{
if (!value.has_value())
return env.Undefined();
return getJSValue(env, value.value());
}
protected:
virtual T parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const = 0;
virtual Value getJSValue(Env env, T value) const = 0;
private:
const std::string registryKey;
const DWORD regType;
std::optional<T> value;
std::optional<T> read(HKEY root)
{
HKEY hKey;
if (ERROR_SUCCESS != RegOpenKeyEx(root, registryKey.c_str(), 0, KEY_READ, &hKey))
return std::nullopt;
BYTE buffer[1024];
DWORD bufferSize = sizeof(buffer);
DWORD type;
auto readResult = RegQueryValueEx(hKey, name.c_str(), 0, &type, buffer, &bufferSize);
RegCloseKey(hKey);
if (ERROR_SUCCESS != readResult || type != regType)
return std::nullopt;
return std::optional<T>{parseRegistryValue(buffer, bufferSize)};
}
};
#endif

21
src/StringPolicy.cc Normal file
View File

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include "StringPolicy.hh"
using namespace Napi;
StringPolicy::StringPolicy(const std::string name, const std::string &productName)
: RegistryPolicy(name, productName, REG_SZ) {}
std::string StringPolicy::parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const
{
return std::string(reinterpret_cast<char *>(buffer), bufferSize - 1);
}
Value StringPolicy::getJSValue(Env env, std::string value) const
{
return String::New(env, value);
}

25
src/StringPolicy.hh Normal file
View File

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef STRING_POLICY_H
#define STRING_POLICY_H
#include <napi.h>
#include <windows.h>
#include "RegistryPolicy.hh"
using namespace Napi;
class StringPolicy : public RegistryPolicy<std::string>
{
public:
StringPolicy(const std::string name, const std::string &productName);
protected:
std::string parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const;
Value getJSValue(Env env, std::string value) const;
};
#endif

77
src/main.cc Normal file
View File

@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include <napi.h>
#include <vector>
#include "Policy.hh"
#include "StringPolicy.hh"
#include "NumberPolicy.hh"
#include "PolicyWatcher.hh"
using namespace Napi;
Value DisposeWatcher(const CallbackInfo &info)
{
auto watcher = (PolicyWatcher *)info.Data();
watcher->Dispose();
return info.Env().Null();
}
Value CreateWatcher(const CallbackInfo &info)
{
auto env = info.Env();
if (info.Length() < 3)
throw TypeError::New(env, "Expected 3 arguments");
else if (!info[0].IsString())
throw TypeError::New(env, "Expected first arg to be string");
else if (!info[1].IsObject())
throw TypeError::New(env, "Expected second arg to be object");
else if (!info[2].IsFunction())
throw TypeError::New(env, "Expected third arg to be function");
auto productName = info[0].As<String>();
auto rawPolicies = info[1].As<Object>();
auto policies = std::vector<std::unique_ptr<Policy>>();
for (auto const &item : rawPolicies)
{
auto rawPolicyName = item.first.As<String>();
auto rawPolicyValue = static_cast<Value>(item.second);
if (!rawPolicyValue.IsObject())
throw TypeError::New(env, "Expected policy to be object");
auto rawPolicy = rawPolicyValue.As<Object>();
auto rawPolicyType = rawPolicy.Get("type");
if (!rawPolicyType.IsString())
throw TypeError::New(env, "Expected policy type to be string");
auto policyType = std::string(rawPolicyType.As<String>());
if (policyType == "string")
policies.push_back(std::make_unique<StringPolicy>(rawPolicyName.As<String>(), productName));
else if (policyType == "number")
policies.push_back(std::make_unique<NumberPolicy>(rawPolicyName.As<String>(), productName));
else
throw TypeError::New(env, "Unknown policy type '" + policyType + "'");
}
auto watcher = new PolicyWatcher(info[2].As<Function>(), std::move(policies));
watcher->Queue();
auto result = Object::New(env);
result.Set(String::New(env, "dispose"), Function::New(env, DisposeWatcher, "disposeWatcher", watcher));
return result;
}
Object Init(Env env, Object exports)
{
return Function::New(env, CreateWatcher, "createWatcher");
}
NODE_API_MODULE(vscodepolicy, Init)