blob: 4cf09adc885e59f88cc21d6d927aea21b37add5f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "CryptoKeyReader.h"
#include <pulsar/Result.h>
#include <pulsar/EncryptionKeyInfo.h>
#include <thread>
#include <future>
class CryptoKeyReaderWrapper : public pulsar::CryptoKeyReader {
public:
CryptoKeyReaderWrapper(const Napi::Object& jsObject) : mainThreadId_(std::this_thread::get_id()) {
jsObject_.Reset(jsObject, 1);
tsfn_ = Napi::ThreadSafeFunction::New(
jsObject.Env(), Napi::Function::New(jsObject.Env(), [](const Napi::CallbackInfo& info) {}), jsObject,
"CryptoKeyReader", 0, 1);
}
~CryptoKeyReaderWrapper() { tsfn_.Release(); }
pulsar::Result getPublicKey(const std::string& keyName, std::map<std::string, std::string>& metadata,
pulsar::EncryptionKeyInfo& encKeyInfo) const override {
return executeCallback("getPublicKey", keyName, metadata, encKeyInfo);
}
pulsar::Result getPrivateKey(const std::string& keyName, std::map<std::string, std::string>& metadata,
pulsar::EncryptionKeyInfo& encKeyInfo) const override {
return executeCallback("getPrivateKey", keyName, metadata, encKeyInfo);
}
private:
Napi::ObjectReference jsObject_;
Napi::ThreadSafeFunction tsfn_;
std::thread::id mainThreadId_;
static void parseEncryptionKeyInfo(const Napi::Object& obj, pulsar::EncryptionKeyInfo& info) {
if (obj.Has("key") && obj.Get("key").IsBuffer()) {
Napi::Buffer<char> keyBuf = obj.Get("key").As<Napi::Buffer<char>>();
info.setKey(std::string(keyBuf.Data(), keyBuf.Length()));
}
if (obj.Has("metadata") && obj.Get("metadata").IsObject()) {
std::map<std::string, std::string> metadata;
Napi::Object metaObj = obj.Get("metadata").As<Napi::Object>();
Napi::Array keys = metaObj.GetPropertyNames();
for (uint32_t i = 0; i < keys.Length(); i++) {
std::string k = keys.Get(i).ToString().Utf8Value();
std::string v = metaObj.Get(k).ToString().Utf8Value();
metadata[k] = v;
}
info.setMetadata(metadata);
}
}
pulsar::Result callJsMethod(Napi::Env env, const std::string& method, const std::string& keyName,
const std::map<std::string, std::string>& metadata,
pulsar::EncryptionKeyInfo& encKeyInfo) const {
Napi::HandleScope scope(env);
if (jsObject_.IsEmpty()) {
return pulsar::Result::ResultCryptoError;
}
Napi::Object obj = jsObject_.Value();
if (!obj.Has(method)) {
return pulsar::Result::ResultCryptoError;
}
Napi::Value funcVal = obj.Get(method);
if (!funcVal.IsFunction()) {
return pulsar::Result::ResultCryptoError;
}
Napi::Function func = funcVal.As<Napi::Function>();
Napi::Object metadataObj = Napi::Object::New(env);
for (const auto& kv : metadata) {
metadataObj.Set(kv.first, kv.second);
}
try {
Napi::Value result = func.Call(obj, {Napi::String::New(env, keyName), metadataObj});
if (result.IsObject()) {
parseEncryptionKeyInfo(result.As<Napi::Object>(), encKeyInfo);
return pulsar::Result::ResultOk;
}
} catch (const Napi::Error& e) {
return pulsar::Result::ResultCryptoError;
} catch (...) {
return pulsar::Result::ResultCryptoError;
}
return pulsar::Result::ResultCryptoError;
}
pulsar::Result executeCallback(const std::string& method, const std::string& keyName,
std::map<std::string, std::string>& metadata,
pulsar::EncryptionKeyInfo& encKeyInfo) const {
if (std::this_thread::get_id() == mainThreadId_) {
return callJsMethod(jsObject_.Env(), method, keyName, metadata, encKeyInfo);
} else {
auto promise = std::make_shared<std::promise<pulsar::Result>>();
auto future = promise->get_future();
napi_status status = tsfn_.BlockingCall([this, promise, &method, &keyName, &metadata, &encKeyInfo](
Napi::Env env, Napi::Function jsCallback) {
promise->set_value(callJsMethod(env, method, keyName, metadata, encKeyInfo));
});
if (status != napi_ok) {
return pulsar::Result::ResultCryptoError;
}
future.wait();
return future.get();
}
}
};
Napi::FunctionReference CryptoKeyReader::constructor;
void CryptoKeyReader::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env, "CryptoKeyReader", {});
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("CryptoKeyReader", func);
}
CryptoKeyReader::CryptoKeyReader(const Napi::CallbackInfo& info) : Napi::ObjectWrap<CryptoKeyReader>(info) {}
CryptoKeyReader::~CryptoKeyReader() {}
std::shared_ptr<pulsar::CryptoKeyReader> CryptoKeyReader::GetCCryptoKeyReader() {
return std::make_shared<CryptoKeyReaderWrapper>(Value());
}