From 14c5421eda64f315ed2098f831ea25d6f4775031 Mon Sep 17 00:00:00 2001 From: TheSomeMan Date: Wed, 8 May 2024 12:10:05 +0700 Subject: [PATCH] Close #381: Add HTTP authentication method "API key" that does not prefix the key with the word "Token" --- src/gw_cfg.mjs | 2 ++ src/gw_cfg_http.mjs | 9 ++++++++ src/gw_cfg_http.test.js | 23 +++++++++++++++++++ src/index.html | 25 +++++++++++++++++++++ src/page_custom_server.mjs | 46 +++++++++++++++++++++++++++++++++----- 5 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/gw_cfg.mjs b/src/gw_cfg.mjs index b9b08dd..3746d24 100644 --- a/src/gw_cfg.mjs +++ b/src/gw_cfg.mjs @@ -140,6 +140,8 @@ export class GwCfg { data.http_bearer_token = this.http.http_bearer_token } else if (this.http.http_auth.isToken()) { data.http_api_key = this.http.http_api_key + } else if (this.http.http_auth.isApiKey()) { + data.http_api_key = this.http.http_api_key } data.http_use_ssl_client_cert = this.http.http_use_ssl_client_cert data.http_use_ssl_server_cert = this.http.http_use_ssl_server_cert diff --git a/src/gw_cfg_http.mjs b/src/gw_cfg_http.mjs index 82f3032..5b5160f 100644 --- a/src/gw_cfg_http.mjs +++ b/src/gw_cfg_http.mjs @@ -16,6 +16,7 @@ export const HTTP_AUTH = Object.freeze({ 'basic': 'basic', 'bearer': 'bearer', 'token': 'token', + 'api_key': 'api_key', }) export class GwCfgHttpDataFormat { @@ -110,6 +111,14 @@ export class GwCfgHttpAuth { setToken () { this.http_auth = HTTP_AUTH.token } + + isApiKey () { + return this.http_auth === HTTP_AUTH.api_key + } + + setApiKey () { + this.http_auth = HTTP_AUTH.api_key + } } export class GwCfgHttp { diff --git a/src/gw_cfg_http.test.js b/src/gw_cfg_http.test.js index 1a17673..b801830 100644 --- a/src/gw_cfg_http.test.js +++ b/src/gw_cfg_http.test.js @@ -182,6 +182,29 @@ describe('GwCfgHttp', () => { expect(Object.keys(data).length).to.equal(0) }) + it('should check use_http=true with http_auth=api_key', () => { + let data = { + use_http_ruuvi: false, + use_http: true, + http_url: 'https://myserver.com:8080/record', + http_data_format: 'ruuvi', + http_auth: 'api_key', + } + let cfg_http = new GwCfgHttp() + cfg_http.parse(data) + expect(cfg_http.use_http_ruuvi).to.be.false + expect(cfg_http.use_http).to.be.true + expect(cfg_http.http_data_format.isRuuvi()).to.be.true + expect(cfg_http.http_auth.isApiKey()).to.be.true + expect(cfg_http.http_url).to.equal('https://myserver.com:8080/record') + expect(cfg_http.http_period).to.equal(30) + expect(cfg_http.http_user).to.equal('') + expect(cfg_http.http_pass).to.be.undefined + expect(cfg_http.http_bearer_token).to.be.undefined + expect(cfg_http.http_api_key).to.be.undefined + expect(Object.keys(data).length).to.equal(0) + }) + it('should check missing use_http_ruuvi', () => { let data = { use_http: true, diff --git a/src/index.html b/src/index.html index 75045a6..4b709b1 100644 --- a/src/index.html +++ b/src/index.html @@ -2027,6 +2027,31 @@

+
+ +
+

+ +

+
+ +
+
+
+ API key + API-avain +
+
+
+
diff --git a/src/page_custom_server.mjs b/src/page_custom_server.mjs index b4b7094..27b9df5 100644 --- a/src/page_custom_server.mjs +++ b/src/page_custom_server.mjs @@ -19,7 +19,7 @@ import {GwCfgMqtt} from './gw_cfg_mqtt.mjs' import gui_loading from './gui_loading.mjs' import GwStatus from './gw_status.mjs' import Navigation from './navigation.mjs' -import {GwCfgHttp} from './gw_cfg_http.mjs' +import {GwCfgHttp, HTTP_AUTH} from './gw_cfg_http.mjs'; import GuiInputTokenWithValidation from './gui_input_token_with_validation.mjs' import Network from './network.mjs' import GuiButtonUpload from "./gui_button_upload.mjs"; @@ -61,6 +61,8 @@ class PageCustomServer { #radio_http_auth_bearer /** @type GuiRadioButtonOption */ #radio_http_auth_token + /** @type GuiRadioButtonOption */ + #radio_http_auth_apikey #div_http_auth_options = new GuiDiv($('#http_auth_options')) #div_http_auth_basic_params = new GuiDiv($('#http_auth_basic_params')) @@ -70,6 +72,8 @@ class PageCustomServer { #input_http_auth_bearer_token = new GuiInputTokenWithValidation($('#http_auth_bearer_api_key')) #div_http_auth_token_params = new GuiDiv($('#http_auth_token_params')) #input_http_auth_token_api_key = new GuiInputTokenWithValidation($('#http_auth_token_api_key')) + #div_http_auth_apikey_params = new GuiDiv($('#http_auth-api_key-params')) + #input_http_auth_apikey_value = new GuiInputTokenWithValidation($('#http_auth-api_key-value')) #checkbox_http_use_client_ssl_cert = new GuiCheckbox($('#http_use_client_ssl_cert')) #div_http_use_client_ssl_cert_options = new GuiDiv($('#http_use_client_ssl_cert_options')) @@ -196,6 +200,7 @@ class PageCustomServer { this.#radio_http_auth_basic = this.#radio_http_auth.addOption('http_auth_basic', false) this.#radio_http_auth_bearer = this.#radio_http_auth.addOption('http_auth_bearer', false) this.#radio_http_auth_token = this.#radio_http_auth.addOption('http_auth_token', false) + this.#radio_http_auth_apikey = this.#radio_http_auth.addOption('http_auth-api_key', false) this.#checkbox_http_use_client_ssl_cert.on_change(() => this.#onChangeUseClientSslCertHttp()) this.#checkbox_http_use_server_ssl_cert.on_change(() => this.#onChangeUseServerSslCertHttp()) this.#button_http_remove_client_cert_and_key.on_click(async () => this.#onRemoveClientCertAndKeyHttp()) @@ -226,6 +231,7 @@ class PageCustomServer { this.#radio_http_auth_basic.on_click(() => this.#onChangeHttpAuth()) this.#radio_http_auth_bearer.on_click(() => this.#onChangeHttpAuth()) this.#radio_http_auth_token.on_click(() => this.#onChangeHttpAuth()) + this.#radio_http_auth_apikey.on_click(() => this.#onChangeHttpAuth()) this.#radio_statistics_use_ruuvi.on_click(() => this.#onChangeUseStatistics()) this.#radio_statistics_use_custom.on_click(() => this.#onChangeUseStatistics()) @@ -323,6 +329,9 @@ class PageCustomServer { } else if (this.#gwCfg.http.http_auth.isToken()) { this.#checkbox_use_http_auth.setChecked() this.#radio_http_auth_token.setChecked() + } else if (this.#gwCfg.http.http_auth.isApiKey()) { + this.#checkbox_use_http_auth.setChecked() + this.#radio_http_auth_apikey.setChecked() } this.#checkbox_http_use_client_ssl_cert.setState(this.#gwCfg.http.http_use_ssl_client_cert) @@ -483,6 +492,9 @@ class PageCustomServer { } else if (this.#radio_http_auth_token.isChecked()) { this.#gwCfg.http.http_auth.setToken() this.#gwCfg.http.http_api_key = this.#input_http_auth_token_api_key.getVal() + } else if (this.#radio_http_auth_apikey.isChecked()) { + this.#gwCfg.http.http_auth.setApiKey() + this.#gwCfg.http.http_api_key = this.#input_http_auth_apikey_value.getVal() } else { throw new Error(`Unknown http_auth`) } @@ -1250,14 +1262,22 @@ class PageCustomServer { this.#div_http_auth_basic_params.show() this.#div_http_auth_bearer_params.hide() this.#div_http_auth_token_params.hide() + this.#div_http_auth_apikey_params.hide() } else if (this.#radio_http_auth_bearer.isChecked()) { this.#div_http_auth_basic_params.hide() this.#div_http_auth_bearer_params.show() this.#div_http_auth_token_params.hide() + this.#div_http_auth_apikey_params.hide() } else if (this.#radio_http_auth_token.isChecked()) { this.#div_http_auth_basic_params.hide() this.#div_http_auth_bearer_params.hide() this.#div_http_auth_token_params.show() + this.#div_http_auth_apikey_params.hide() + } else if (this.#radio_http_auth_apikey.isChecked()) { + this.#div_http_auth_basic_params.hide() + this.#div_http_auth_bearer_params.hide() + this.#div_http_auth_token_params.hide() + this.#div_http_auth_apikey_params.show() } } else { this.#div_http_auth_options.hide() @@ -1543,7 +1563,7 @@ class PageCustomServer { } if (!this.#checkbox_use_http_auth.isChecked()) { - const auth_type = 'none' + const auth_type = HTTP_AUTH.none return validate_url(this.#auth, this.#input_http_url.getVal(), 'check_post_advs', auth_type, { input_url: this.#input_http_url, use_ssl_client_cert: this.#checkbox_http_use_client_ssl_cert.isChecked(), @@ -1561,7 +1581,7 @@ class PageCustomServer { resolve(true) }) } - const auth_type = 'basic' + const auth_type = HTTP_AUTH.basic return validate_url(this.#auth, this.#input_http_url.getVal(), 'check_post_advs', auth_type, { input_url: this.#input_http_url, input_user: this.#input_http_auth_basic_user, @@ -1578,7 +1598,7 @@ class PageCustomServer { resolve(true) }) } - const auth_type = 'bearer' + const auth_type = HTTP_AUTH.bearer return validate_url(this.#auth, this.#input_http_url.getVal(), 'check_post_advs', auth_type, { input_url: this.#input_http_url, input_token: this.#input_http_auth_bearer_token, @@ -1594,13 +1614,29 @@ class PageCustomServer { resolve(true) }) } - const auth_type = 'token' + const auth_type = HTTP_AUTH.token return validate_url(this.#auth, this.#input_http_url.getVal(), 'check_post_advs', auth_type, { input_url: this.#input_http_url, input_token: this.#input_http_auth_token_api_key, error: this.#text_http_validation_error_desc, div_status: this.#div_http_validation_error, }) + } else if (this.#radio_http_auth_apikey.isChecked()) { + if (this.#input_http_auth_apikey_value.getVal() === '') { + this.#input_http_auth_apikey_value.setInvalid() + this.#text_http_validation_error_desc.setVal(`API key is empty`) + this.#div_http_validation_error.show() + return new Promise(function (resolve) { + resolve(true) + }) + } + const auth_type = HTTP_AUTH.api_key + return validate_url(this.#auth, this.#input_http_url.getVal(), 'check_post_advs', auth_type, { + input_url: this.#input_http_url, + input_token: this.#input_http_auth_apikey_value, + error: this.#text_http_validation_error_desc, + div_status: this.#div_http_validation_error, + }) } else { throw new Error(`Unknown http_auth_type`) }