Преглед изворни кода

chore: add more test for api_key

Zhongwen Deng пре 3 година
родитељ
комит
5d4a1933a2

+ 0 - 221
apps/emqx_management/i18n/emqx_mgmt_api_key.conf

@@ -1,221 +0,0 @@
-emqx_dashboard_schema {
-  listeners {
-    desc {
-      en: """HTTP(s) listeners are identified by their protocol type and are
-used to serve dashboard UI and restful HTTP API.
-Listeners must have a unique combination of port number and IP address.
-For example, an HTTP listener can listen on all configured IP addresses
-on a given port for a machine by specifying the IP address 0.0.0.0.
-Alternatively, the HTTP listener can specify a unique IP address for each listener,
-but use the same port."""
-      zh: """仪表盘监听器设置。"""
-    }
-    label {
-      en: "Listeners"
-      zh: "监听器"
-    }
-  }
-  sample_interval {
-    desc {
-      en: """How often to update metrics displayed in the dashboard.
-Note: `sample_interval` should be a divisor of 60."""
-      zh: """更新仪表板中显示的指标的时间间隔。必须小于60,且被60的整除。"""
-    }
-  }
-  token_expired_time {
-    desc {
-      en: "JWT token expiration time."
-      zh: "JWT token 过期时间"
-    }
-    label {
-      en: "Token expired time"
-      zh: "JWT 过期时间"
-    }
-  }
-  num_acceptors {
-    desc {
-      en: "Socket acceptor pool size for TCP protocols."
-      zh: "TCP协议的Socket acceptor池大小"
-    }
-    label {
-      en: "Number of acceptors"
-      zh: "Acceptor 数量"
-    }
-  }
-  max_connections {
-    desc {
-      en: "Maximum number of simultaneous connections."
-      zh: "同时处理的最大连接数"
-    }
-    label {
-      en: "Maximum connections"
-      zh: "最大连接数"
-    }
-  }
-  backlog {
-    desc {
-      en: "Defines the maximum length that the queue of pending connections can grow to."
-      zh: "排队等待连接的队列的最大长度"
-    }
-    label {
-      en: "Backlog"
-      zh: "排队长度"
-    }
-  }
-  send_timeout {
-    desc {
-      en: "Send timeout for the socket."
-      zh: "Socket发送超时时间"
-    }
-    label {
-      en: "Send timeout"
-      zh: "发送超时时间"
-    }
-  }
-  inet6 {
-    desc {
-      en: "Enable IPv6 support, default is false, which means IPv4 only."
-      zh: "启用IPv6, 如果机器不支持IPv6,请关闭此选项,否则会导致仪表盘无法使用。"
-    }
-    label {
-      en: "IPv6"
-      zh: "IPv6"
-    }
-  }
-  ipv6_v6only {
-    desc {
-      en: "Disable IPv4-to-IPv6 mapping for the listener."
-      zh: "当开启 inet6 功能的同时禁用 IPv4-to-IPv6 映射。该配置仅在 inet6 功能开启时有效。"
-    }
-    label {
-      en: "IPv6 only"
-      zh: "IPv6 only"
-    }
-  }
-  desc_dashboard {
-    desc {
-      en: "Configuration for EMQX dashboard."
-      zh: "EMQX仪表板配置"
-    }
-    label {
-      en: "Dashboard"
-      zh: "仪表板"
-    }
-  }
-  desc_listeners {
-    desc {
-      en: "Configuration for the dashboard listener."
-      zh: "仪表板监听器配置"
-    }
-    label {
-      en: "Listeners"
-      zh: "监听器"
-    }
-  }
-  desc_http {
-    desc {
-      en: "Configuration for the dashboard listener (plaintext)."
-      zh: "仪表板监听器(HTTP)配置"
-    }
-    label {
-      en: "HTTP"
-      zh: "HTTP"
-    }
-  }
-  desc_https {
-    desc {
-      en: "Configuration for the dashboard listener (TLS)."
-      zh: "仪表板监听器(HTTPS)配置"
-    }
-    label {
-      en: "HTTPS"
-      zh: "HTTPS"
-    }
-  }
-  listener_enable {
-    desc {
-        en: "Ignore or enable this listener"
-        zh: "忽略或启用该监听器配置"
-    }
-    label {
-        en: "Enable"
-        zh: "启用"
-    }
-  }
-  bind {
-    desc {
-      en: "Port without IP(18083) or port with specified IP(127.0.0.1:18083)."
-      zh: "监听的地址与端口,在dashboard更新此配置时,会重启dashboard服务。"
-    }
-    label {
-      en: "Bind"
-      zh: "绑定端口"
-    }
-  }
-  default_username {
-    desc {
-      en: "The default username of the automatically created dashboard user."
-      zh: "默认的仪表板用户名"
-    }
-    label {
-      en: "Default username"
-      zh: "默认用户名"
-    }
-  }
-  default_password {
-    desc {
-      en: """The initial default password for dashboard 'admin' user.
-For safety, it should be changed as soon as possible."""
-      zh: """默认的仪表板用户密码
-为了安全,应该尽快修改密码。"""
-    }
-    label {
-      en: "Default password"
-      zh: "默认密码"
-    }
-  }
-  cors {
-    desc {
-      en: """Support Cross-Origin Resource Sharing (CORS).
-Allows a server to indicate any origins (domain, scheme, or port) other than
-its own from which a browser should permit loading resources."""
-      zh: """支持跨域资源共享(CORS)
-允许服务器指示任何来源(域名、协议或端口),除了本服务器之外的任何浏览器应允许加载资源。"""
-    }
-    label {
-      en: "CORS"
-      zh: "跨域资源共享"
-    }
-  }
-  i18n_lang {
-    desc {
-      en: "Internationalization language support."
-      zh: "swagger多语言支持"
-    }
-    label {
-      en: "I18n language"
-      zh: "多语言支持"
-    }
-  }
-  bootstrap_users_file {
-    desc {
-      en: "Initialize users file."
-      zh: "初始化用户文件"
-    }
-    label {
-      en: """Is used to add an administrative user to Dashboard when emqx is first launched,
-      the format is:
-       ```
-       username1:password1
-       username2:password2
-       ```
-"""
-      zh: """用于在首次启动 emqx 时,为 Dashboard 添加管理用户,其格式为:
-      ```
-      username1:password1
-      username2:password2
-      ```
-"""
-    }
-  }
-}

+ 6 - 5
apps/emqx_management/src/emqx_mgmt_auth.erl

@@ -48,7 +48,7 @@
     api_secret_hash = <<>> :: binary() | '_',
     enable = true :: boolean() | '_',
     desc = <<>> :: binary() | '_',
-    expired_at = 0 :: integer() | undefined | '_',
+    expired_at = 0 :: integer() | undefined | infinity | '_',
     created_at = 0 :: integer() | '_'
 }).
 
@@ -68,7 +68,7 @@ init_bootstrap_file() ->
     init_bootstrap_file(File).
 
 create(Name, Enable, ExpiredAt, Desc) ->
-    case mnesia:table_info(?APP, size) < 1024 of
+    case mnesia:table_info(?APP, size) < 100 of
         true -> create_app(Name, Enable, ExpiredAt, Desc);
         false -> {error, "Maximum ApiKey"}
     end.
@@ -237,16 +237,17 @@ init_bootstrap_file(File) ->
         {ok, Dev} ->
             {ok, MP} = re:compile(<<"(\.+):(\.+$)">>, [ungreedy]),
             init_bootstrap_file(File, Dev, MP);
-        {error, Reason} = Error ->
+        {error, Reason0} ->
+            Reason = emqx_misc:explain_posix(Reason0),
             ?SLOG(
                 error,
                 #{
                     msg => "failed_to_open_the_bootstrap_file",
                     file => File,
-                    reason => emqx_misc:explain_posix(Reason)
+                    reason => Reason
                 }
             ),
-            Error
+            {error, Reason}
     end.
 
 init_bootstrap_file(File, Dev, MP) ->

+ 51 - 4
apps/emqx_management/test/emqx_mgmt_api_api_keys_SUITE.erl

@@ -25,15 +25,62 @@ suite() -> [{timetrap, {minutes, 1}}].
 groups() ->
     [
         {parallel, [parallel], [t_create, t_update, t_delete, t_authorize, t_create_unexpired_app]},
-        {sequence, [], [t_create_failed]}
+        {sequence, [], [t_bootstrap_file, t_create_failed]}
     ].
 
 init_per_suite(Config) ->
-    emqx_mgmt_api_test_util:init_suite(),
+    emqx_mgmt_api_test_util:init_suite([emqx_conf]),
     Config.
 
 end_per_suite(_) ->
-    emqx_mgmt_api_test_util:end_suite().
+    emqx_mgmt_api_test_util:end_suite([emqx_conf]).
+
+t_bootstrap_file(_) ->
+    TestPath = <<"/api/v5/status">>,
+    Bin = <<"test-1:secret-1\ntest-2:secret-2">>,
+    File = "./bootstrap_api_keys.txt",
+    ok = file:write_file(File, Bin),
+    emqx:update_config([api_key, bootstrap_file], File),
+    ok = emqx_mgmt_auth:init_bootstrap_file(),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-1">>, <<"secret-1">>)),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-2">>, <<"secret-2">>)),
+    ?assertMatch({error, _}, emqx_mgmt_auth:authorize(TestPath, <<"test-2">>, <<"secret-1">>)),
+
+    %% relaunch to check if the table is changed.
+    Bin1 = <<"test-1:new-secret-1\ntest-2:new-secret-2">>,
+    ok = file:write_file(File, Bin1),
+    ok = emqx_mgmt_auth:init_bootstrap_file(),
+    ?assertMatch({error, _}, emqx_mgmt_auth:authorize(TestPath, <<"test-1">>, <<"secret-1">>)),
+    ?assertMatch({error, _}, emqx_mgmt_auth:authorize(TestPath, <<"test-2">>, <<"secret-2">>)),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-1">>, <<"new-secret-1">>)),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-2">>, <<"new-secret-2">>)),
+
+    %% Compatibility
+    Bin2 = <<"test-3:new-secret-3\ntest-4:new-secret-4">>,
+    ok = file:write_file(File, Bin2),
+    emqx:update_config([api_key, bootstrap_file], <<>>),
+    emqx:update_config([dashboard, bootstrap_users_file], File),
+    ok = emqx_mgmt_auth:init_bootstrap_file(),
+    ?assertMatch(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-1">>, <<"new-secret-1">>)),
+    ?assertMatch(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-2">>, <<"new-secret-2">>)),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-3">>, <<"new-secret-3">>)),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-4">>, <<"new-secret-4">>)),
+
+    %% not found
+    NotFoundFile = "./bootstrap_apps_not_exist.txt",
+    emqx:update_config([api_key, bootstrap_file], NotFoundFile),
+    ?assertMatch({error, "No such file or directory"}, emqx_mgmt_auth:init_bootstrap_file()),
+
+    %% bad format
+    BadBin = <<"test-1:secret-11\ntest-2 secret-12">>,
+    ok = file:write_file(File, BadBin),
+    emqx:update_config([api_key, bootstrap_file], File),
+    ?assertMatch({error, #{reason := "invalid_format"}}, emqx_mgmt_auth:init_bootstrap_file()),
+    ?assertEqual(ok, emqx_mgmt_auth:authorize(TestPath, <<"test-1">>, <<"secret-11">>)),
+    ?assertMatch({error, _}, emqx_mgmt_auth:authorize(TestPath, <<"test-2">>, <<"secret-12">>)),
+    emqx:update_config([api_key, bootstrap_file], <<>>),
+    emqx:update_config([dashboard, bootstrap_users_file], <<>>),
+    ok.
 
 t_create(_Config) ->
     Name = <<"EMQX-API-KEY-1">>,
@@ -69,7 +116,7 @@ t_create_failed(_Config) ->
     ?assertEqual(BadRequest, create_app(LongName)),
 
     {ok, List} = list_app(),
-    CreateNum = 1024 - erlang:length(List),
+    CreateNum = 100 - erlang:length(List),
     Names = lists:map(
         fun(Seq) ->
             <<"EMQX-API-FAILED-KEY-", (integer_to_binary(Seq))/binary>>