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// clang-format off
53
54/// @ingroup userver_clients
55///
56/// @brief Client to retrieve credentials from the components::Secdist.
57///
58/// ## Example usage:
59///
60/// Declare a type that would work with the credentials:
61///
62/// @snippet storages/secdist/secdist_test.cpp UserPasswords
63///
64/// Fill the components::Secdist `config` from file with the secure data:
65///
66/// @snippet storages/secdist/secdist_test.cpp Secdist Usage Sample - json
67///
68/// Retrieve SecdistConfig from components::Secdist and get the type from it:
69///
70/// @snippet storages/secdist/secdist_test.cpp Secdist Usage Sample - SecdistConfig
71///
72/// Json with secure data can also be loaded from environment variable with name defined in `environment_secrets_key`.
73/// Sample variable value: `{"user-passwords":{"username":"password","another username":"another password"}}`.
74/// It has the same format as data from file.
75/// If both sources are presented, data from environment variable will be merged with data from file
76/// (json objects will be merged, duplicate fields of other types will be overridden by data from environment variable).
77
78// clang-format on
79class SecdistConfig final {
80public:
81 struct Settings {
82 SecdistProvider* provider{nullptr};
83 std::chrono::milliseconds update_period{std::chrono::milliseconds::zero()};
84 };
85
86 SecdistConfig();
87 explicit SecdistConfig(const Settings& settings);
88
89 template <typename T>
90 static std::size_t Register(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
99private:
100 void Init(const formats::json::Value& doc);
101
102 static std::size_t Register(std::function<std::any(const formats::json::Value&)>&& factory);
103 const std::any& Get(const std::type_index& type, std::size_t index) const;
104
105 template <typename T>
106 friend class detail::SecdistModule;
107
108 std::vector<std::any> configs_;
109};
110
111/// @ingroup userver_clients
112///
113/// @brief Client to retrieve credentials from the components::Secdist and to
114/// subscribe to their updates.
115class Secdist final {
116public:
117 explicit Secdist(SecdistConfig::Settings settings);
118 ~Secdist();
119
120 /// Returns secdist data loaded on service start.
121 /// Does not support secdist updating during service work.
122 const storages::secdist::SecdistConfig& Get() const;
123
124 /// Returns fresh secdist data (from last update).
125 /// Supports secdist updating during service work.
127
128 /// Subscribes to secdist updates using a member function, named
129 /// `OnSecdistUpdate` by convention. Also immediately invokes the function
130 /// with the current secdist data.
131 template <typename Class>
132 concurrent::AsyncEventSubscriberScope UpdateAndListen(
133 Class* obj,
134 std::string_view name,
135 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
136 );
137
138 bool IsPeriodicUpdateEnabled() const;
139
140private:
141 using EventSource = concurrent::AsyncEventSource<const SecdistConfig&>;
142
143 concurrent::AsyncEventSubscriberScope
144 DoUpdateAndListen(concurrent::FunctionId id, std::string_view name, EventSource::Function&& func);
145
146 class Impl;
147 utils::FastPimpl<Impl, 1280, 16> impl_;
148};
149
150template <typename Class>
151concurrent::AsyncEventSubscriberScope Secdist::UpdateAndListen(
152 Class* obj,
153 std::string_view name,
154 void (Class::*func)(const storages::secdist::SecdistConfig& secdist)
155) {
156 return DoUpdateAndListen(concurrent::FunctionId(obj), name, [obj, func](const SecdistConfig& config) {
157 (obj->*func)(config);
158 });
159}
160
161namespace detail {
162
163template <typename T>
164const T& SecdistModule<T>::Get(const SecdistConfig& config) {
165 return std::any_cast<const T&>(config.Get(typeid(T), index_));
166}
167
168template <typename T>
169std::size_t SecdistModule<T>::index_ = SecdistConfig::Register<T>(&SecdistModule<T>::Factory);
170
171} // namespace detail
172
173} // namespace storages::secdist
174
175USERVER_NAMESPACE_END