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
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 storages/secdist/secdist_test.cpp UserPasswords
61///
62/// Fill the components::Secdist `config` from file with the secure data:
63///
64/// @snippet 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 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 template <typename Class>
128 concurrent::AsyncEventSubscriberScope UpdateAndListen(
129 Class* obj,
130 std::string_view name,
131 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
132 );
133
134 bool IsPeriodicUpdateEnabled() const;
135
136private:
137 using EventSource = concurrent::AsyncEventSource<const SecdistConfig&>;
138
139 concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
140 concurrent::FunctionId id,
141 std::string_view name,
142 EventSource::Function&& func
143 );
144
145 class Impl;
146 utils::FastPimpl<Impl, 1280, 16> impl_;
147};
148
149template <typename Class>
150concurrent::AsyncEventSubscriberScope Secdist::UpdateAndListen(
151 Class* obj,
152 std::string_view name,
153 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
154) {
155 return DoUpdateAndListen(concurrent::FunctionId(obj), name, [obj, func](const SecdistConfig& config) {
156 (obj->*func)(config);
157 });
158}
159
160namespace detail {
161
162template <typename T>
163const T& SecdistModule<T>::Get(const SecdistConfig& config) {
164 return std::any_cast<const T&>(config.Get(typeid(T), index));
165}
166
167template <typename T>
168std::size_t SecdistModule<T>::index = SecdistConfig::Register<T>(&SecdistModule<T>::Factory);
169
170} // namespace detail
171
172} // namespace storages::secdist
173
174USERVER_NAMESPACE_END