Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NODE-6333): Allow callers to specify the 'protect' flag #198

Merged
merged 10 commits into from
Sep 5, 2024
4 changes: 0 additions & 4 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ functions:
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
PROJECT: ${project}
GYP_DEFINES: ${GYP_DEFINES|}
NPM_OPTIONS: ${NPM_OPTIONS|}
args:
- run
- '--interactive'
Expand All @@ -80,8 +79,6 @@ functions:
- PROJECT_DIRECTORY=/app
- '--env'
- GYP_DEFINES
- '--env'
- NPM_OPTIONS
- 'ubuntu:22.04'
- /bin/bash
- /app/.evergreen/run-tests-ubuntu.sh
Expand Down Expand Up @@ -125,7 +122,6 @@ tasks:
- func: run tests ubuntu
vars:
GYP_DEFINES: kerberos_use_rtld=false
NPM_OPTIONS: --build-from-source
- name: run-prebuild
commands:
- func: install dependencies
Expand Down
2 changes: 1 addition & 1 deletion .evergreen/install-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,5 @@ fi
echo "npm location: $(which npm)"
echo "npm version: $(npm -v)"

npm install "${NPM_OPTIONS}" --build-from-source
npm install --build-from-source
ldd build/*/kerberos.node || true
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ Processes a single kerberos client-side step using the supplied server challenge
| challenge | <code>string</code> | The response returned after calling `unwrap` |
| [options] | <code>object</code> | Optional settings |
| [options.user] | <code>string</code> | The user to authorize |
| [options.protect] | <code>boolean</code> | Indicates if the wrap should request message confidentiality |
| [callback] | <code>function</code> | |

Perform the client side kerberos wrap step.
Expand Down
1 change: 1 addition & 0 deletions lib/kerberos.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
11 changes: 11 additions & 0 deletions src/kerberos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ std::string ToStringWithNonStringAsEmpty(Napi::Value value) {
return value.As<String>();
}

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<Boolean>();
return protect ? 1 : 0;
}

Function KerberosClient::Init(Napi::Env env) {
return
DefineClass(env,
Expand Down
2 changes: 2 additions & 0 deletions src/kerberos.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class KerberosClient : public Napi::ObjectWrap<KerberosClient> {
void UnwrapData(const Napi::CallbackInfo& info);
void WrapData(const Napi::CallbackInfo& info);

int ParseWrapOptionsProtect(const Napi::Object& options);

private:
friend class Napi::ObjectWrap<KerberosClient>;
explicit KerberosClient(const Napi::CallbackInfo& info);
Expand Down
3 changes: 1 addition & 2 deletions src/unix/kerberos_unix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ void KerberosClient::WrapData(const CallbackInfo& info) {
Object options = info[1].ToObject();
Function callback = info[2].As<Function>();
std::string user = ToStringWithNonStringAsEmpty(options["user"]);

int protect = 0; // NOTE: this should be an option
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
int protect = ParseWrapOptionsProtect(options);

KerberosWorker::Run(callback, "kerberos:ClientWrap", [=](KerberosWorker::SetOnFinishedHandler onFinished) {
gss_result result = authenticate_gss_client_wrap(
Expand Down
2 changes: 1 addition & 1 deletion src/win32/kerberos_win32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void KerberosClient::WrapData(const CallbackInfo& info) {
Object options = info[1].ToObject();
Function callback = info[2].As<Function>();
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");
Expand Down
47 changes: 47 additions & 0 deletions test/kerberos_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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/);
});
});
});
});
});