Skip to content

Commit d1c156a

Browse files
committed
MB-38322 GET /_uiroles REST API
called without parameters returns the following json: {"folders": [....], "parameters": {....}} value of folders key is an array of maps: { "name": <name of folder>, "roles": [....] } roles array contains roles that belong to the folder in the following format {"role": <role>, "params": <array of role parameters>, "name": <name of the role to show in UI>, "desc": <description of the role>} value of the key "parameters" is a tree structure representing all possible parameter values Here's how UI should enumerate all possible parameters: Let's say the role has parameters [bucket_name, scope_name, collection_name] 1. UI code should look in the "parameters" map for "bucket_name" and retrieve an array of the following structures: {"value": <bucket name>, "children": <optional map of child parameters>} 2. If "children" is specified, UI code should look for the second parameter "scope_name" in the map and so on.. It is implied that each parameter can also be a wildcard, therefore wildcards are not returned by the API. Example of output: { "folders": [ { "name": "Administrative Roles", "roles": [ { "role": "admin", "params": [], "name": "Full Admin", "desc": "Can manage all cluster features..." }, .................. { "name": "Data Service", "roles": [ { "role": "data_reader", "params": [ "bucket_name", "scope_name", "collection_name" ], "name": "Data Reader", "desc": "Can read data from a given bucket, scope..." }, .................. { "name": "Views", "roles": [ { "role": "views_admin", "params": [ "bucket_name" ], "name": "Views Admin", "desc": "Can create and manage views of a given..." } ] }, ------------- "parameters": { "bucket_name": [ { "value": "test", "children": { "scope_name": [ { "value": "_default", "children": { "collection_name": [ { "value": "_default" } ] } } ] } } ] } } Change-Id: I8341456851efb93a52e85bb05b25cb1152fcfa4f Reviewed-on: http://review.couchbase.org/c/ns_server/+/129449 Tested-by: Artem Stemkovski <[email protected]> Well-Formed: Build Bot <[email protected]> Reviewed-by: Timofey Barmin <[email protected]>
1 parent c3d6ea5 commit d1c156a

File tree

4 files changed

+92
-13
lines changed

4 files changed

+92
-13
lines changed

src/collections.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
get_manifest/1,
3737
get_scope/2,
3838
get_collection/2,
39-
get_uid/1]).
39+
get_uid/1,
40+
get_scopes/1,
41+
get_collections/1]).
4042

4143
%% rpc from other nodes
4244
-export([wait_for_manifest_uid/4]).

src/menelaus_roles.erl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
external_auth_polling_interval/0,
7070
get_param_defs/2,
7171
ui_folders/0,
72+
get_visible_role_definitions/1,
7273
strip_ids/2]).
7374

7475
-export([start_compiled_roles_cache/0]).
@@ -953,13 +954,16 @@ validate_role(Role, Params, Definitions, Buckets) ->
953954
false
954955
end.
955956

957+
get_visible_role_definitions(Config) ->
958+
pipes:run(pipes:stream_list(get_definitions(Config, public)),
959+
visible_roles_filter(),
960+
pipes:collect()).
961+
956962
-spec validate_roles([rbac_role()], ns_config()) ->
957963
{GoodRoles :: [rbac_role()],
958964
BadRoles :: [rbac_role()]}.
959965
validate_roles(Roles, Config) ->
960-
Definitions = pipes:run(pipes:stream_list(get_definitions(Config, public)),
961-
visible_roles_filter(),
962-
pipes:collect()),
966+
Definitions = get_visible_role_definitions(Config),
963967
Buckets = ns_bucket:get_buckets(Config),
964968
lists:foldl(fun (Role, {Validated, Unknown}) ->
965969
case validate_role(Role, Definitions, Buckets) of

src/menelaus_web.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ get_action(Req, {AppRoot, IsSSL, Plugins}, Path, PathTokens) ->
226226
["_uistats"] ->
227227
{{[ui], read},
228228
fun menelaus_stats:serve_ui_stats/1};
229+
["_uiroles"] ->
230+
{{[ui], read}, fun menelaus_web_rbac:handle_get_uiroles/1};
229231
["_uiEnv"] ->
230232
{done, serve_ui_env(Req)};
231233
["poolsStreaming", "default"] ->

src/menelaus_web_rbac.erl

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
handle_delete_profile/2,
5959
handle_put_profile/2,
6060
handle_lookup_ldap_user/2,
61-
gen_password/1
61+
gen_password/1,
62+
handle_get_uiroles/1
6263
]).
6364

6465
-define(MIN_USERS_PAGE_SIZE, 2).
@@ -189,19 +190,23 @@ get_roles_by_permission(Permission, Config) ->
189190
menelaus_roles:produce_roles_by_permission(Permission, Config),
190191
pipes:collect()).
191192

193+
maybe_remove_security_roles(Req, Config, Roles) ->
194+
Roles --
195+
case menelaus_auth:has_permission(?SECURITY_READ, Req) of
196+
true ->
197+
[];
198+
false ->
199+
menelaus_roles:get_security_roles(Config)
200+
end.
201+
192202
handle_get_roles(Req) ->
193203
Config = ns_config:get(),
194204
validator:handle(
195205
fun (Values) ->
196206
Permission = proplists:get_value(permission, Values),
197-
Roles =
198-
get_roles_by_permission(Permission, Config) --
199-
case menelaus_auth:has_permission(?SECURITY_READ, Req) of
200-
true ->
201-
[];
202-
false ->
203-
menelaus_roles:get_security_roles(Config)
204-
end,
207+
Roles = maybe_remove_security_roles(
208+
Req, Config,
209+
get_roles_by_permission(Permission, Config)),
205210
Json =
206211
[{role_to_json(Role) ++ jsonify_props(Props)} ||
207212
{Role, Props} <- Roles],
@@ -1677,6 +1682,72 @@ handle_put_profile(RawIdentity, Req) ->
16771682
menelaus_util:reply_json(Req, <<"Invalid Json">>, 400)
16781683
end.
16791684

1685+
handle_get_uiroles(Req) ->
1686+
menelaus_util:require_permission(Req, {[admin, security], read}),
1687+
1688+
Roles =
1689+
maybe_remove_security_roles(Req, ns_config:latest(),
1690+
menelaus_roles:get_visible_role_definitions(
1691+
ns_config:latest())),
1692+
Folders =
1693+
lists:filtermap(build_ui_folder(_, Roles), menelaus_roles:ui_folders()),
1694+
1695+
Buckets = menelaus_auth:get_accessible_buckets(
1696+
?cut({[{bucket, _}, settings], read}), Req),
1697+
1698+
Parameters = {[build_ui_parameters(bucket_name, Buckets)]},
1699+
1700+
menelaus_util:reply_json(Req, {[{folders, Folders},
1701+
{parameters, Parameters}]}).
1702+
1703+
build_ui_folder({Key, Name}, Roles) ->
1704+
case lists:filter(fun ({_, _, Props, _}) ->
1705+
proplists:get_value(folder, Props) =:= Key
1706+
end, Roles) of
1707+
[] ->
1708+
false;
1709+
FolderRoles ->
1710+
{true, {[{name, list_to_binary(Name)},
1711+
{roles, [build_ui_role(Role) || Role <- FolderRoles]}]}}
1712+
end.
1713+
1714+
build_ui_role({Role, Params, Props, _}) ->
1715+
{[{role, Role}, {params, Params} | jsonify_props(Props)]}.
1716+
1717+
build_ui_value(Value, Children) ->
1718+
case lists:filter(fun ({_, L}) -> L =/= [] end, Children) of
1719+
[] ->
1720+
{[{value, list_to_binary(Value)}]};
1721+
NonEmpty ->
1722+
{[{value, list_to_binary(Value)},
1723+
{children, {NonEmpty}}]}
1724+
end.
1725+
1726+
build_ui_parameters(Name, List) ->
1727+
{Name, build_ui_values(Name, List)}.
1728+
1729+
build_ui_values(bucket_name, Buckets) ->
1730+
lists:map(
1731+
fun ({Name, BucketCfg}) ->
1732+
Scopes =
1733+
case cluster_compat_mode:is_enterprise() andalso
1734+
collections:enabled(BucketCfg) of
1735+
true ->
1736+
collections:get_scopes(
1737+
collections:get_manifest(BucketCfg));
1738+
false ->
1739+
[]
1740+
end,
1741+
build_ui_value(Name, [build_ui_parameters(scope_name, Scopes)])
1742+
end, Buckets);
1743+
build_ui_values(scope_name, Scopes) ->
1744+
[build_ui_value(
1745+
Name, [build_ui_parameters(collection_name,
1746+
collections:get_collections(Scope))]) ||
1747+
{Name, Scope} <- Scopes];
1748+
build_ui_values(collection_name, Collections) ->
1749+
[build_ui_value(Name, []) || {Name, _} <- Collections].
1750+
16801751
-ifdef(TEST).
16811752
role_to_string_test() ->
16821753
?assertEqual("role", role_to_string(role)),

0 commit comments

Comments
 (0)