grpc-js: add ServerCredentials support

This commit adds ServerCredentials to the pure JS implementation.
This commit is contained in:
cjihrig 2019-03-31 14:44:37 -04:00
parent f345593d1d
commit 40eef7af22
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
3 changed files with 238 additions and 9 deletions

View File

@ -27,6 +27,7 @@ import {LogVerbosity, Status} from './constants';
import * as logging from './logging';
import {Deserialize, loadPackageDefinition, makeClientConstructor, Serialize} from './make-client';
import {Metadata} from './metadata';
import {KeyCertPair, ServerCredentials} from './server-credentials';
import {StatusBuilder} from './status-builder';
const supportedNodeVersions = '^8.11.2 || >=9.4';
@ -226,15 +227,9 @@ export const Server = (options: any) => {
throw new Error('Not yet implemented');
};
export const ServerCredentials = {
createSsl:
(rootCerts: any, keyCertPairs: any, checkClientCertificate: any) => {
throw new Error('Not yet implemented');
},
createInsecure: () => {
throw new Error('Not yet implemented');
}
};
export {ServerCredentials};
export {KeyCertPair};
export const getClientChannel = (client: Client) => {
return Client.prototype.getChannel.call(client);

View File

@ -0,0 +1,108 @@
/*
* Copyright 2019 gRPC authors.
*
* Licensed 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.
*
*/
import {SecureServerOptions} from 'http2';
export type KeyCertPair = {
private_key: Buffer,
cert_chain: Buffer
};
export abstract class ServerCredentials {
abstract _isSecure(): boolean;
abstract _getSettings(): SecureServerOptions|null;
static createInsecure(): ServerCredentials {
return new InsecureServerCredentials();
}
static createSsl(
rootCerts: Buffer|null, keyCertPairs: KeyCertPair[],
checkClientCertificate = false): ServerCredentials {
if (rootCerts !== null && !Buffer.isBuffer(rootCerts)) {
throw new TypeError('rootCerts must be null or a Buffer');
}
if (!Array.isArray(keyCertPairs)) {
throw new TypeError('keyCertPairs must be an array');
}
if (typeof checkClientCertificate !== 'boolean') {
throw new TypeError('checkClientCertificate must be a boolean');
}
const cert = [];
const key = [];
for (let i = 0; i < keyCertPairs.length; i++) {
const pair = keyCertPairs[i];
if (pair === null || typeof pair !== 'object') {
throw new TypeError(`keyCertPair[${i}] must be an object`);
}
if (!Buffer.isBuffer(pair.private_key)) {
throw new TypeError(`keyCertPair[${i}].private_key must be a Buffer`);
}
if (!Buffer.isBuffer(pair.cert_chain)) {
throw new TypeError(`keyCertPair[${i}].cert_chain must be a Buffer`);
}
cert.push(pair.cert_chain);
key.push(pair.private_key);
}
return new SecureServerCredentials({
ca: rootCerts || undefined,
cert,
key,
requestCert: checkClientCertificate
});
}
}
class InsecureServerCredentials extends ServerCredentials {
_isSecure(): boolean {
return false;
}
_getSettings(): null {
return null;
}
}
class SecureServerCredentials extends ServerCredentials {
private options: SecureServerOptions;
constructor(options: SecureServerOptions) {
super();
this.options = options;
}
_isSecure(): boolean {
return true;
}
_getSettings(): SecureServerOptions {
return this.options;
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2019 gRPC authors.
*
* Licensed 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.
*
*/
// Allow `any` data type for testing runtime type checking.
// tslint:disable no-any
import * as assert from 'assert';
import {readFileSync} from 'fs';
import {join} from 'path';
import {ServerCredentials} from '../src';
const ca = readFileSync(join(__dirname, 'fixtures', 'ca.pem'));
const key = readFileSync(join(__dirname, 'fixtures', 'server1.key'));
const cert = readFileSync(join(__dirname, 'fixtures', 'server1.pem'));
describe('Server Credentials', () => {
describe('createInsecure', () => {
it('creates insecure credentials', () => {
const creds = ServerCredentials.createInsecure();
assert.strictEqual(creds._isSecure(), false);
assert.strictEqual(creds._getSettings(), null);
});
});
describe('createSsl', () => {
it('accepts a buffer and array as the first two arguments', () => {
const creds = ServerCredentials.createSsl(ca, []);
assert.strictEqual(creds._isSecure(), true);
assert.deepStrictEqual(
creds._getSettings(), {ca, cert: [], key: [], requestCert: false});
});
it('accepts a boolean as the third argument', () => {
const creds = ServerCredentials.createSsl(ca, [], true);
assert.strictEqual(creds._isSecure(), true);
assert.deepStrictEqual(
creds._getSettings(), {ca, cert: [], key: [], requestCert: true});
});
it('accepts an object with two buffers in the second argument', () => {
const keyCertPairs = [{private_key: key, cert_chain: cert}];
const creds = ServerCredentials.createSsl(null, keyCertPairs);
assert.strictEqual(creds._isSecure(), true);
assert.deepStrictEqual(
creds._getSettings(),
{ca: undefined, cert: [cert], key: [key], requestCert: false});
});
it('accepts multiple objects in the second argument', () => {
const keyCertPairs = [
{private_key: key, cert_chain: cert},
{private_key: key, cert_chain: cert}
];
const creds = ServerCredentials.createSsl(null, keyCertPairs, false);
assert.strictEqual(creds._isSecure(), true);
assert.deepStrictEqual(creds._getSettings(), {
ca: undefined,
cert: [cert, cert],
key: [key, key],
requestCert: false
});
});
it('fails if the second argument is not an Array', () => {
assert.throws(() => {
ServerCredentials.createSsl(ca, 'test' as any);
}, /TypeError: keyCertPairs must be an array/);
});
it('fails if the first argument is a non-Buffer value', () => {
assert.throws(() => {
ServerCredentials.createSsl('test' as any, []);
}, /TypeError: rootCerts must be null or a Buffer/);
});
it('fails if the third argument is a non-boolean value', () => {
assert.throws(() => {
ServerCredentials.createSsl(ca, [], 'test' as any);
}, /TypeError: checkClientCertificate must be a boolean/);
});
it('fails if the array elements are not objects', () => {
assert.throws(() => {
ServerCredentials.createSsl(ca, ['test'] as any);
}, /TypeError: keyCertPair\[0\] must be an object/);
assert.throws(() => {
ServerCredentials.createSsl(ca, [null] as any);
}, /TypeError: keyCertPair\[0\] must be an object/);
});
it('fails if the object does not have a Buffer private key', () => {
const keyCertPairs: any = [{private_key: 'test', cert_chain: cert}];
assert.throws(() => {
ServerCredentials.createSsl(null, keyCertPairs);
}, /TypeError: keyCertPair\[0\].private_key must be a Buffer/);
});
it('fails if the object does not have a Buffer cert chain', () => {
const keyCertPairs: any = [{private_key: key, cert_chain: 'test'}];
assert.throws(() => {
ServerCredentials.createSsl(null, keyCertPairs);
}, /TypeError: keyCertPair\[0\].cert_chain must be a Buffer/);
});
});
});