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

make getCertificates work again without chained name #2318

Merged
merged 1 commit into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions packages/adapter/src/lib/_Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,17 @@ export interface InternalCalculatePermissionsOptions {
}

export type GetCertificatesCallback = (
err: string | null,
err?: Error | null,
certs?: ioBroker.Certificates,
useLetsEncryptCert?: boolean
) => void;

export type GetCertificatesPromiseReturnType = [cert: ioBroker.Certificates, useLetsEncryptCert?: boolean];

export interface InternalGetCertificatesOptions {
publicName: string;
privateName: string;
chainedName: string;
publicName?: string;
privateName?: string;
chainedName?: string;
callback?: GetCertificatesCallback;
}

Expand Down
149 changes: 91 additions & 58 deletions packages/adapter/src/lib/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ import type {
CheckStatesResult,
Pattern,
MessageCallbackObject,
SendToOptions
SendToOptions,
GetCertificatesPromiseReturnType
} from '../_Types';

tools.ensureDNSOrder();
Expand Down Expand Up @@ -324,7 +325,11 @@ export interface AdapterClass {
options?: unknown
): ioBroker.SetObjectPromise;
// TODO: correct types
getCertificatesAsync(...args: any[]): Promise<any>;
getCertificatesAsync(
publicName?: string,
privateName?: string,
chainedName?: string
): Promise<GetCertificatesPromiseReturnType>;
/** Get all states, channels, devices and folders of this adapter */
getAdapterObjectsAsync(): Promise<Record<string, ioBroker.AdapterScopedObject>>;

Expand Down Expand Up @@ -2259,89 +2264,117 @@ export class AdapterClass extends EventEmitter {
* }
* ```
*/
getCertificates(publicName: unknown, privateName: unknown, chainedName: unknown, callback: unknown): void {
getCertificates(
publicName: unknown,
privateName: unknown,
chainedName: unknown,
callback: unknown
): Promise<GetCertificatesPromiseReturnType | void> {
if (!this.config) {
throw new Error(tools.ERRORS.ERROR_NOT_READY);
}

if (typeof publicName === 'function') {
callback = publicName;
publicName = null;
publicName = undefined;
}
if (typeof privateName === 'function') {
callback = privateName;
privateName = null;
privateName = undefined;
}
if (typeof chainedName === 'function') {
callback = chainedName;
chainedName = null;
chainedName = undefined;
}
publicName = publicName || this.config.certPublic;
privateName = privateName || this.config.certPrivate;
chainedName = chainedName || this.config.certChained;

Validator.assertString(publicName, 'publicName');
Validator.assertString(privateName, 'privateName');
Validator.assertString(chainedName, 'chainedName');
if (publicName !== undefined) {
Validator.assertString(publicName, 'publicName');
}

if (privateName !== undefined) {
Validator.assertString(privateName, 'privateName');
}

if (chainedName !== undefined) {
Validator.assertString(chainedName, 'chainedName');
}

Validator.assertOptionalCallback(callback, 'callback');

return this._getCertificates({ publicName, privateName, chainedName, callback });
}

private _getCertificates(options: InternalGetCertificatesOptions): void {
// Load certificates
this.getForeignObject('system.certificates', null, (err, obj) => {
if (
err ||
!obj ||
!obj.native.certificates ||
!options.publicName ||
!options.privateName ||
!obj.native.certificates[options.publicName] ||
!obj.native.certificates[options.privateName] ||
(options.chainedName && !obj.native.certificates[options.chainedName])
) {
this._logger.error(
`${this.namespaceLog} Cannot configure secure web server, because no certificates found: ${options.publicName}, ${options.privateName}, ${options.chainedName}`
private async _getCertificates(
options: InternalGetCertificatesOptions
): Promise<[cert: ioBroker.Certificates, useLetsEncryptCert?: boolean] | void> {
const { publicName, chainedName, privateName, callback } = options;
let obj: ioBroker.OtherObject | undefined | null;

if (!adapterObjects) {
this._logger.info(
`${this.namespaceLog} getCertificates not processed because Objects database not connected`
);
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
}

try {
// Load certificates
obj = await adapterObjects.getObject('system.certificates');
} catch {
// ignore
}

if (
!obj ||
!obj.native.certificates ||
!publicName ||
!privateName ||
!obj.native.certificates[publicName] ||
!obj.native.certificates[privateName] ||
(chainedName && !obj.native.certificates[chainedName])
) {
this._logger.error(
`${this.namespaceLog} Cannot configure secure web server, because no certificates found: ${publicName}, ${privateName}, ${chainedName}`
);
if (publicName === 'defaultPublic' || privateName === 'defaultPrivate') {
this._logger.info(
`${this.namespaceLog} Default certificates seem to be configured but missing. You can execute "iobroker cert create" in your shell to create these.`
);
if (options.publicName === 'defaultPublic' || options.privateName === 'defaultPrivate') {
this._logger.info(
`${this.namespaceLog} Default certificates seem to be configured but missing. You can execute "iobroker cert create" in your shell to create these.`
);
}
// @ts-expect-error
return tools.maybeCallbackWithError(options.callback, tools.ERRORS.ERROR_NOT_FOUND);
} else {
let ca;
if (options.chainedName) {
const chained = this._readFileCertificate(obj.native.certificates[options.chainedName]).split(
'-----END CERTIFICATE-----\r\n'
);
// it is still file name and the file maybe does not exist, but we can omit this error
if (chained.join('').length >= 512) {
ca = [];
for (const cert of chained) {
if (cert.replace(/(\r\n|\r|\n)/g, '').trim()) {
ca.push(`${cert}-----END CERTIFICATE-----\r\n`);
}
}

return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_NOT_FOUND);
} else {
let ca: string | undefined;
if (chainedName) {
const chained = this._readFileCertificate(obj.native.certificates[chainedName]).split(
'-----END CERTIFICATE-----\r\n'
);
// it is still file name and the file maybe does not exist, but we can omit this error
if (chained.join('').length >= 512) {
const caArr = [];
for (const cert of chained) {
if (cert.replace(/(\r\n|\r|\n)/g, '').trim()) {
caArr.push(`${cert}-----END CERTIFICATE-----\r\n`);
}
ca = ca.join('');
}
ca = caArr.join('');
}

return tools.maybeCallbackWithError(
// @ts-expect-error
options.callback,
null,
{
key: this._readFileCertificate(obj.native.certificates[options.privateName]),
cert: this._readFileCertificate(obj.native.certificates[options.publicName]),
ca
},
obj.native.letsEncrypt
);
}
});

return tools.maybeCallbackWithError(
callback,
null,
{
key: this._readFileCertificate(obj.native.certificates[privateName]),
cert: this._readFileCertificate(obj.native.certificates[publicName]),
ca
},
obj.native.letsEncrypt
);
}
}

/**
Expand Down
59 changes: 0 additions & 59 deletions packages/controller/test/lib/testAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,65 +286,6 @@ function testAdapter(options: Record<string, any>): void {
test.register(it, expect, context);
}

// sendTo => controller => adapter
// sendToHost - cannot be tested

// this test is 15 seconds long. Enable it only if ready to push
/*
it(`${options.name} ${context.adapterShortName} adapter: Check if uptime changes`, function (done) {
this.timeout(20_000);
context.states.getState(`system.adapter.${context.adapterShortName}.0.uptime`, function (err, state1) {
expect(err).to.be.not.ok;
expect(state1).to.be.ok;
expect(state1.val).to.be.ok;
setTimeout(function () {
context.states.getState(
`system.adapter.${context.adapterShortName}.0.uptime`,
function (err, state2) {
expect(err).to.be.not.ok;
expect(state2).to.be.ok;
expect(state2.val).to.be.ok;
if (state2.val !== state1.val) {
expect(state2.val).to.be.above(state1.val);
done();
} else {
setTimeout(function () {
context.states.getState(
`system.adapter.${context.adapterShortName}.0.uptime`,
function (err, state2) {
expect(err).to.be.not.ok;
expect(state2).to.be.ok;
expect(state2.val).to.be.ok;
if (state2.val !== state1.val) {
expect(state2.val).to.be.above(state1.val);
done();
} else {
setTimeout(function () {
context.states.getState(
`system.adapter.${context.adapterShortName}.0.uptime`,
function (err, state2) {
expect(err).to.be.not.ok;
expect(state2).to.be.ok;
expect(state2.val).to.be.ok;
expect(state2.val).to.be.above(state1.val);
done();
}
);
}, 6_000);
}
}
);
}, 5_000);
}
}
);
}, 5_000);
});
});
*/

after(`${options.name} ${context.adapterShortName} adapter: Stop js-controller`, async function () {
this.timeout(35_000);

Expand Down
11 changes: 5 additions & 6 deletions packages/controller/test/lib/testAdapterHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,11 @@ export function register(it: Mocha.TestFunction, expect: Chai.ExpectStatic, cont
done();
});

//getCertificates
it(context.name + ' ' + context.adapterShortName + ' adapter: returns SSL certificates by name', function (done) {
this.timeout(3_000);
// TODO: sync
// TODO: async
done();
// getCertificates
it(context.name + ' ' + context.adapterShortName + ' adapter: returns SSL certificates by name', async () => {
// has to work without chained certificate
const certs = await context.adapter.getCertificatesAsync('defaultPublic', 'defaultPrivate');
expect(certs).to.be.ok;
});

it(context.name + ' ' + context.adapterShortName + ' adapter: get the user id', async () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/types-dev/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,11 @@ declare global {

interface Certificates {
/** private key file */
key: string | Buffer;
key: string;
/** public certificate */
cert: string | Buffer;
cert: string;
/** chained CA certificates */
ca: Array<string | Buffer>;
ca?: string;
}

type MessagePayload = any;
Expand Down Expand Up @@ -395,7 +395,7 @@ declare global {
SecondParameterOf<T>,
null | undefined
>;
/** Infers the return type from a callback-style API and and leaves null and undefined in */
/** Infers the return type from a callback-style API and leaves null and undefined in */
type CallbackReturnTypeOf<T extends (...args: any[]) => any> = SecondParameterOf<T>;

type GetStateCallback = (err?: Error | null, state?: State | null) => void;
Expand Down
4 changes: 2 additions & 2 deletions packages/types-dev/objects.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ declare global {
}

interface MetaCommon extends ObjectCommon {
// Meta objects have two additional CommonTypes
type: CommonType | 'meta.user' | 'meta.folder';
// Can be of type `user` for folders, where a user can store files or `folder` for adapter internal structures
type: 'meta.user' | 'meta.folder';

// Make it possible to narrow the object type using the custom property
custom?: undefined;
Expand Down
Loading
Loading