userver: userver/storages/secdist/secdist.hpp Source File
Loading...
Searching...
No Matches
secdist.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/secdist/secdist.hpp
4/// @brief @copybrief storages::secdist::SecdistConfig
5
6#include <any>
7#include <chrono>
8#include <cstdint>
9#include <functional>
10#include <optional>
11#include <stdexcept>
12#include <string>
13#include <string_view>
14#include <typeindex>
15#include <vector>
16
17#include <userver/concurrent/async_event_source.hpp>
18#include <userver/engine/task/task_processor_fwd.hpp>
19#include <userver/formats/json/value.hpp>
20#include <userver/rcu/rcu.hpp>
21#include <userver/storages/secdist/provider.hpp>
22#include <userver/utils/fast_pimpl.hpp>
23#include <userver/utils/resource_scopes_fwd.hpp>
24
25USERVER_NAMESPACE_BEGIN
26
27/// Credentials storage
28namespace storages::secdist {
29
30class SecdistConfig;
31
32namespace detail {
33
34template <typename T>
35class SecdistModule final {
36public:
37 static const T& Get(const SecdistConfig& config);
38 static std::any Factory(const formats::json::Value& data) { return T(data); }
39
40private:
41 static std::size_t index;
42};
43
44} // namespace detail
45
46/// Secdist file format
47enum class SecdistFormat {
48 kJson,
49 kYaml,
50 kYamlConfig,
51};
52
53/// @ingroup userver_clients
54///
55/// @brief Client to retrieve credentials from the components::Secdist.
56///
57/// ## Example usage:
58///
59/// Declare a type that would work with the credentials:
60///
61/// @snippet core/src/storages/secdist/secdist_test.cpp UserPasswords
62///
63/// Fill the components::Secdist `config` from file with the secure data:
64///
65/// @snippet core/src/storages/secdist/secdist_test.cpp Secdist Usage Sample - json
66///
67/// Retrieve SecdistConfig from components::Secdist and get the type from it:
68///
69/// @snippet core/src/storages/secdist/secdist_test.cpp Secdist Usage Sample - SecdistConfig
70///
71/// Json with secure data can also be loaded from environment variable with name defined in `environment_secrets_key`.
72/// Sample variable value: `{"user-passwords":{"username":"password","another username":"another password"}}`.
73/// It has the same format as data from file.
74/// If both sources are presented, data from environment variable will be merged with data from file
75/// (json objects will be merged, duplicate fields of other types will be overridden by data from environment variable).
76class SecdistConfig final {
77public:
78 struct Settings {
79 SecdistProvider* provider{nullptr};
80 std::chrono::milliseconds update_period{std::chrono::milliseconds::zero()};
81 };
82
83 SecdistConfig();
84 explicit SecdistConfig(const Settings& settings);
85
86 template <typename T>
87 static std::size_t Register(std::function<std::any(const formats::json::Value&)>&& factory) {
88 return Register(std::move(factory));
89 }
90
91 template <typename T>
92 const T& Get() const {
93 return detail::SecdistModule<T>::Get(*this);
94 }
95
96private:
97 void Init(const formats::json::Value& doc);
98
99 static std::size_t Register(std::function<std::any(const formats::json::Value&)>&& factory);
100 const std::any& Get(const std::type_index& type, std::size_t index) const;
101
102 template <typename T>
103 friend class detail::SecdistModule;
104
105 std::vector<std::any> configs_;
106};
107
108/// @ingroup userver_clients
109///
110/// @brief Client to retrieve credentials from the components::Secdist and to
111/// subscribe to their updates.
112class Secdist final {
113public:
114 explicit Secdist(SecdistConfig::Settings settings);
115 ~Secdist();
116
117 /// Returns secdist data loaded on service start.
118 /// Does not support secdist updating during service work.
119 const storages::secdist::SecdistConfig& Get() const;
120
121 /// Returns fresh secdist data (from last update).
122 /// Supports secdist updating during service work.
123 rcu::ReadablePtr<storages::secdist::SecdistConfig> GetSnapshot() const;
124
125 /// Subscribes to secdist updates using a member function, named
126 /// `OnSecdistUpdate` by convention. Also immediately invokes the function
127 /// with the current secdist data.
128 ///
129 /// Further updates are delivered after @ref utils::ResourceScopeStorage::AfterConstruction, including those updates
130 /// that arrived before it. Unsubscribe runs in @ref utils::ResourceScopeStorage::BeforeDestruction.
131 ///
132 /// @param scopes storage that owns the subscription lifetime. In a component constructor pass `context.Scopes()`
133 /// or @ref components::GetResourceScopes.
134 template <typename Class>
135 void UpdateAndListen(
136 utils::ResourceScopeStorage& scopes,
137 Class* obj,
138 std::string_view name,
139 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
140 );
141
142 /// @overload
143 /// @deprecated Use the overload that takes @ref utils::ResourceScopeStorage.
144 ///
145 /// Store the returned scope as a member and call `Unsubscribe` explicitly.
146 template <typename Class>
147 concurrent::AsyncEventSubscriberScope UpdateAndListen(
148 Class* obj,
149 std::string_view name,
150 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
151 );
152
153 bool IsPeriodicUpdateEnabled() const noexcept;
154
155private:
156 using EventSource = concurrent::AsyncEventSource<const SecdistConfig&>;
157
158 concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
159 concurrent::FunctionId id,
160 std::string_view name,
161 EventSource::Function&& func
162 );
163
164 void DoUpdateAndListen(
165 utils::ResourceScopeStorage& scopes,
166 concurrent::FunctionId id,
167 std::string_view name,
168 EventSource::Function&& func
169 );
170
171 class Impl;
172 utils::FastPimpl<Impl, 1280, 16> impl_;
173};
174
175template <typename Class>
176void Secdist::UpdateAndListen(
177 utils::ResourceScopeStorage& scopes,
178 Class* obj,
179 std::string_view name,
180 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
181) {
182 DoUpdateAndListen(scopes, concurrent::FunctionId(obj), name, [obj, func](const SecdistConfig& config) {
183 (obj->*func)(config);
184 });
185}
186
187template <typename Class>
188concurrent::AsyncEventSubscriberScope Secdist::UpdateAndListen(
189 Class* obj,
190 std::string_view name,
191 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
192) {
193 return DoUpdateAndListen(concurrent::FunctionId(obj), name, [obj, func](const SecdistConfig& config) {
194 (obj->*func)(config);
195 });
196}
197
198namespace detail {
199
200template <typename T>
201const T& SecdistModule<T>::Get(const SecdistConfig& config) {
202 return std::any_cast<const T&>(config.Get(typeid(T), index));
203}
204
205template <typename T>
206std::size_t SecdistModule<T>::index = SecdistConfig::Register<T>(&SecdistModule<T>::Factory);
207
208} // namespace detail
209
210} // namespace storages::secdist
211
212USERVER_NAMESPACE_END