|
| 1 | +%% @author Couchbase <[email protected]> |
| 2 | +%% @copyright 2024-Present Couchbase, Inc. |
| 3 | +%% |
| 4 | +%% Use of this software is governed by the Business Source License included in |
| 5 | +%% the file licenses/BSL-Couchbase.txt. As of the Change Date specified in that |
| 6 | +%% file, in accordance with the Business Source License, use of this software |
| 7 | +%% will be governed by the Apache License, Version 2.0, included in the file |
| 8 | +%% licenses/APL2.txt. |
| 9 | +%% |
| 10 | +-module(menelaus_web_encr_at_rest). |
| 11 | + |
| 12 | +-include("ns_common.hrl"). |
| 13 | +-include_lib("ns_common/include/cut.hrl"). |
| 14 | +-include("cb_cluster_secrets.hrl"). |
| 15 | + |
| 16 | +-export([handle_get/2, handle_post/2, get_settings/1]). |
| 17 | + |
| 18 | +params() -> |
| 19 | + [{"config.encryptionMethod", |
| 20 | + #{cfg_key => [config_encryption, encryption], |
| 21 | + type => {one_of, existing_atom, |
| 22 | + [disabled, encryption_service, secret]}}}, |
| 23 | + {"config.encryptionSecretId", |
| 24 | + #{cfg_key => [config_encryption, secret_id], |
| 25 | + type => {int, -1, infinity}}}]. |
| 26 | + |
| 27 | +handle_get(Path, Req) -> |
| 28 | + Settings = get_settings(direct), |
| 29 | + List = maps:to_list(maps:map(fun (_, V) -> maps:to_list(V) end, Settings)), |
| 30 | + menelaus_web_settings2:handle_get(Path, params(), undefined, List, Req). |
| 31 | + |
| 32 | +handle_post(Path, Req) -> |
| 33 | + menelaus_web_settings2:handle_post( |
| 34 | + fun (Params, Req2) -> |
| 35 | + NewSettings = maps:map(fun (_, V) -> maps:from_list(V) end, |
| 36 | + maps:groups_from_list( |
| 37 | + fun ({[K1, _K2], _V}) -> K1 end, |
| 38 | + fun ({[_K1, K2], V}) -> {K2, V} end, |
| 39 | + Params)), |
| 40 | + RV = chronicle_kv:transaction( |
| 41 | + kv, [?CHRONICLE_SECRETS_KEY, |
| 42 | + ?CHRONICLE_ENCR_AT_REST_SETTINGS_KEY], |
| 43 | + fun (Snapshot) -> |
| 44 | + #{config_encryption := Cfg} = ToApply = |
| 45 | + get_settings(Snapshot, NewSettings), |
| 46 | + case validate_sec_settings(config_encryption, |
| 47 | + Cfg, Snapshot) of |
| 48 | + ok -> |
| 49 | + {commit, [{set, |
| 50 | + ?CHRONICLE_ENCR_AT_REST_SETTINGS_KEY, |
| 51 | + ToApply}]}; |
| 52 | + {error, _} = Error -> |
| 53 | + {abort, Error} |
| 54 | + end |
| 55 | + end), |
| 56 | + case RV of |
| 57 | + {ok, _} -> |
| 58 | + cb_cluster_secrets:sync_with_all_node_monitors(), |
| 59 | + handle_get(Path, Req2); |
| 60 | + {error, Msg} -> |
| 61 | + menelaus_util:reply_global_error(Req2, Msg) |
| 62 | + end |
| 63 | + end, Path, params(), undefined, Req). |
| 64 | + |
| 65 | +get_settings(Snapshot) -> get_settings(Snapshot, #{}). |
| 66 | +get_settings(Snapshot, ExtraSettings) -> |
| 67 | + Merge = fun (Settings1, Settings2) -> |
| 68 | + maps:merge_with(fun (_K, V1, V2) -> maps:merge(V1, V2) end, |
| 69 | + Settings1, Settings2) |
| 70 | + end, |
| 71 | + Settings = chronicle_compat:get(Snapshot, |
| 72 | + ?CHRONICLE_ENCR_AT_REST_SETTINGS_KEY, |
| 73 | + #{default => #{}}), |
| 74 | + Merge(Merge(defaults(), Settings), ExtraSettings). |
| 75 | + |
| 76 | +defaults() -> |
| 77 | + #{config_encryption => #{encryption => disabled, |
| 78 | + secret_id => ?SECRET_ID_NOT_SET}}. |
| 79 | + |
| 80 | +validate_sec_settings(_, #{encryption := disabled, |
| 81 | + secret_id := ?SECRET_ID_NOT_SET}, _) -> |
| 82 | + ok; |
| 83 | +validate_sec_settings(_, #{encryption := disabled, |
| 84 | + secret_id := _}, _) -> |
| 85 | + {error, "Secret id must not be set when encryption is disabled"}; |
| 86 | +validate_sec_settings(_, #{encryption := encryption_service, |
| 87 | + secret_id := ?SECRET_ID_NOT_SET}, _) -> |
| 88 | + ok; |
| 89 | +validate_sec_settings(_, #{encryption := encryption_service, |
| 90 | + secret_id := _}, _) -> |
| 91 | + {error, "Secret id must not be set when encryption_service is used"}; |
| 92 | +validate_sec_settings(_, #{encryption := secret, |
| 93 | + secret_id := ?SECRET_ID_NOT_SET}, _) -> |
| 94 | + {error, "Secret id must be set"}; |
| 95 | +validate_sec_settings(Name, #{encryption := secret, |
| 96 | + secret_id := Id}, Snapshot) -> |
| 97 | + case cb_cluster_secrets:is_allowed_usage_for_secret(Id, Name, Snapshot) of |
| 98 | + ok -> ok; |
| 99 | + {error, not_found} -> {error, "Secret not found"}; |
| 100 | + {error, not_allowed} -> {error, "Secret not allowed"} |
| 101 | + end. |
0 commit comments