blob: 202b9c03b3b4139d798f42a6202c7ac9da3ff28d [file] [log] [blame]
#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 <cstdint>
#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);
// Returns the peer role from the auth context.
// On error, peer_role will be empty.
grpc::Status GetPeerRoleFromAuthContext(const grpc::AuthContext& context,
std::string& peer_role) const;
// Returns the sample rate limit for the peer role.
uint64_t GetSampleRateLimit(const std::string& peer_role) const;
// Reloads the redfish authorizer with the given configuration path.
void ReloadRedfishAuthorizer(const std::string& configuration_path);
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_