From 09913857ca560453322c7f2b4932f2c86191e763 Mon Sep 17 00:00:00 2001 From: Gymee Date: Thu, 6 Jan 2022 21:20:45 +0800 Subject: [PATCH] check client access authority Signed-off-by: Gymee Change-Id: I296507f759354cf22ff48efd0d1e39eb7d0abea6 --- bundle.json | 5 +- common/include/device_profile_errors.h | 1 + permission/BUILD.gn | 22 ++ permission/authority.json | 2 + services/core/BUILD.gn | 2 + .../include/authority/authority_manager.h | 74 ++++ .../subscribemanager/profile_change_handler.h | 2 + .../subscribemanager/profile_event_handler.h | 3 +- .../core/src/authority/authority_manager.cpp | 325 ++++++++++++++++++ .../distributed_device_profile_service.cpp | 22 +- .../src/distributed_device_profile_stub.cpp | 5 + .../profile_change_handler.cpp | 21 ++ services/core/test/BUILD.gn | 15 +- .../test/unittest/profile_authority_test.cpp | 209 +++++++++++ .../core/test/unittest/profile_crud_test.cpp | 111 +----- .../resource/authority/invalid_authority.json | 62 ++++ test/resource/authority/valid_authority.json | 17 + test/resource/ohos_test.xml | 22 ++ 18 files changed, 812 insertions(+), 108 deletions(-) create mode 100644 permission/BUILD.gn create mode 100644 permission/authority.json mode change 100755 => 100644 services/core/BUILD.gn create mode 100644 services/core/include/authority/authority_manager.h create mode 100644 services/core/src/authority/authority_manager.cpp mode change 100755 => 100644 services/core/src/distributed_device_profile_service.cpp create mode 100644 services/core/test/unittest/profile_authority_test.cpp create mode 100644 test/resource/authority/invalid_authority.json create mode 100644 test/resource/authority/valid_authority.json create mode 100644 test/resource/ohos_test.xml diff --git a/bundle.json b/bundle.json index b039cf8..82eed6c 100644 --- a/bundle.json +++ b/bundle.json @@ -4,7 +4,7 @@ "version": "3.1", "publishAs": "code-segment", "segment": { - "destpath": "foundation/deviceprofile/device_profile_core" + "destPath": "foundation/deviceprofile/device_profile_core" }, "dirs": {}, "scripts": {}, @@ -35,7 +35,8 @@ "sub_component": [ "//foundation/deviceprofile/device_profile_core/sa_profile:dps_sa_profile", "//foundation/deviceprofile/device_profile_core/services/core:distributed_device_profile", - "//foundation/deviceprofile/device_profile_core/tools/dp:dp" + "//foundation/deviceprofile/device_profile_core/tools/dp:dp", + "//foundation/deviceprofile/device_profile_core/permission:authority_json" ], "inner_kits": [ { diff --git a/common/include/device_profile_errors.h b/common/include/device_profile_errors.h index ad00a33..5dff3e9 100755 --- a/common/include/device_profile_errors.h +++ b/common/include/device_profile_errors.h @@ -39,6 +39,7 @@ enum { ERR_DP_SUBSCRIBE_LIMIT_EXCEEDED = 98566152, ERR_DP_POST_TASK_FAILED = 98566153, ERR_DP_DEVICE_SYNC_BUSY = 98566154, + ERR_DP_PERMISSION_DENIED = 98566155, }; } // namespace DeviceProfile } // namespace OHOS diff --git a/permission/BUILD.gn b/permission/BUILD.gn new file mode 100644 index 0000000..60c5407 --- /dev/null +++ b/permission/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") + +ohos_prebuilt_etc("authority_json") { + install_enable = true + source = "authority.json" + relative_install_dir = "deviceprofile" + part_name = "device_profile_core" + subsystem_name = "deviceprofile" +} diff --git a/permission/authority.json b/permission/authority.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/permission/authority.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/services/core/BUILD.gn b/services/core/BUILD.gn old mode 100755 new mode 100644 index d2789c3..d7039c9 --- a/services/core/BUILD.gn +++ b/services/core/BUILD.gn @@ -23,6 +23,7 @@ config("device_profile_core_config") { visibility = [ ":*" ] include_dirs = [ "include", + "include/authority", "include/devicemanager", "include/contentsensor", "include/dbstorage", @@ -36,6 +37,7 @@ config("device_profile_core_config") { ohos_shared_library("distributed_device_profile") { install_enable = true sources = [ + "src/authority/authority_manager.cpp", "src/contentsensor/content_collector.cpp", "src/contentsensor/content_sensor_manager.cpp", "src/contentsensor/device_info_collector.cpp", diff --git a/services/core/include/authority/authority_manager.h b/services/core/include/authority/authority_manager.h new file mode 100644 index 0000000..b27d57e --- /dev/null +++ b/services/core/include/authority/authority_manager.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_DISTRIBUTED_DEVICE_PROFILE_AUTHORITY_MANAGER_H +#define OHOS_DISTRIBUTED_DEVICE_PROFILE_AUTHORITY_MANAGER_H + +#include +#include + +#include "single_instance.h" + +#include "nlohmann/json.hpp" + +namespace OHOS { +namespace DeviceProfile { +enum AuthValue : uint16_t { + // 0000 0000 0000 0001, the authority of read + AUTH_R = 0x0001, + // 0000 0000 0000 0010, the authority of write + AUTH_W = 0x0002, + // 0000 0000 0000 0011, the authority of both read and write + AUTH_RW = 0x0003 +}; + +class AuthorityManager { + DECLARE_SINGLE_INSTANCE(AuthorityManager); + +public: + bool Init(); + + bool CheckCallerTrust(); + bool CheckInterfaceAuthority(const std::string& ifaceName); + bool CheckServiceAuthority(AuthValue authVal, const std::string& serviceId); + bool CheckServicesAuthority(AuthValue authVal, const std::vector& serviceIds); + +private: + bool LoadAuthorityCfg(const std::string& filePath); + void InitSupportedInterfaces(); + + void ValidateAuthorityCfg(); + bool ValidateProcess(nlohmann::json& processJson); + bool ValidateInterfaces(nlohmann::json& interfacesJson); + bool ValidateServices(nlohmann::json& servicesJson); + bool ValidateServicesHelper(nlohmann::json& servicesJson); + bool ValidateService(const nlohmann::json& authValJson, bool readOnly); + + bool CheckServicesAuth(const nlohmann::json& servicesJson, AuthValue authVal, + const std::vector& serviceIds); + bool CheckPrefixServiceAuth(const nlohmann::json& prefixSvcsJson, + const std::string& serviceId, AuthValue authVal); + bool CheckSpecificServiceAuth(const nlohmann::json& specificSvcsJson, + const std::string& serviceId, AuthValue authVal); + + std::string GetCallingProcName(); + +private: + nlohmann::json authJson_; + std::unordered_set supportedInterfaces_; +}; +} // namespace DeviceProfile +} // namespace OHOS +#endif // OHOS_DISTRIBUTED_DEVICE_PROFILE_AUTHORITY_MANAGER_H \ No newline at end of file diff --git a/services/core/include/subscribemanager/profile_change_handler.h b/services/core/include/subscribemanager/profile_change_handler.h index acd18a4..3415c92 100755 --- a/services/core/include/subscribemanager/profile_change_handler.h +++ b/services/core/include/subscribemanager/profile_change_handler.h @@ -40,6 +40,8 @@ public: private: int32_t Register() override; int32_t Unregister() override; + int32_t Subscribe(const SubscribeInfo& subscribeInfo, + const sptr& profileEventNotifier) override; void ConvertEntry(const std::vector& entries, ProfileChangeType changeType, std::vector& profileEntries, Service2Index& service2Index); diff --git a/services/core/include/subscribemanager/profile_event_handler.h b/services/core/include/subscribemanager/profile_event_handler.h index 3ea7505..8ab79ea 100755 --- a/services/core/include/subscribemanager/profile_event_handler.h +++ b/services/core/include/subscribemanager/profile_event_handler.h @@ -47,7 +47,8 @@ public: bool Init(); bool IsRegistered() const; - int32_t Subscribe(const SubscribeInfo& subscribeInfo, const sptr& profileEventNotifier); + virtual int32_t Subscribe(const SubscribeInfo& subscribeInfo, + const sptr& profileEventNotifier); int32_t Unsubscribe(const sptr& profileEventNotifier); void OnSubscriberDied(const sptr& profileEventNotifier); diff --git a/services/core/src/authority/authority_manager.cpp b/services/core/src/authority/authority_manager.cpp new file mode 100644 index 0000000..b33836e --- /dev/null +++ b/services/core/src/authority/authority_manager.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "authority_manager.h" + +#include + +#include "file_ex.h" +#include "ipc_skeleton.h" +#include "securec.h" + +#include "device_profile_log.h" + +namespace OHOS { +namespace DeviceProfile { +namespace { +const std::string TAG = "AuthorityManager"; + +constexpr int32_t BUF_SIZE = 256; +constexpr int32_t ROOT_UID = 0; +constexpr int32_t SYSTEM_UID = 1000; +const std::string INTERFACES = "interfacesAuthority"; +const std::string SERVICES = "servicesAuthority"; +const std::string SERVICES_ALL = "all"; +const std::string SERVICES_SPECIFIC = "specific"; +const std::string SERVICES_PREFIX = "prefix"; +const std::string AUTHORITY_JSON_PATH = "/system/etc/deviceprofile/authority.json"; +} + +IMPLEMENT_SINGLE_INSTANCE(AuthorityManager); + +bool AuthorityManager::Init() +{ + if (!LoadAuthorityCfg(AUTHORITY_JSON_PATH)) { + return false; + } + InitSupportedInterfaces(); + ValidateAuthorityCfg(); + HILOGI("init succeeded"); + return true; +} + +void AuthorityManager::InitSupportedInterfaces() +{ + supportedInterfaces_ = { "sync" }; +} + +bool AuthorityManager::LoadAuthorityCfg(const std::string& filePath) +{ + std::ifstream ifs(filePath.c_str()); + if (!ifs.good()) { + HILOGE("load json file failed"); + return false; + } + authJson_ = nlohmann::json::parse(ifs, nullptr, false); + if (authJson_.is_discarded()) { + HILOGE("parse failed"); + return false; + } + HILOGD("authority json %{public}s", authJson_.dump().c_str()); + return true; +} + +void AuthorityManager::ValidateAuthorityCfg() +{ + for (auto iter = authJson_.begin(); iter != authJson_.end();) { + if (!ValidateProcess(iter.value())) { + HILOGE("authority for proc %{public}s is invalid, deleted", iter.key().c_str()); + authJson_.erase(iter++); + } else { + iter++; + } + } +} + +bool AuthorityManager::ValidateProcess(nlohmann::json& processJson) +{ + for (auto& [key, value] : processJson.items()) { + if (key != INTERFACES && key != SERVICES) { + HILOGE("invalid key:%{public}s under proc", key.c_str()); + return false; + } + if (key == INTERFACES && !ValidateInterfaces(value)) { + return false; + } + if (key == SERVICES && !ValidateServices(value)) { + return false; + } + } + return true; +} + +bool AuthorityManager::ValidateInterfaces(nlohmann::json& interfacesJson) +{ + if (interfacesJson.empty()) { + return false; + } + + for (auto& [key, value] : interfacesJson.items()) { + if (supportedInterfaces_.find(key) == supportedInterfaces_.end()) { + return false; + } + // value should be type of object, e.g. "sync": {} + if (!value.is_object()) { + return false; + } + } + return true; +} + +bool AuthorityManager::ValidateServices(nlohmann::json& servicesJson) +{ + if (servicesJson.empty()) { + return false; + } + + if (servicesJson.contains(SERVICES_ALL)) { + // currently only read permission is allowed being with SERVICES_ALL + if (!ValidateService(servicesJson[SERVICES_ALL], true)) { + return false; + } + } + + if (servicesJson.contains(SERVICES_SPECIFIC) && + !ValidateServicesHelper(servicesJson[SERVICES_SPECIFIC])) { + return false; + } + if (servicesJson.contains(SERVICES_PREFIX) && + !ValidateServicesHelper(servicesJson[SERVICES_PREFIX])) { + return false; + } + return true; +} + +bool AuthorityManager::ValidateServicesHelper(nlohmann::json& servicesJson) +{ + for (auto& [key, value] : servicesJson.items()) { + if (!ValidateService(value, false)) { + HILOGW("service:%{public}s is invalid, deleted", key.c_str()); + return false; + } + } + return true; +} + +bool AuthorityManager::ValidateService(const nlohmann::json& authValJson, bool readOnly) +{ + if (!authValJson.is_number_unsigned()) { + HILOGE("not number type"); + return false; + } + auto authVal = authValJson.get(); + // single AUTH_W is meaningless and disallowed + if (authVal != AuthValue::AUTH_RW && + authVal != AuthValue::AUTH_R) { + HILOGE("invalid auth value"); + return false; + } + if (readOnly && authVal != AuthValue::AUTH_R) { + HILOGE("read only"); + return false; + } + return true; +} + +bool AuthorityManager::CheckInterfaceAuthority(const std::string& ifaceName) +{ + std::string procName = GetCallingProcName(); + if (!authJson_.contains(procName)) { + HILOGE("can't find entry for proc:%{public}s", procName.c_str()); + return false; + } + + auto& interfacesJson = authJson_[procName][INTERFACES]; + if (interfacesJson == nullptr) { + HILOGE("can't find interfaces entry"); + return false; + } + + if (!interfacesJson.contains(ifaceName)) { + HILOGE("%{public}s is disallowed for %{public}s", ifaceName.c_str(), + procName.c_str()); + return false; + } + return true; +} + +bool AuthorityManager::CheckServiceAuthority(AuthValue authVal, + const std::string& serviceId) +{ + return CheckServicesAuthority(authVal, { serviceId }); +} + +bool AuthorityManager::CheckServicesAuthority(AuthValue authVal, + const std::vector& serviceIds) +{ + bool hasEmpty = std::any_of(serviceIds.begin(), serviceIds.end(), + [](const auto& serviceId) { return serviceId.empty(); }); + if (hasEmpty) { + HILOGE("empty serviceId"); + return false; + } + + std::string procName = GetCallingProcName(); + if (!authJson_.contains(procName)) { + HILOGE("can't find entry for proc:%{public}s", procName.c_str()); + return false; + } + auto& servicesJson = authJson_[procName][SERVICES]; + if (servicesJson == nullptr) { + HILOGE("can't find services entry"); + return false; + } + + if (!CheckServicesAuth(servicesJson, authVal, serviceIds)) { + return false; + } + return true; +} + +bool AuthorityManager::CheckServicesAuth(const nlohmann::json& servicesJson, + AuthValue authVal, const std::vector& serviceIds) +{ + bool isReadWithAll = (authVal == AuthValue::AUTH_R) && servicesJson.contains(SERVICES_ALL); + if (serviceIds.empty()) { + // in case where no serviceIds provided, which means all services; but + // SERVICES_ALL is only allowed with AUTH_R + return isReadWithAll; + } + if (isReadWithAll) { + HILOGI("check pass, SERVICES_ALL with read"); + return true; + } + + const auto& specificSvcsJson = servicesJson.contains(SERVICES_SPECIFIC) ? + servicesJson[SERVICES_SPECIFIC] : nlohmann::json {}; + const auto& prefixSvcsJson = servicesJson.contains(SERVICES_PREFIX) ? + servicesJson[SERVICES_PREFIX] : nlohmann::json {}; + if (specificSvcsJson == nullptr && prefixSvcsJson == nullptr) { + HILOGE("check failed, no specific and prefix service"); + return false; + } + for (const auto& serviceId : serviceIds) { + if ((specificSvcsJson != nullptr) && + CheckSpecificServiceAuth(specificSvcsJson, serviceId, authVal)) { + continue; + } + if ((prefixSvcsJson != nullptr) && + CheckPrefixServiceAuth(prefixSvcsJson, serviceId, authVal)) { + continue; + } + HILOGE("denied to access service:%{public}s", serviceId.c_str()); + return false; + } + HILOGI("check pass, authorities statisfied"); + return true; +} + +bool AuthorityManager::CheckSpecificServiceAuth(const nlohmann::json& specificSvcsJson, + const std::string& serviceId, AuthValue authVal) +{ + if (!specificSvcsJson.contains(serviceId)) { + return false; + } + auto& authValJson = specificSvcsJson[serviceId]; + return ((authValJson.get() & authVal) != 0); +} + +bool AuthorityManager::CheckPrefixServiceAuth(const nlohmann::json& prefixSvcsJson, + const std::string& serviceId, AuthValue authVal) +{ + for (auto& [prefixSvcId, authValJson] : prefixSvcsJson.items()) { + if ((serviceId.compare(0, prefixSvcId.size(), prefixSvcId) == 0)) { + HILOGI("service:%{public}s, prefix:%{public}s", serviceId.c_str(), prefixSvcId.c_str()); + return ((authValJson.get() & authVal) != 0); + } + } + return false; +} + +bool AuthorityManager::CheckCallerTrust() +{ + int32_t callingUid = IPCSkeleton::GetCallingUid(); + HILOGI("calling uid is %{public}d", callingUid); + return (callingUid == SYSTEM_UID || callingUid == ROOT_UID); +} + +std::string AuthorityManager::GetCallingProcName() +{ + char buf[BUF_SIZE] = {0}; + int32_t pid = IPCSkeleton::GetCallingPid(); + HILOGI("pid = %{public}d", pid); + (void)snprintf_s(buf, BUF_SIZE, BUF_SIZE - 1, "/proc/%d/cmdline", pid); + + std::string procName; + if (!LoadStringFromFile(std::string(buf, strlen(buf)), procName)) { + HILOGE("load string failed"); + return procName; + } + auto pos = procName.find_last_not_of(" \n\r\t"); + if (pos != std::string::npos) { + procName.erase(pos + 1); + } + + pos = procName.find_last_of("/"); + if (pos != std::string::npos) { + procName = procName.substr(pos + 1); + } + HILOGI("calling proc is %{public}s", procName.c_str()); + return procName.c_str(); +} +} // namespace DeviceProfile +} // namespace OHOS \ No newline at end of file diff --git a/services/core/src/distributed_device_profile_service.cpp b/services/core/src/distributed_device_profile_service.cpp old mode 100755 new mode 100644 index 066b32e..2010f2f --- a/services/core/src/distributed_device_profile_service.cpp +++ b/services/core/src/distributed_device_profile_service.cpp @@ -15,8 +15,10 @@ #include "distributed_device_profile_service.h" +#include "authority_manager.h" #include "content_sensor_manager.h" #include "device_manager.h" +#include "device_profile_errors.h" #include "device_profile_log.h" #include "device_profile_storage_manager.h" #include "service_characteristic_profile.h" @@ -56,24 +58,39 @@ bool DistributedDeviceProfileService::Init() HILOGE("SubscribeManager init failed"); return false; } + if (!AuthorityManager::GetInstance().Init()) { + HILOGE("AuthorityManager init failed"); + return false; + } HILOGI("init succeeded"); return true; } int32_t DistributedDeviceProfileService::PutDeviceProfile(const ServiceCharacteristicProfile& profile) { + if (!AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_W, + profile.GetServiceId())) { + return ERR_DP_PERMISSION_DENIED; + } return DeviceProfileStorageManager::GetInstance().PutDeviceProfile(profile); } int32_t DistributedDeviceProfileService::GetDeviceProfile(const std::string& udid, const std::string& serviceId, ServiceCharacteristicProfile& profile) { + if (!AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_R, + serviceId)) { + return ERR_DP_PERMISSION_DENIED; + } return DeviceProfileStorageManager::GetInstance().GetDeviceProfile(udid, serviceId, profile); } int32_t DistributedDeviceProfileService::DeleteDeviceProfile(const std::string& serviceId) { - HILOGI("service id %{public}s", serviceId.c_str()); + if (!AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_W, + serviceId)) { + return ERR_DP_PERMISSION_DENIED; + } return DeviceProfileStorageManager::GetInstance().DeleteDeviceProfile(serviceId); } @@ -96,6 +113,9 @@ int32_t DistributedDeviceProfileService::UnsubscribeProfileEvents(const std::lis int32_t DistributedDeviceProfileService::SyncDeviceProfile(const SyncOptions& syncOptions, const sptr& profileEventNotifier) { + if (!AuthorityManager::GetInstance().CheckInterfaceAuthority("sync")) { + return ERR_DP_PERMISSION_DENIED; + } return DeviceProfileStorageManager::GetInstance().SyncDeviceProfile(syncOptions, profileEventNotifier); } diff --git a/services/core/src/distributed_device_profile_stub.cpp b/services/core/src/distributed_device_profile_stub.cpp index 86c8f63..6158727 100755 --- a/services/core/src/distributed_device_profile_stub.cpp +++ b/services/core/src/distributed_device_profile_stub.cpp @@ -17,6 +17,7 @@ #include +#include "authority_manager.h" #include "device_profile_errors.h" #include "device_profile_log.h" #include "device_profile_storage.h" @@ -57,6 +58,10 @@ int32_t DistributedDeviceProfileStub::OnRemoteRequest(uint32_t code, MessageParc HILOGW("check interface token failed"); return ERR_DP_INTERFACE_CHECK_FAILED; } + if (!AuthorityManager::GetInstance().CheckCallerTrust()) { + HILOGE("caller is not trusted"); + return ERR_DP_PERMISSION_DENIED; + } if (func != nullptr) { return (this->*func)(data, reply); } diff --git a/services/core/src/subscribemanager/profile_change_handler.cpp b/services/core/src/subscribemanager/profile_change_handler.cpp index f001f09..a9add9a 100755 --- a/services/core/src/subscribemanager/profile_change_handler.cpp +++ b/services/core/src/subscribemanager/profile_change_handler.cpp @@ -20,6 +20,8 @@ #include "datetime_ex.h" #include "string_ex.h" +#include "authority_manager.h" +#include "device_profile_errors.h" #include "device_profile_log.h" #include "device_profile_storage_manager.h" #include "device_profile_utils.h" @@ -242,5 +244,24 @@ int32_t ProfileChangeHandler::Unregister() HILOGI("called"); return DeviceProfileStorageManager::GetInstance().UnSubscribeKvStore(shared_from_this()); } + +int32_t ProfileChangeHandler::Subscribe(const SubscribeInfo& subscribeInfo, + const sptr& profileEventNotifier) +{ + const auto& extraInfo = subscribeInfo.extraInfo; + const auto& serviceIdsJson = extraInfo["serviceIds"]; + std::vector serviceIds; + for (const auto& serviceIdJson : serviceIdsJson) { + std::string serviceId = serviceIdJson; + HILOGI("serviceId = %{public}s", serviceId.c_str()); + serviceIds.emplace_back(std::move(serviceId)); + } + + if (!AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_R, + serviceIds)) { + return ERR_DP_PERMISSION_DENIED; + } + return ProfileEventHandler::Subscribe(subscribeInfo, profileEventNotifier); +} } // namespace DeviceProfile } // namespace OHOS diff --git a/services/core/test/BUILD.gn b/services/core/test/BUILD.gn index 6ebe735..e10099c 100755 --- a/services/core/test/BUILD.gn +++ b/services/core/test/BUILD.gn @@ -54,10 +54,23 @@ ohos_unittest("event_subscribe_test") { external_deps = device_profile_external_deps } +ohos_unittest("profile_authority_test") { + module_out_path = module_output_path + + sources = [ + "${device_profile_service}/core/src/authority/authority_manager.cpp", + "unittest/profile_authority_test.cpp", + ] + configs = device_profile_configs + deps = [ "//third_party/googletest:gtest_main" ] + external_deps = device_profile_external_deps +} + group("unittest") { testonly = true deps = [ - ":event_subscribe_test", + # ":event_subscribe_test", + ":profile_authority_test", ":profile_crud_test", ] } diff --git a/services/core/test/unittest/profile_authority_test.cpp b/services/core/test/unittest/profile_authority_test.cpp new file mode 100644 index 0000000..6e84557 --- /dev/null +++ b/services/core/test/unittest/profile_authority_test.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "utils.h" + +#define protected public +#define private public +#include "authority_manager.h" +#undef private +#undef protected +#include "device_profile_errors.h" + +namespace OHOS { +namespace DeviceProfile { +using namespace testing; +using namespace testing::ext; + +namespace { +const std::string AUTHORITY_JSON_DIR = "/data/test/resource/deviceprofile/authority/"; +const std::string INVALID_AUTHORITY = AUTHORITY_JSON_DIR + "invalid_authority.json"; +const std::string VALID_AUTHORITY = AUTHORITY_JSON_DIR + "valid_authority.json"; +} + +class ProfileAuthorityTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + static bool LoadAuthorityCfg(const std::string& filePath); + void SetUp(); + void TearDown(); +}; + +void ProfileAuthorityTest::SetUpTestCase() +{ +} + +void ProfileAuthorityTest::TearDownTestCase() +{ +} + +void ProfileAuthorityTest::SetUp() +{ +} + +void ProfileAuthorityTest::TearDown() +{ +} + +bool ProfileAuthorityTest::LoadAuthorityCfg(const std::string& filePath) +{ + if (!AuthorityManager::GetInstance().LoadAuthorityCfg(filePath)) { + DTEST_LOG << "not found " << filePath << std::endl; + return false; + } + AuthorityManager::GetInstance().InitSupportedInterfaces(); + AuthorityManager::GetInstance().ValidateAuthorityCfg(); + return true; +} + +/** + * @tc.name: PrecheckAuthority_001 + * @tc.desc: precheck an authority json with invalid config + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, PrecheckAuthority_001, TestSize.Level0) +{ + if (!LoadAuthorityCfg(INVALID_AUTHORITY)) { + return; + } + + EXPECT_EQ(AuthorityManager::GetInstance().authJson_.dump(), R"({})"); +} + +/** + * @tc.name: CheckAuthority_002 + * @tc.desc: check authority of a fake serviceId + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_002, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_FALSE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_W, + "fakeServiceId")); +} + +/** + * @tc.name: CheckAuthority_003 + * @tc.desc: check authority of specific serviceId with diff auth value + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_003, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_R, + "storage")); + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_W, + "storage")); +} + +/** + * @tc.name: CheckAuthority_004 + * @tc.desc: check authority of a empty serviceId + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_004, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_FALSE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_R, + "")); + EXPECT_FALSE(AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_R, + {"system", ""})); +} + +/** + * @tc.name: CheckAuthority_005 + * @tc.desc: check authority of all with diff auth value + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_005, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_R, {})); + EXPECT_FALSE(AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_W, {})); +} + +/** + * @tc.name: CheckAuthority_006 + * @tc.desc: check authority of serviceIds with same prefix + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_006, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_R, + "cameraRear1")); + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_W, + "cameraRear1")); + + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_R, + "cameraRear2")); + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServiceAuthority(AuthValue::AUTH_W, + "cameraRear2")); +} + +/** + * @tc.name: CheckAuthority_007 + * @tc.desc: check authority of mulitiple serviceIds + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_007, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_W, + {"cameraRear1", "cameraRear2", "cameraRear3"})); + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_W, + {"storage", "system"})); + EXPECT_TRUE(AuthorityManager::GetInstance().CheckServicesAuthority(AuthValue::AUTH_W, + {"cameraRear1", "cameraRear2", "cameraRear3", "storage", "system"})); +} + +/** + * @tc.name: CheckAuthority_008 + * @tc.desc: check authority of interfaces + * @tc.type: FUNC + */ +HWTEST_F(ProfileAuthorityTest, CheckAuthority_008, TestSize.Level0) +{ + if (!LoadAuthorityCfg(VALID_AUTHORITY)) { + return; + } + + EXPECT_TRUE(AuthorityManager::GetInstance().CheckInterfaceAuthority("sync")); + EXPECT_FALSE(AuthorityManager::GetInstance().CheckInterfaceAuthority("fakeInterface")); + EXPECT_FALSE(AuthorityManager::GetInstance().CheckInterfaceAuthority("")); +} +} +} \ No newline at end of file diff --git a/services/core/test/unittest/profile_crud_test.cpp b/services/core/test/unittest/profile_crud_test.cpp index fed209a..9e3b708 100755 --- a/services/core/test/unittest/profile_crud_test.cpp +++ b/services/core/test/unittest/profile_crud_test.cpp @@ -59,7 +59,7 @@ void ProfileCrudTest::TearDown() * @tc.desc: put device profile with empty service id * @tc.type: FUNC */ -HWTEST_F(ProfileCrudTest, PutDeviceProfile_001, TestSize.Level1) +HWTEST_F(ProfileCrudTest, PutDeviceProfile_001, TestSize.Level0) { auto dps = DistributedDeviceProfileClient::GetInstance().GetDeviceProfileService(); if (dps == nullptr) { @@ -83,7 +83,7 @@ HWTEST_F(ProfileCrudTest, PutDeviceProfile_001, TestSize.Level1) * @tc.desc: put device profile with empty service type * @tc.type: FUNC */ -HWTEST_F(ProfileCrudTest, PutDeviceProfile_002, TestSize.Level1) +HWTEST_F(ProfileCrudTest, PutDeviceProfile_002, TestSize.Level0) { auto dps = DistributedDeviceProfileClient::GetInstance().GetDeviceProfileService(); if (dps == nullptr) { @@ -107,7 +107,7 @@ HWTEST_F(ProfileCrudTest, PutDeviceProfile_002, TestSize.Level1) * @tc.desc: put device profile with empty characteristics * @tc.type: FUNC */ -HWTEST_F(ProfileCrudTest, PutDeviceProfile_003, TestSize.Level1) +HWTEST_F(ProfileCrudTest, PutDeviceProfile_003, TestSize.Level0) { auto dps = DistributedDeviceProfileClient::GetInstance().GetDeviceProfileService(); if (dps == nullptr) { @@ -130,7 +130,7 @@ HWTEST_F(ProfileCrudTest, PutDeviceProfile_003, TestSize.Level1) * @tc.desc: put device profile without set characteristics * @tc.type: FUNC */ -HWTEST_F(ProfileCrudTest, PutDeviceProfile_004, TestSize.Level1) +HWTEST_F(ProfileCrudTest, PutDeviceProfile_004, TestSize.Level0) { auto dps = DistributedDeviceProfileClient::GetInstance().GetDeviceProfileService(); if (dps == nullptr) { @@ -145,91 +145,12 @@ HWTEST_F(ProfileCrudTest, PutDeviceProfile_004, TestSize.Level1) EXPECT_TRUE(result == ERR_DP_INVALID_PARAMS); } -/** - * @tc.name: PutDeviceProfile_005 - * @tc.desc: put device profile with normal value - * @tc.type: FUNC - */ -HWTEST_F(ProfileCrudTest, PutDeviceProfile_005, TestSize.Level1) -{ - auto dps = DistributedDeviceProfileClient::GetInstance().GetDeviceProfileService(); - if (dps == nullptr) { - DTEST_LOG << "device profile service is nullptr" << std::endl; - return; - } - - ServiceCharacteristicProfile profile; - profile.SetServiceId("test"); - profile.SetServiceType("test"); - nlohmann::json j; - j["testVersion"] = "3.0.0"; - j["testApiLevel"] = 7; - profile.SetCharacteristicProfileJson(j.dump()); - int32_t result = DistributedDeviceProfileClient::GetInstance().PutDeviceProfile(profile); - EXPECT_TRUE(result == ERR_OK); -} - -/** - * @tc.name: GetDeviceProfile_001 - * @tc.desc: get device profile with normal deviceid - * @tc.type: FUNC - */ -HWTEST_F(ProfileCrudTest, GetDeviceProfile_001, TestSize.Level1) -{ - ServiceCharacteristicProfile profile; - DistributedDeviceProfileClient::GetInstance().GetDeviceProfile("", "test", profile); - std::string jsonData = profile.GetCharacteristicProfileJson(); - auto json = nlohmann::json::parse(jsonData); - EXPECT_EQ(json["testApiLevel"], 7); -} - -/** - * @tc.name: GetDeviceProfile_002 - * @tc.desc: get device profile with unnormal deviceid - * @tc.type: FUNC - */ -HWTEST_F(ProfileCrudTest, GetDeviceProfile_002, TestSize.Level1) -{ - ServiceCharacteristicProfile profile; - DistributedDeviceProfileClient::GetInstance().GetDeviceProfile("123456789", "test", profile); - std::string jsonData = profile.GetCharacteristicProfileJson(); - EXPECT_TRUE(jsonData.empty()); -} - -/** - * @tc.name: CollectDeviceProfile_001 - * @tc.desc: collect system profile infomation - * @tc.type: FUNC - */ -HWTEST_F(ProfileCrudTest, CollectDeviceProfile_001, TestSize.Level1) -{ - ServiceCharacteristicProfile profile; - DistributedDeviceProfileClient::GetInstance().GetDeviceProfile("", "system", profile); - std::string jsonData = profile.GetCharacteristicProfileJson(); - auto json = nlohmann::json::parse(jsonData); - EXPECT_EQ(json["type"], 11); -} - -/** - * @tc.name: CollectDeviceProfile_002 - * @tc.desc: collect device profile infomation - * @tc.type: FUNC - */ -HWTEST_F(ProfileCrudTest, CollectDeviceProfile_002, TestSize.Level1) -{ - ServiceCharacteristicProfile profile; - DistributedDeviceProfileClient::GetInstance().GetDeviceProfile("", "device", profile); - std::string jsonData = profile.GetCharacteristicProfileJson(); - auto json = nlohmann::json::parse(jsonData); - EXPECT_TRUE(!jsonData.empty()); -} - /** * @tc.name: DeleteDeviceProfile_001 - * @tc.desc: delete device profile + * @tc.desc: delete an empty profile * @tc.type: FUNC */ -HWTEST_F(ProfileCrudTest, DeleteDeviceProfile_001, TestSize.Level1) +HWTEST_F(ProfileCrudTest, DeleteDeviceProfile_001, TestSize.Level0) { auto dps = DistributedDeviceProfileClient::GetInstance().GetDeviceProfileService(); if (dps == nullptr) { @@ -237,24 +158,8 @@ HWTEST_F(ProfileCrudTest, DeleteDeviceProfile_001, TestSize.Level1) return; } - ServiceCharacteristicProfile profilePut; - profilePut.SetServiceId("fakeSystem"); - profilePut.SetServiceType("fakeSystem"); - nlohmann::json j; - j["harmonyVersion"] = "2.2.0"; - profilePut.SetCharacteristicProfileJson(j.dump()); - int32_t result = DistributedDeviceProfileClient::GetInstance().PutDeviceProfile(profilePut); - EXPECT_TRUE(result == ERR_OK); - result = DistributedDeviceProfileClient::GetInstance().DeleteDeviceProfile("fakeSystem"); - EXPECT_TRUE(result == ERR_OK); - - ServiceCharacteristicProfile profileGet; - DistributedDeviceProfileClient::GetInstance().GetDeviceProfile("", "fakeSystem", profileGet); - auto profileStr = profileGet.GetCharacteristicProfileJson(); - auto profileJson = nlohmann::json::parse(profileStr, nullptr, false); - if (!profileJson.is_discarded()) { - EXPECT_TRUE(!profileJson.empty()); - } + int32_t result = DistributedDeviceProfileClient::GetInstance().DeleteDeviceProfile(""); + EXPECT_TRUE(result == ERR_DP_INVALID_PARAMS); } } } \ No newline at end of file diff --git a/test/resource/authority/invalid_authority.json b/test/resource/authority/invalid_authority.json new file mode 100644 index 0000000..939763d --- /dev/null +++ b/test/resource/authority/invalid_authority.json @@ -0,0 +1,62 @@ +{ + "fakeProcess1": { + "servicesAuthority": { + "all": 3 + } + }, + "fakeProcess2": { + "servicesAuthority": { + "all": true + } + }, + "fakeProcess3": { + "servicesAuthority": { + "all": "read" + } + }, + "fakeProcess4": { + "servicesAuthority": { + "specific": { + "storage": -1 + } + } + }, + "fakeProcess5": { + "servicesAuthority": { + "specific": { + "system": 4 + } + } + }, + "fakeProcess6": { + "servicesAuthority": { + "prefix": { + "camerRear": 2 + } + } + }, + "fakeProcess7": { + "interfacesAuthority": { + "fakeInterface": {} + } + }, + "fakeProcess8": { + "interfacesAuthority": { + "sync": 1 + } + }, + "fakeProcess9": { + "interfacesAuthority": { + } + }, + "fakeProcess10": { + "servicesAuthority": { + } + }, + "fakeProcess11": { + "fakeKey1": { + }, + "fakeKey2": { + } + } +} \ No newline at end of file diff --git a/test/resource/authority/valid_authority.json b/test/resource/authority/valid_authority.json new file mode 100644 index 0000000..f7a7033 --- /dev/null +++ b/test/resource/authority/valid_authority.json @@ -0,0 +1,17 @@ +{ + "profile_authority_test": { + "servicesAuthority": { + "all": 1, + "specific": { + "storage": 3, + "system": 3 + }, + "prefix": { + "cameraRear": 3 + } + }, + "interfacesAuthority": { + "sync": {} + } + } +} \ No newline at end of file diff --git a/test/resource/ohos_test.xml b/test/resource/ohos_test.xml new file mode 100644 index 0000000..67978a3 --- /dev/null +++ b/test/resource/ohos_test.xml @@ -0,0 +1,22 @@ + + + + + + + +