userver: userver/storages/secdist/secdist.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 {
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.
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, 1136, 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