blob: c3408210a64f422dca629eca306571fa8d493235 [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 "Authentication.h"
#include <future>
static const std::string PARAM_TLS_CERT = "certificatePath";
static const std::string PARAM_TLS_KEY = "privateKeyPath";
static const std::string PARAM_TOKEN = "token";
static const std::string PARAM_USERNAME = "username";
static const std::string PARAM_PASSWORD = "password";
void FinalizeTokenSupplierCallback(Napi::Env env, TokenSupplierCallback *cb, void *) { delete cb; }
struct TokenSupplierProxyData {
std::function<void(void)> callback;
std::string token;
TokenSupplierProxyData(std::function<void(void)> callback) : callback(callback), token(std::string()) {}
};
void TokenSupplierProxy(Napi::Env env, Napi::Function jsCallback, TokenSupplierProxyData *data) {
Napi::Value ret = jsCallback.Call({});
if (ret.IsPromise()) {
Napi::Promise promise = ret.As<Napi::Promise>();
Napi::Value thenValue = promise.Get("then");
if (thenValue.IsFunction()) {
Napi::Function then = thenValue.As<Napi::Function>();
Napi::Function callback = Napi::Function::New(env, [data](const Napi::CallbackInfo &info) {
Napi::Value value = info[0];
if (value.IsString()) {
data->token = value.ToString().Utf8Value();
}
data->callback();
});
then.Call(promise, {callback});
return;
}
}
if (ret.IsString()) {
data->token = ret.ToString().Utf8Value();
}
data->callback();
}
char *TokenSupplier(void *ctx) {
TokenSupplierCallback *tokenSupplierCallback = (TokenSupplierCallback *)ctx;
if (tokenSupplierCallback->callback.Acquire() != napi_ok) {
char *empty = (char *)malloc(0);
return empty;
}
std::promise<void> promise;
std::future<void> future = promise.get_future();
std::unique_ptr<TokenSupplierProxyData> dataPtr(
new TokenSupplierProxyData([&promise]() { promise.set_value(); }));
tokenSupplierCallback->callback.BlockingCall(dataPtr.get(), TokenSupplierProxy);
tokenSupplierCallback->callback.Release();
future.wait();
char *token = (char *)malloc(dataPtr->token.size());
strcpy(token, dataPtr->token.c_str());
return token;
}
Napi::FunctionReference Authentication::constructor;
Napi::Object Authentication::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env, "Authentication", {});
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("Authentication", func);
return exports;
}
Authentication::Authentication(const Napi::CallbackInfo &info)
: Napi::ObjectWrap<Authentication>(info), cAuthentication(nullptr), tokenSupplier(nullptr) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (info.Length() < 1 || !info[0].IsString() || info[0].ToString().Utf8Value().empty()) {
Napi::Error::New(env, "Authentication method is not specified").ThrowAsJavaScriptException();
return;
}
std::string authMethod = info[0].ToString().Utf8Value();
if (authMethod == "tls" || authMethod == "token" || authMethod == "basic") {
if (info.Length() < 2 || !info[1].IsObject()) {
Napi::Error::New(env, "Authentication parameter must be a object").ThrowAsJavaScriptException();
return;
}
Napi::Object obj = info[1].ToObject();
if (authMethod == "tls") {
if (!obj.Has(PARAM_TLS_CERT) || !obj.Get(PARAM_TLS_CERT).IsString() || !obj.Has(PARAM_TLS_KEY) ||
!obj.Get(PARAM_TLS_KEY).IsString()) {
Napi::Error::New(env, "Missing required parameter").ThrowAsJavaScriptException();
return;
}
this->cAuthentication =
pulsar_authentication_tls_create(obj.Get(PARAM_TLS_CERT).ToString().Utf8Value().c_str(),
obj.Get(PARAM_TLS_KEY).ToString().Utf8Value().c_str());
} else if (authMethod == "token") {
if (obj.Has(PARAM_TOKEN)) {
if (obj.Get(PARAM_TOKEN).IsString()) {
this->cAuthentication =
pulsar_authentication_token_create(obj.Get(PARAM_TOKEN).ToString().Utf8Value().c_str());
return;
}
if (obj.Get(PARAM_TOKEN).IsFunction()) {
this->tokenSupplier = new TokenSupplierCallback();
Napi::ThreadSafeFunction callback = Napi::ThreadSafeFunction::New(
obj.Env(), obj.Get(PARAM_TOKEN).As<Napi::Function>(), "Token Supplier Callback", 1, 1,
(void *)NULL, FinalizeTokenSupplierCallback, this->tokenSupplier);
this->tokenSupplier->callback = std::move(callback);
this->cAuthentication =
pulsar_authentication_token_create_with_supplier(&TokenSupplier, this->tokenSupplier);
return;
}
}
Napi::Error::New(env, "Missing required parameter").ThrowAsJavaScriptException();
return;
} else if (authMethod == "basic") {
if (!obj.Has(PARAM_USERNAME) || !obj.Get(PARAM_USERNAME).IsString() || !obj.Has(PARAM_PASSWORD) ||
!obj.Get(PARAM_PASSWORD).IsString()) {
Napi::Error::New(env, "Missing required parameter").ThrowAsJavaScriptException();
return;
}
this->cAuthentication =
pulsar_authentication_basic_create(obj.Get(PARAM_USERNAME).ToString().Utf8Value().c_str(),
obj.Get(PARAM_PASSWORD).ToString().Utf8Value().c_str());
}
} else if (authMethod == "athenz") {
if (info.Length() < 2 || !info[1].IsString()) {
Napi::Error::New(env, "Authentication parameter must be a JSON string").ThrowAsJavaScriptException();
return;
}
this->cAuthentication = pulsar_authentication_athenz_create(info[1].ToString().Utf8Value().c_str());
} else if (authMethod == "oauth2") {
if (info.Length() < 2 || !info[1].IsString()) {
Napi::Error::New(env, "Authentication parameter must be a JSON string for oauth2")
.ThrowAsJavaScriptException();
return;
}
this->cAuthentication = pulsar_authentication_oauth2_create(info[1].ToString().Utf8Value().c_str());
} else {
Napi::Error::New(env, "Unsupported authentication method").ThrowAsJavaScriptException();
return;
}
}
Authentication::~Authentication() {
if (this->tokenSupplier != nullptr) {
this->tokenSupplier->callback.Release();
}
if (this->cAuthentication != nullptr) {
pulsar_authentication_free(this->cAuthentication);
}
}
pulsar_authentication_t *Authentication::GetCAuthentication() { return this->cAuthentication; }