Authz api mnesia refactor (#6082)
* refactor(authz): macro move to hrl and rename, add meta example * refactor(authz_api_mnesia): use config schemas for request validations * chore(authz_api_mnesia): suite module typerefl fit * chore(style): authz_api_mnesia style format
This commit is contained in:
parent
3c1ae385ee
commit
a65597e302
|
@ -67,3 +67,52 @@
|
|||
-define(AUTHZ_METRICS(K), ?METRICS(authz_metrics, K)).
|
||||
|
||||
-define(CONF_KEY_PATH, [authorization, sources]).
|
||||
|
||||
-define(USERNAME_RULES_EXAMPLE, #{username => user1,
|
||||
rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(CLIENTID_RULES_EXAMPLE, #{clientid => client1,
|
||||
rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(ALL_RULES_EXAMPLE, #{rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(META_EXAMPLE, #{ page => 1
|
||||
, limit => 100
|
||||
, count => 1
|
||||
}).
|
||||
|
|
|
@ -21,492 +21,252 @@
|
|||
-include("emqx_authz.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
|
||||
-define(EXAMPLE_USERNAME, #{username => user1,
|
||||
rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(EXAMPLE_CLIENTID, #{clientid => client1,
|
||||
rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(EXAMPLE_ALL , #{rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(FORMAT_USERNAME_FUN, {?MODULE, format_by_username}).
|
||||
-define(FORMAT_CLIENTID_FUN, {?MODULE, format_by_clientid}).
|
||||
|
||||
|
||||
-export([ api_spec/0
|
||||
, purge/2
|
||||
, users/2
|
||||
, user/2
|
||||
, paths/0
|
||||
, schema/1
|
||||
, fields/1
|
||||
]).
|
||||
|
||||
%% operation funs
|
||||
-export([ users/2
|
||||
, clients/2
|
||||
, user/2
|
||||
, client/2
|
||||
, all/2
|
||||
, purge/2
|
||||
]).
|
||||
|
||||
-export([ format_by_username/1
|
||||
, format_by_clientid/1]).
|
||||
|
||||
-define(BAD_REQUEST, 'BAD_REQUEST').
|
||||
-define(NOT_FOUND, 'NOT_FOUND').
|
||||
|
||||
-define(TYPE_REF, ref).
|
||||
-define(TYPE_ARRAY, array).
|
||||
-define(PAGE_QUERY_EXAMPLE, example_in_data).
|
||||
-define(PUT_MAP_EXAMPLE, in_put_requestBody).
|
||||
-define(POST_ARRAY_EXAMPLE, in_post_requestBody).
|
||||
|
||||
api_spec() ->
|
||||
{[ purge_api()
|
||||
, users_api()
|
||||
, user_api()
|
||||
, clients_api()
|
||||
, client_api()
|
||||
, all_api()
|
||||
], definitions()}.
|
||||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||
|
||||
definitions() ->
|
||||
Rules = #{
|
||||
type => array,
|
||||
items => #{
|
||||
type => object,
|
||||
required => [topic, permission, action],
|
||||
properties => #{
|
||||
topic => #{
|
||||
type => string,
|
||||
example => <<"test/topic/1">>
|
||||
},
|
||||
permission => #{
|
||||
type => string,
|
||||
enum => [<<"allow">>, <<"deny">>],
|
||||
example => <<"allow">>
|
||||
},
|
||||
action => #{
|
||||
type => string,
|
||||
enum => [<<"publish">>, <<"subscribe">>, <<"all">>],
|
||||
example => <<"publish">>
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Username = #{
|
||||
type => object,
|
||||
required => [username, rules],
|
||||
properties => #{
|
||||
username => #{
|
||||
type => string,
|
||||
example => <<"username">>
|
||||
},
|
||||
rules => minirest:ref(<<"rules">>)
|
||||
}
|
||||
},
|
||||
Clientid = #{
|
||||
type => object,
|
||||
required => [clientid, rules],
|
||||
properties => #{
|
||||
clientid => #{
|
||||
type => string,
|
||||
example => <<"clientid">>
|
||||
},
|
||||
rules => minirest:ref(<<"rules">>)
|
||||
}
|
||||
},
|
||||
ALL = #{
|
||||
type => object,
|
||||
required => [rules],
|
||||
properties => #{
|
||||
rules => minirest:ref(<<"rules">>)
|
||||
}
|
||||
},
|
||||
[ #{<<"rules">> => Rules}
|
||||
, #{<<"username">> => Username}
|
||||
, #{<<"clientid">> => Clientid}
|
||||
, #{<<"all">> => ALL}
|
||||
].
|
||||
paths() ->
|
||||
[ "/authorization/sources/built-in-database/username"
|
||||
, "/authorization/sources/built-in-database/clientid"
|
||||
, "/authorization/sources/built-in-database/username/:username"
|
||||
, "/authorization/sources/built-in-database/clientid/:clientid"
|
||||
, "/authorization/sources/built-in-database/all"
|
||||
, "/authorization/sources/built-in-database/purge-all"].
|
||||
|
||||
users_api() ->
|
||||
Metadata = #{
|
||||
%%--------------------------------------------------------------------
|
||||
%% Schema for each URI
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
schema("/authorization/sources/built-in-database/username") ->
|
||||
#{
|
||||
'operationId' => users,
|
||||
get => #{
|
||||
description => "Show the list of record for username",
|
||||
parameters => [
|
||||
#{
|
||||
name => page,
|
||||
in => query,
|
||||
required => false,
|
||||
description => <<"Page Index">>,
|
||||
schema => #{type => integer}
|
||||
},
|
||||
#{
|
||||
name => limit,
|
||||
in => query,
|
||||
required => false,
|
||||
description => <<"Page limit">>,
|
||||
schema => #{type => integer}
|
||||
}
|
||||
],
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Show the list of record for username">>,
|
||||
parameters => [ hoconsc:ref(emqx_dashboard_swagger, page)
|
||||
, hoconsc:ref(emqx_dashboard_swagger, limit)],
|
||||
responses => #{
|
||||
<<"200">> => #{
|
||||
description => <<"OK">>,
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => #{
|
||||
type => array,
|
||||
items => minirest:ref(<<"username">>)
|
||||
},
|
||||
examples => #{
|
||||
username => #{
|
||||
summary => <<"Username">>,
|
||||
value => jsx:encode([?EXAMPLE_USERNAME])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
200 => swagger_with_example( {username_response_data, ?TYPE_REF}
|
||||
, {username, ?PAGE_QUERY_EXAMPLE})
|
||||
}
|
||||
},
|
||||
post => #{
|
||||
description => "Add new records for username",
|
||||
requestBody => #{
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => #{
|
||||
type => array,
|
||||
items => #{
|
||||
oneOf => [ minirest:ref(<<"username">>)
|
||||
]
|
||||
}
|
||||
},
|
||||
examples => #{
|
||||
username => #{
|
||||
summary => <<"Username">>,
|
||||
value => jsx:encode([?EXAMPLE_USERNAME])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Add new records for username">>,
|
||||
'requestBody' => swagger_with_example( {rules_for_username, ?TYPE_ARRAY}
|
||||
, {username, ?POST_ARRAY_EXAMPLE}),
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"Created">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"Created">>,
|
||||
400 => emqx_dashboard_swagger:error_codes( [?BAD_REQUEST]
|
||||
, <<"Bad username or bad rule schema">>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{"/authorization/sources/built-in-database/username", Metadata, users}.
|
||||
|
||||
clients_api() ->
|
||||
Metadata = #{
|
||||
};
|
||||
schema("/authorization/sources/built-in-database/clientid") ->
|
||||
#{
|
||||
'operationId' => clients,
|
||||
get => #{
|
||||
description => "Show the list of record for clientid",
|
||||
parameters => [
|
||||
#{
|
||||
name => page,
|
||||
in => query,
|
||||
required => false,
|
||||
description => <<"Page Index">>,
|
||||
schema => #{type => integer}
|
||||
},
|
||||
#{
|
||||
name => limit,
|
||||
in => query,
|
||||
required => false,
|
||||
description => <<"Page limit">>,
|
||||
schema => #{type => integer}
|
||||
}
|
||||
],
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Show the list of record for clientid">>,
|
||||
parameters => [ hoconsc:ref(emqx_dashboard_swagger, page)
|
||||
, hoconsc:ref(emqx_dashboard_swagger, limit)],
|
||||
responses => #{
|
||||
<<"200">> => #{
|
||||
description => <<"OK">>,
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => #{
|
||||
type => array,
|
||||
items => minirest:ref(<<"clientid">>)
|
||||
},
|
||||
examples => #{
|
||||
clientid => #{
|
||||
summary => <<"Clientid">>,
|
||||
value => jsx:encode([?EXAMPLE_CLIENTID])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
200 => swagger_with_example( {clientid_response_data, ?TYPE_REF}
|
||||
, {clientid, ?PAGE_QUERY_EXAMPLE})
|
||||
}
|
||||
},
|
||||
post => #{
|
||||
description => "Add new records for clientid",
|
||||
requestBody => #{
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => #{
|
||||
type => array,
|
||||
items => #{
|
||||
oneOf => [ minirest:ref(<<"clientid">>)
|
||||
]
|
||||
}
|
||||
},
|
||||
examples => #{
|
||||
clientid => #{
|
||||
summary => <<"Clientid">>,
|
||||
value => jsx:encode([?EXAMPLE_CLIENTID])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Add new records for clientid">>,
|
||||
'requestBody' => swagger_with_example( {rules_for_clientid, ?TYPE_ARRAY}
|
||||
, {clientid, ?POST_ARRAY_EXAMPLE}),
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"Created">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"Created">>,
|
||||
400 => emqx_dashboard_swagger:error_codes( [?BAD_REQUEST]
|
||||
, <<"Bad clientid or bad rule schema">>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{"/authorization/sources/built-in-database/clientid", Metadata, clients}.
|
||||
|
||||
user_api() ->
|
||||
Metadata = #{
|
||||
};
|
||||
schema("/authorization/sources/built-in-database/username/:username") ->
|
||||
#{
|
||||
'operationId' => user,
|
||||
get => #{
|
||||
description => "Get record info for username",
|
||||
parameters => [
|
||||
#{
|
||||
name => username,
|
||||
in => path,
|
||||
schema => #{
|
||||
type => string
|
||||
},
|
||||
required => true
|
||||
}
|
||||
],
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Get record info for username">>,
|
||||
parameters => [hoconsc:ref(username)],
|
||||
responses => #{
|
||||
<<"200">> => #{
|
||||
description => <<"OK">>,
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => minirest:ref(<<"username">>),
|
||||
examples => #{
|
||||
username => #{
|
||||
summary => <<"Username">>,
|
||||
value => jsx:encode(?EXAMPLE_USERNAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
<<"404">> => emqx_mgmt_util:bad_request(<<"Not Found">>)
|
||||
200 => swagger_with_example( {rules_for_username, ?TYPE_REF}
|
||||
, {username, ?PUT_MAP_EXAMPLE}),
|
||||
404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||
}
|
||||
},
|
||||
put => #{
|
||||
description => "Set record for username",
|
||||
parameters => [
|
||||
#{
|
||||
name => username,
|
||||
in => path,
|
||||
schema => #{
|
||||
type => string
|
||||
},
|
||||
required => true
|
||||
}
|
||||
],
|
||||
requestBody => #{
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => minirest:ref(<<"username">>),
|
||||
examples => #{
|
||||
username => #{
|
||||
summary => <<"Username">>,
|
||||
value => jsx:encode(?EXAMPLE_USERNAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Set record for username">>,
|
||||
parameters => [hoconsc:ref(username)],
|
||||
'requestBody' => swagger_with_example( {rules_for_username, ?TYPE_REF}
|
||||
, {username, ?PUT_MAP_EXAMPLE}),
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"Updated">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"Updated">>,
|
||||
400 => emqx_dashboard_swagger:error_codes( [?BAD_REQUEST]
|
||||
, <<"Bad username or bad rule schema">>)
|
||||
}
|
||||
},
|
||||
delete => #{
|
||||
description => "Delete one record for username",
|
||||
parameters => [
|
||||
#{
|
||||
name => username,
|
||||
in => path,
|
||||
schema => #{
|
||||
type => string
|
||||
},
|
||||
required => true
|
||||
}
|
||||
],
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Delete one record for username">>,
|
||||
parameters => [hoconsc:ref(username)],
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"No Content">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"No Content">>,
|
||||
400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad username">>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{"/authorization/sources/built-in-database/username/:username", Metadata, user}.
|
||||
|
||||
client_api() ->
|
||||
Metadata = #{
|
||||
};
|
||||
schema("/authorization/sources/built-in-database/clientid/:clientid") ->
|
||||
#{
|
||||
'operationId' => client,
|
||||
get => #{
|
||||
description => "Get record info for clientid",
|
||||
parameters => [
|
||||
#{
|
||||
name => clientid,
|
||||
in => path,
|
||||
schema => #{
|
||||
type => string
|
||||
},
|
||||
required => true
|
||||
}
|
||||
],
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Get record info for clientid">>,
|
||||
parameters => [hoconsc:ref(clientid)],
|
||||
responses => #{
|
||||
<<"200">> => #{
|
||||
description => <<"OK">>,
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => minirest:ref(<<"clientid">>),
|
||||
examples => #{
|
||||
clientid => #{
|
||||
summary => <<"Clientid">>,
|
||||
value => jsx:encode(?EXAMPLE_CLIENTID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
<<"404">> => emqx_mgmt_util:bad_request(<<"Not Found">>)
|
||||
200 => swagger_with_example( {rules_for_clientid, ?TYPE_REF}
|
||||
, {clientid, ?PUT_MAP_EXAMPLE}),
|
||||
404 => emqx_dashboard_swagger:error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||
}
|
||||
},
|
||||
put => #{
|
||||
description => "Set record for clientid",
|
||||
parameters => [
|
||||
#{
|
||||
name => clientid,
|
||||
in => path,
|
||||
schema => #{
|
||||
type => string
|
||||
},
|
||||
required => true
|
||||
}
|
||||
],
|
||||
requestBody => #{
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => minirest:ref(<<"clientid">>),
|
||||
examples => #{
|
||||
clientid => #{
|
||||
summary => <<"Clientid">>,
|
||||
value => jsx:encode(?EXAMPLE_CLIENTID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Set record for clientid">>,
|
||||
parameters => [hoconsc:ref(clientid)],
|
||||
'requestBody' => swagger_with_example( {rules_for_clientid, ?TYPE_REF}
|
||||
, {clientid, ?PUT_MAP_EXAMPLE}),
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"Updated">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"Updated">>,
|
||||
400 => emqx_dashboard_swagger:error_codes(
|
||||
[?BAD_REQUEST], <<"Bad clientid or bad rule schema">>)
|
||||
}
|
||||
},
|
||||
delete => #{
|
||||
description => "Delete one record for clientid",
|
||||
parameters => [
|
||||
#{
|
||||
name => clientid,
|
||||
in => path,
|
||||
schema => #{
|
||||
type => string
|
||||
},
|
||||
required => true
|
||||
}
|
||||
],
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Delete one record for clientid">>,
|
||||
parameters => [hoconsc:ref(clientid)],
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"No Content">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"No Content">>,
|
||||
400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad clientid">>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{"/authorization/sources/built-in-database/clientid/:clientid", Metadata, client}.
|
||||
|
||||
all_api() ->
|
||||
Metadata = #{
|
||||
};
|
||||
schema("/authorization/sources/built-in-database/all") ->
|
||||
#{
|
||||
'operationId' => all,
|
||||
get => #{
|
||||
description => "Show the list of rules for all",
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Show the list of rules for all">>,
|
||||
responses => #{
|
||||
<<"200">> => #{
|
||||
description => <<"OK">>,
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => minirest:ref(<<"clientid">>),
|
||||
examples => #{
|
||||
clientid => #{
|
||||
summary => <<"All">>,
|
||||
value => jsx:encode(?EXAMPLE_ALL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
200 => swagger_with_example({rules_for_all, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE})
|
||||
}
|
||||
},
|
||||
put => #{
|
||||
description => "Set the list of rules for all",
|
||||
requestBody => #{
|
||||
content => #{
|
||||
'application/json' => #{
|
||||
schema => minirest:ref(<<"all">>),
|
||||
examples => #{
|
||||
all => #{
|
||||
summary => <<"All">>,
|
||||
value => jsx:encode(?EXAMPLE_ALL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Set the list of rules for all">>,
|
||||
'requestBody' =>
|
||||
swagger_with_example({rules_for_all, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE}),
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"Created">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"Created">>,
|
||||
400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad rule schema">>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{"/authorization/sources/built-in-database/all", Metadata, all}.
|
||||
|
||||
purge_api() ->
|
||||
Metadata = #{
|
||||
};
|
||||
schema("/authorization/sources/built-in-database/purge-all") ->
|
||||
#{
|
||||
'operationId' => purge,
|
||||
delete => #{
|
||||
description => "Purge all records",
|
||||
tags => [<<"authorization">>],
|
||||
description => <<"Purge all records">>,
|
||||
responses => #{
|
||||
<<"204">> => #{description => <<"No Content">>},
|
||||
<<"400">> => emqx_mgmt_util:bad_request()
|
||||
204 => <<"No Content">>,
|
||||
400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{"/authorization/sources/built-in-database/purge-all", Metadata, purge}.
|
||||
}.
|
||||
|
||||
fields(rule_item) ->
|
||||
[ {topic, hoconsc:mk( string()
|
||||
, #{ required => true
|
||||
, desc => <<"Rule on specific topic">>
|
||||
, example => <<"test/topic/1">>})}
|
||||
, {permission, hoconsc:mk( hoconsc:enum([allow, deny])
|
||||
, #{desc => <<"Permission">>, required => true, example => allow})}
|
||||
, {action, hoconsc:mk( hoconsc:enum([publish, subscribe, all])
|
||||
, #{ required => true, example => publish
|
||||
, desc => <<"Authorized action">> })} ];
|
||||
fields(clientid) ->
|
||||
[ {clientid, hoconsc:mk( binary()
|
||||
, #{ in => path, required => true
|
||||
, desc => <<"ClientID">>, example => <<"client1">>})}
|
||||
];
|
||||
fields(username) ->
|
||||
[ {username, hoconsc:mk( binary()
|
||||
, #{ in => path, required => true
|
||||
, desc => <<"Username">>, example => <<"user1">>})}
|
||||
];
|
||||
fields(rules_for_username) ->
|
||||
[ {rules, hoconsc:mk(hoconsc:array(hoconsc:ref(rule_item)), #{})}
|
||||
] ++ fields(username);
|
||||
fields(username_response_data) ->
|
||||
[ {data, hoconsc:mk(hoconsc:array(hoconsc:ref(rules_for_username)), #{})}
|
||||
, {meta, hoconsc:ref(meta)}
|
||||
];
|
||||
fields(rules_for_clientid) ->
|
||||
[ {rules, hoconsc:mk(hoconsc:array(hoconsc:ref(rule_item)), #{})}
|
||||
] ++ fields(clientid);
|
||||
fields(clientid_response_data) ->
|
||||
[ {data, hoconsc:mk(hoconsc:array(hoconsc:ref(rules_for_clientid)), #{})}
|
||||
, {meta, hoconsc:ref(meta)}
|
||||
];
|
||||
fields(rules_for_all) ->
|
||||
[ {rules, hoconsc:mk(hoconsc:array(hoconsc:ref(rule_item)), #{})}
|
||||
];
|
||||
fields(meta) ->
|
||||
emqx_dashboard_swagger:fields(page)
|
||||
++ emqx_dashboard_swagger:fields(limit)
|
||||
++ [{count, hoconsc:mk(integer(), #{example => 1})}].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% HTTP API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
users(get, #{query_string := PageParams}) ->
|
||||
MatchSpec = ets:fun2ms(
|
||||
|
@ -609,7 +369,8 @@ purge(delete, _) ->
|
|||
{204};
|
||||
[#{<<"enable">> := true}] ->
|
||||
{400, #{code => <<"BAD_REQUEST">>,
|
||||
message => <<"'built-in-database' type source must be disabled before purge.">>}};
|
||||
message =>
|
||||
<<"'built-in-database' type source must be disabled before purge.">>}};
|
||||
[] ->
|
||||
{404, #{code => <<"BAD_REQUEST">>,
|
||||
message => <<"'built-in-database' type source is not found.">>
|
||||
|
@ -642,6 +403,43 @@ format_by_clientid([{clientid, Clientid}, {rules, Rules}]) ->
|
|||
atom(B) when is_binary(B) ->
|
||||
try binary_to_existing_atom(B, utf8)
|
||||
catch
|
||||
_ -> binary_to_atom(B)
|
||||
_Error:_Expection -> binary_to_atom(B)
|
||||
end;
|
||||
atom(A) when is_atom(A) -> A.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
swagger_with_example({Ref, TypeP}, {_Name, _Type} = Example) ->
|
||||
emqx_dashboard_swagger:schema_with_examples(
|
||||
case TypeP of
|
||||
?TYPE_REF -> hoconsc:ref(?MODULE, Ref);
|
||||
?TYPE_ARRAY -> hoconsc:array(hoconsc:ref(?MODULE, Ref))
|
||||
end,
|
||||
rules_example(Example)).
|
||||
|
||||
rules_example({ExampleName, ExampleType}) ->
|
||||
{Summary, Example} =
|
||||
case ExampleName of
|
||||
username -> {<<"Username">>, ?USERNAME_RULES_EXAMPLE};
|
||||
clientid -> {<<"ClientID">>, ?CLIENTID_RULES_EXAMPLE};
|
||||
all -> {<<"All">>, ?ALL_RULES_EXAMPLE}
|
||||
end,
|
||||
Value =
|
||||
case ExampleType of
|
||||
?PAGE_QUERY_EXAMPLE -> #{
|
||||
data => [Example],
|
||||
meta => ?META_EXAMPLE
|
||||
};
|
||||
?PUT_MAP_EXAMPLE ->
|
||||
Example;
|
||||
?POST_ARRAY_EXAMPLE ->
|
||||
[Example]
|
||||
end,
|
||||
#{
|
||||
'password-based:built-in-database' => #{
|
||||
summary => Summary,
|
||||
value => Value
|
||||
}
|
||||
}.
|
||||
|
|
|
@ -35,50 +35,6 @@ authorization
|
|||
-define(API_VERSION, "v5").
|
||||
-define(BASE_PATH, "api").
|
||||
|
||||
-define(EXAMPLE_USERNAME, #{username => user1,
|
||||
rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(EXAMPLE_CLIENTID, #{clientid => client1,
|
||||
rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
-define(EXAMPLE_ALL , #{rules => [ #{topic => <<"test/toopic/1">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"publish">>
|
||||
}
|
||||
, #{topic => <<"test/toopic/2">>,
|
||||
permission => <<"allow">>,
|
||||
action => <<"subscribe">>
|
||||
}
|
||||
, #{topic => <<"eq test/#">>,
|
||||
permission => <<"deny">>,
|
||||
action => <<"all">>
|
||||
}
|
||||
]
|
||||
}).
|
||||
|
||||
roots() -> ["authorization"].
|
||||
|
||||
|
@ -126,65 +82,149 @@ set_special_configs(_App) ->
|
|||
%%------------------------------------------------------------------------------
|
||||
|
||||
t_api(_) ->
|
||||
{ok, 204, _} = request(post, uri(["authorization", "sources", "built-in-database", "username"]), [?EXAMPLE_USERNAME]),
|
||||
{ok, 200, Request1} = request(get, uri(["authorization", "sources", "built-in-database", "username"]), []),
|
||||
{ok, 200, Request2} = request(get, uri(["authorization", "sources", "built-in-database", "username", "user1"]), []),
|
||||
{ok, 204, _} =
|
||||
request( post
|
||||
, uri(["authorization", "sources", "built-in-database", "username"])
|
||||
, [?USERNAME_RULES_EXAMPLE]),
|
||||
{ok, 200, Request1} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "username"])
|
||||
, []),
|
||||
{ok, 200, Request2} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "username", "user1"])
|
||||
, []),
|
||||
#{<<"data">> := [#{<<"username">> := <<"user1">>, <<"rules">> := Rules1}],
|
||||
<<"meta">> := #{<<"count">> := 1,<<"limit">> := 100,<<"page">> := 1}} = jsx:decode(Request1),
|
||||
#{<<"username">> := <<"user1">>, <<"rules">> := Rules1} = jsx:decode(Request2),
|
||||
?assertEqual(3, length(Rules1)),
|
||||
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database", "username", "user1"]), ?EXAMPLE_USERNAME#{rules => []}),
|
||||
{ok, 200, Request3} = request(get, uri(["authorization", "sources", "built-in-database", "username", "user1"]), []),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database", "username", "user1"])
|
||||
, ?USERNAME_RULES_EXAMPLE#{rules => []}),
|
||||
{ok, 200, Request3} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "username", "user1"])
|
||||
, []),
|
||||
#{<<"username">> := <<"user1">>, <<"rules">> := Rules2} = jsx:decode(Request3),
|
||||
?assertEqual(0, length(Rules2)),
|
||||
|
||||
{ok, 204, _} = request(delete, uri(["authorization", "sources", "built-in-database", "username", "user1"]), []),
|
||||
{ok, 404, _} = request(get, uri(["authorization", "sources", "built-in-database", "username", "user1"]), []),
|
||||
{ok, 204, _} =
|
||||
request( delete
|
||||
, uri(["authorization", "sources", "built-in-database", "username", "user1"])
|
||||
, []),
|
||||
{ok, 404, _} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "username", "user1"])
|
||||
, []),
|
||||
|
||||
{ok, 204, _} = request(post, uri(["authorization", "sources", "built-in-database", "clientid"]), [?EXAMPLE_CLIENTID]),
|
||||
{ok, 200, Request4} = request(get, uri(["authorization", "sources", "built-in-database", "clientid"]), []),
|
||||
{ok, 200, Request5} = request(get, uri(["authorization", "sources", "built-in-database", "clientid", "client1"]), []),
|
||||
{ok, 204, _} =
|
||||
request( post
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid"])
|
||||
, [?CLIENTID_RULES_EXAMPLE]),
|
||||
{ok, 200, Request4} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid"])
|
||||
, []),
|
||||
{ok, 200, Request5} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid", "client1"])
|
||||
, []),
|
||||
#{<<"data">> := [#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules3}],
|
||||
<<"meta">> := #{<<"count">> := 1, <<"limit">> := 100, <<"page">> := 1}} = jsx:decode(Request4),
|
||||
<<"meta">> := #{<<"count">> := 1, <<"limit">> := 100, <<"page">> := 1}}
|
||||
= jsx:decode(Request4),
|
||||
#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules3} = jsx:decode(Request5),
|
||||
?assertEqual(3, length(Rules3)),
|
||||
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database", "clientid", "client1"]), ?EXAMPLE_CLIENTID#{rules => []}),
|
||||
{ok, 200, Request6} = request(get, uri(["authorization", "sources", "built-in-database", "clientid", "client1"]), []),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid", "client1"])
|
||||
, ?CLIENTID_RULES_EXAMPLE#{rules => []}),
|
||||
{ok, 200, Request6} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid", "client1"])
|
||||
, []),
|
||||
#{<<"clientid">> := <<"client1">>, <<"rules">> := Rules4} = jsx:decode(Request6),
|
||||
?assertEqual(0, length(Rules4)),
|
||||
|
||||
{ok, 204, _} = request(delete, uri(["authorization", "sources", "built-in-database", "clientid", "client1"]), []),
|
||||
{ok, 404, _} = request(get, uri(["authorization", "sources", "built-in-database", "clientid", "client1"]), []),
|
||||
{ok, 204, _} =
|
||||
request( delete
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid", "client1"])
|
||||
, []),
|
||||
{ok, 404, _} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid", "client1"])
|
||||
, []),
|
||||
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database", "all"]), ?EXAMPLE_ALL),
|
||||
{ok, 200, Request7} = request(get, uri(["authorization", "sources", "built-in-database", "all"]), []),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database", "all"])
|
||||
, ?ALL_RULES_EXAMPLE),
|
||||
{ok, 200, Request7} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "all"])
|
||||
, []),
|
||||
#{<<"rules">> := Rules5} = jsx:decode(Request7),
|
||||
?assertEqual(3, length(Rules5)),
|
||||
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database", "all"]), ?EXAMPLE_ALL#{rules => []}),
|
||||
{ok, 200, Request8} = request(get, uri(["authorization", "sources", "built-in-database", "all"]), []),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database", "all"])
|
||||
, ?ALL_RULES_EXAMPLE#{rules => []}),
|
||||
{ok, 200, Request8} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "all"])
|
||||
, []),
|
||||
#{<<"rules">> := Rules6} = jsx:decode(Request8),
|
||||
?assertEqual(0, length(Rules6)),
|
||||
|
||||
{ok, 204, _} = request(post, uri(["authorization", "sources", "built-in-database", "username"]), [ #{username => N, rules => []} || N <- lists:seq(1, 20) ]),
|
||||
{ok, 200, Request9} = request(get, uri(["authorization", "sources", "built-in-database", "username?page=2&limit=5"]), []),
|
||||
{ok, 204, _} =
|
||||
request( post
|
||||
, uri(["authorization", "sources", "built-in-database", "username"])
|
||||
, [ #{username => erlang:integer_to_binary(N), rules => []}
|
||||
|| N <- lists:seq(1, 20) ]),
|
||||
{ok, 200, Request9} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "username?page=2&limit=5"])
|
||||
, []),
|
||||
#{<<"data">> := Data1} = jsx:decode(Request9),
|
||||
?assertEqual(5, length(Data1)),
|
||||
|
||||
{ok, 204, _} = request(post, uri(["authorization", "sources", "built-in-database", "clientid"]), [ #{clientid => N, rules => []} || N <- lists:seq(1, 20) ]),
|
||||
{ok, 200, Request10} = request(get, uri(["authorization", "sources", "built-in-database", "clientid?limit=5"]), []),
|
||||
{ok, 204, _} =
|
||||
request( post
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid"])
|
||||
, [ #{clientid => erlang:integer_to_binary(N), rules => []}
|
||||
|| N <- lists:seq(1, 20) ]),
|
||||
{ok, 200, Request10} =
|
||||
request( get
|
||||
, uri(["authorization", "sources", "built-in-database", "clientid?limit=5"])
|
||||
, []),
|
||||
#{<<"data">> := Data2} = jsx:decode(Request10),
|
||||
?assertEqual(5, length(Data2)),
|
||||
|
||||
{ok, 400, Msg1} = request(delete, uri(["authorization", "sources", "built-in-database", "purge-all"]), []),
|
||||
{ok, 400, Msg1} =
|
||||
request( delete
|
||||
, uri(["authorization", "sources", "built-in-database", "purge-all"])
|
||||
, []),
|
||||
?assertMatch({match, _}, re:run(Msg1, "must\sbe\sdisabled\sbefore")),
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database"]), #{<<"enable">> => true}),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database"])
|
||||
, #{<<"enable">> => true}),
|
||||
%% test idempotence
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database"]), #{<<"enable">> => true}),
|
||||
{ok, 204, _} = request(put, uri(["authorization", "sources", "built-in-database"]), #{<<"enable">> => false}),
|
||||
{ok, 204, _} = request(delete, uri(["authorization", "sources", "built-in-database", "purge-all"]), []),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database"])
|
||||
, #{<<"enable">> => true}),
|
||||
{ok, 204, _} =
|
||||
request( put
|
||||
, uri(["authorization", "sources", "built-in-database"])
|
||||
, #{<<"enable">> => false}),
|
||||
{ok, 204, _} =
|
||||
request( delete
|
||||
, uri(["authorization", "sources", "built-in-database", "purge-all"])
|
||||
, []),
|
||||
?assertEqual([], mnesia:dirty_all_keys(?ACL_TABLE)),
|
||||
ok.
|
||||
|
||||
|
|
Loading…
Reference in New Issue