| #ifndef THIRD_PARTY_MILOTIC_INTERNAL_CC_AUTHZ_BMCWEB_AUTHORIZER_SINGLETON_H_ |
| #define THIRD_PARTY_MILOTIC_INTERNAL_CC_AUTHZ_BMCWEB_AUTHORIZER_SINGLETON_H_ |
| |
| #include <cstddef> |
| #include <map> |
| #include <string> |
| #include <string_view> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/base/thread_annotations.h" |
| #include "absl/synchronization/mutex.h" |
| #include "boost/beast/http/verb.hpp" // NOLINT |
| #include "authorizer_enums.h" |
| #include "redfish_v1.pb.h" |
| #include "grpcpp/security/auth_context.h" |
| #include "grpcpp/support/status.h" |
| #include "grpcpp/support/string_ref.h" |
| #include "nlohmann/json.hpp" |
| #include "config_parser.h" |
| #include "redfish_authorizer.h" |
| #include "zatar/certificate_metadata.h" |
| |
| namespace milotic::authz { |
| |
| // Performs dynamic authorization based on configuration files. |
| // It encapsulates all states and operations that're needed for BMC |
| // authorization. |
| // This class is thread-safe. |
| class BmcWebAuthorizerSingleton { |
| public: |
| struct RequestState { |
| bool with_trust_bundle = true; |
| bool peer_authenticated = false; |
| std::unordered_set<std::string> peer_privileges; |
| }; |
| |
| virtual ~BmcWebAuthorizerSingleton() = default; |
| |
| // Note: the first call will initialize the function local instance. All |
| // future calls won't change the state of the singleton. |
| // Typical usage: |
| // // Initialize with arguments |
| // BmcWebAuthorizerSingleton::Initialize(...); |
| // // Reference without arguments |
| // BmcWebAuthorizerSingleton::GetInstance().CheckTarget(...); |
| static BmcWebAuthorizerSingleton& Initialize(const AuthorizerOptions& options, |
| std::string_view oauth_key_path); |
| static BmcWebAuthorizerSingleton& GetInstance(); |
| |
| // Checks if the peer's target matches with the BMC. |
| // Shall be called in every RPC. |
| grpc::Status CheckTarget( |
| const grpc::AuthContext& context, |
| const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata, |
| const ::redfish::v1::Request& request) const; |
| |
| // Authorizes peer via its identity and the status of the request. |
| // The identity of the peer can be derived from peer's X.509 SAN, or from |
| // OAuth Token. |
| // Returns OK status if the request is authorized. Otherwise returns |
| // corresponding error. |
| grpc::Status Authorize(std::string_view uri, boost::beast::http::verb verb, |
| const RequestState& request_state) const; |
| |
| // Processes the OAuth workflow and gets peer's privileges |
| // - decodes the token |
| // - verifies the signature of the token |
| // - verifies the peer X.509 certificate matches the subject |
| // - extract peer's privileges |
| grpc::Status GetPrivilegesViaOAuth( |
| const grpc::AuthContext& context, const std::string& token, |
| std::unordered_set<std::string>& peer_privileges) const; |
| |
| // Looks up the peer's privileges by its MTls identity and the authorization |
| // configuration |
| grpc::Status GetPrivilegesViaMTls( |
| const grpc::AuthContext& context, |
| std::unordered_set<std::string>& peer_privileges) const; |
| |
| // Reloads the authorization configuration and all credentials (GMI, OAuth |
| // key, etc). |
| void ReloadConfiguration(); |
| |
| // Records a new subscription for the peer and returns if it is allowed. |
| grpc::Status RecordNewSubscription(const PeerSpiffeIdentity& peer); |
| // Records a new unsubscription for the peer. |
| grpc::Status RecordNewUnsubscription(const PeerSpiffeIdentity& peer); |
| |
| static grpc::Status GetPeerIdentityFromAuthContext( |
| const grpc::AuthContext& context, PeerSpiffeIdentity& peer_identity); |
| |
| static ecclesia::Operation BoostVerbToOperation( |
| boost::beast::http::verb verb); |
| |
| ecclesia::ResourceEntity GetEntityTypeFromRedfishUri( |
| std::string_view uri) const; |
| |
| size_t GetNodeIndexInPatternArray(std::string_view uri) const; |
| |
| // Get current Redfish Privilege Registry |
| nlohmann::json GetRedfishPrivilegeRegistry() const; |
| |
| bool IsBasePrivilegeRegistryFound() const; |
| |
| void SetBasePrivilegesFolder(std::string_view base_privileges_folder); |
| |
| // Reset GMI path, used by unit test only. |
| void ReloadGmiPaths(std::string_view gmi_path); |
| |
| BmcWebAuthorizerSingleton(const AuthorizerOptions& options, |
| std::string_view oauth_key_path); |
| |
| protected: |
| std::string ReadPrimaryFqdnFromGmi() const; |
| std::string ReadOauthKeyFromFile() const; |
| std::vector<std::string> |
| ReadDomainNameFromOfflineNodeEntity(std::string_view primary_fqdn) const; |
| |
| virtual grpc::Status AuthorizeWithoutTrustBundle( |
| std::string_view uri, boost::beast::http::verb verb) const; |
| |
| virtual grpc::Status AuthorizeWithTrustBundle( |
| std::string_view uri, boost::beast::http::verb verb, |
| const RequestState& request_state) const; |
| |
| std::string GetPrimaryFqdn() const { |
| absl::MutexLock lock(&mutex_); |
| return primary_fqdn_; |
| } |
| std::string GetOauthKey() const { |
| absl::MutexLock lock(&mutex_); |
| return oauth_key_; |
| } |
| std::vector<std::string> GetInterfaceFqdns() const |
| { |
| absl::MutexLock lock(&mutex_); |
| return interface_fqdns_; |
| } |
| void SetPrimaryFqdn(std::string_view fqdn) { |
| absl::MutexLock lock(&mutex_); |
| primary_fqdn_ = fqdn; |
| } |
| void SetOauthKey(std::string_view oauth_key) { |
| absl::MutexLock lock(&mutex_); |
| oauth_key_ = oauth_key; |
| } |
| void SetInterfaceFqdns(std::vector<std::string>&& fqdns) |
| { |
| absl::MutexLock lock(&mutex_); |
| interface_fqdns_ = std::move(fqdns); |
| } |
| |
| private: |
| mutable absl::Mutex mutex_; |
| |
| std::string gmi_path_; |
| std::string oauth_key_path_; |
| std::string offline_node_entity_path_; |
| |
| // |primary_fqdn_| is read from GMI file at |gmi_path_|; empty string means an |
| // error happened, e.g., missing or corrupted GMI file |
| std::string primary_fqdn_ ABSL_GUARDED_BY(mutex_); |
| |
| // |interface_fqdns_| is read from offline node entity file; including all |
| // interfaces' domain name |
| std::vector<std::string> interface_fqdns_ ABSL_GUARDED_BY(mutex_); |
| |
| // |oauth_key_| is read from key file at |oauth_key_path_|; empty string means |
| // an error happened, e.g., missing or corrupted GMI file |
| std::string oauth_key_ ABSL_GUARDED_BY(mutex_); |
| |
| RedfishAuthorizer redfish_authorizer_; |
| ::milotic::redfish::CertificateMetadataParser metadata_parser_; |
| }; |
| } // namespace milotic::authz |
| |
| #endif // THIRD_PARTY_MILOTIC_INTERNAL_CC_AUTHZ_BMCWEB_AUTHORIZER_SINGLETON_H_ |