Github   Telegram
Loading...
Searching...
No Matches
secdist.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file 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 {
35 public:
36 static const T& Get(const SecdistConfig& config);
37 static std::any Factory(const formats::json::Value& data) { return T(data); }
38
39 private:
40 static std::size_t index_;
41};
42
43} // namespace detail
44
45/// Secdist file format
46enum class SecdistFormat {
47 kJson,
48 kYaml,
49};
50
51// clang-format off
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 storages/secdist/secdist_test.cpp UserPasswords
62///
63/// Fill the components::Secdist `config` from file with the secure data:
64///
65/// @snippet 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 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).
76
77// clang-format on
78class SecdistConfig final {
79 public:
80 struct Settings {
81 SecdistProvider* provider{nullptr};
82 std::chrono::milliseconds update_period{std::chrono::milliseconds::zero()};
83 };
84
85 SecdistConfig();
86 explicit SecdistConfig(const Settings& settings);
87
88 template <typename T>
89 static std::size_t Register(
90 std::function<std::any(const formats::json::Value&)>&& factory) {
91 return Register(std::move(factory));
92 }
93
94 template <typename T>
95 const T& Get() const {
96 return detail::SecdistModule<T>::Get(*this);
97 }
98
99 private:
100 void Init(const formats::json::Value& doc);
101
102 static std::size_t Register(
103 std::function<std::any(const formats::json::Value&)>&& factory);
104 const std::any& Get(const std::type_index& type, std::size_t index) const;
105
106 template <typename T>
107 friend class detail::SecdistModule;
108
109 std::vector<std::any> configs_;
110};
111
112/// @ingroup userver_clients
113///
114/// @brief Client to retrieve credentials from the components::Secdist and to
115/// subscribe to their updates.
116class Secdist final {
117 public:
118 explicit Secdist(SecdistConfig::Settings settings);
119 ~Secdist();
120
121 /// Returns secdist data loaded on service start.
122 /// Does not support secdist updating during service work.
123 const storages::secdist::SecdistConfig& Get() const;
124
125 /// Returns fresh secdist data (from last update).
126 /// Supports secdist updating during service work.
127 rcu::ReadablePtr<storages::secdist::SecdistConfig> GetSnapshot() const;
128
129 /// Subscribes to secdist updates using a member function, named
130 /// `OnSecdistUpdate` by convention. Also immediately invokes the function
131 /// with the current secdist data.
132 template <typename Class>
133 concurrent::AsyncEventSubscriberScope UpdateAndListen(
134 Class* obj, std::string_view name,
135 void (Class::*func)(const storages::secdist::SecdistConfig& secdist));
136
137 bool IsPeriodicUpdateEnabled() const;
138
139 private:
140 using EventSource = concurrent::AsyncEventSource<const SecdistConfig&>;
141
142 concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
143 concurrent::FunctionId id, std::string_view name,
144 EventSource::Function&& func);
145
146 class Impl;
147 utils::FastPimpl<Impl, 1040, 16> impl_;
148};
149
150template <typename Class>
151concurrent::AsyncEventSubscriberScope Secdist::UpdateAndListen(
152 Class* obj, std::string_view name,
153 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)) {
154 return DoUpdateAndListen(
155 concurrent::FunctionId(obj), name,
156 [obj, func](const SecdistConfig& config) { (obj->*func)(config); });
157}
158
159namespace detail {
160
161template <typename T>
162const T& SecdistModule<T>::Get(const SecdistConfig& config) {
163 return std::any_cast<const T&>(config.Get(typeid(T), index_));
164}
165
166template <typename T>
167std::size_t SecdistModule<T>::index_ =
168 SecdistConfig::Register<T>(&SecdistModule<T>::Factory);
169
170} // namespace detail
171
172} // namespace storages::secdist
173
174USERVER_NAMESPACE_END