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