diff --git a/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf
new file mode 100644
index 000000000..56f41aa89
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_api_i18n.conf
@@ -0,0 +1,228 @@
+emqx_authn_api {
+
+ authentication_get {
+ desc {
+ en: """List authenticators for global authentication"""
+ zh: """列出全局认证链上的认证器信息"""
+ }
+ }
+
+ authentication_post {
+ desc {
+ en: """Create authenticator for global authentication"""
+ zh: """为全局认证链创建认证器信息"""
+ }
+ }
+
+ authentication_id_get {
+ desc {
+ en: """Get authenticator from global authentication chain"""
+ zh: """获取全局认证链上指定的认证器信息"""
+ }
+ }
+
+ authentication_id_put {
+ desc {
+ en: """Update authenticator from global authentication chain"""
+ zh: """更新全局认证链上指定的认证器"""
+ }
+ }
+
+ authentication_id_delete {
+ desc {
+ en: """Delete authenticator from global authentication chain"""
+ zh: """删除全局认证链上指定的认证器信息"""
+ }
+ }
+
+ authentication_id_status_get {
+ desc {
+ en: """Get authenticator status from global authentication chain"""
+ zh: """获取全局认证链上指定的认证器状态"""
+ }
+ }
+
+ listeners_listener_id_authentication_get {
+ desc {
+ en: """List authenticators for listener authentication"""
+ zh: """列出监听器上的认证器信息"""
+ }
+ }
+
+ listeners_listener_id_authentication_post {
+ desc {
+ en: """Create authenticator for listener authentication"""
+ zh: """在指定的监听器上创建认证器"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_get {
+ desc {
+ en: """Get authenticator from listener authentication chain"""
+ zh: """获取监听器上指定的认证器信息"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_put {
+ desc {
+ en: """Update authenticator from listener authentication chain"""
+ zh: """更新监听器上指定的认证器"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_delete {
+ desc {
+ en: """Delete authenticator from listener authentication chain"""
+ zh: """删除监听器上指定的认证器"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_status_get {
+ desc {
+ en: """Get authenticator status from listener authentication chain"""
+ zh: """获取监听器上指定的认证器状态"""
+ }
+ }
+
+ authentication_id_move_post {
+ desc {
+ en: """Move authenticator in global authentication chain"""
+ zh: """移动全局认证链上认证器的顺序"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_move_post {
+ desc {
+ en: """Move authenticator in listener authentication chain"""
+ zh: """移动监听器上认证器的顺序"""
+ }
+ }
+
+ authentication_id_import_users_post {
+ desc {
+ en: """Import users into authenticator in global authentication chain"""
+ zh: """为全局认证链上的认证器导入用户数据"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_import_users_post {
+ desc {
+ en: """Import users into authenticator in listener authentication chain"""
+ zh: """为监听器上的认证器导入用户数据"""
+ }
+ }
+
+ authentication_id_users_post {
+ desc {
+ en: """Create users for authenticator in global authentication chain"""
+ zh: """为全局认证链上的认证器创建用户数据"""
+ }
+ }
+
+ authentication_id_users_get {
+ desc {
+ en: """List users in authenticator in global authentication chain"""
+ zh: """获取全局认证链上指定的认证器中的用户数据"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_users_post {
+ desc {
+ en: """Create users for authenticator in listener authentication chain"""
+ zh: """更新指定监听器上认证器中的用户数据"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_users_get {
+ desc {
+ en: """List users in authenticator in listener authentication chain"""
+ zh: """列出指定监听器上认证器中的用户数据"""
+ }
+ }
+
+ authentication_id_users_user_id_get {
+ desc {
+ en: """Get user from authenticator in global authentication chain"""
+ zh: """获取指定监听器上认证器中指定的用户数据"""
+ }
+ }
+
+ authentication_id_users_user_id_put {
+ desc {
+ en: """Update user in authenticator in global authentication chain"""
+ zh: """为指定监听器上认证器添加用户数据"""
+ }
+ }
+
+ authentication_id_users_user_id_delete {
+ desc {
+ en: """Update user in authenticator in global authentication chain"""
+ zh: """更新指定监听器上认证器中的用户数据"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_users_user_id_get {
+ desc {
+ en: """Get user from authenticator in listener authentication chain"""
+ zh: """获取指定监听器上认证器中指定的用户数据"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_users_user_id_put {
+ desc {
+ en: """Update user in authenticator in listener authentication chain"""
+ zh: """更新指定监听器上认证器中的用户数据"""
+ }
+ }
+
+ listeners_listener_id_authentication_id_users_user_id_delete {
+ desc {
+ en: """Delete user in authenticator in listener authentication chain"""
+ zh: """删除指定监听器上认证器中的用户数据"""
+ }
+ }
+
+ param_auth_id {
+ desc {
+ en: """Authenticator ID"""
+ zh: """认证器 ID"""
+ }
+ }
+
+ param_listener_id {
+ desc {
+ en: """Listener ID"""
+ zh: """监听器 ID"""
+ }
+ }
+
+
+ param_user_id {
+ desc {
+ en: """User ID"""
+ zh: """用户 ID"""
+ }
+ }
+
+ like_username {
+ desc {
+ en: """Fuzzy search username"""
+ zh: """使用用户名模糊查询"""
+ }
+ label {
+ en: """like_username"""
+ zh: """模糊用户名"""
+ }
+ }
+
+ like_clientid {
+ desc {
+ en: """Fuzzy search clientid"""
+ zh: """使用客户端标识符模糊查询"""
+ }
+ label {
+ en: """like_clientid"""
+ zh: """模糊用户名"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_http_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_http_i18n.conf
new file mode 100644
index 000000000..634d5433f
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_http_i18n.conf
@@ -0,0 +1,89 @@
+emqx_authn_http {
+ get {
+ desc {
+ en: """Settings for HTTP-based authentication (GET)."""
+ zh: """基于 HTTP 的认证请求 (GET)"""
+ }
+ label: {
+ en: """get"""
+ zh: """get"""
+ }
+ }
+
+ post {
+ desc {
+ en: """Settings for HTTP-based authentication (POST)."""
+ zh: """基于 HTTP 的认证请求 (POST)"""
+ }
+ label: {
+ en: """post"""
+ zh: """post"""
+ }
+ }
+
+ method {
+ desc {
+ en: """HTTP method."""
+ zh: """HTTP 请求方法"""
+ }
+ label: {
+ en: """method"""
+ zh: """请求方法"""
+ }
+ }
+
+ url {
+ desc {
+ en: """URL of the auth server."""
+ zh: """认证服务器地址"""
+ }
+ label: {
+ en: """url"""
+ zh: """统一资源定位符"""
+ }
+ }
+
+ headers {
+ desc {
+ en: """List of HTTP headers."""
+ zh: """HTTP 请求头列表"""
+ }
+ label: {
+ en: """headers"""
+ zh: """请求头"""
+ }
+ }
+
+ headers_no_content_type {
+ desc {
+ en: """List of HTTP headers. (without content-type)"""
+ zh: """HTTP 请求头列表(无 content-type)"""
+ }
+ label: {
+ en: """headers_no_content_type"""
+ zh: """无content_type的请求头"""
+ }
+ }
+
+ body {
+ desc {
+ en: """Body of the HTTP request."""
+ zh: """HTTP 请求体"""
+ }
+ label: {
+ en: """body"""
+ zh: """请求体"""
+ }
+ }
+
+ request_timeout {
+ desc {
+ en: """HTTP request timeout"""
+ zh: """HTTP 请求超时时长"""
+ }
+ label: {
+ en: """request_timeout"""
+ zh: """请求超时时间"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_i18n.conf
new file mode 100644
index 000000000..b2573357c
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_i18n.conf
@@ -0,0 +1,155 @@
+emqx_authn_schema {
+ enable {
+ desc {
+ en: """Set to true or false to disable this auth provider"""
+ zh: """设为 true 或 false 以启用或禁用此认证数据源"""
+ }
+ label: {
+ en: """enable"""
+ zh: """启用"""
+ }
+ }
+
+ mechanism {
+ desc {
+ en: """Authentication mechanism."""
+ zh: """认证机制"""
+ }
+ label: {
+ en: """mechanism"""
+ zh: """机制"""
+ }
+ }
+
+ backend {
+ desc {
+ en: """Backend type."""
+ zh: """后端类型"""
+ }
+ label: {
+ en: """backend"""
+ zh: """后端"""
+ }
+ }
+
+ metrics {
+ desc {
+ en: """The metrics of the resource"""
+ zh: """统计指标"""
+ }
+ label: {
+ en: """metrics"""
+ zh: """指标"""
+ }
+ }
+
+ node_metrics {
+ desc {
+ en: """The metrics of the resource for each node"""
+ zh: """每个节点上资源的统计指标"""
+ }
+ label: {
+ en: """node_metrics"""
+ zh: """节点指标"""
+ }
+ }
+
+ status {
+ desc {
+ en: """The status of the resource"""
+ zh: """资源状态"""
+ }
+ label: {
+ en: """status"""
+ zh: """状态"""
+ }
+ }
+
+ node_status {
+ desc {
+ en: """The status of the resource for each node"""
+ zh: """每个节点上资源的状态"""
+ }
+ label: {
+ en: """node_status"""
+ zh: """节点状态"""
+ }
+ }
+
+ matched {
+ desc {
+ en: """Count of this resource is queried"""
+ zh: """请求命中次数"""
+ }
+ label: {
+ en: """matched"""
+ zh: """已命中"""
+ }
+ }
+
+ success {
+ desc {
+ en: """Count of query success"""
+ zh: """请求成功次数"""
+ }
+ label: {
+ en: """success"""
+ zh: """成功"""
+ }
+ }
+
+ failed {
+ desc {
+ en: """Count of query failed"""
+ zh: """请求失败次数"""
+ }
+ label: {
+ en: """failed"""
+ zh: """失败"""
+ }
+ }
+
+ rate {
+ desc {
+ en: """The rate of matched, times/second"""
+ zh: """命中速率,单位: 次/秒"""
+ }
+ label: {
+ en: """rate"""
+ zh: """速率"""
+ }
+ }
+
+ rate_max {
+ desc {
+ en: """The max rate of matched, times/second"""
+ zh: """最大命中速率,单位: 次/秒"""
+ }
+ label: {
+ en: """rate_max"""
+ zh: """最大速率"""
+ }
+ }
+
+ rate_last5m {
+ desc {
+ en: """The average rate of matched in the last 5 minutes, times/second"""
+ zh: """5分钟内平均命中速率,单位: 次/秒"""
+ }
+ label: {
+ en: """rate_last5m"""
+ zh: """5分钟内速率"""
+ }
+ }
+
+ node {
+ desc {
+ en: """The node name"""
+ zh: """节点名"""
+ }
+ label: {
+ en: """node"""
+ zh: """节点"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_jwt_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_jwt_i18n.conf
new file mode 100644
index 000000000..44a903f47
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_jwt_i18n.conf
@@ -0,0 +1,232 @@
+emqx_authn_jwt {
+ use_jwks {
+ desc {
+ en: """jwks flag"""
+ zh: """jwks 状态"""
+ }
+ label {
+ en: """use_jwks"""
+ zh: """使用jwks"""
+ }
+ }
+
+ algorithm {
+ desc {
+ en: """Signing algorithm."""
+ zh: """签名算法"""
+ }
+ label {
+ en: """algorithm"""
+ zh: """算法"""
+ }
+ }
+
+ certificate {
+ desc {
+ en: """The certificate used for signing the token."""
+ zh: """用于签名token的证书"""
+ }
+ label {
+ en: """certificate"""
+ zh: """证书"""
+ }
+ }
+
+ secret_base64_encoded {
+ desc {
+ en: """Enable/disable base64 encoding of the secret."""
+ zh: """启用/关闭私匙 base64 编码"""
+ }
+ label {
+ en: """secret_base64_encoded"""
+ zh: """密钥 base64 编码"""
+ }
+ }
+
+ secret {
+ desc {
+ en: """The key to verify the JWT Token using HMAC algorithm."""
+ zh: """使用对称加密的算法"""
+ }
+ label {
+ en: """secret"""
+ zh: """secret"""
+ }
+ }
+
+ endpoint {
+ desc {
+ en: """JWKs endpoint"""
+ zh: """JWKs endpoint"""
+ }
+ label {
+ en: """endpoint"""
+ zh: """endpoint"""
+ }
+ }
+
+ refresh_interval {
+ desc {
+ en: """JWKs refresh interval"""
+ zh: """JWKs 更新间隔"""
+ }
+ label {
+ en: """refresh_interval"""
+ zh: """更新间隔"""
+ }
+ }
+
+ cacertfile {
+ desc {
+ en: """Path to the SSL CA certificate file."""
+ zh: """SSL CA 证书公钥文件路径"""
+ }
+ label {
+ en: """cacertfile"""
+ zh: """CA 证书文件"""
+ }
+ }
+
+ certfile {
+ desc {
+ en: """Path to the SSL certificate file."""
+ zh: """证书文件路径"""
+ }
+ label {
+ en: """certfile"""
+ zh: """证书文件"""
+ }
+ }
+
+ keyfile {
+ desc {
+ en: """Path to the SSL secret key file."""
+ zh: """SSL 私钥文件路径"""
+ }
+ label {
+ en: """keyfile"""
+ zh: """私钥文件"""
+ }
+ }
+
+ verify {
+ desc {
+ en: """Enable or disable SSL peer verification."""
+ zh: """指定握手过程中是否校验客户端"""
+ }
+ label {
+ en: """verify"""
+ zh: """verify"""
+ }
+ }
+
+ server_name_indication {
+ desc {
+ en: """SSL SNI (Server Name Indication)"""
+ zh: """SSL SNI (服务器名称指示)"""
+ }
+ label {
+ en: """server_name_indication"""
+ zh: """服务器名称指示"""
+ }
+ }
+
+ verify_claims {
+ desc {
+ en: """The list of claims to verify."""
+ zh: """The list of claims to verify."""
+ }
+ label {
+ en: """verify_claims"""
+ zh: """verify_claims"""
+ }
+ }
+
+ pool_size {
+ desc {
+ en: """JWKs connection count"""
+ zh: """JWKs 连接数量"""
+ }
+ label {
+ en: """pool_size"""
+ zh: """pool_size"""
+ }
+ }
+
+ ssl {
+ desc {
+ en: """SSL options."""
+ zh: """SSL 选项"""
+ }
+ label {
+ en: """ssl"""
+ zh: """ssl"""
+ }
+ }
+
+ enable {
+ desc {
+ en: """Enable/disable SSL."""
+ zh: """启用/禁用 SSL"""
+ }
+ label {
+ en: """enable"""
+ zh: """启用"""
+ }
+ }
+
+ hmac-based {
+ desc {
+ en: """Settings for HMAC-based token signing algorithm."""
+ zh: """HMAC-based token 签名配置"""
+ }
+ label {
+ en: """hmac-based"""
+ zh: """hmac-based"""
+ }
+ }
+
+ public-key {
+ desc {
+ en: """Settings for public key-based token signing algorithm."""
+ zh: """公钥token签名配置"""
+ }
+ label {
+ en: """public-key"""
+ zh: """public-key"""
+ }
+ }
+
+ jwks {
+ desc {
+ en: """Settings for a signing using JSON Web Key Set (JWKs)."""
+ zh: """JWks 签名配置"""
+ }
+ label {
+ en: """jwks"""
+ zh: """jwks"""
+ }
+ }
+
+ ssl_disable {
+ desc {
+ en: """SSL disabled"""
+ zh: """SSL 关闭"""
+ }
+ label {
+ en: """ssl_disable"""
+ zh: """关闭SSL"""
+ }
+ }
+
+ ssl_enable {
+ desc {
+ en: """SSL configuration."""
+ zh: """SSL 配置"""
+ }
+ label {
+ en: """ssl_enable"""
+ zh: """启用SSL"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_mnesia_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_mnesia_i18n.conf
new file mode 100644
index 000000000..a545acaa7
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_mnesia_i18n.conf
@@ -0,0 +1,23 @@
+emqx_authn_mnesia {
+ authentication {
+ desc {
+ en: """Configuration for authentication using the built-in database."""
+ zh: """内置数据库认证配置"""
+ }
+ label: {
+ en: """authentication"""
+ zh: """认证配置"""
+ }
+ }
+
+ user_id_type {
+ desc {
+ en: """Authenticate by Client ID or Username."""
+ zh: """认证类型,基于 Client ID 或 Username"""
+ }
+ label: {
+ en: """user_id_type"""
+ zh: """用户标识类型"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_mongo_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_mongo_i18n.conf
new file mode 100644
index 000000000..740d842ec
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_mongo_i18n.conf
@@ -0,0 +1,99 @@
+emqx_authn_mongodb {
+ standalone {
+ desc {
+ en: """Configuration for a standalone MongoDB instance."""
+ zh: """MongoDB 单节点认证配置"""
+ }
+ label: {
+ en: """standalone"""
+ zh: """standalone 模式"""
+ }
+ }
+
+ replica-set {
+ desc {
+ en: """Configuration for a replica set."""
+ zh: """MongoDB replica set 模式认证配置"""
+ }
+ label: {
+ en: """replica-set"""
+ zh: """replica-set 模式"""
+ }
+ }
+
+ sharded-cluster {
+ desc {
+ en: """Configuration for a sharded cluster."""
+ zh: """MongoDB sharded cluster 模式认证配置"""
+ }
+ label: {
+ en: """sharded-cluster"""
+ zh: """sharded-cluster 模式"""
+ }
+ }
+
+ collection {
+ desc {
+ en: """Collection used to store authentication data."""
+ zh: """认证数据集名称"""
+ }
+ label: {
+ en: """collection"""
+ zh: """数据集"""
+ }
+ }
+
+ selector {
+ desc {
+ en: """
+Statement that is executed during the authentication process.
+Commands can support following wildcards:\n
+- `${username}`: substituted with client's username\n
+- `${clientid}`: substituted with the clientid
+"""
+ zh: """
+认证过程中所使用的查询命令。
+查询命令支持如下占位符:
+- `${username}`: 代替客户端的用户名
+- `${clientid}`: 代替客户端的客户端标识符
+"""
+ }
+ label: {
+ en: """selector"""
+ zh: """认证查询"""
+ }
+ }
+
+ password_hash_field {
+ desc {
+ en: """Document field that contains password hash."""
+ zh: """数据文档中的密码散列值"""
+ }
+ label: {
+ en: """password_hash_field"""
+ zh: """密码散列字段"""
+ }
+ }
+
+ salt_field {
+ desc {
+ en: """Document field that contains the password salt."""
+ zh: """数据文档中的密码加盐"""
+ }
+ label: {
+ en: """salt_field"""
+ zh: """加盐字段"""
+ }
+ }
+
+ is_superuser_field {
+ desc {
+ en: """Document field that defines if the user has superuser privileges."""
+ zh: """数据文档中的超级用户权限记录"""
+ }
+ label: {
+ en: """is_superuser_field"""
+ zh: """超级用户字段"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_mysql_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_mysql_i18n.conf
new file mode 100644
index 000000000..1c931cd0e
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_mysql_i18n.conf
@@ -0,0 +1,34 @@
+emqx_authn_mysql {
+ authentication {
+ desc {
+ en: """Configuration for authentication using MySQL database."""
+ zh: """MySQL 认证配置"""
+ }
+ label: {
+ en: """authentication"""
+ zh: """认证配置"""
+ }
+ }
+
+ query {
+ desc {
+ en: """SQL query used to lookup client data."""
+ zh: """客户端数据查询 SQL 语句"""
+ }
+ label: {
+ en: """query"""
+ zh: """请求"""
+ }
+ }
+
+ query_timeout {
+ dsec {
+ en: """Timeout for the SQL query."""
+ zh: """SQL 查询超时时长"""
+ }
+ label: {
+ en: """query_timeout"""
+ zh: """请求超时"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_pgsql_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_pgsql_i18n.conf
new file mode 100644
index 000000000..4f936be1d
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_pgsql_i18n.conf
@@ -0,0 +1,23 @@
+emqx_authn_pgsql {
+ authentication {
+ desc {
+ en: """Configuration for PostgreSQL authentication backend."""
+ zh: """PostgreSQL 认证配置"""
+ }
+ label: {
+ en: """authentication"""
+ zh: """认证配置"""
+ }
+ }
+
+ query {
+ desc {
+ en: """`SQL` query for looking up authentication data."""
+ zh: """客户端数据查询 SQL 语句"""
+ }
+ label: {
+ en: """query"""
+ zh: """请求"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/i18n/emqx_authn_redis_i18n.conf b/apps/emqx_authn/i18n/emqx_authn_redis_i18n.conf
new file mode 100644
index 000000000..0ae12b959
--- /dev/null
+++ b/apps/emqx_authn/i18n/emqx_authn_redis_i18n.conf
@@ -0,0 +1,45 @@
+emqx_authn_redis {
+ standalone {
+ desc {
+ en: """Configuration for a standalone Redis instance."""
+ zh: """Redis 单节点认证配置"""
+ }
+ label: {
+ en: """standalone"""
+ zh: """standalone"""
+ }
+ }
+
+ cluster {
+ desc {
+ en: """Configuration for a Redis cluster."""
+ zh: """Redis 集群模式认证配置"""
+ }
+ label: {
+ en: """cluster"""
+ zh: """cluster"""
+ }
+ }
+
+ sentinel {
+ desc {
+ en: """Configuration for a Redis Sentinel."""
+ zh: """Redis 哨兵模式认证配置"""
+ }
+ label: {
+ en: """sentinel"""
+ zh: """sentinel"""
+ }
+ }
+
+ cmd {
+ desc {
+ en: """Database query used to retrieve authentication data."""
+ zh: """Redis 认证数据查询语句"""
+ }
+ label: {
+ en: """cmd"""
+ zh: """查询命令"""
+ }
+ }
+}
diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl
index c785bf0f6..2e4c7a579 100644
--- a/apps/emqx_authn/src/emqx_authn_api.erl
+++ b/apps/emqx_authn/src/emqx_authn_api.erl
@@ -18,11 +18,11 @@
-behaviour(minirest_api).
--include_lib("typerefl/include/types.hrl").
-include("emqx_authn.hrl").
--include_lib("emqx/include/emqx_placeholder.hrl").
-include_lib("emqx/include/logger.hrl").
+-include_lib("emqx/include/emqx_placeholder.hrl").
-include_lib("emqx/include/emqx_authentication.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-import(hoconsc, [mk/2, ref/1, ref/2]).
-import(emqx_dashboard_swagger, [error_codes/2]).
@@ -154,7 +154,7 @@ schema("/authentication") ->
'operationId' => authenticators,
get => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"List authenticators for global authentication">>,
+ description => ?DESC(authentication_get),
responses => #{
200 => emqx_dashboard_swagger:schema_with_example(
hoconsc:array(emqx_authn_schema:authenticator_type()),
@@ -164,7 +164,7 @@ schema("/authentication") ->
},
post => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Create authenticator for global authentication">>,
+ description => ?DESC(authentication_post),
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
emqx_authn_schema:authenticator_type(),
authenticator_examples()
@@ -184,7 +184,7 @@ schema("/authentication/:id") ->
'operationId' => authenticator,
get => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Get authenticator from global authentication chain">>,
+ description => ?DESC(authentication_id_get),
parameters => [param_auth_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_examples(
@@ -196,7 +196,7 @@ schema("/authentication/:id") ->
},
put => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Update authenticator from global authentication chain">>,
+ description => ?DESC(authentication_id_put),
parameters => [param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
emqx_authn_schema:authenticator_type(),
@@ -214,7 +214,7 @@ schema("/authentication/:id") ->
},
delete => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Delete authenticator from global authentication chain">>,
+ description => ?DESC(authentication_id_delete),
parameters => [param_auth_id()],
responses => #{
204 => <<"Authenticator deleted">>,
@@ -227,7 +227,7 @@ schema("/authentication/:id/status") ->
'operationId' => authenticator_status,
get => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Get authenticator status from global authentication chain">>,
+ description => ?DESC(authentication_id_status_get),
parameters => [param_auth_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_examples(
@@ -243,7 +243,7 @@ schema("/listeners/:listener_id/authentication") ->
'operationId' => listener_authenticators,
get => #{
tags => ?API_TAGS_SINGLE,
- description => <<"List authenticators for listener authentication">>,
+ description => ?DESC(listeners_listener_id_authentication_get),
parameters => [param_listener_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_example(
@@ -254,7 +254,7 @@ schema("/listeners/:listener_id/authentication") ->
},
post => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Create authenticator for listener authentication">>,
+ description => ?DESC(listeners_listener_id_authentication_post),
parameters => [param_listener_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
emqx_authn_schema:authenticator_type(),
@@ -275,7 +275,7 @@ schema("/listeners/:listener_id/authentication/:id") ->
'operationId' => listener_authenticator,
get => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Get authenticator from listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_get),
parameters => [param_listener_id(), param_auth_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_examples(
@@ -287,7 +287,7 @@ schema("/listeners/:listener_id/authentication/:id") ->
},
put => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Update authenticator from listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_put),
parameters => [param_listener_id(), param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
emqx_authn_schema:authenticator_type(),
@@ -305,7 +305,7 @@ schema("/listeners/:listener_id/authentication/:id") ->
},
delete => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Delete authenticator from listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_delete),
parameters => [param_listener_id(), param_auth_id()],
responses => #{
204 => <<"Authenticator deleted">>,
@@ -318,7 +318,7 @@ schema("/listeners/:listener_id/authentication/:id/status") ->
'operationId' => listener_authenticator_status,
get => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Get authenticator status from listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_status_get),
parameters => [param_listener_id(), param_auth_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_examples(
@@ -334,7 +334,7 @@ schema("/authentication/:id/move") ->
'operationId' => authenticator_move,
post => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Move authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_move_post),
parameters => [param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_move),
@@ -352,7 +352,7 @@ schema("/listeners/:listener_id/authentication/:id/move") ->
'operationId' => listener_authenticator_move,
post => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Move authenticator in listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_move_post),
parameters => [param_listener_id(), param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_move),
@@ -370,7 +370,7 @@ schema("/authentication/:id/import_users") ->
'operationId' => authenticator_import_users,
post => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Import users into authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_import_users_post),
parameters => [param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_import_users),
@@ -388,7 +388,7 @@ schema("/listeners/:listener_id/authentication/:id/import_users") ->
'operationId' => listener_authenticator_import_users,
post => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Import users into authenticator in listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_import_users_post),
parameters => [param_listener_id(), param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_import_users),
@@ -406,7 +406,7 @@ schema("/authentication/:id/users") ->
'operationId' => authenticator_users,
post => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Create users for authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_users_post),
parameters => [param_auth_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_user_create),
@@ -423,7 +423,7 @@ schema("/authentication/:id/users") ->
},
get => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"List users in authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_users_get),
parameters => [
param_auth_id(),
ref(emqx_dashboard_swagger, page),
@@ -431,13 +431,13 @@ schema("/authentication/:id/users") ->
{like_username,
mk(binary(), #{
in => query,
- desc => <<"Fuzzy search username">>,
+ desc => ?DESC(like_username),
required => false
})},
{like_clientid,
mk(binary(), #{
in => query,
- desc => <<"Fuzzy search clientid">>,
+ desc => ?DESC(like_clientid),
required => false
})}
],
@@ -455,7 +455,7 @@ schema("/listeners/:listener_id/authentication/:id/users") ->
'operationId' => listener_authenticator_users,
post => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Create users for authenticator in global authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_users_post),
parameters => [param_auth_id(), param_listener_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_user_create),
@@ -472,7 +472,7 @@ schema("/listeners/:listener_id/authentication/:id/users") ->
},
get => #{
tags => ?API_TAGS_SINGLE,
- description => <<"List users in authenticator in listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_users_get),
parameters => [
param_listener_id(),
param_auth_id(),
@@ -493,7 +493,7 @@ schema("/authentication/:id/users/:user_id") ->
'operationId' => authenticator_user,
get => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Get user from authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_users_user_id_get),
parameters => [param_auth_id(), param_user_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_examples(
@@ -505,7 +505,7 @@ schema("/authentication/:id/users/:user_id") ->
},
put => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Update user in authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_users_user_id_put),
parameters => [param_auth_id(), param_user_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
ref(request_user_update),
@@ -522,7 +522,7 @@ schema("/authentication/:id/users/:user_id") ->
},
delete => #{
tags => ?API_TAGS_GLOBAL,
- description => <<"Update user in authenticator in global authentication chain">>,
+ description => ?DESC(authentication_id_users_user_id_delete),
parameters => [param_auth_id(), param_user_id()],
responses => #{
204 => <<"User deleted">>,
@@ -535,7 +535,7 @@ schema("/listeners/:listener_id/authentication/:id/users/:user_id") ->
'operationId' => listener_authenticator_user,
get => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Get user from authenticator in listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_users_user_id_get),
parameters => [param_listener_id(), param_auth_id(), param_user_id()],
responses => #{
200 => emqx_dashboard_swagger:schema_with_example(
@@ -547,7 +547,7 @@ schema("/listeners/:listener_id/authentication/:id/users/:user_id") ->
},
put => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Update user in authenticator in listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_users_user_id_put),
parameters => [param_listener_id(), param_auth_id(), param_user_id()],
'requestBody' => emqx_dashboard_swagger:schema_with_example(
ref(request_user_update),
@@ -564,7 +564,7 @@ schema("/listeners/:listener_id/authentication/:id/users/:user_id") ->
},
delete => #{
tags => ?API_TAGS_SINGLE,
- description => <<"Update user in authenticator in listener authentication chain">>,
+ description => ?DESC(listeners_listener_id_authentication_id_users_user_id_delete),
parameters => [param_listener_id(), param_auth_id(), param_user_id()],
responses => #{
204 => <<"User deleted">>,
@@ -578,7 +578,7 @@ param_auth_id() ->
id,
mk(binary(), #{
in => path,
- desc => <<"Authenticator ID">>,
+ desc => ?DESC(param_auth_id),
required => true
})
}.
@@ -588,7 +588,7 @@ param_listener_id() ->
listener_id,
mk(binary(), #{
in => path,
- desc => <<"Listener ID">>,
+ desc => ?DESC(param_listener_id),
required => true,
example => emqx_listeners:id_example()
})
@@ -599,7 +599,7 @@ param_user_id() ->
user_id,
mk(binary(), #{
in => path,
- desc => <<"User ID">>
+ desc => ?DESC(param_user_id)
})
}.
diff --git a/apps/emqx_authn/src/emqx_authn_schema.erl b/apps/emqx_authn/src/emqx_authn_schema.erl
index 32994cbbe..ef20e36cf 100644
--- a/apps/emqx_authn/src/emqx_authn_schema.erl
+++ b/apps/emqx_authn/src/emqx_authn_schema.erl
@@ -17,7 +17,7 @@
-module(emqx_authn_schema).
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-import(hoconsc, [mk/2, ref/2]).
-export([
@@ -38,7 +38,7 @@ common_fields() ->
enable(type) -> boolean();
enable(default) -> true;
-enable(desc) -> "Set to false to disable this auth provider";
+enable(desc) -> ?DESC(?FUNCTION_NAME);
enable(_) -> undefined.
authenticator_type() ->
@@ -74,7 +74,7 @@ mechanism(Name) ->
hoconsc:enum([Name]),
#{
required => true,
- desc => "Authentication mechanism."
+ desc => ?DESC("mechanism")
}
).
@@ -83,51 +83,47 @@ backend(Name) ->
hoconsc:enum([Name]),
#{
required => true,
- desc => "Backend type."
+ desc => ?DESC("backend")
}
).
fields("metrics_status_fields") ->
[
- {"metrics", mk(ref(?MODULE, "metrics"), #{desc => "The metrics of the resource"})},
+ {"metrics", mk(ref(?MODULE, "metrics"), #{desc => ?DESC("metrics")})},
{"node_metrics",
mk(
hoconsc:array(ref(?MODULE, "node_metrics")),
- #{desc => "The metrics of the resource for each node"}
+ #{desc => ?DESC("node_metrics")}
)},
- {"status", mk(status(), #{desc => "The status of the resource"})},
+ {"status", mk(status(), #{desc => ?DESC("status")})},
{"node_status",
mk(
hoconsc:array(ref(?MODULE, "node_status")),
- #{desc => "The status of the resource for each node"}
+ #{desc => ?DESC("node_status")}
)}
];
fields("metrics") ->
[
- {"matched", mk(integer(), #{desc => "Count of this resource is queried"})},
- {"success", mk(integer(), #{desc => "Count of query success"})},
- {"failed", mk(integer(), #{desc => "Count of query failed"})},
- {"rate", mk(float(), #{desc => "The rate of matched, times/second"})},
- {"rate_max", mk(float(), #{desc => "The max rate of matched, times/second"})},
- {"rate_last5m",
- mk(
- float(),
- #{desc => "The average rate of matched in the last 5 minutes, times/second"}
- )}
+ {"matched", mk(integer(), #{desc => ?DESC("matched")})},
+ {"success", mk(integer(), #{desc => ?DESC("success")})},
+ {"failed", mk(integer(), #{desc => ?DESC("failed")})},
+ {"rate", mk(float(), #{desc => ?DESC("rate")})},
+ {"rate_max", mk(float(), #{desc => ?DESC("rate_max")})},
+ {"rate_last5m", mk(float(), #{desc => ?DESC("rate_last5m")})}
];
fields("node_metrics") ->
[
node_name(),
- {"metrics", mk(ref(?MODULE, "metrics"), #{})}
+ {"metrics", mk(ref(?MODULE, "metrics"), #{desc => ?DESC("metrics")})}
];
fields("node_status") ->
[
node_name(),
- {"status", mk(status(), #{desc => "Status of the node."})}
+ {"status", mk(status(), #{desc => ?DESC("node_status")})}
].
status() ->
hoconsc:enum([connected, disconnected, connecting]).
node_name() ->
- {"node", mk(binary(), #{desc => "The node name", example => "emqx@127.0.0.1"})}.
+ {"node", mk(binary(), #{desc => ?DESC("node"), example => "emqx@127.0.0.1"})}.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl
index d13f2c69f..b03a7f1b1 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl
@@ -18,7 +18,7 @@
-include("emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-include_lib("emqx_connector/include/emqx_connector.hrl").
-behaviour(hocon_schema).
@@ -62,19 +62,19 @@ roots() ->
fields(get) ->
[
- {method, #{type => get, required => true, default => post, desc => "HTTP method."}},
+ {method, #{type => get, required => true, default => post, desc => ?DESC(method)}},
{headers, fun headers_no_content_type/1}
] ++ common_fields();
fields(post) ->
[
- {method, #{type => post, required => true, default => post, desc => "HTTP method."}},
+ {method, #{type => post, required => true, default => post, desc => ?DESC(method)}},
{headers, fun headers/1}
] ++ common_fields().
desc(get) ->
- "Settings for HTTP-based authentication (GET).";
+ ?DESC(get);
desc(post) ->
- "Settings for HTTP-based authentication (POST).";
+ ?DESC(post);
desc(_) ->
undefined.
@@ -83,8 +83,7 @@ common_fields() ->
{mechanism, emqx_authn_schema:mechanism(password_based)},
{backend, emqx_authn_schema:backend(http)},
{url, fun url/1},
- {body,
- hoconsc:mk(map([{fuzzy, term(), binary()}]), #{desc => "Body of the HTTP request."})},
+ {body, hoconsc:mk(map([{fuzzy, term(), binary()}]), #{desc => ?DESC(body)})},
{request_timeout, fun request_timeout/1}
] ++ emqx_authn_schema:common_fields() ++
maps:to_list(
@@ -104,7 +103,7 @@ validations() ->
].
url(type) -> binary();
-url(desc) -> "URL of the auth server.";
+url(desc) -> ?DESC(?FUNCTION_NAME);
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
url(required) -> true;
url(_) -> undefined.
@@ -112,7 +111,7 @@ url(_) -> undefined.
headers(type) ->
map();
headers(desc) ->
- "List of HTTP headers.";
+ ?DESC(?FUNCTION_NAME);
headers(converter) ->
fun(Headers) ->
maps:merge(default_headers(), transform_header_name(Headers))
@@ -125,7 +124,7 @@ headers(_) ->
headers_no_content_type(type) ->
map();
headers_no_content_type(desc) ->
- "List of HTTP headers.";
+ ?DESC(?FUNCTION_NAME);
headers_no_content_type(converter) ->
fun(Headers) ->
maps:merge(default_headers_no_content_type(), transform_header_name(Headers))
@@ -136,7 +135,7 @@ headers_no_content_type(_) ->
undefined.
request_timeout(type) -> emqx_schema:duration_ms();
-request_timeout(desc) -> "HTTP request timeout";
+request_timeout(desc) -> ?DESC(?FUNCTION_NAME);
request_timeout(default) -> <<"5s">>;
request_timeout(_) -> undefined.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl
index b296e32a6..c5d84e98d 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl
@@ -17,8 +17,8 @@
-module(emqx_authn_jwt).
-include("emqx_authn.hrl").
--include_lib("typerefl/include/types.hrl").
-include_lib("emqx/include/logger.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-behaviour(hocon_schema).
-behaviour(emqx_authentication).
@@ -55,22 +55,22 @@ roots() ->
fields('hmac-based') ->
[
- {use_jwks, sc(hoconsc:enum([false]), #{required => true, desc => ""})},
+ {use_jwks, sc(hoconsc:enum([false]), #{required => true, desc => ?DESC(use_jwks)})},
{algorithm,
- sc(hoconsc:enum(['hmac-based']), #{required => true, desc => "Signing algorithm."})},
+ sc(hoconsc:enum(['hmac-based']), #{required => true, desc => ?DESC(algorithm)})},
{secret, fun secret/1},
{secret_base64_encoded, fun secret_base64_encoded/1}
] ++ common_fields();
fields('public-key') ->
[
- {use_jwks, sc(hoconsc:enum([false]), #{required => true, desc => ""})},
+ {use_jwks, sc(hoconsc:enum([false]), #{required => true, desc => ?DESC(use_jwks)})},
{algorithm,
- sc(hoconsc:enum(['public-key']), #{required => true, desc => "Signing algorithm."})},
+ sc(hoconsc:enum(['public-key']), #{required => true, desc => ?DESC(algorithm)})},
{certificate, fun certificate/1}
] ++ common_fields();
fields('jwks') ->
[
- {use_jwks, sc(hoconsc:enum([true]), #{required => true, desc => ""})},
+ {use_jwks, sc(hoconsc:enum([true]), #{required => true, desc => ?DESC(use_jwks)})},
{endpoint, fun endpoint/1},
{pool_size, fun pool_size/1},
{refresh_interval, fun refresh_interval/1},
@@ -79,14 +79,14 @@ fields('jwks') ->
hoconsc:ref(?MODULE, ssl_enable),
hoconsc:ref(?MODULE, ssl_disable)
]),
- desc => "Enable/disable SSL.",
+ desc => ?DESC(ssl),
default => #{<<"enable">> => false},
required => false
}}
] ++ common_fields();
fields(ssl_enable) ->
[
- {enable, #{type => true, desc => ""}},
+ {enable, #{type => true, desc => ?DESC(enable)}},
{cacertfile, fun cacertfile/1},
{certfile, fun certfile/1},
{keyfile, fun keyfile/1},
@@ -94,18 +94,18 @@ fields(ssl_enable) ->
{server_name_indication, fun server_name_indication/1}
];
fields(ssl_disable) ->
- [{enable, #{type => false, desc => ""}}].
+ [{enable, #{type => false, desc => ?DESC(enable)}}].
desc('hmac-based') ->
- "Settings for HMAC-based token signing algorithm.";
+ ?DESC('hmac-based');
desc('public-key') ->
- "Settings for public key-based token signing algorithm.";
+ ?DESC('public-key');
desc('jwks') ->
- "Settings for a signing using JSON Web Key Set (JWKs).";
+ ?DESC('jwks');
desc(ssl_disable) ->
- "";
+ ?DESC(ssl_disable);
desc(ssl_enable) ->
- "SSL configuration.";
+ ?DESC(ssl_enable);
desc(_) ->
undefined.
@@ -116,56 +116,56 @@ common_fields() ->
] ++ emqx_authn_schema:common_fields().
secret(type) -> binary();
-secret(desc) -> "The key to verify the JWT Token using HMAC algorithm.";
+secret(desc) -> ?DESC(?FUNCTION_NAME);
secret(required) -> true;
secret(_) -> undefined.
secret_base64_encoded(type) -> boolean();
-secret_base64_encoded(desc) -> "Enable/disable base64 encoding of the secret.";
+secret_base64_encoded(desc) -> ?DESC(?FUNCTION_NAME);
secret_base64_encoded(default) -> false;
secret_base64_encoded(_) -> undefined.
certificate(type) -> string();
-certificate(desc) -> "The certificate used for signing the token.";
+certificate(desc) -> ?DESC(?FUNCTION_NAME);
certificate(required) -> ture;
certificate(_) -> undefined.
endpoint(type) -> string();
-endpoint(desc) -> "JWKs endpoint.";
+endpoint(desc) -> ?DESC(?FUNCTION_NAME);
endpoint(required) -> true;
endpoint(_) -> undefined.
refresh_interval(type) -> integer();
-refresh_interval(desc) -> "JWKs refresh interval";
+refresh_interval(desc) -> ?DESC(?FUNCTION_NAME);
refresh_interval(default) -> 300;
refresh_interval(validator) -> [fun(I) -> I > 0 end];
refresh_interval(_) -> undefined.
cacertfile(type) -> string();
-cacertfile(desc) -> "Path to the SSL CA certificate file.";
+cacertfile(desc) -> ?DESC(?FUNCTION_NAME);
cacertfile(_) -> undefined.
certfile(type) -> string();
-certfile(desc) -> "Path to the SSL certificate file.";
+certfile(desc) -> ?DESC(?FUNCTION_NAME);
certfile(_) -> undefined.
keyfile(type) -> string();
-keyfile(desc) -> "Path to the SSL secret key file.";
+keyfile(desc) -> ?DESC(?FUNCTION_NAME);
keyfile(_) -> undefined.
verify(type) -> hoconsc:enum([verify_peer, verify_none]);
-verify(desc) -> "Enable or disable SSL peer verification.";
+verify(desc) -> ?DESC(?FUNCTION_NAME);
verify(default) -> verify_none;
verify(_) -> undefined.
server_name_indication(type) -> string();
-server_name_indication(desc) -> "SSL SNI (Server Name Indication)";
+server_name_indication(desc) -> ?DESC(?FUNCTION_NAME);
server_name_indication(_) -> undefined.
verify_claims(type) ->
list();
verify_claims(desc) ->
- "The list of claims to verify.";
+ ?DESC(?FUNCTION_NAME);
verify_claims(default) ->
#{};
verify_claims(validator) ->
@@ -180,7 +180,7 @@ verify_claims(_) ->
undefined.
pool_size(type) -> integer();
-pool_size(desc) -> "JWKS connection count";
+pool_size(desc) -> ?DESC(?FUNCTION_NAME);
pool_size(default) -> 8;
pool_size(validator) -> [fun(I) -> I > 0 end];
pool_size(_) -> undefined.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_mnesia.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_mnesia.erl
index 7971ce049..b3bb91ff7 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_mnesia.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_mnesia.erl
@@ -18,7 +18,7 @@
-include("emqx_authn.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-behaviour(hocon_schema).
-behaviour(emqx_authentication).
@@ -110,12 +110,12 @@ fields(?CONF_NS) ->
] ++ emqx_authn_schema:common_fields().
desc(?CONF_NS) ->
- "Configuration for authentication using the built-in database.";
+ ?DESC(?CONF_NS);
desc(_) ->
undefined.
user_id_type(type) -> user_id_type();
-user_id_type(desc) -> "Authenticate by client ID or username.";
+user_id_type(desc) -> ?DESC(?FUNCTION_NAME);
user_id_type(default) -> <<"username">>;
user_id_type(required) -> true;
user_id_type(_) -> undefined.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_mongodb.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_mongodb.erl
index f5ceaa577..2174406e0 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_mongodb.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_mongodb.erl
@@ -18,7 +18,7 @@
-include("emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-behaviour(hocon_schema).
-behaviour(emqx_authentication).
@@ -61,17 +61,17 @@ fields('sharded-cluster') ->
common_fields() ++ emqx_connector_mongo:fields(sharded).
desc(standalone) ->
- "Configuration for a standalone MongoDB instance.";
+ ?DESC(standalone);
desc('replica-set') ->
- "Configuration for a replica set.";
+ ?DESC('replica-set');
desc('sharded-cluster') ->
- "Configuration for a sharded cluster.";
+ ?DESC('sharded-cluster');
desc(_) ->
undefined.
common_fields() ->
[
- {mechanism, emqx_authn_schema:mechanism('password_based')},
+ {mechanism, emqx_authn_schema:mechanism(password_based)},
{backend, emqx_authn_schema:backend(mongodb)},
{collection, fun collection/1},
{selector, fun selector/1},
@@ -82,32 +82,29 @@ common_fields() ->
] ++ emqx_authn_schema:common_fields().
collection(type) -> binary();
-collection(desc) -> "Collection used to store authentication data.";
+collection(desc) -> ?DESC(?FUNCTION_NAME);
collection(required) -> true;
collection(_) -> undefined.
selector(type) ->
map();
selector(desc) ->
- "Statement that is executed during the authentication process. "
- "Commands can support following wildcards:\n"
- " - `${username}`: substituted with client's username\n"
- " - `${clientid}`: substituted with the clientid";
+ ?DESC(?FUNCTION_NAME);
selector(_) ->
undefined.
password_hash_field(type) -> binary();
-password_hash_field(desc) -> "Document field that contains password hash.";
+password_hash_field(desc) -> ?DESC(?FUNCTION_NAME);
password_hash_field(required) -> false;
password_hash_field(_) -> undefined.
salt_field(type) -> binary();
-salt_field(desc) -> "Document field that contains the password salt.";
+salt_field(desc) -> ?DESC(?FUNCTION_NAME);
salt_field(required) -> false;
salt_field(_) -> undefined.
is_superuser_field(type) -> binary();
-is_superuser_field(desc) -> "Document field that defines if the user has superuser privileges.";
+is_superuser_field(desc) -> ?DESC(?FUNCTION_NAME);
is_superuser_field(required) -> false;
is_superuser_field(_) -> undefined.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl
index 9cbe0c174..11575149c 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl
@@ -18,7 +18,7 @@
-include("emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-behaviour(hocon_schema).
-behaviour(emqx_authentication).
@@ -56,20 +56,20 @@ fields(?CONF_NS) ->
{query, fun query/1},
{query_timeout, fun query_timeout/1}
] ++ emqx_authn_schema:common_fields() ++
- proplists:delete(prepare_statement, emqx_connector_mysql:fields(config)).
+ proplists:delete(prepare_statement, emqx_connector_mysql:fields(config)).
desc(?CONF_NS) ->
- "Configuration for authentication using MySQL database.";
+ ?DESC(?CONF_NS);
desc(_) ->
undefined.
query(type) -> string();
-query(desc) -> "SQL query used to lookup client data.";
+query(desc) -> ?DESC(?FUNCTION_NAME);
query(required) -> true;
query(_) -> undefined.
query_timeout(type) -> emqx_schema:duration_ms();
-query_timeout(desc) -> "Timeout for the SQL query.";
+query_timeout(desc) -> ?DESC(?FUNCTION_NAME);
query_timeout(default) -> "5s";
query_timeout(_) -> undefined.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_pgsql.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_pgsql.erl
index cb8340a4a..47eb57634 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_pgsql.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_pgsql.erl
@@ -19,7 +19,7 @@
-include("emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
-include_lib("epgsql/include/epgsql.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-behaviour(hocon_schema).
-behaviour(emqx_authentication).
@@ -63,12 +63,12 @@ fields(?CONF_NS) ->
proplists:delete(prepare_statement, emqx_connector_pgsql:fields(config)).
desc(?CONF_NS) ->
- "Configuration for PostgreSQL authentication backend.";
+ ?DESC(?CONF_NS);
desc(_) ->
undefined.
query(type) -> string();
-query(desc) -> "`SQL` query for looking up authentication data.";
+query(desc) -> ?DESC(?FUNCTION_NAME);
query(required) -> true;
query(_) -> undefined.
diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_redis.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_redis.erl
index b29f0acdf..37870448c 100644
--- a/apps/emqx_authn/src/simple_authn/emqx_authn_redis.erl
+++ b/apps/emqx_authn/src/simple_authn/emqx_authn_redis.erl
@@ -18,7 +18,7 @@
-include("emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-behaviour(hocon_schema).
-behaviour(emqx_authentication).
@@ -61,24 +61,24 @@ fields(sentinel) ->
common_fields() ++ emqx_connector_redis:fields(sentinel).
desc(standalone) ->
- "Configuration for a standalone Redis instance.";
+ ?DESC(standalone);
desc(cluster) ->
- "Configuration for a Redis cluster.";
+ ?DESC(cluster);
desc(sentinel) ->
- "Configuration for a Redis Sentinel.";
+ ?DESC(sentinel);
desc(_) ->
"".
common_fields() ->
[
- {mechanism, emqx_authn_schema:mechanism('password_based')},
+ {mechanism, emqx_authn_schema:mechanism(password_based)},
{backend, emqx_authn_schema:backend(redis)},
{cmd, fun cmd/1},
{password_hash_algorithm, fun emqx_authn_password_hashing:type_ro/1}
] ++ emqx_authn_schema:common_fields().
cmd(type) -> string();
-cmd(desc) -> "Redis query.";
+cmd(desc) -> ?DESC(?FUNCTION_NAME);
cmd(required) -> true;
cmd(_) -> undefined.
diff --git a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl
index b6ae6d514..3d2dba895 100644
--- a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl
+++ b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl
@@ -198,10 +198,10 @@ t_update(_Config) ->
>>
},
- {ok, _} = emqx:update_config(
- ?PATH,
- {create_authenticator, ?GLOBAL, IncorrectConfig}
- ),
+ {ok, _} = emqx:update_config(
+ ?PATH,
+ {create_authenticator, ?GLOBAL, IncorrectConfig}
+ ),
{error, not_authorized} = emqx_access_control:authenticate(
#{
diff --git a/apps/emqx_authz/i18n/emqx_authz_api_cache_i18n.conf b/apps/emqx_authz/i18n/emqx_authz_api_cache_i18n.conf
new file mode 100644
index 000000000..25bf5273e
--- /dev/null
+++ b/apps/emqx_authz/i18n/emqx_authz_api_cache_i18n.conf
@@ -0,0 +1,6 @@
+emqx_authz_api_cache {
+ authorization_cache_delete {
+ en: """Clean all authorization cache in the cluster."""
+ zh: """清除集群中所有鉴权数据缓存"""
+ }
+}
diff --git a/apps/emqx_authz/i18n/emqx_authz_api_mnesia_i18n.conf b/apps/emqx_authz/i18n/emqx_authz_api_mnesia_i18n.conf
new file mode 100644
index 000000000..50f644097
--- /dev/null
+++ b/apps/emqx_authz/i18n/emqx_authz_api_mnesia_i18n.conf
@@ -0,0 +1,177 @@
+emqx_authz_api_mnesia {
+ users_username_get {
+ desc {
+ en: """Show the list of record for username"""
+ zh: """获取内置数据库中所有用户名类型的规则记录"""
+ }
+ }
+
+ users_username_post {
+ desc {
+ en: """Add new records for username"""
+ zh: """添加内置数据库中用户名类型的规则记录"""
+ }
+ }
+
+ users_clientid_get {
+ desc {
+ en: """Show the list of record for clientid"""
+ zh: """获取内置数据库中所有客户端标识符类型的规则记录"""
+ }
+ }
+
+ users_clientid_post {
+ desc {
+ en: """Add new records for clientid"""
+ zh: """添加内置数据库中客户端标识符类型的规则记录"""
+ }
+ }
+
+
+ user_username_get {
+ desc {
+ en: """Get record info for username"""
+ zh: """获取内置数据库中指定用户名类型的规则记录"""
+ }
+ }
+
+ user_username_put {
+ desc {
+ en: """Set record for username"""
+ zh: """更新内置数据库中指定用户名类型的规则记录"""
+ }
+ }
+
+ user_username_delete {
+ desc {
+ en: """Delete one record for username"""
+ zh: """删除内置数据库中指定用户名类型的规则记录"""
+ }
+ }
+
+ user_clientid_get {
+ desc {
+ en: """Get record info for clientid"""
+ zh: """获取内置数据库中指定客户端标识符类型的规则记录"""
+ }
+ }
+
+ user_clientid_put {
+ desc {
+ en: """Set record for clientid"""
+ zh: """更新内置数据库中指定客户端标识符类型的规则记录"""
+ }
+ }
+
+ user_clientid_delete {
+ desc {
+ en: """Delete one record for clientid"""
+ zh: """删除内置数据库中指定客户端标识符类型的规则记录"""
+ }
+ }
+
+
+ rules_for_all_get {
+ desc {
+ en: """Show the list of rules for all"""
+ zh: """列出为所有客户端启用的规则列表"""
+ }
+ }
+
+ rules_for_all_post {
+ desc {
+ en: """
+Create/Update the list of rules for all.
+Set a empty list to clean up rules
+"""
+ zh: """
+创建/更新 为所有客户端启用的规则列表。
+设为空列表以清楚所有规则
+"""
+ }
+ }
+
+ purge_all_delete {
+ desc {
+ en: """Purge all records for username/clientid/all"""
+ zh: """清除所有内置数据库中的规则, 用户名/客户端标识符/所有"""
+ }
+ }
+
+ fuzzy_username {
+ desc {
+ en: """Fuzzy search `username` as substring"""
+ zh: """使用字串匹配模糊搜索用户名"""
+ }
+ label {
+ en: """fuzzy_username"""
+ zh: """用户名子串"""
+ }
+ }
+
+ fuzzy_clientid {
+ desc {
+ en: """Fuzzy search `clientid` as substring"""
+ zh: """使用字串匹配模糊搜索客户端标识符"""
+ }
+ label {
+ en: """fuzzy_clientid"""
+ zh: """客户端标识符子串"""
+ }
+ }
+
+ topic {
+ desc {
+ en: """Rule on specific topic"""
+ zh: """在指定主题上的规则"""
+ }
+ label {
+ en: """topic"""
+ zh: """主题"""
+ }
+ }
+
+ permission {
+ desc {
+ en: """Permission"""
+ zh: """权限"""
+ }
+ label {
+ en: """permission"""
+ zh: """权限"""
+ }
+ }
+
+ action {
+ desc {
+ en: """Authorized action (pub/sub/all)"""
+ zh: """被授权的行为 (发布/订阅/所有)"""
+ }
+ label {
+ en: """action"""
+ zh: """行为"""
+ }
+ }
+
+ clientid {
+ desc {
+ en: """ClientID"""
+ zh: """客户端标识符"""
+ }
+ label {
+ en: """clientid"""
+ zh: """客户端标识符"""
+ }
+ }
+
+ username {
+ desc {
+ en: """Username"""
+ zh: """用户名"""
+ }
+ label {
+ en: """username"""
+ zh: """用户名"""
+ }
+ }
+}
diff --git a/apps/emqx_authz/i18n/emqx_authz_api_schema_i18n.conf b/apps/emqx_authz/i18n/emqx_authz_api_schema_i18n.conf
new file mode 100644
index 000000000..28fae341d
--- /dev/null
+++ b/apps/emqx_authz/i18n/emqx_authz_api_schema_i18n.conf
@@ -0,0 +1,188 @@
+emqx_authz_api_schema {
+ enable {
+ desc {
+ en: """Set to true or false to disable this ACL provider"""
+ zh: """设为 true 或 false 以启用或禁用此访问控制数据源"""
+ }
+ label {
+ en: """enable"""
+ zh: """enable"""
+ }
+ }
+
+ type {
+ desc {
+ en: """Backend type."""
+ zh: """数据后端类型"""
+ }
+ label {
+ en: """type"""
+ zh: """type"""
+ }
+ }
+
+#==== authz_file
+
+ rules {
+ desc {
+ en: """Authorization static file rules."""
+ zh: """静态鉴权文件规则"""
+ }
+ label {
+ en: """rules"""
+ zh: """规则"""
+ }
+ }
+
+#==== authz_http
+
+ method {
+ desc {
+ en: """HTTP method."""
+ zh: """HTTP 请求方法"""
+ }
+ label {
+ en: """method"""
+ zh: """method"""
+ }
+ }
+
+ url {
+ desc {
+ en: """URL of the auth server."""
+ zh: """认证服务器 URL"""
+ }
+ label {
+ en: """url"""
+ zh: """url"""
+ }
+ }
+
+ headers {
+ desc {
+ en: """List of HTTP headers."""
+ zh: """"""
+ }
+ label {
+ en: """headers"""
+ zh: """请求头"""
+ }
+ }
+
+ headers_no_content_type {
+ desc {
+ en: """List of HTTP headers (without `content_type`)."""
+ zh: """"""
+ }
+ label {
+ en: """headers_no_content_type"""
+ zh: """请求头(无 content-type)"""
+ }
+ }
+
+ body {
+ desc {
+ en: """HTTP request body."""
+ zh: """HTTP 请求体"""
+ }
+ label {
+ en: """body"""
+ zh: """请求体"""
+ }
+ }
+
+ request_timeout {
+ desc {
+ en: """Request timeout."""
+ zh: """请求超时时间"""
+ }
+ label {
+ en: """request_timeout"""
+ zh: """请求超时"""
+ }
+ }
+
+#==== authz_mnesia
+
+# only common fields(`enable` and `type`)
+
+#==== authz_mongo
+
+ collection {
+ desc {
+ en: """`MongoDB` collection containing the authorization data."""
+ zh: """`MongoDB` 鉴权数据集"""
+ }
+ label {
+ en: """collection"""
+ zh: """数据集"""
+ }
+ }
+
+ selector {
+ desc {
+ en: """
+Statement that is executed during the authorize process.
+Commands can support following wildcards:\n
+ - `${username}`: substituted with client's username\n
+ - `${clientid}`: substituted with the clientid
+"""
+ zh: """
+鉴权过程中所使用的查询命令。
+查询命令支持如下占位符:
+- `${username}`: 代替客户端的用户名
+- `${clientid}`: 代替客户端的客户端标识符"""
+ }
+ label {
+ en: """selector"""
+ zh: """selector"""
+ }
+ }
+
+#==== authz_mysql
+
+# `query`, is common field
+
+#==== authz_pgsql
+
+# `query`, is common field
+
+#==== authz_redis
+
+ cmd {
+ desc {
+ en: """Database query used to retrieve authorization data."""
+ zh: """访问控制数据查询命令"""
+ }
+ label {
+ en: """cmd"""
+ zh: """查询命令"""
+ }
+ }
+
+#==== common field for DBs (except mongodb and redis)
+
+ query {
+ desc {
+ en: """Database query used to retrieve authorization data."""
+ zh: """访问控制数据查询语句"""
+ }
+ label {
+ en: """query"""
+ zh: """查询语句"""
+ }
+ }
+
+#==== fields
+
+ position {
+ desc {
+ en: """Where to place the source"""
+ zh: """认证数据源位置"""
+ }
+ label {
+ en: """position"""
+ zh: """位置"""
+ }
+ }
+}
diff --git a/apps/emqx_authz/i18n/emqx_authz_api_settings_i18n.conf b/apps/emqx_authz/i18n/emqx_authz_api_settings_i18n.conf
new file mode 100644
index 000000000..72249e236
--- /dev/null
+++ b/apps/emqx_authz/i18n/emqx_authz_api_settings_i18n.conf
@@ -0,0 +1,11 @@
+emqx_authz_api_settings {
+ authorization_settings_get {
+ en: """Get authorization settings"""
+ zh: """获取鉴权配置"""
+ }
+
+ authorization_settings_put {
+ en: """Update authorization settings"""
+ zh: """更新鉴权配置"""
+ }
+}
diff --git a/apps/emqx_authz/i18n/emqx_authz_api_sources_i18n.conf b/apps/emqx_authz/i18n/emqx_authz_api_sources_i18n.conf
new file mode 100644
index 000000000..6c3f59d93
--- /dev/null
+++ b/apps/emqx_authz/i18n/emqx_authz_api_sources_i18n.conf
@@ -0,0 +1,116 @@
+emqx_authz_api_sources {
+ authorization_sources_get {
+ desc {
+ en: """List all authorization sources"""
+ zh: """列出所有鉴权数据源"""
+ }
+ }
+
+ authorization_sources_post {
+ desc {
+ en: """Add a new source"""
+ zh: """添加鉴权数据源"""
+ }
+ }
+
+ authorization_sources_type_get {
+ desc {
+ en: """Get a authorization source"""
+ zh: """获取指定类型的鉴权数据源"""
+ }
+ }
+
+ authorization_sources_type_put {
+ desc {
+ en: """Update source"""
+ zh: """更新指定类型的鉴权数据源"""
+ }
+ }
+
+ authorization_sources_type_delete {
+ desc {
+ en: """Delete source"""
+ zh: """删除指定类型的鉴权数据源"""
+ }
+ }
+
+ authorization_sources_type_status_get {
+ desc {
+ en: """Get a authorization source"""
+ zh: """获取指定鉴权数据源的状态"""
+ }
+ }
+
+ authorization_sources_type_move_post {
+ desc {
+ en: """Change the exection order of sources"""
+ zh: """更新鉴权数据源的优先执行顺序"""
+ }
+ }
+
+ sources {
+ desc {
+ en: """Authorization source"""
+ zh: """鉴权数据源列表"""
+ }
+ label {
+ en: """sources"""
+ zh: """数据源列表"""
+ }
+ }
+
+ sources {
+ desc {
+ en: """Authorization sources"""
+ zh: """鉴权数据源列表"""
+ }
+ label {
+ en: """sources"""
+ zh: """数据源列表"""
+ }
+ }
+
+ source_config {
+ desc {
+ en: """Source config"""
+ zh: """数据源配置"""
+ }
+ label {
+ en: """source_config"""
+ zh: """数据源配置"""
+ }
+ }
+
+ source {
+ desc {
+ en: """Authorization source"""
+ zh: """鉴权数据源"""
+ }
+ label {
+ en: """source"""
+ zh: """数据源"""
+ }
+ }
+
+ source_config {
+ desc {
+ en: """Source config"""
+ zh: """数据源配置"""
+ }
+ label {
+ en: """source_config"""
+ zh: """数据源配置"""
+ }
+ }
+
+ source_type {
+ desc {
+ en: """Authorization type"""
+ zh: """数据源类型"""
+ }
+ label {
+ en: """source_type"""
+ zh: """数据源类型"""
+ }
+ }
+}
diff --git a/apps/emqx_authz/i18n/emqx_authz_schema_i18n.conf b/apps/emqx_authz/i18n/emqx_authz_schema_i18n.conf
new file mode 100644
index 000000000..39cc3abb2
--- /dev/null
+++ b/apps/emqx_authz/i18n/emqx_authz_schema_i18n.conf
@@ -0,0 +1,349 @@
+emqx_authz_schema {
+ sources {
+ desc {
+ en: """
+Authorization data sources.
+An array of authorization (ACL) data providers.
+It is designed as an array, not a hash-map, so the sources can be
+ordered to form a chain of access controls.
+
+When authorizing a 'publish' or 'subscribe' action, the configured
+sources are checked in order. When checking an ACL source,
+in case the client (identified by username or client ID) is not found,
+it moves on to the next source. And it stops immediately
+once an 'allow' or 'deny' decision is returned.
+
+If the client is not found in any of the sources,
+the default action configured in 'authorization.no_match' is applied.
+
+NOTE:
+The source elements are identified by their 'type'.
+It is NOT allowed to configure two or more sources of the same type.
+"""
+ zh: """"""
+ }
+ label {
+ en: """sources"""
+ zh: """"""
+ }
+ }
+
+ authorization {
+ desc {
+ en: """Configuration related to the client authorization."""
+ zh: """"""
+ }
+ label {
+ en: """authorization"""
+ zh: """授权"""
+ }
+ }
+
+ enable {
+ desc {
+ en: """Set to true or false to disable this ACL provider"""
+ zh: """设为 true 或 false 以启用或禁用此访问控制数据源"""
+ }
+ label {
+ en: """enable"""
+ zh: """enable"""
+ }
+ }
+
+ type {
+ desc {
+ en: """Backend type."""
+ zh: """数据后端类型"""
+ }
+ label {
+ en: """type"""
+ zh: """type"""
+ }
+ }
+
+#==== authz_file
+
+ file {
+ desc {
+ en: """Authorization using a static file."""
+ zh: """使用静态文件鉴权"""
+ }
+ label {
+ en: """file"""
+ zh: """文件"""
+ }
+ }
+
+ path {
+ desc {
+ en: """
+Path to the file which contains the ACL rules.
+If the file provisioned before starting EMQX node,
+it can be placed anywhere as long as EMQX has read access to it.
+
+In case the rule-set is created from EMQX dashboard or management API,
+the file will be placed in `authz` subdirectory inside EMQX's `data_dir`,
+and the new rules will override all rules from the old config file.
+"""
+ zh: """"""
+ }
+ label {
+ en: """path"""
+ zh: """path"""
+ }
+ }
+
+#==== authz_http
+
+ http_get {
+ desc {
+ en: """Authorization using an external HTTP server (via GET requests)."""
+ zh: """使用外部 HTTP 服务器鉴权(GET 请求)."""
+ }
+ label {
+ en: """http_get"""
+ zh: """http_get"""
+ }
+ }
+
+ http_post {
+ desc {
+ en: """Authorization using an external HTTP server (via POST requests)."""
+ zh: """使用外部 HTTP 服务器鉴权(POST 请求)."""
+ }
+ label {
+ en: """http_post"""
+ zh: """http_post"""
+ }
+ }
+
+ method {
+ desc {
+ en: """HTTP method."""
+ zh: """HTTP 请求方法"""
+ }
+ label {
+ en: """method"""
+ zh: """method"""
+ }
+ }
+
+ url {
+ desc {
+ en: """URL of the auth server."""
+ zh: """认证服务器 URL"""
+ }
+ label {
+ en: """url"""
+ zh: """url"""
+ }
+ }
+
+ headers {
+ desc {
+ en: """List of HTTP headers."""
+ zh: """"""
+ }
+ label {
+ en: """headers"""
+ zh: """请求头"""
+ }
+ }
+
+ headers_no_content_type {
+ desc {
+ en: """List of HTTP headers (without `content_type`)."""
+ zh: """"""
+ }
+ label {
+ en: """headers_no_content_type"""
+ zh: """请求头(无 content-type)"""
+ }
+ }
+
+ body {
+ desc {
+ en: """HTTP request body."""
+ zh: """HTTP 请求体"""
+ }
+ label {
+ en: """body"""
+ zh: """请求体"""
+ }
+ }
+
+ request_timeout {
+ desc {
+ en: """Request timeout."""
+ zh: """请求超时时间"""
+ }
+ label {
+ en: """request_timeout"""
+ zh: """请求超时"""
+ }
+ }
+
+#==== authz_mnesia
+
+ mnesia {
+ desc {
+ en: """Authorization using a built-in database (mnesia)."""
+ zh: """使用内部数据库鉴权 (mnesia)."""
+ }
+ label {
+ en: """mnesia"""
+ zh: """mnesia"""
+ }
+ }
+
+#==== authz_mongo
+
+ mongo_single {
+ desc {
+ en: """Authorization using a single MongoDB instance."""
+ zh: """使用 MongoDB 鉴权(单实例)"""
+ }
+ label {
+ en: """mongo_single"""
+ zh: """mongo_single"""
+ }
+ }
+
+ mongo_rs {
+ desc {
+ en: """Authorization using a MongoDB replica set."""
+ zh: """使用 MongoDB 鉴权(副本集模式)"""
+ }
+ label {
+ en: """mongo_rs"""
+ zh: """mongo_rs"""
+ }
+ }
+
+ mongo_sharded {
+ desc {
+ en: """Authorization using a sharded MongoDB cluster."""
+ zh: """使用 MongoDB 鉴权(分片集群模式)"""
+ }
+ label {
+ en: """mongo_sharded"""
+ zh: """mongo_sharded"""
+ }
+ }
+
+ collection {
+ desc {
+ en: """`MongoDB` collection containing the authorization data."""
+ zh: """`MongoDB` 鉴权数据集"""
+ }
+ label {
+ en: """collection"""
+ zh: """数据集"""
+ }
+ }
+
+ selector {
+ desc {
+ en: """
+Statement that is executed during the authorize process.
+Commands can support following wildcards:\n
+ - `${username}`: substituted with client's username\n
+ - `${clientid}`: substituted with the clientid
+"""
+ zh: """
+鉴权过程中所使用的查询命令。
+查询命令支持如下占位符:
+- `${username}`: 代替客户端的用户名
+- `${clientid}`: 代替客户端的客户端标识符"""
+ }
+ label {
+ en: """selector"""
+ zh: """selector"""
+ }
+ }
+
+#==== authz_mysql
+
+ mysql {
+ desc {
+ en: """Authorization using a MySQL database."""
+ zh: """使用 MySOL 数据库鉴权"""
+ }
+ label {
+ en: """mysql"""
+ zh: """mysql"""
+ }
+ }
+
+#==== authz_pgsql
+
+ postgresql {
+ desc {
+ en: """Authorization using a PostgreSQL database."""
+ zh: """使用 PostgreSQL 数据库鉴权"""
+ }
+ label {
+ en: """postgresql"""
+ zh: """postgresql"""
+ }
+ }
+
+#==== authz_redis
+
+ redis_single {
+ desc {
+ en: """Authorization using a single Redis instance."""
+ zh: """使用 Redis 鉴权(单实例)"""
+ }
+ label {
+ en: """redis_single"""
+ zh: """redis_single"""
+ }
+ }
+
+ redis_sentinel {
+ desc {
+ en: """Authorization using a Redis Sentinel."""
+ zh: """使用 Redis 鉴权(哨兵模式)"""
+ }
+ label {
+ en: """redis_sentinel"""
+ zh: """redis_sentinel"""
+ }
+ }
+
+ redis_cluster {
+ desc {
+ en: """Authorization using a Redis cluster."""
+ zh: """使用 Redis 鉴权(集群模式)"""
+ }
+ label {
+ en: """redis_cluster"""
+ zh: """redis_cluster"""
+ }
+ }
+
+ cmd {
+ desc {
+ en: """Database query used to retrieve authorization data."""
+ zh: """访问控制数据查查询命令"""
+ }
+ label {
+ en: """cmd"""
+ zh: """查询命令"""
+ }
+ }
+
+#==== common field for DBs (except redis)
+
+ query {
+ desc {
+ en: """Database query used to retrieve authorization data."""
+ zh: """访问控制数据查询语句/查询命令"""
+ }
+ label {
+ en: """query"""
+ zh: """查询语句"""
+ }
+ }
+}
diff --git a/apps/emqx_authz/src/emqx_authz_api_cache.erl b/apps/emqx_authz/src/emqx_authz_api_cache.erl
index e6d3b941c..bfcab823c 100644
--- a/apps/emqx_authz/src/emqx_authz_api_cache.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_cache.erl
@@ -18,6 +18,8 @@
-behaviour(minirest_api).
+-include_lib("hocon/include/hoconsc.hrl").
+
-export([
api_spec/0,
paths/0,
@@ -47,7 +49,7 @@ schema("/authorization/cache") ->
'operationId' => clean_cache,
delete =>
#{
- description => <<"Clean all authorization cache in the cluster.">>,
+ description => ?DESC(authorization_cache_delete),
responses =>
#{
204 => <<"No Content">>,
diff --git a/apps/emqx_authz/src/emqx_authz_api_mnesia.erl b/apps/emqx_authz/src/emqx_authz_api_mnesia.erl
index addb11584..452e34e5e 100644
--- a/apps/emqx_authz/src/emqx_authz_api_mnesia.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_mnesia.erl
@@ -20,7 +20,7 @@
-include("emqx_authz.hrl").
-include_lib("emqx/include/logger.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-import(hoconsc, [mk/1, mk/2, ref/1, ref/2, array/1, enum/1]).
@@ -87,7 +87,7 @@ schema("/authorization/sources/built_in_database/username") ->
get =>
#{
tags => [<<"authorization">>],
- description => <<"Show the list of record for username">>,
+ description => ?DESC(users_username_get),
parameters =>
[
ref(emqx_dashboard_swagger, page),
@@ -96,7 +96,7 @@ schema("/authorization/sources/built_in_database/username") ->
mk(binary(), #{
in => query,
required => false,
- desc => <<"Fuzzy search `username` as substring">>
+ desc => ?DESC(fuzzy_username)
})}
],
responses =>
@@ -110,7 +110,7 @@ schema("/authorization/sources/built_in_database/username") ->
post =>
#{
tags => [<<"authorization">>],
- description => <<"Add new records for username">>,
+ description => ?DESC(users_username_post),
'requestBody' => swagger_with_example(
{rules_for_username, ?TYPE_ARRAY},
{username, ?POST_ARRAY_EXAMPLE}
@@ -130,7 +130,7 @@ schema("/authorization/sources/built_in_database/clientid") ->
get =>
#{
tags => [<<"authorization">>],
- description => <<"Show the list of record for clientid">>,
+ description => ?DESC(users_clientid_get),
parameters =>
[
ref(emqx_dashboard_swagger, page),
@@ -141,7 +141,7 @@ schema("/authorization/sources/built_in_database/clientid") ->
#{
in => query,
required => false,
- desc => <<"Fuzzy search `clientid` as substring">>
+ desc => ?DESC(fuzzy_clientid)
}
)}
],
@@ -156,7 +156,7 @@ schema("/authorization/sources/built_in_database/clientid") ->
post =>
#{
tags => [<<"authorization">>],
- description => <<"Add new records for clientid">>,
+ description => ?DESC(users_clientid_post),
'requestBody' => swagger_with_example(
{rules_for_clientid, ?TYPE_ARRAY},
{clientid, ?POST_ARRAY_EXAMPLE}
@@ -176,7 +176,7 @@ schema("/authorization/sources/built_in_database/username/:username") ->
get =>
#{
tags => [<<"authorization">>],
- description => <<"Get record info for username">>,
+ description => ?DESC(user_username_get),
parameters => [ref(username)],
responses =>
#{
@@ -192,7 +192,7 @@ schema("/authorization/sources/built_in_database/username/:username") ->
put =>
#{
tags => [<<"authorization">>],
- description => <<"Set record for username">>,
+ description => ?DESC(user_username_put),
parameters => [ref(username)],
'requestBody' => swagger_with_example(
{rules_for_username, ?TYPE_REF},
@@ -209,7 +209,7 @@ schema("/authorization/sources/built_in_database/username/:username") ->
delete =>
#{
tags => [<<"authorization">>],
- description => <<"Delete one record for username">>,
+ description => ?DESC(user_username_delete),
parameters => [ref(username)],
responses =>
#{
@@ -229,7 +229,7 @@ schema("/authorization/sources/built_in_database/clientid/:clientid") ->
get =>
#{
tags => [<<"authorization">>],
- description => <<"Get record info for clientid">>,
+ description => ?DESC(user_clientid_get),
parameters => [ref(clientid)],
responses =>
#{
@@ -245,7 +245,7 @@ schema("/authorization/sources/built_in_database/clientid/:clientid") ->
put =>
#{
tags => [<<"authorization">>],
- description => <<"Set record for clientid">>,
+ description => ?DESC(user_clientid_put),
parameters => [ref(clientid)],
'requestBody' => swagger_with_example(
{rules_for_clientid, ?TYPE_REF},
@@ -262,7 +262,7 @@ schema("/authorization/sources/built_in_database/clientid/:clientid") ->
delete =>
#{
tags => [<<"authorization">>],
- description => <<"Delete one record for clientid">>,
+ description => ?DESC(user_clientid_delete),
parameters => [ref(clientid)],
responses =>
#{
@@ -282,17 +282,14 @@ schema("/authorization/sources/built_in_database/all") ->
get =>
#{
tags => [<<"authorization">>],
- description => <<"Show the list of rules for all">>,
+ description => ?DESC(rules_for_all_get),
responses =>
#{200 => swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE})}
},
post =>
#{
tags => [<<"authorization">>],
- description => <<
- "Create/Update the list of rules for all. "
- "Set a empty list to clean up rules"
- >>,
+ description => ?DESC(rules_for_all_post),
'requestBody' =>
swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE}),
responses =>
@@ -310,7 +307,7 @@ schema("/authorization/sources/built_in_database/purge-all") ->
delete =>
#{
tags => [<<"authorization">>],
- description => <<"Purge all records">>,
+ description => ?DESC(purge_all_delete),
responses =>
#{
204 => <<"Deleted">>,
@@ -328,7 +325,7 @@ fields(rule_item) ->
string(),
#{
required => true,
- desc => <<"Rule on specific topic">>,
+ desc => ?DESC(topic),
example => <<"test/topic/1">>
}
)},
@@ -336,7 +333,7 @@ fields(rule_item) ->
mk(
enum([allow, deny]),
#{
- desc => <<"Permission">>,
+ desc => ?DESC(permission),
required => true,
example => allow
}
@@ -345,9 +342,9 @@ fields(rule_item) ->
mk(
enum([publish, subscribe, all]),
#{
+ desc => ?DESC(action),
required => true,
- example => publish,
- desc => <<"Authorized action">>
+ example => publish
}
)}
];
@@ -359,7 +356,7 @@ fields(clientid) ->
#{
in => path,
required => true,
- desc => <<"ClientID">>,
+ desc => ?DESC(clientid),
example => <<"client1">>
}
)}
@@ -372,7 +369,7 @@ fields(username) ->
#{
in => path,
required => true,
- desc => <<"Username">>,
+ desc => ?DESC(username),
example => <<"user1">>
}
)}
diff --git a/apps/emqx_authz/src/emqx_authz_api_schema.erl b/apps/emqx_authz/src/emqx_authz_api_schema.erl
index 19c7e5cfd..a398479bd 100644
--- a/apps/emqx_authz/src/emqx_authz_api_schema.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_schema.erl
@@ -17,7 +17,7 @@
-module(emqx_authz_api_schema).
-include("emqx_authz.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-include_lib("emqx_connector/include/emqx_connector.hrl").
-import(hoconsc, [mk/2, enum/1]).
@@ -32,14 +32,26 @@
%% Hocon Schema
%%------------------------------------------------------------------------------
+fields(file) ->
+ authz_common_fields(file) ++
+ [
+ {rules, #{
+ type => binary(),
+ required => true,
+ example =>
+ <<"{allow,{username,\"^dashboard?\"},", "subscribe,[\"$SYS/#\"]}.\n",
+ "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>,
+ desc => ?DESC(rules)
+ }}
+ ];
fields(http_get) ->
[
- {method, #{type => get, default => get, required => true}},
+ {method, #{type => get, default => get, required => true, desc => ?DESC(method)}},
{headers, fun headers_no_content_type/1}
] ++ authz_http_common_fields();
fields(http_post) ->
[
- {method, #{type => post, default => post, required => true}},
+ {method, #{type => post, default => post, required => true, desc => ?DESC(method)}},
{headers, fun headers/1}
] ++ authz_http_common_fields();
fields(built_in_database) ->
@@ -55,11 +67,11 @@ fields(mongo_sharded) ->
emqx_connector_mongo:fields(sharded);
fields(mysql) ->
authz_common_fields(mysql) ++
- [{query, mk(binary(), #{required => true})}] ++
+ [{query, query()}] ++
proplists:delete(prepare_statement, emqx_connector_mysql:fields(config));
fields(postgresql) ->
authz_common_fields(postgresql) ++
- [{query, mk(binary(), #{required => true})}] ++
+ [{query, query()}] ++
proplists:delete(prepare_statement, emqx_connector_pgsql:fields(config));
fields(redis_single) ->
authz_redis_common_fields() ++
@@ -70,24 +82,13 @@ fields(redis_sentinel) ->
fields(redis_cluster) ->
authz_redis_common_fields() ++
emqx_connector_redis:fields(cluster);
-fields(file) ->
- authz_common_fields(file) ++
- [
- {rules, #{
- type => binary(),
- required => true,
- example =>
- <<"{allow,{username,\"^dashboard?\"},", "subscribe,[\"$SYS/#\"]}.\n",
- "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
- }}
- ];
fields(position) ->
[
{position,
mk(
string(),
#{
- desc => <<"Where to place the source">>,
+ desc => ?DESC(position),
required => true,
in => body
}
@@ -102,7 +103,8 @@ authz_http_common_fields() ->
[
{url, fun url/1},
{body, map([{fuzzy, term(), binary()}])},
- {request_timeout, mk_duration("Request timeout", #{default => "30s"})}
+ {request_timeout,
+ mk_duration("Request timeout", #{default => "30s", desc => ?DESC(request_timeout)})}
] ++
maps:to_list(
maps:without(
@@ -117,12 +119,13 @@ authz_http_common_fields() ->
url(type) -> binary();
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
url(required) -> true;
+url(desc) -> ?DESC(?FUNCTION_NAME);
url(_) -> undefined.
headers(type) ->
map();
headers(desc) ->
- "List of HTTP headers.";
+ ?DESC(?FUNCTION_NAME);
headers(converter) ->
fun(Headers) ->
maps:merge(default_headers(), transform_header_name(Headers))
@@ -135,7 +138,7 @@ headers(_) ->
headers_no_content_type(type) ->
map();
headers_no_content_type(desc) ->
- "List of HTTP headers.";
+ ?DESC(?FUNCTION_NAME);
headers_no_content_type(converter) ->
fun(Headers) ->
maps:merge(default_headers_no_content_type(), transform_header_name(Headers))
@@ -182,17 +185,14 @@ authz_mongo_common_fields() ->
].
collection(type) -> binary();
-collection(desc) -> "Collection used to store authentication data.";
+collection(desc) -> ?DESC(?FUNCTION_NAME);
collection(required) -> true;
collection(_) -> undefined.
selector(type) ->
map();
selector(desc) ->
- "Statement that is executed during the authentication process. "
- "Commands can support following wildcards:\n"
- " - `${username}`: substituted with client's username\n"
- " - `${clientid}`: substituted with the clientid";
+ ?DESC(?FUNCTION_NAME);
selector(_) ->
undefined.
@@ -205,6 +205,7 @@ authz_redis_common_fields() ->
{cmd,
mk(binary(), #{
required => true,
+ desc => ?DESC(cmd),
example => <<"HGETALL mqtt_authz">>
})}
].
@@ -216,18 +217,35 @@ authz_common_fields(Type) when is_atom(Type) ->
[
{enable, fun enable/1},
{type, #{
- type => enum([Type]),
+ type => Type,
default => Type,
required => true,
+ desc => ?DESC(type),
in => body
}}
].
enable(type) -> boolean();
enable(default) -> true;
-enable(desc) -> "Set to false to disable this auth provider";
+enable(desc) -> ?DESC(?FUNCTION_NAME);
enable(_) -> undefined.
+%%------------------------------------------------------------------------------
+%% Authz DB query
+
+query() ->
+ #{
+ type => binary(),
+ desc => ?DESC(query),
+ required => true,
+ validator => fun(S) ->
+ case size(S) > 0 of
+ true -> ok;
+ _ -> {error, "Request query"}
+ end
+ end
+ }.
+
%%------------------------------------------------------------------------------
%% Internal funcs
diff --git a/apps/emqx_authz/src/emqx_authz_api_settings.erl b/apps/emqx_authz/src/emqx_authz_api_settings.erl
index cd00d131e..c5409b16b 100644
--- a/apps/emqx_authz/src/emqx_authz_api_settings.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_settings.erl
@@ -18,6 +18,8 @@
-behaviour(minirest_api).
+-include_lib("hocon/include/hoconsc.hrl").
+
-import(hoconsc, [mk/1, ref/2]).
-export([
@@ -45,13 +47,13 @@ schema("/authorization/settings") ->
'operationId' => settings,
get =>
#{
- description => <<"Get authorization settings">>,
+ description => ?DESC(authorization_settings_get),
responses =>
#{200 => ref_authz_schema()}
},
put =>
#{
- description => <<"Update authorization settings">>,
+ description => ?DESC(authorization_settings_put),
'requestBody' => ref_authz_schema(),
responses =>
#{
diff --git a/apps/emqx_authz/src/emqx_authz_api_sources.erl b/apps/emqx_authz/src/emqx_authz_api_sources.erl
index 5529bbfca..c05f9f133 100644
--- a/apps/emqx_authz/src/emqx_authz_api_sources.erl
+++ b/apps/emqx_authz/src/emqx_authz_api_sources.erl
@@ -18,9 +18,9 @@
-behaviour(minirest_api).
--include_lib("typerefl/include/types.hrl").
-include("emqx_authz.hrl").
-include_lib("emqx/include/logger.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-import(hoconsc, [mk/1, mk/2, ref/2, array/1, enum/1]).
@@ -63,27 +63,26 @@ paths() ->
%%--------------------------------------------------------------------
%% Schema for each URI
%%--------------------------------------------------------------------
-
schema("/authorization/sources") ->
#{
'operationId' => sources,
get =>
#{
- description => <<"List all authorization sources">>,
+ description => ?DESC(authorization_sources_get),
responses =>
#{
200 => mk(
array(hoconsc:union(authz_sources_type_refs())),
- #{desc => <<"Authorization source">>}
+ #{desc => ?DESC(sources)}
)
}
},
post =>
#{
- description => <<"Add a new source">>,
+ description => ?DESC(authorization_sources_post),
'requestBody' => mk(
hoconsc:union(authz_sources_type_refs()),
- #{desc => <<"Source config">>}
+ #{desc => ?DESC(source_config)}
),
responses =>
#{
@@ -100,20 +99,20 @@ schema("/authorization/sources/:type") ->
'operationId' => source,
get =>
#{
- description => <<"Get a authorization source">>,
+ description => ?DESC(authorization_sources_type_get),
parameters => parameters_field(),
responses =>
#{
200 => mk(
hoconsc:union(authz_sources_type_refs()),
- #{desc => <<"Authorization source">>}
+ #{desc => ?DESC(source)}
),
404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
}
},
put =>
#{
- description => <<"Update source">>,
+ description => ?DESC(authorization_sources_type_put),
parameters => parameters_field(),
'requestBody' => mk(hoconsc:union(authz_sources_type_refs())),
responses =>
@@ -124,7 +123,7 @@ schema("/authorization/sources/:type") ->
},
delete =>
#{
- description => <<"Delete source">>,
+ description => ?DESC(authorization_sources_type_delete),
parameters => parameters_field(),
responses =>
#{
@@ -138,7 +137,7 @@ schema("/authorization/sources/:type/status") ->
'operationId' => source_status,
get =>
#{
- description => <<"Get a authorization source">>,
+ description => ?DESC(authorization_sources_type_status_get),
parameters => parameters_field(),
responses =>
#{
@@ -158,7 +157,7 @@ schema("/authorization/sources/:type/move") ->
'operationId' => move_source,
post =>
#{
- description => <<"Change the order of sources">>,
+ description => ?DESC(authorization_sources_type_move_post),
parameters => parameters_field(),
'requestBody' =>
emqx_dashboard_swagger:schema_with_examples(
@@ -508,7 +507,7 @@ parameters_field() ->
{type,
mk(
enum(?API_SCHEMA_MODULE:authz_sources_types(simple)),
- #{in => path, desc => <<"Authorization type">>}
+ #{in => path, desc => ?DESC(source_type)}
)}
].
diff --git a/apps/emqx_authz/src/emqx_authz_mnesia.erl b/apps/emqx_authz/src/emqx_authz_mnesia.erl
index 0bb85b96a..267338493 100644
--- a/apps/emqx_authz/src/emqx_authz_mnesia.erl
+++ b/apps/emqx_authz/src/emqx_authz_mnesia.erl
@@ -99,7 +99,7 @@ authorize(
} = Client,
PubSub,
Topic,
- #{type := 'built_in_database'}
+ #{type := built_in_database}
) ->
Rules =
case mnesia:dirty_read(?ACL_TABLE, {?ACL_TABLE_CLIENTID, Clientid}) of
diff --git a/apps/emqx_authz/src/emqx_authz_schema.erl b/apps/emqx_authz/src/emqx_authz_schema.erl
index 011b1585d..042568ce4 100644
--- a/apps/emqx_authz/src/emqx_authz_schema.erl
+++ b/apps/emqx_authz/src/emqx_authz_schema.erl
@@ -17,7 +17,7 @@
-module(emqx_authz_schema).
-include("emqx_authz.hrl").
--include_lib("typerefl/include/types.hrl").
+-include_lib("hocon/include/hoconsc.hrl").
-include_lib("emqx_connector/include/emqx_connector.hrl").
-reflect_type([
@@ -71,134 +71,104 @@ fields("authorization") ->
]
),
default => [],
- desc =>
- "\n"
- "Authorization data sources.
\n"
- "An array of authorization (ACL) data providers.\n"
- "It is designed as an array, not a hash-map, so the sources can be\n"
- "ordered to form a chain of access controls.
\n"
- "\n"
- "When authorizing a 'publish' or 'subscribe' action, the configured\n"
- "sources are checked in order. When checking an ACL source,\n"
- "in case the client (identified by username or client ID) is not found,\n"
- "it moves on to the next source. And it stops immediately\n"
- "once an 'allow' or 'deny' decision is returned.
\n"
- "\n"
- "If the client is not found in any of the sources,\n"
- "the default action configured in 'authorization.no_match' is applied.
\n"
- "\n"
- "NOTE:\n"
- "The source elements are identified by their 'type'.\n"
- "It is NOT allowed to configure two or more sources of the same type.\n"
+ desc => ?DESC(sources)
}}
];
fields(file) ->
- [
- {type, #{type => file, required => true, desc => "Backend type."}},
- {enable, #{
- type => boolean(),
- default => true,
- desc => "Enable this backend."
- }},
- {path, #{
- type => string(),
- required => true,
- desc =>
- "\n"
- "Path to the file which contains the ACL rules.
\n"
- "If the file provisioned before starting EMQX node,\n"
- "it can be placed anywhere as long as EMQX has read access to it.\n"
- "\n"
- "In case the rule-set is created from EMQX dashboard or management API,\n"
- "the file will be placed in `authz` subdirectory inside EMQX's `data_dir`,\n"
- "and the new rules will override all rules from the old config file.\n"
- }}
- ];
+ authz_common_fields(file) ++
+ [{path, #{type => string(), required => true, desc => ?DESC(path)}}];
fields(http_get) ->
- [
- {method, #{type => get, default => get, required => true, desc => "HTTP method."}},
- {headers, fun headers_no_content_type/1}
- ] ++ http_common_fields();
+ authz_common_fields(http) ++
+ http_common_fields() ++
+ [
+ {method, #{type => get, default => get, required => true, desc => ?DESC(method)}},
+ {headers, fun headers_no_content_type/1}
+ ];
fields(http_post) ->
- [
- {method, #{type => post, default => post, required => true, desc => "HTTP method."}},
- {headers, fun headers/1}
- ] ++ http_common_fields();
+ authz_common_fields(http) ++
+ http_common_fields() ++
+ [
+ {method, #{type => post, default => post, required => true, desc => ?DESC(method)}},
+ {headers, fun headers/1}
+ ];
fields(mnesia) ->
- [
- {type, #{type => 'built_in_database', required => true, desc => "Backend type."}},
- {enable, #{
- type => boolean(),
- default => true,
- desc => "Enable this backend."
- }}
- ];
+ authz_common_fields(built_in_database);
fields(mongo_single) ->
- mongo_common_fields() ++ emqx_connector_mongo:fields(single);
+ authz_common_fields(mongodb) ++
+ mongo_common_fields() ++
+ emqx_connector_mongo:fields(single);
fields(mongo_rs) ->
- mongo_common_fields() ++ emqx_connector_mongo:fields(rs);
+ authz_common_fields(mongodb) ++
+ mongo_common_fields() ++
+ emqx_connector_mongo:fields(rs);
fields(mongo_sharded) ->
- mongo_common_fields() ++ emqx_connector_mongo:fields(sharded);
+ authz_common_fields(mongodb) ++
+ mongo_common_fields() ++
+ emqx_connector_mongo:fields(sharded);
fields(mysql) ->
- connector_fields(mysql) ++
+ authz_common_fields(mysql) ++
+ connector_fields(mysql) ++
[{query, query()}];
fields(postgresql) ->
- [
- {query, query()},
- {type, #{type => postgresql, required => true, desc => "Backend type."}},
- {enable, #{
- type => boolean(),
- desc => "Enable this backend.",
- default => true
- }}
- ] ++ emqx_connector_pgsql:fields(config);
+ authz_common_fields(postgresql) ++
+ emqx_connector_pgsql:fields(config) ++
+ [{query, query()}];
fields(redis_single) ->
- connector_fields(redis, single) ++
- [{cmd, query()}];
+ authz_common_fields(redis) ++
+ connector_fields(redis, single) ++
+ [{cmd, cmd()}];
fields(redis_sentinel) ->
- connector_fields(redis, sentinel) ++
- [{cmd, query()}];
+ authz_common_fields(redis) ++
+ connector_fields(redis, sentinel) ++
+ [{cmd, cmd()}];
fields(redis_cluster) ->
- connector_fields(redis, cluster) ++
- [{cmd, query()}].
+ authz_common_fields(redis) ++
+ connector_fields(redis, cluster) ++
+ [{cmd, cmd()}].
-desc("authorization") ->
- "Configuration related to the client authorization.";
+desc(?CONF_NS) ->
+ ?DESC(?CONF_NS);
desc(file) ->
- "Authorization using a static file.";
+ ?DESC(file);
desc(http_get) ->
- "Authorization using an external HTTP server (via GET requests).";
+ ?DESC(http_get);
desc(http_post) ->
- "Authorization using an external HTTP server (via POST requests).";
+ ?DESC(http_post);
desc(mnesia) ->
- "Authorization using a built-in database (mnesia).";
+ ?DESC(mnesia);
desc(mongo_single) ->
- "Authorization using a single MongoDB instance.";
+ ?DESC(mongo_single);
desc(mongo_rs) ->
- "Authorization using a MongoDB replica set.";
+ ?DESC(mongo_rs);
desc(mongo_sharded) ->
- "Authorization using a sharded MongoDB cluster.";
+ ?DESC(mongo_sharded);
desc(mysql) ->
- "Authorization using a MySQL database.";
+ ?DESC(mysql);
desc(postgresql) ->
- "Authorization using a PostgreSQL database.";
+ ?DESC(postgresql);
desc(redis_single) ->
- "Authorization using a single Redis instance.";
+ ?DESC(redis_single);
desc(redis_sentinel) ->
- "Authorization using a Redis Sentinel.";
+ ?DESC(redis_sentinel);
desc(redis_cluster) ->
- "Authorization using a Redis cluster.";
+ ?DESC(redis_cluster);
desc(_) ->
undefined.
+authz_common_fields(Type) ->
+ [
+ {type, #{type => Type, required => true, desc => ?DESC(type)}},
+ {enable, #{type => boolean(), default => true, desc => ?DESC(enable)}}
+ ].
+
http_common_fields() ->
[
{url, fun url/1},
{request_timeout,
emqx_schema:mk_duration("Request timeout", #{
- default => "30s", desc => "Request timeout."
+ default => "30s", desc => ?DESC(request_timeout)
})},
- {body, #{type => map(), required => false, desc => "HTTP request body."}}
+ {body, #{type => map(), required => false, desc => ?DESC(body)}}
] ++
maps:to_list(
maps:without(
@@ -215,18 +185,12 @@ mongo_common_fields() ->
{collection, #{
type => atom(),
required => true,
- desc => "`MongoDB` collection containing the authorization data."
+ desc => ?DESC(collection)
}},
{selector, #{
type => map(),
required => true,
- desc => "MQL query used to select the authorization record."
- }},
- {type, #{type => mongodb, required => true, desc => "Database backend."}},
- {enable, #{
- type => boolean(),
- default => true,
- desc => "Enable or disable the backend."
+ desc => ?DESC(selector)
}}
].
@@ -238,7 +202,7 @@ validations() ->
headers(type) ->
list({binary(), binary()});
headers(desc) ->
- "List of HTTP headers.";
+ ?DESC(?FUNCTION_NAME);
headers(converter) ->
fun(Headers) ->
maps:to_list(maps:merge(default_headers(), transform_header_name(Headers)))
@@ -251,7 +215,7 @@ headers(_) ->
headers_no_content_type(type) ->
list({binary(), binary()});
headers_no_content_type(desc) ->
- "List of HTTP headers.";
+ ?DESC(?FUNCTION_NAME);
headers_no_content_type(converter) ->
fun(Headers) ->
maps:to_list(maps:merge(default_headers_no_content_type(), transform_header_name(Headers)))
@@ -269,7 +233,7 @@ headers_no_content_type(_) ->
undefined.
url(type) -> binary();
-url(desc) -> "URL of the auth server.";
+url(desc) -> ?DESC(?FUNCTION_NAME);
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
url(required) -> true;
url(_) -> undefined.
@@ -328,7 +292,20 @@ union_array(Item) when is_list(Item) ->
query() ->
#{
type => binary(),
- desc => "Database query used to retrieve authorization data.",
+ desc => ?DESC(query),
+ required => true,
+ validator => fun(S) ->
+ case size(S) > 0 of
+ true -> ok;
+ _ -> {error, "Request query"}
+ end
+ end
+ }.
+
+cmd() ->
+ #{
+ type => binary(),
+ desc => ?DESC(cmd),
required => true,
validator => fun(S) ->
case size(S) > 0 of
@@ -351,14 +328,7 @@ connector_fields(DB, Fields) ->
error:Reason ->
erlang:error(Reason)
end,
- [
- {type, #{type => DB, desc => "Database backend."}},
- {enable, #{
- type => boolean(),
- default => true,
- desc => "Enable or disable the backend."
- }}
- ] ++ erlang:apply(Mod, fields, [Fields]).
+ erlang:apply(Mod, fields, [Fields]).
to_list(A) when is_atom(A) ->
atom_to_list(A);