1 /* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 #include "grant_uri_permission.h" 16 17 #include "ability.h" 18 #include "datashare_helper.h" 19 #include "datashare_values_bucket.h" 20 #include "grant_permissions.h" 21 #include "ipc_skeleton.h" 22 #include "log.h" 23 #include "remote_uri.h" 24 #include "tokenid_kit.h" 25 #include "uri_permission_manager_client.h" 26 #include "want.h" 27 28 using namespace OHOS::DataShare; 29 using namespace OHOS::FileManagement::LibN; 30 using namespace OHOS::DistributedFS::ModuleRemoteUri; 31 32 namespace OHOS { 33 namespace AppFileService { 34 namespace ModuleFileShare { 35 enum MediaFileTable { 36 FILE_TABLE = 0, 37 PHOTO_TABLE = 1, 38 AUDIO_TABLE = 2, 39 }; 40 41 struct UriPermissionInfo { 42 unsigned int flag; 43 string mode; 44 string bundleName; 45 string uri; 46 }; 47 IsSystemApp()48 static bool IsSystemApp() 49 { 50 uint64_t fullTokenId = OHOS::IPCSkeleton::GetCallingFullTokenID(); 51 return Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId); 52 } 53 GetIdFromUri(string uri)54 static int32_t GetIdFromUri(string uri) 55 { 56 std::replace(uri.begin(), uri.end(), '/', ' '); 57 stringstream ss(uri); 58 string tmp; 59 int fileId = -1; 60 ss >> tmp >> tmp >> tmp >> fileId; 61 return fileId; 62 } 63 GetModeFromFlag(unsigned int flag)64 static string GetModeFromFlag(unsigned int flag) 65 { 66 string mode = ""; 67 if (flag & OHOS::AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION) { 68 mode += "r"; 69 } 70 if (flag & OHOS::AAFwk::Want::FLAG_AUTH_WRITE_URI_PERMISSION) { 71 mode += "w"; 72 } 73 return mode; 74 } 75 GetFlagFromMode(const string & mode)76 static unsigned int GetFlagFromMode(const string &mode) 77 { 78 unsigned int flag = AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION; 79 if (mode.find("w") != string::npos) { 80 flag = AAFwk::Want::FLAG_AUTH_WRITE_URI_PERMISSION; 81 } 82 return flag; 83 } 84 GetMediaTypeAndApiFromUri(const std::string & uri,bool & isApi10)85 static int32_t GetMediaTypeAndApiFromUri(const std::string &uri, bool &isApi10) 86 { 87 if (uri.find(MEDIA_FILE_URI_PHOTO_PREFEX) == 0) { 88 isApi10 = true; 89 return MediaFileTable::PHOTO_TABLE; 90 } else if (uri.find(MEDIA_FILE_URI_VIDEO_PREFEX) == 0 || 91 uri.find(MEDIA_FILE_URI_IMAGE_PREFEX) == 0) { 92 return MediaFileTable::PHOTO_TABLE; 93 } else if (uri.find(MEDIA_FILE_URI_AUDIO_PREFEX) == 0) { 94 isApi10 = true; 95 return MediaFileTable::AUDIO_TABLE; 96 } else if (uri.find(MEDIA_FILE_URI_Audio_PREFEX) == 0) { 97 return MediaFileTable::AUDIO_TABLE; 98 } else if (uri.find(MEDIA_FILE_URI_FILE_PREFEX) == 0) { 99 return MediaFileTable::FILE_TABLE; 100 } 101 102 return MediaFileTable::FILE_TABLE; 103 } 104 InsertByDatashare(const DataShareValuesBucket & valuesBucket,bool isApi10)105 static int InsertByDatashare(const DataShareValuesBucket &valuesBucket, bool isApi10) 106 { 107 int ret = -1; 108 std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr; 109 sptr<FileShareGrantToken> remote = new IRemoteStub<FileShareGrantToken>(); 110 if (remote == nullptr) { 111 LOGE("FileShare::InsertByDatashare get remoteObject failed!"); 112 return -ENOMEM; 113 } 114 115 dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI); 116 if (!dataShareHelper) { 117 LOGE("FileShare::InsertByDatashare connect to datashare failed!"); 118 return -E_PERMISSION; 119 } 120 string uriStr = MEDIA_GRANT_URI_PERMISSION; 121 if (isApi10) { 122 uriStr += MEDIA_API_VERSION_10; 123 } 124 125 Uri uri(uriStr); 126 ret = dataShareHelper->Insert(uri, valuesBucket); 127 if (ret < 0) { 128 LOGE("FileShare::InsertByDatashare insert failed with error code %{public}d!", ret); 129 return ret; 130 } 131 return ret; 132 } 133 InitValuesBucket(const UriPermissionInfo & uriPermInfo,Uri & uri,bool & isApi10,DataShareValuesBucket & valuesBucket)134 static int InitValuesBucket(const UriPermissionInfo &uriPermInfo, Uri &uri, bool &isApi10, 135 DataShareValuesBucket &valuesBucket) 136 { 137 int32_t fileId = GetIdFromUri(uriPermInfo.uri); 138 if (fileId == -1) { 139 LOGE("FileShare::InitValuesBucket get fileId parameter failed!"); 140 return -EINVAL; 141 } 142 143 int32_t filesType = GetMediaTypeAndApiFromUri(uri.ToString(), isApi10); 144 valuesBucket.Put(PERMISSION_FILE_ID, fileId); 145 valuesBucket.Put(PERMISSION_BUNDLE_NAME, uriPermInfo.bundleName); 146 valuesBucket.Put(PERMISSION_MODE, uriPermInfo.mode); 147 valuesBucket.Put(PERMISSION_TABLE_TYPE, filesType); 148 return 0; 149 } 150 GrantInMediaLibrary(const UriPermissionInfo & uriPermInfo,Uri & uri)151 static int GrantInMediaLibrary(const UriPermissionInfo &uriPermInfo, Uri &uri) 152 { 153 bool isApi10 = false; 154 DataShareValuesBucket valuesBucket; 155 int ret = InitValuesBucket(uriPermInfo, uri, isApi10, valuesBucket); 156 if (ret < 0) { 157 return ret; 158 } 159 160 ret = InsertByDatashare(valuesBucket, isApi10); 161 if (ret < 0) { 162 return ret; 163 } 164 return 0; 165 } 166 DoGrantUriPermission(const UriPermissionInfo & uriPermInfo)167 static int DoGrantUriPermission(const UriPermissionInfo &uriPermInfo) 168 { 169 Uri uri(uriPermInfo.uri); 170 string authority = uri.GetAuthority(); 171 string path = uri.GetPath(); 172 if (authority == MEDIA_AUTHORITY && path.find(".") == string::npos) { 173 return GrantInMediaLibrary(uriPermInfo, uri); 174 } else { 175 auto& uriPermissionClient = AAFwk::UriPermissionManagerClient::GetInstance(); 176 int ret = uriPermissionClient.GrantUriPermission(uri, uriPermInfo.flag, 177 uriPermInfo.bundleName); 178 if (ret != 0) { 179 LOGD("uriPermissionClient.GrantUriPermission by uri permission client failed!"); 180 return GrantInMediaLibrary(uriPermInfo, uri); 181 } 182 } 183 184 return 0; 185 } 186 CheckValidPublicUri(const string & inputUri)187 static bool CheckValidPublicUri(const string &inputUri) 188 { 189 Uri uri(inputUri); 190 string scheme = uri.GetScheme(); 191 if (scheme != FILE_SCHEME) { 192 return false; 193 } 194 195 string authority = uri.GetAuthority(); 196 if (authority != MEDIA_AUTHORITY && authority != FILE_MANAGER_AUTHORITY) { 197 return false; 198 } 199 200 return true; 201 } 202 GetJSArgs(napi_env env,const NFuncArg & funcArg,UriPermissionInfo & uriPermInfo)203 static bool GetJSArgs(napi_env env, const NFuncArg &funcArg, UriPermissionInfo &uriPermInfo) 204 { 205 auto [succUri, uri, lenUri] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); 206 if (!succUri) { 207 LOGE("FileShare::GetJSArgs get uri parameter failed!"); 208 NError(EINVAL).ThrowErr(env); 209 return false; 210 } 211 212 uriPermInfo.uri = string(uri.get()); 213 if (!CheckValidPublicUri(uriPermInfo.uri)) { 214 LOGE("FileShare::GetJSArgs uri parameter format error!"); 215 NError(EINVAL).ThrowErr(env); 216 return false; 217 } 218 219 auto [succBundleName, bundleName, lenBundleName] = NVal(env, funcArg[NARG_POS::SECOND]).ToUTF8String(); 220 if (!succBundleName) { 221 LOGE("FileShare::GetJSArgs get bundleName parameter failed!"); 222 NError(EINVAL).ThrowErr(env); 223 return false; 224 } 225 uriPermInfo.bundleName = string(bundleName.get()); 226 227 if (NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_number)) { 228 auto [succFlag, flag] = NVal(env, funcArg[NARG_POS::THIRD]).ToUint32(); 229 uriPermInfo.flag = flag; 230 uriPermInfo.mode = GetModeFromFlag(flag); 231 } else if (NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_string)) { 232 auto [succFlag, flag, lenFlag] = NVal(env, funcArg[NARG_POS::THIRD]).ToUTF8String(); 233 uriPermInfo.mode = string(flag.get()); 234 uriPermInfo.flag = GetFlagFromMode(uriPermInfo.mode); 235 } else { 236 LOGE("FileShare::GetJSArgs get flag parameter failed!"); 237 NError(EINVAL).ThrowErr(env); 238 return false; 239 } 240 241 return true; 242 } 243 Async(napi_env env,napi_callback_info info)244 napi_value GrantUriPermission::Async(napi_env env, napi_callback_info info) 245 { 246 LOGD("FileShare::GrantUriPermission begin!"); 247 if (!IsSystemApp()) { 248 LOGE("FileShare::GrantUriPermission is not System App!"); 249 NError(E_PERMISSION_SYS).ThrowErr(env); 250 return nullptr; 251 } 252 NFuncArg funcArg(env, info); 253 if (!funcArg.InitArgs(NARG_CNT::THREE, NARG_CNT::FOUR)) { 254 LOGE("FileShare::GrantUriPermission GetJSArgsForGrantUriPermission Number of arguments unmatched!"); 255 NError(EINVAL).ThrowErr(env); 256 return nullptr; 257 } 258 259 if (!NVal(env, funcArg[NARG_POS::FIRST]).TypeIs(napi_string)) { 260 LOGI("is grant dec permissions."); 261 return GrantDecUriPermission(env, funcArg); 262 } 263 264 UriPermissionInfo uriPermInfo; 265 bool result = GetJSArgs(env, funcArg, uriPermInfo); 266 if (!result) { 267 LOGE("FileShare::GrantUriPermission GetJSArgs failed!"); 268 NError(EINVAL).ThrowErr(env); 269 return nullptr; 270 } 271 272 auto cbExec = [uriPermInfo, env]() -> NError { 273 int ret = DoGrantUriPermission(uriPermInfo); 274 if (ret < 0) { 275 LOGE("FileShare::GrantUriPermission DoGrantUriPermission failed with %{public}d", ret); 276 return NError(-ret); 277 } 278 LOGD("FileShare::GrantUriPermission DoGrantUriPermission successfully!"); 279 return NError(ERRNO_NOERR); 280 }; 281 282 auto cbCompl = [](napi_env env, NError err) -> NVal { 283 if (err) { 284 return { env, err.GetNapiErr(env) }; 285 } 286 return NVal::CreateUndefined(env); 287 }; 288 289 NVal thisVar(env, funcArg.GetThisVar()); 290 if (funcArg.GetArgc() == NARG_CNT::THREE) { 291 return NAsyncWorkPromise(env, thisVar).Schedule(GRANT_URI_NAME, cbExec, cbCompl).val_; 292 } else { 293 NVal cb(env, funcArg[NARG_POS::FOURTH]); 294 return NAsyncWorkCallback(env, thisVar, cb).Schedule(GRANT_URI_NAME, cbExec, cbCompl).val_; 295 } 296 } 297 } // namespace ModuleFileShare 298 } // namespace AppFileService 299 } // namespace OHOS 300