diff --git a/README.md b/README.md
index 321dad0c..8c1fb361 100644
--- a/README.md
+++ b/README.md
@@ -182,6 +182,7 @@ Processes a single kerberos client-side step using the supplied server challenge
| challenge | string
| The response returned after calling `unwrap` |
| [options] | object
| Optional settings |
| [options.user] | string
| The user to authorize |
+| [options.protect] | boolean
| Indicates if the wrap should request message confidentiality |
| [callback] | function
| |
Perform the client side kerberos wrap step.
diff --git a/lib/kerberos.js b/lib/kerberos.js
index 09f8425f..f203d891 100644
--- a/lib/kerberos.js
+++ b/lib/kerberos.js
@@ -52,6 +52,7 @@ KerberosClient.prototype.step = defineOperation(KerberosClient.prototype.step, [
* @param {string} challenge The response returned after calling `unwrap`
* @param {object} [options] Optional settings
* @param {string} [options.user] The user to authorize
+ * @param {boolean} [options.protect] Indicates if the wrap should request message confidentiality
* @param {function} [callback]
* @return {Promise} returns Promise if no callback passed
*/
diff --git a/src/kerberos.cc b/src/kerberos.cc
index 7a760423..a79dfdd4 100644
--- a/src/kerberos.cc
+++ b/src/kerberos.cc
@@ -37,6 +37,17 @@ std::string ToStringWithNonStringAsEmpty(Napi::Value value) {
return value.As();
}
+int KerberosClient::ParseWrapOptionsProtect(const Napi::Object& options) {
+ if (!options.Has("protect")) return 0;
+
+ if (!options.Get("protect").IsBoolean()) {
+ throw TypeError::New(options.Env(), "options.protect must be a boolean.");
+ }
+
+ bool protect = options.Get("protect").As();
+ return protect ? 1 : 0;
+}
+
Function KerberosClient::Init(Napi::Env env) {
return
DefineClass(env,
diff --git a/src/kerberos.h b/src/kerberos.h
index 7f1c2096..deec6e33 100644
--- a/src/kerberos.h
+++ b/src/kerberos.h
@@ -55,6 +55,8 @@ class KerberosClient : public Napi::ObjectWrap {
void UnwrapData(const Napi::CallbackInfo& info);
void WrapData(const Napi::CallbackInfo& info);
+ int ParseWrapOptionsProtect(const Napi::Object& options);
+
private:
friend class Napi::ObjectWrap;
explicit KerberosClient(const Napi::CallbackInfo& info);
diff --git a/src/unix/kerberos_unix.cc b/src/unix/kerberos_unix.cc
index 57a67ae8..fafa937b 100644
--- a/src/unix/kerberos_unix.cc
+++ b/src/unix/kerberos_unix.cc
@@ -74,8 +74,7 @@ void KerberosClient::WrapData(const CallbackInfo& info) {
Object options = info[1].ToObject();
Function callback = info[2].As();
std::string user = ToStringWithNonStringAsEmpty(options["user"]);
-
- int protect = 0; // NOTE: this should be an option
+ int protect = ParseWrapOptionsProtect(options);
KerberosWorker::Run(callback, "kerberos:ClientWrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) {
gss_result result = authenticate_gss_client_wrap(
diff --git a/src/win32/kerberos_win32.cc b/src/win32/kerberos_win32.cc
index aa653d43..21977799 100644
--- a/src/win32/kerberos_win32.cc
+++ b/src/win32/kerberos_win32.cc
@@ -92,7 +92,7 @@ void KerberosClient::WrapData(const CallbackInfo& info) {
Object options = info[1].ToObject();
Function callback = info[2].As();
std::string user = ToStringWithNonStringAsEmpty(options["user"]);
- int protect = 0; // NOTE: this should be an option
+ int protect = ParseWrapOptionsProtect(options);
if (isStringTooLong(user)) {
throw Error::New(info.Env(), "User name is too long");
diff --git a/test/kerberos_tests.js b/test/kerberos_tests.js
index 18263bff..4be9de67 100644
--- a/test/kerberos_tests.js
+++ b/test/kerberos_tests.js
@@ -4,6 +4,7 @@ const request = require('request');
const chai = require('chai');
const expect = chai.expect;
const os = require('os');
+const { test } = require('mocha');
chai.use(require('chai-string'));
// environment variables
@@ -122,4 +123,50 @@ describe('Kerberos', function () {
});
});
});
+
+ describe('Client.wrap()', function () {
+ async function establishConext() {
+ const service = `HTTP@${hostname}`;
+ client = await kerberos.initializeClient(service, {});
+ server = await kerberos.initializeServer(service);
+ const clientResponse = await client.step('');
+ const serverResponse = await server.step(clientResponse);
+ await client.step(serverResponse);
+ expect(client.contextComplete).to.be.true;
+ return { client, server };
+ }
+
+ let client;
+ let server;
+
+ before(establishConext);
+ describe('options.protect', function () {
+ context('valid values for `protect`', function () {
+ test('no options provided', async function () {
+ await client.wrap('challenge');
+ });
+
+ test('options provided (protect omitted)', async function () {
+ await client.wrap('challenge', {});
+ });
+
+ test('protect = false', async function () {
+ await client.wrap('challenge', { protect: false });
+ });
+
+ test('protect = true', async function () {
+ await client.wrap('challenge', { protect: true });
+ });
+ });
+
+ context('when set to an invalid value', function () {
+ it('throws a TypeError', async function () {
+ const error = await client.wrap('challenge', { protect: 'non-boolean' }).catch(e => e);
+ expect(error)
+ .to.be.instanceOf(TypeError)
+ .to.match(/options.protect must be a boolean/);
+ });
+ });
+ });
+ });
});