| #include "config_parser.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstddef> |
| #include <cstdint> |
| #include <fstream> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "gmi/machine_identity.pb.h" |
| #include "one/node_entities_api.pb.h" |
| #include "one/offline_node_entities.pb.h" |
| #include "one/resolved_entities.pb.h" |
| #include "one/public_offline_node_entities.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/ascii.h" |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_join.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/synchronization/mutex.h" |
| #include "nlohmann/json.hpp" |
| #include "nlohmann/json_fwd.hpp" |
| #include "json_utils.h" |
| #include "authorized_entity.pb.h" |
| #include "zatar/certificate_metadata.h" |
| |
| namespace milotic::authz { |
| |
| using ::milotic::redfish::CertificateMetadataParser; |
| using ::production_msv::node_entities::ReadOfflineNodeEntityInformation; |
| using ::production_msv::node_entities_proto::OfflineNodeEntityInformation; |
| using ::security_prodid::GoogleMachineIdentityProto; |
| using EntityTagIdentity = AuthzConfiguration::EntityTagIdentity; |
| |
| AuthzConfiguration::AuthzConfiguration( |
| const nlohmann::json& config, absl::string_view offline_node_entities_path, |
| absl::string_view gmi_path) |
| : offline_node_entities_path_(offline_node_entities_path), |
| gmi_path_(gmi_path) { |
| ReloadConfig(config, nlohmann::json()); |
| }; |
| |
| std::optional<AuthzConfiguration::SpiffeIdentityMatcher> |
| AuthzConfiguration::CreateIdentityMatcherFromPeerSpiffeIdentity( |
| const PeerSpiffeIdentity& peer) { |
| AuthzConfiguration::SpiffeIdentityMatcher matcher; |
| matcher.fqdn = peer.fqdn; |
| |
| const std::string spiffe_id = absl::AsciiStrToLower(peer.spiffe_id); |
| // As per SPIFFE specification, SPIFFE schema and trust domain are case |
| // insensitive. |
| // https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#24-spiffe-id-parsing |
| constexpr std::string_view kSpiffeSchema = "spiffe://"; |
| if (!absl::StartsWithIgnoreCase(spiffe_id, kSpiffeSchema)) { |
| return std::nullopt; |
| } |
| |
| std::size_t path_start = spiffe_id.find_first_of('/', kSpiffeSchema.size()); |
| if (path_start == std::string_view::npos) { |
| return std::nullopt; |
| } |
| |
| matcher.spiffe_id_matcher.path = spiffe_id.substr(path_start); |
| |
| const std::string trust_domain = absl::AsciiStrToLower(spiffe_id.substr( |
| kSpiffeSchema.size(), path_start - kSpiffeSchema.size())); |
| |
| // Trust domain consists of (issuing-role).(context).(realm).(suffix) |
| // See go/zatar-prod-certificates for details. |
| std::vector<std::string_view> trust_domain_pieces = |
| absl::StrSplit(trust_domain, '.'); |
| |
| // There should be at least 4 elements in this array for a property trust |
| // domain |
| if (trust_domain_pieces.size() < 4) { |
| return std::nullopt; |
| } |
| |
| matcher.spiffe_id_matcher.issuer = trust_domain_pieces[0]; |
| matcher.spiffe_id_matcher.ca_context = trust_domain_pieces[1]; |
| matcher.spiffe_id_matcher.realm = trust_domain_pieces[2]; |
| |
| matcher.spiffe_id_matcher.trust_domain_suffix = |
| absl::StrCat(".", absl::StrJoin(trust_domain_pieces.begin() + 3, |
| trust_domain_pieces.end(), ".")); |
| |
| return matcher; |
| } |
| |
| std::optional<AuthzConfiguration::SpiffeIdentityMatcher> |
| AuthzConfiguration::ParseSpiffeIdentityMatcher(const nlohmann::json& config) { |
| const nlohmann::json* spiffe_id_matcher = |
| GetValueAsJson(config, "spiffe_id_matcher"); |
| if (spiffe_id_matcher == nullptr) { |
| return std::nullopt; |
| } |
| const std::string* trust_domain_suffix = |
| GetValueAsString(*spiffe_id_matcher, "trust_domain_suffix"); |
| if (trust_domain_suffix == nullptr) { |
| LOG(WARNING) << "Warning: trust_domain_suffix isn't a string; " |
| << "|spiffe_id_matcher|=" << spiffe_id_matcher->dump(2); |
| return std::nullopt; |
| } |
| |
| AuthzConfiguration::SpiffeIdentityMatcher matcher; |
| matcher.spiffe_id_matcher.trust_domain_suffix = |
| absl::AsciiStrToLower(*trust_domain_suffix); |
| |
| const std::string* path = GetValueAsString(*spiffe_id_matcher, "path"); |
| if (path == nullptr) { |
| return std::nullopt; |
| } |
| matcher.spiffe_id_matcher.path = absl::AsciiStrToLower(*path); |
| |
| const std::string* ca_context = |
| GetValueAsString(*spiffe_id_matcher, "ca_context"); |
| if (ca_context == nullptr) { |
| return std::nullopt; |
| } |
| matcher.spiffe_id_matcher.ca_context = absl::AsciiStrToLower(*ca_context); |
| |
| const std::string* issuer = GetValueAsString(*spiffe_id_matcher, "issuer"); |
| if (issuer != nullptr) { |
| matcher.spiffe_id_matcher.issuer = absl::AsciiStrToLower(*issuer); |
| } |
| |
| // If realm_check is enabled, get current realm from GMI |
| const bool* realm_check = GetValueAsBool(*spiffe_id_matcher, "realm_check"); |
| if (realm_check != nullptr && *realm_check) { |
| matcher.spiffe_id_matcher.realm = GetNodeRealm(); |
| } |
| |
| const std::string* fqdn = GetValueAsString(config, "fqdn"); |
| // Non-string value |
| if (fqdn == nullptr && config.find("fqdn") != config.end()) { |
| return std::nullopt; |
| } |
| |
| if (fqdn != nullptr) { |
| matcher.fqdn = *fqdn; |
| } |
| |
| return matcher; |
| } |
| |
| bool AuthzConfiguration::IsResourceOwnerExisting( |
| const ResourceOwner& resource_owner) const { |
| absl::MutexLock lock(&mutex_); |
| return std::binary_search(resource_owners_.begin(), resource_owners_.end(), |
| resource_owner); |
| } |
| |
| bool AuthzConfiguration::IsPeerResourceOwner( |
| const PeerSpiffeIdentity& peer) const { |
| std::optional<SpiffeIdentityMatcher> peer_matcher = |
| CreateIdentityMatcherFromPeerSpiffeIdentity(peer); |
| |
| if (peer_matcher == std::nullopt) { |
| LOG(WARNING) << "Peer spiffe id" << peer.spiffe_id << "is malformed"; |
| return false; |
| } |
| ResourceOwner peer_to_resource_owner{.identity_matcher = *peer_matcher}; |
| |
| return IsResourceOwnerExisting(peer_to_resource_owner); |
| } |
| |
| absl::StatusOr<std::string> AuthzConfiguration::GetRedfishRoleOfSpiffeMatcher( |
| const SpiffeUser& spiffe_user) const { |
| absl::MutexLock lock(&mutex_); |
| |
| auto it = |
| std::lower_bound(spiffe_users_.begin(), spiffe_users_.end(), spiffe_user); |
| // Comparing equality |
| if (it == spiffe_users_.end() || *it != spiffe_user) { |
| return absl::NotFoundError( |
| absl::StrCat("No user with matching spiffe; spiffe_user is ", |
| spiffe_user.identity_matcher.DebugString())); |
| } |
| |
| return it->redfish_role; |
| } |
| |
| std::string AuthzConfiguration::GetPeerRedfishRole( |
| const PeerSpiffeIdentity& peer) const { |
| std::optional<SpiffeIdentityMatcher> peer_matcher = |
| CreateIdentityMatcherFromPeerSpiffeIdentity(peer); |
| if (peer_matcher == std::nullopt) { |
| LOG(WARNING) << "Peer spiffe id" << peer.spiffe_id << "is malformed"; |
| return ""; |
| } |
| |
| SpiffeUser peer_to_spiffe_user{.identity_matcher = *peer_matcher, |
| .redfish_role = ""}; |
| |
| absl::StatusOr<std::string> role = |
| GetRedfishRoleOfSpiffeMatcher(peer_to_spiffe_user); |
| |
| if (!role.ok()) { |
| LOG(WARNING) << role.status(); |
| return ""; |
| } |
| |
| return *role; |
| } |
| |
| uint64_t AuthzConfiguration::GetSampleRateLimit( |
| const std::string& peer_role) const { |
| absl::MutexLock lock(&mutex_); |
| auto it = role_to_sample_rate_limit_.find(peer_role); |
| if (it != role_to_sample_rate_limit_.end()) { |
| return it->second; |
| } |
| return 0; |
| } |
| |
| std::unordered_set<std::string> AuthzConfiguration::GetPeerRedfishPrivileges( |
| const PeerSpiffeIdentity& peer) const { |
| std::optional<SpiffeIdentityMatcher> peer_matcher = |
| CreateIdentityMatcherFromPeerSpiffeIdentity(peer); |
| |
| if (peer_matcher == std::nullopt) { |
| LOG(WARNING) << "Peer spiffe id" << peer.spiffe_id << "is malformed"; |
| return {"NoAuth"}; |
| } |
| |
| SpiffeUser peer_to_spiffe_user{.identity_matcher = *peer_matcher, |
| .redfish_role = ""}; |
| |
| absl::StatusOr<std::string> role = |
| GetRedfishRoleOfSpiffeMatcher(peer_to_spiffe_user); |
| |
| if (!role.ok()) { |
| LOG(WARNING) << role.status(); |
| return {"NoAuth"}; |
| } |
| |
| return GetRedfishPrivileges(*role); |
| } |
| |
| std::unordered_set<std::string> AuthzConfiguration::GetPeerRedfishPrivileges( |
| const PeerLoasIdentity& peer) const { |
| std::optional<std::string> role = GetRoleByUsername(peer.username); |
| if (role) { |
| return GetRedfishPrivileges(*role); |
| } |
| role = GetRoleByGroup(peer.username); |
| if (role) { |
| return GetRedfishPrivileges(*role); |
| } |
| return {"NoAuth"}; |
| } |
| |
| absl::string_view AuthzConfiguration::GetCertNodeOwner( |
| absl::string_view node_owner) const { |
| if (IsHwopsState()) { |
| return kHwopsRole; |
| } |
| return node_owner; |
| } |
| |
| std::vector<AuthzConfiguration::SpiffeIdentityMatcher> |
| AuthzConfiguration::ParseSpiffeIdentityMatchersUsingOne( |
| const nlohmann::json& config) const { |
| const std::string* entity = GetValueAsString(config, "entity"); |
| if (entity == nullptr) { |
| return {}; |
| } |
| |
| AuthorizedEntity entity_enum; |
| if (!AuthorizedEntity_Parse(*entity, &entity_enum)) { |
| LOG(WARNING) << "Warning: entity " << *entity |
| << " is not a valid AuthorizedEntity"; |
| return {}; |
| }; |
| std::vector<AuthzConfiguration::SpiffeIdentityMatcher> matchers; |
| |
| for (const std::string& entity_tag : |
| GetAllEntityTagsFromAuthorizedEntity(entity_enum)) { |
| AuthzConfiguration::SpiffeIdentityMatcher matcher; |
| absl::StatusOr<AuthzConfiguration::EntityTagIdentity> entity_tag_identity = |
| GetEntityTagIdentity(entity_tag); |
| if (!entity_tag_identity.ok()) { |
| continue; |
| } |
| matcher.fqdn = |
| absl::StrCat(entity_tag_identity->hostname, ".prod.google.com"); |
| |
| matcher.spiffe_id_matcher.trust_domain_suffix = ".prod.google.com"; |
| matcher.spiffe_id_matcher.ca_context = |
| CertificateMetadataParser::kCaContextNode; |
| |
| matcher.spiffe_id_matcher.path = |
| absl::StrCat("/role/", GetCertNodeOwner(entity_tag_identity->owner)); |
| |
| matchers.push_back(std::move(matcher)); |
| } |
| |
| return matchers; |
| } |
| |
| void AuthzConfiguration::ReloadSampleRateLimit(const nlohmann::json& config) { |
| if (config.is_discarded()) { |
| return; |
| } |
| const nlohmann::json::array_t* role_sample_rate_limits = |
| GetValueAsArray(config, "role_sample_rate_limits"); |
| if (role_sample_rate_limits == nullptr) { |
| return; |
| } |
| std::unordered_map<std::string, uint64_t> new_role_to_sample_rate_limit; |
| for (const nlohmann::json& role_sample_rate_limit : |
| *role_sample_rate_limits) { |
| const std::string* role = |
| GetValueAsString(role_sample_rate_limit, "redfish_role"); |
| if (role == nullptr) { |
| continue; |
| } |
| const uint64_t* sample_rate_limit = |
| GetValueAsUint(role_sample_rate_limit, "sample_rate_limit"); |
| if (sample_rate_limit == nullptr) { |
| continue; |
| } |
| new_role_to_sample_rate_limit[*role] = *sample_rate_limit; |
| } |
| absl::MutexLock lock(&mutex_); |
| role_to_sample_rate_limit_ = std::move(new_role_to_sample_rate_limit); |
| } |
| |
| void AuthzConfiguration::ReloadResourceOwners(const nlohmann::json& config) { |
| if (config.is_discarded()) { |
| return; |
| } |
| const nlohmann::json::array_t* resource_owners = |
| GetValueAsArray(config, "resource_owners"); |
| if (resource_owners == nullptr) { |
| return; |
| } |
| std::vector<ResourceOwner> new_resource_owners; |
| for (const nlohmann::json& resource_owner : *resource_owners) { |
| // If the spiffe matcher is explicitly defined, parse it. |
| if (resource_owner.contains("spiffe_id_matcher")) { |
| ResourceOwner owner; |
| std::optional<SpiffeIdentityMatcher> matcher = |
| ParseSpiffeIdentityMatcher(resource_owner); |
| if (matcher) { |
| owner.identity_matcher = *matcher; |
| new_resource_owners.push_back(std::move(owner)); |
| } |
| continue; |
| } |
| // Otherwise continue with entity based parsing |
| std::vector<SpiffeIdentityMatcher> matchers = |
| ParseSpiffeIdentityMatchersUsingOne(resource_owner); |
| for (const SpiffeIdentityMatcher& matcher : matchers) { |
| ResourceOwner owner; |
| owner.identity_matcher = matcher; |
| new_resource_owners.push_back(std::move(owner)); |
| } |
| } |
| |
| std::sort(new_resource_owners.begin(), new_resource_owners.end()); |
| |
| absl::MutexLock lock(&mutex_); |
| resource_owners_ = std::move(new_resource_owners); |
| } |
| |
| void AuthzConfiguration::ReloadRedfishUsers(const nlohmann::json& config) { |
| if (config.is_discarded()) { |
| return; |
| } |
| const nlohmann::json::array_t* exchange_mappings = |
| GetValueAsArray(config, "exchange_mappings"); |
| if (exchange_mappings == nullptr) { |
| return; |
| } |
| std::vector<SpiffeUser> new_spiffe_users; |
| std::unordered_map<std::string, std::string> new_username_to_role; |
| std::unordered_map<std::string, std::string> new_group_to_role; |
| for (const nlohmann::json& exchange_mapping : *exchange_mappings) { |
| const std::string* redfish_role = |
| GetValueAsString(exchange_mapping, "redfish_role"); |
| if (redfish_role == nullptr) { |
| continue; |
| } |
| // If peer is explicitly defined, then create exchange mapping with peer. |
| if (exchange_mapping.contains("peer")) { |
| const nlohmann::json* peer = GetValueAsJson(exchange_mapping, "peer"); |
| if (peer == nullptr) { |
| continue; |
| } |
| // If it is username based, parse the username and continue |
| const std::string* username = GetValueAsString(*peer, "username"); |
| if (username != nullptr) { |
| new_username_to_role[*username] = *redfish_role; |
| continue; |
| } |
| // If it is group based, parse the group and continue |
| const std::string* group = GetValueAsString(*peer, "group"); |
| if (group != nullptr) { |
| new_group_to_role[*group] = *redfish_role; |
| continue; |
| } |
| // Otherwise, it is spiffe based |
| SpiffeUser user; |
| std::optional<SpiffeIdentityMatcher> matcher = |
| ParseSpiffeIdentityMatcher(*peer); |
| if (!matcher) { |
| continue; |
| } |
| user.identity_matcher = *matcher; |
| user.redfish_role = *redfish_role; |
| new_spiffe_users.push_back(std::move(user)); |
| continue; |
| } |
| |
| // Otherwise, continue with entity based parsing |
| std::vector<SpiffeIdentityMatcher> matchers = |
| ParseSpiffeIdentityMatchersUsingOne(exchange_mapping); |
| for (const SpiffeIdentityMatcher& matcher : matchers) { |
| SpiffeUser user; |
| user.identity_matcher = matcher; |
| user.redfish_role = *redfish_role; |
| // TODO(edwarddl): This is n^2. We can make it linear with refactoring of |
| // vector into hashmap. Do profiling and benchmarking to see if this would |
| // lead to better performance. |
| |
| // Update the redfish_role of the matcher if |
| // the user already exists. |
| auto it = |
| std::find(new_spiffe_users.begin(), new_spiffe_users.end(), user); |
| if (it != new_spiffe_users.end()) { |
| it->redfish_role = *redfish_role; |
| continue; |
| } |
| |
| // Add the user to the list if they aren't there |
| new_spiffe_users.push_back(std::move(user)); |
| } |
| } |
| |
| std::sort(new_spiffe_users.begin(), new_spiffe_users.end()); |
| |
| absl::MutexLock lock(&mutex_); |
| spiffe_users_ = std::move(new_spiffe_users); |
| username_to_role_ = std::move(new_username_to_role); |
| group_to_role_ = std::move(new_group_to_role); |
| } |
| |
| static void AddRoleToPrivilegesToMap( |
| const nlohmann::json& role_privileges, |
| std::unordered_map<std::string, std::unordered_set<std::string>>& |
| role_to_privileges) { |
| if (role_privileges.is_discarded()) { |
| return; |
| } |
| |
| for (const nlohmann::json& role_privilege : role_privileges) { |
| const std::string* role = GetValueAsString(role_privilege, "redfish_role"); |
| if (role == nullptr) { |
| continue; |
| } |
| const nlohmann::json* privileges = |
| GetValueAsJson(role_privilege, "privileges"); |
| if (privileges == nullptr) { |
| continue; |
| } |
| std::unordered_set<std::string> privileges_set; |
| const nlohmann::json::array_t* base_privileges = |
| GetValueAsArray(*privileges, "base_privileges"); |
| if (base_privileges != nullptr) { |
| for (const nlohmann::json& privilege : *base_privileges) { |
| const std::string* privilege_str = |
| privilege.get_ptr<const std::string*>(); |
| if (privilege_str == nullptr) { |
| continue; |
| } |
| privileges_set.insert(*privilege_str); |
| } |
| } |
| const nlohmann::json::array_t* oem_privileges = |
| GetValueAsArray(*privileges, "oem_privileges"); |
| if (oem_privileges != nullptr) { |
| for (const nlohmann::json& privilege : *oem_privileges) { |
| const std::string* privilege_str = |
| privilege.get_ptr<const std::string*>(); |
| if (privilege_str == nullptr) { |
| continue; |
| } |
| privileges_set.insert(*privilege_str); |
| } |
| } |
| |
| privileges_set.insert("NoAuth"); |
| role_to_privileges[*role] = std::move(privileges_set); |
| } |
| } |
| |
| void AuthzConfiguration::ReloadRoleToPrivileges( |
| const nlohmann::json& config, const nlohmann::json& platform_config) { |
| if (config.is_discarded()) { |
| return; |
| } |
| const nlohmann::json::array_t* role_privileges = |
| GetValueAsArray(config, "role_privileges_mappings"); |
| std::unordered_map<std::string, std::unordered_set<std::string>> |
| new_role_to_privileges; |
| if (role_privileges != nullptr) { |
| AddRoleToPrivilegesToMap(*role_privileges, new_role_to_privileges); |
| } |
| |
| const nlohmann::json::array_t* platform_role_privileges = |
| GetValueAsArray(platform_config, "role_privileges_mappings"); |
| if (platform_role_privileges != nullptr) { |
| AddRoleToPrivilegesToMap(*platform_role_privileges, new_role_to_privileges); |
| } |
| |
| absl::MutexLock lock(&mutex_); |
| role_to_privileges_ = std::move(new_role_to_privileges); |
| } |
| |
| void AuthzConfiguration::ReloadOfflineNodeEntities() { |
| if (offline_node_entities_path_.empty()) { |
| return; |
| } |
| |
| absl::StatusOr<OfflineNodeEntityInformation> parsed_offline_node_entities = |
| ReadOfflineNodeEntityInformation(offline_node_entities_path_); |
| |
| if (!parsed_offline_node_entities.ok()) { |
| LOG(WARNING) << "Could not read offline node entities information from " |
| << offline_node_entities_path_ << ": " |
| << parsed_offline_node_entities.status().message(); |
| return; |
| } |
| |
| absl::MutexLock lock(&mutex_); |
| offline_node_entities_ = std::move(*parsed_offline_node_entities); |
| } |
| |
| void AuthzConfiguration::ReloadGmi() { |
| if (gmi_path_.empty()) { |
| return; |
| } |
| |
| std::ifstream gmi_file(gmi_path_); |
| if (!gmi_file.is_open()) { |
| LOG(ERROR) << "GMI file at '" << gmi_path_ << "' is missing."; |
| return; |
| } |
| GoogleMachineIdentityProto gmi; |
| if (!gmi.ParseFromIstream(&gmi_file)) { |
| LOG(ERROR) << "GMI parsing failed at '" << gmi_path_ << "'"; |
| return; |
| } |
| |
| absl::MutexLock lock(&mutex_); |
| gmi_ = std::move(gmi); |
| } |
| |
| void AuthzConfiguration::ReloadConfig(const nlohmann::json& config, |
| const nlohmann::json& platform_config) { |
| if (config.is_discarded()) { |
| LOG(WARNING) << "Warning: config isn't a valid JSON"; |
| return; |
| } |
| |
| ReloadGmi(); |
| ReloadOfflineNodeEntities(); |
| ReloadResourceOwners(config); |
| ReloadRedfishUsers(config); |
| ReloadRoleToPrivileges(config, platform_config); |
| ReloadSampleRateLimit(config); |
| } |
| |
| std::unordered_set<std::string> AuthzConfiguration::GetOemPrivileges() const { |
| std::unordered_set<std::string> privileges; |
| |
| absl::MutexLock lock(&mutex_); |
| for (const auto& [role, privilege_set] : role_to_privileges_) { |
| for (const std::string& privilege : privilege_set) { |
| // If the privilege is not a default redfish privilege, it is OEM. |
| if (!std::binary_search(kDefaultRedfishPrivileges.begin(), |
| kDefaultRedfishPrivileges.end(), privilege)) { |
| privileges.insert(privilege); |
| } |
| } |
| } |
| |
| return privileges; |
| } |
| |
| absl::StatusOr<EntityTagIdentity> AuthzConfiguration::GetEntityTagIdentity( |
| absl::string_view entity_tag) const { |
| absl::MutexLock lock(&mutex_); |
| auto it = |
| offline_node_entities_.resolved_config().entities().find(entity_tag); |
| if (it == offline_node_entities_.resolved_config().entities().end()) { |
| return absl::NotFoundError("Entity Tag Not Found"); |
| } |
| return EntityTagIdentity{.hostname = it->second.hostname(), |
| .owner = it->second.owner()}; |
| } |
| |
| std::string AuthzConfiguration::GetMachineManagerEntityTag() const { |
| absl::MutexLock lock(&mutex_); |
| return offline_node_entities_.resolved_config() |
| .ecclesia_machine_manager_entity(); |
| } |
| |
| std::vector<std::string> AuthzConfiguration::GetAllComputeNodeEntityTags() |
| const { |
| std::vector<std::string> entity_tags; |
| absl::MutexLock lock(&mutex_); |
| for (const auto& [entity_tag, entity] : |
| offline_node_entities_.resolved_config().entities()) { |
| if (entity.node_type_info().is_compute()) { |
| entity_tags.push_back(entity_tag); |
| } |
| } |
| |
| return entity_tags; |
| } |
| |
| std::vector<std::string> AuthzConfiguration::GetEntityTagsFromRedfishSystemId( |
| absl::string_view system_id) const { |
| std::vector<std::string> entity_tags; |
| absl::MutexLock lock(&mutex_); |
| for (const auto& [entity_tag, entity] : |
| offline_node_entities_.resolved_config().entities()) { |
| if (entity.redfish_system_id() == system_id) { |
| entity_tags.push_back(entity_tag); |
| } |
| } |
| |
| return entity_tags; |
| } |
| |
| std::vector<std::string> |
| AuthzConfiguration::GetAllEntityTagsFromAuthorizedEntity( |
| AuthorizedEntity entity) const { |
| switch (entity) { |
| case AuthorizedEntity::ComputeNode: |
| return GetAllComputeNodeEntityTags(); |
| case AuthorizedEntity::ComputeNode1: |
| return GetEntityTagsFromRedfishSystemId("system1"); |
| case AuthorizedEntity::ComputeNode2: |
| return GetEntityTagsFromRedfishSystemId("system2"); |
| case AuthorizedEntity::EcclesiaMachineManagerEntity: |
| return {GetMachineManagerEntityTag()}; |
| case AuthorizedEntity::Undefined: |
| return {}; |
| case AuthorizedEntity_INT_MIN_SENTINEL_DO_NOT_USE_: |
| case AuthorizedEntity_INT_MAX_SENTINEL_DO_NOT_USE_: |
| return {}; |
| } |
| return {}; |
| } |
| |
| bool AuthzConfiguration::IsHwopsState() const { |
| absl::MutexLock lock(&mutex_); |
| if (gmi_.has_in_hwops_state()) { |
| return gmi_.in_hwops_state(); |
| } |
| return false; |
| } |
| |
| std::string AuthzConfiguration::GetNodeRealm() const { |
| absl::MutexLock lock(&mutex_); |
| // GMI only supports 1 realm |
| if (gmi_.security_realms_size() != 1) { |
| LOG(ERROR) << "Machine GMI's realm is broken. All realm specific Borg jobs " |
| "will be denied."; |
| return ""; |
| } |
| return gmi_.security_realms(0); |
| } |
| |
| } // namespace milotic::authz |