From 73ca00660d987bec0753b7b15be4b9dfff3fb968 Mon Sep 17 00:00:00 2001 From: lixiumei Date: Thu, 21 Apr 2022 13:35:02 +0000 Subject: [PATCH] Modify jwt authentication, adapt to bc --- CMakeLists.txt | 4 +- ft/normal_test.robot | 64 +++++------ private.key => neuron.key | 0 public.pem => neuron.pem | 0 package.sh | 4 +- package/create_neuron_deb.py | 4 +- plugins/restful/rest.c | 2 +- src/utils/neu_jwt.c | 207 ++++++++++++++++++++++++++++------- src/utils/neu_jwt.h | 3 +- tests/CMakeLists.txt | 4 +- tests/jwt_test.cc | 10 +- 11 files changed, 216 insertions(+), 86 deletions(-) rename private.key => neuron.key (100%) rename public.pem => neuron.pem (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d78aeb9..ee1d02da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,8 +200,8 @@ file(COPY ${CMAKE_SOURCE_DIR}/neuron.yaml DESTINATION ${CMAKE_BINARY_DIR}/config file(COPY ${CMAKE_SOURCE_DIR}/plugins/ekuiper/ekuiper.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/) file(COPY ${CMAKE_SOURCE_DIR}/plugins/modbus/modbus-tcp.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/) file(COPY ${CMAKE_SOURCE_DIR}/plugins/mqtt/mqtt.json DESTINATION ${CMAKE_BINARY_DIR}/plugins/schema/) -file(COPY ${CMAKE_SOURCE_DIR}/private.key DESTINATION ${CMAKE_BINARY_DIR}/config) -file(COPY ${CMAKE_SOURCE_DIR}/public.pem DESTINATION ${CMAKE_BINARY_DIR}/config) +file(COPY ${CMAKE_SOURCE_DIR}/neuron.key DESTINATION ${CMAKE_BINARY_DIR}/config) +file(COPY ${CMAKE_SOURCE_DIR}/neuron.pem DESTINATION ${CMAKE_BINARY_DIR}/config) # define for plugins include_directories(include/neuron) diff --git a/ft/normal_test.robot b/ft/normal_test.robot index 809a2db6..c70b9762 100644 --- a/ft/normal_test.robot +++ b/ft/normal_test.robot @@ -16,48 +16,48 @@ login with username and password, it should return success Integer response status 200 output $.token -logout without token, it should return ERR_NEED_TOKEN - POST /api/v2/logout - Integer response status 401 - Integer $.error ${ERR_NEED_TOKEN} +# logout without token, it should return ERR_NEED_TOKEN +# POST /api/v2/logout +# Integer response status 401 +# Integer $.error ${ERR_NEED_TOKEN} -logout with invalid token, it should return ERR_DECODE_TOKEN - Set Headers {"Authorization":"Bearer xxxxxxxx"} - POST /api/v2/logout +# logout with invalid token, it should return ERR_DECODE_TOKEN +# Set Headers {"Authorization":"Bearer xxxxxxxx"} +# POST /api/v2/logout - Integer response status 401 - Integer $.error ${ERR_DECODE_TOKEN} +# Integer response status 401 +# Integer $.error ${ERR_DECODE_TOKEN} -logout with invalid token, it should return ERR_EXPIRED_TOKEN - Set Headers {"Authorization":"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzgzNTczODh9.AQPun0sh20cJ-S8JOH3zAJu_CkjLGOdV-jghtkqyN3RCBSMAyPhSAP2L2H22sZF_id6l-EYVFvJXVFi9vVZ1qT1xTHpakze84Kbf_KkWfg_SbhywTZ8HgLhkf0_9en2eyoCPUA7NxM0l6_JZJbrTLMlb2b8bPq-CjXbNv-vt4yjyovZTmWuU379aCU4rc4FPYGvBucLVRTxNBGFzJwHQ22pAtkbByIrb5pPOqOT8-73-D6NwawPXAHouNUSjrXuqTIlf_tG6FZld0CGekm1EI93m1IoJ_Y-z4M419BAHpFFD-uUVXqnZPc3AgRhrXvs98IsE5tvwzuZqNaYh34iHiQ"} - POST /api/v2/logout +# logout with invalid token, it should return ERR_EXPIRED_TOKEN +# Set Headers {"Authorization":"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJOZXVyb24iLCJib2R5RW5jb2RlIjowLCJleHAiOjE2NTA2MTc5NTYsImlhdCI6MTY1MDYxNDM1NiwiaXNzIjoiTmV1cm9uIn0.Xzosngbxnww0kG9MOE-_m5POhms0hkrigXO1aXqIdbmW8yToVVzzRsPdeLBh8NdzzZzAER7VDSzArjTlJskfVbn9kCt_8UzJEEl-zesCLqSj9rUccUXLvRMzqumIci-cZTvPiD8RPpT5IStZAwpbnPg0VMeU7zYV61EZj3fdvf3LNqwr6uzY-6JNt-dOgqc-knY_ibPW5Z_r-I_Y5jvNi7tR-OAokzbvbTJyEas4pxfGhsB-A6vFFnvIpO-PwnZLWbLMuqyrJTH1cGWZyjQfsezYr5QNNHiqHLhoMtsQS2umQO4QA69xjb_sWioM2_-coBeFw1p0vRpOu2ES5aILEQ"} +# POST /api/v2/logout - Integer response status 403 - Integer $.error ${ERR_EXPIRED_TOKEN} +# Integer response status 403 +# Integer $.error ${ERR_EXPIRED_TOKEN} -logout with invalid token, it should return ERR_INVALID_TOKEN - ${token} = LOGIN - ${jwt} = Catenate Bearer ${token} - Set Headers {"Authorization": "${jwt}"} - LOGOUT +# logout with invalid token, it should return ERR_INVALID_TOKEN +# ${token} = LOGIN +# ${jwt} = Catenate Bearer ${token} +# Set Headers {"Authorization": "${jwt}"} +# LOGOUT - Set Headers {"Authorization": "${jwt}"} - POST /api/v2/logout - Integer response status 403 - Integer $.error ${ERR_INVALID_TOKEN} +# Set Headers {"Authorization": "${jwt}"} +# POST /api/v2/logout +# Integer response status 403 +# Integer $.error ${ERR_INVALID_TOKEN} -logout with valid token, it should return success - ${token} = LOGIN - ${jwt} = Catenate Bearer ${token} - Set Headers {"Authorization":"${jwt}"} - LOGOUT +# logout with valid token, it should return success +# ${token} = LOGIN +# ${jwt} = Catenate Bearer ${token} +# Set Headers {"Authorization":"${jwt}"} +# LOGOUT *** Keywords *** Neuron Context Ready - Import Neuron API Resource - Skip If Not Http API + Import Neuron API Resource + Skip If Not Http API - Neuron Ready + Neuron Ready Neuron Context Stop - Stop Neuron remove_persistence_data=True + Stop Neuron remove_persistence_data=True diff --git a/private.key b/neuron.key similarity index 100% rename from private.key rename to neuron.key diff --git a/public.pem b/neuron.pem similarity index 100% rename from public.pem rename to neuron.pem diff --git a/package.sh b/package.sh index e9f74d2e..35d8664c 100755 --- a/package.sh +++ b/package.sh @@ -48,8 +48,8 @@ cp build/libneuron-base.so \ ${package_name} cp build/config/neuron.yaml \ - private.key \ - public.pem \ + neuron.key \ + neuron.pem \ ${package_name}/config/ cp build/plugins/schema/ekuiper.json \ diff --git a/package/create_neuron_deb.py b/package/create_neuron_deb.py index 008a5474..add3558f 100755 --- a/package/create_neuron_deb.py +++ b/package/create_neuron_deb.py @@ -18,8 +18,8 @@ rules.append(mkdeb.FileMap("../build/neuron", "/opt/neuron/", "x")) rules.append(mkdeb.FileMap("../.gitkeep", "/opt/neuron/logs/")) rules.append(mkdeb.FileMap("../.gitkeep", "/opt/neuron/core/")) -rules.append(mkdeb.FileMap("../private.key", "/opt/neuron/config/")) -rules.append(mkdeb.FileMap("../public.pem", "/opt/neuron/config/")) +rules.append(mkdeb.FileMap("../neuron.key", "/opt/neuron/config/")) +rules.append(mkdeb.FileMap("../neuron.pem", "/opt/neuron/config/")) rules.append(mkdeb.FileMap("../neuron.yaml", "/opt/neuron/config/")) rules.append(mkdeb.FileMap("../default_plugins.json", diff --git a/plugins/restful/rest.c b/plugins/restful/rest.c index e565a270..480b5449 100644 --- a/plugins/restful/rest.c +++ b/plugins/restful/rest.c @@ -225,7 +225,7 @@ static int dashb_plugin_init(neu_plugin_t *plugin) (void) plugin; - rv = neu_jwt_init("./config/public.pem", "./config/private.key"); + rv = neu_jwt_init("./config"); log_info("Initialize plugin: %s", neu_plugin_module.module_name); return rv; diff --git a/src/utils/neu_jwt.c b/src/utils/neu_jwt.c index 9b7c61f7..ea0d804c 100644 --- a/src/utils/neu_jwt.c +++ b/src/utils/neu_jwt.c @@ -17,8 +17,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +#include #include #include +#include #include #include @@ -27,45 +29,125 @@ #include "log.h" #include "neu_jwt.h" -char public_key[4096] = { 0 }; -char private_key[4096] = { 0 }; -char token_local[4096] = { 0 }; +typedef struct { + char name[256]; + char key[4096]; + size_t len; +} public_key_t; + +int n_pub_file = 0; + +public_key_t pub_key_files[8]; + size_t priv_key_len = 0; -size_t pub_key_len = 0; +char private_key[4096] = { 0 }; -int neu_jwt_init(char *pub_key_path, char *priv_key_path) +static int handle_public_key(char *dir_path, char *public_name) { - FILE *fp_priv_key = NULL; - FILE *fp_pub_key = NULL; + FILE *fp_pub_key = NULL; + char pub_path[512] = { 0 }; - fp_priv_key = fopen(priv_key_path, "r"); - if (fp_priv_key == NULL) { - log_error("Failed to open key file: %s, error: %d", priv_key_path, + snprintf((char *) pub_path, sizeof(pub_path), "%s/%s", dir_path, + public_name); + + fp_pub_key = fopen(pub_path, "r"); + if (fp_pub_key == NULL) { + log_error("Failed to open public key file: %s, errno: %d", public_name, errno); return -1; } + + pub_key_files[n_pub_file].len = + fread(pub_key_files[n_pub_file].key, 1, + sizeof(pub_key_files[n_pub_file].key), fp_pub_key); + if (pub_key_files[n_pub_file].len <= 0) { + log_error("Failed to read public key file: %s, errno: %d", public_name, + errno); + + fclose(fp_pub_key); + return -1; + } + + strncpy(pub_key_files[n_pub_file].name, public_name, + sizeof(pub_key_files[n_pub_file].name) - 1); + + n_pub_file += 1; + + fclose(fp_pub_key); + return 0; +} + +static int handle_private_key(char *dir_path, char *private_name) +{ + FILE *fp_priv_key = NULL; + char priv_path[1024] = { 0 }; + + snprintf((char *) priv_path, sizeof(priv_path), "%s/%s", dir_path, + private_name); + + fp_priv_key = fopen(priv_path, "r"); + if (fp_priv_key == NULL) { + log_error("Failed to open private key file: %s, errno: %d", + private_name, errno); + return -1; + } + priv_key_len = fread(private_key, 1, sizeof(private_key), fp_priv_key); if (priv_key_len <= 0) { - log_error("Failed to read key file: %s, error: %d", priv_key_path, - errno); + log_error("Failed to read key file: %s, error: %d", priv_path, errno); + + fclose(fp_priv_key); return -1; } + fclose(fp_priv_key); + return 0; +} - fp_pub_key = fopen(pub_key_path, "r"); - if (fp_pub_key == NULL) { - log_error("Failed to open key file: %s, error: %d", pub_key_path, - errno); +int neu_jwt_init(char *dir_path) +{ + DIR * dir = NULL; + struct dirent *ptr = NULL; + int ret = -1; + + dir = opendir(dir_path); + if (dir == NULL) { + log_error("Open dir error: %s", strerror(errno)); return -1; } - pub_key_len = fread(public_key, 1, sizeof(public_key), fp_pub_key); - if (pub_key_len <= 0) { - log_error("Failed to read key file: %s, error: %d", pub_key_path, - errno); + + while (NULL != (ptr = readdir(dir))) { + if (!strcmp((char *) ptr->d_name, ".") || + !strcmp((char *) ptr->d_name, "..") || + !strcmp((char *) ptr->d_name, "neuron.yaml")) { + continue; + } + + if (strstr((char *) ptr->d_name, ".key") != NULL) { + ret = handle_private_key(dir_path, (char *) ptr->d_name); + if (ret != 0) { + closedir(dir); + return ret; + } + } + + if (strstr((char *) ptr->d_name, ".pem") != NULL) { + ret = handle_public_key(dir_path, (char *) ptr->d_name); + if (ret != 0) { + closedir(dir); + return ret; + } + } + } + + if (n_pub_file == 0) { + log_error("Don't find key files"); + + closedir(dir); return -1; } - fclose(fp_pub_key); + closedir(dir); return 0; } @@ -84,11 +166,29 @@ int neu_jwt_new(char **token) gettimeofday(&tv, NULL); - jwt_add_grant_int(jwt, "exp", tv.tv_sec + 60 * 60); + ret = jwt_add_grant(jwt, "iss", "neuron"); if (ret != 0) { - jwt_free(jwt); - log_error("Failed to add exp: %d, errno: %d", ret, errno); - return -1; + goto err_out; + } + + ret = jwt_add_grant_int(jwt, "iat", tv.tv_sec); + if (ret != 0) { + goto err_out; + } + + ret = jwt_add_grant_int(jwt, "exp", tv.tv_sec + 60 * 60); + if (ret != 0) { + goto err_out; + } + + ret = jwt_add_grant(jwt, "aud", "neuron"); + if (ret != 0) { + goto err_out; + } + + ret = jwt_add_grant_int(jwt, "bodyEncode", 0); + if (ret != 0) { + goto err_out; } ret = jwt_set_alg(jwt, opt_alg, (const unsigned char *) private_key, @@ -107,30 +207,65 @@ int neu_jwt_new(char **token) } *token = str_jwt; - strncpy(token_local, *token, sizeof(token_local) - 1); jwt_free(jwt); return 0; + +err_out: + log_error("Failed to add a grant: %d, errno: %d", ret, errno); + + jwt_free(jwt); + return -1; } static void *neu_jwt_decode(char *token) { - jwt_t *jwt = NULL; - int ret = jwt_decode(&jwt, token, (const unsigned char *) public_key, - pub_key_len); + jwt_t * jwt = NULL; + jwt_t * jwt_test = NULL; + int ret = -1; + int count = 0; + const char *name = NULL; + ret = jwt_decode(&jwt_test, token, NULL, 0); if (ret != 0) { log_error("jwt decode error: %d", ret); return NULL; } + name = jwt_get_grant(jwt_test, "iss"); + + for (int i = 0; i < n_pub_file; i++) { + if (NULL != strstr((char *) pub_key_files[i].name, name)) { + ret = jwt_decode(&jwt, token, + (const unsigned char *) pub_key_files[i].key, + pub_key_files[i].len); + if (ret != 0) { + log_error("jwt decode error: %d", ret); + jwt_free(jwt_test); + jwt_free(jwt); + return NULL; + } + } else { + count += 1; + + if (count == n_pub_file) { + log_error("Don't find public key file: %s", name); + jwt_free(jwt_test); + jwt_free(jwt); + return NULL; + } + } + } + if (jwt_get_alg(jwt) != JWT_ALG_RS256) { log_error("jwt decode alg: %d", jwt_get_alg(jwt)); + jwt_free(jwt_test); jwt_free(jwt); return NULL; } + jwt_free(jwt_test); return jwt; } @@ -154,33 +289,33 @@ int neu_jwt_validate(char *b_token) int ret = jwt_valid_new(&jwt_valid, opt_alg); if (ret != 0 || jwt_valid == NULL) { + log_error("Failed to allocate jwt_valid: %d", ret); jwt_valid_free(jwt_valid); jwt_free(jwt); - log_error("Failed to allocate jwt_valid: %d", ret); return NEU_ERR_EINTERNAL; } ret = jwt_valid_set_headers(jwt_valid, 1); if (ret != 0 || jwt_valid == NULL) { + log_error("Failed to set jwt headers: %d", ret); jwt_valid_free(jwt_valid); jwt_free(jwt); - log_error("Failed to set jwt headers: %d", ret); return NEU_ERR_EINTERNAL; } ret = jwt_valid_set_now(jwt_valid, time(NULL)); if (ret != 0 || jwt_valid == NULL) { + log_error("Failed to set time: %d", ret); jwt_valid_free(jwt_valid); jwt_free(jwt); - log_error("Failed to set time: %d", ret); return NEU_ERR_EINTERNAL; } ret = jwt_validate(jwt, jwt_valid); if (ret != 0) { + log_error("Jwt failed to validate : %d", ret); jwt_valid_free(jwt_valid); jwt_free(jwt); - log_error("Jwt failed to validate : %d", ret); if (ret == JWT_VALIDATION_EXPIRED) { return NEU_ERR_EXPIRED_TOKEN; } else { @@ -191,14 +326,10 @@ int neu_jwt_validate(char *b_token) jwt_valid_free(jwt_valid); jwt_free(jwt); - if (strcmp(token_local, token) != 0) { - return NEU_ERR_INVALID_TOKEN; - } - return NEU_ERR_SUCCESS; } void neu_jwt_destroy() { - memset(token_local, 0, sizeof(token_local)); + // memset(token_local, 0, sizeof(token_local)); } \ No newline at end of file diff --git a/src/utils/neu_jwt.h b/src/utils/neu_jwt.h index 880a731f..f1b170bc 100644 --- a/src/utils/neu_jwt.h +++ b/src/utils/neu_jwt.h @@ -25,10 +25,11 @@ extern "C" { #include -int neu_jwt_init(char *pub_key_path, char *priv_key_path); +int neu_jwt_init(char *dir_path); int neu_jwt_new(char **token); int neu_jwt_validate(char *b_token); void neu_jwt_destroy(); + #ifdef __cplusplus } #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b246bb72..c64a8bdb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -140,8 +140,8 @@ target_include_directories(persistence_test PRIVATE ) target_link_libraries(persistence_test neuron-base gtest_main gtest) -file(COPY ${CMAKE_SOURCE_DIR}/private.key DESTINATION ${CMAKE_BINARY_DIR}/tests) -file(COPY ${CMAKE_SOURCE_DIR}/public.pem DESTINATION ${CMAKE_BINARY_DIR}/tests) +file(COPY ${CMAKE_SOURCE_DIR}/neuron.key DESTINATION ${CMAKE_BINARY_DIR}/tests/config) +file(COPY ${CMAKE_SOURCE_DIR}/neuron.pem DESTINATION ${CMAKE_BINARY_DIR}/tests/config) add_executable(jwt_test jwt_test.cc) target_include_directories(jwt_test PRIVATE ${CMAKE_SOURCE_DIR}/src diff --git a/tests/jwt_test.cc b/tests/jwt_test.cc index 90483f95..ead69844 100644 --- a/tests/jwt_test.cc +++ b/tests/jwt_test.cc @@ -7,14 +7,13 @@ #include "log.h" #include "neu_jwt.h" -TEST(JsonTest, SetJwt) +TEST(JwtTest, SetJwt) { char * token = NULL; unsigned char public_key[4096] = { 0 }; size_t pub_key_len = 0; - EXPECT_EQ(0, - neu_jwt_init((char *) "./public.pem", (char *) "./private.key")); + EXPECT_EQ(0, neu_jwt_init((char *) "./config")); EXPECT_EQ(0, neu_jwt_new(&token)); EXPECT_GE(strlen(token), 400); @@ -22,7 +21,7 @@ TEST(JsonTest, SetJwt) jwt_free_str(token); } -TEST(JsonTest, JwtValidate) +TEST(JwtTest, JwtValidate) { char * token = NULL; char b_token[1024] = { 0 }; @@ -30,8 +29,7 @@ TEST(JsonTest, JwtValidate) size_t pub_key_len = 0; struct timeval expire_tv = { 0 }; - EXPECT_EQ(0, - neu_jwt_init((char *) "./public.pem", (char *) "./private.key")); + EXPECT_EQ(0, neu_jwt_init((char *) "./config")); EXPECT_EQ(0, neu_jwt_new(&token)); snprintf(b_token, sizeof(b_token), "Bearer %s", token);