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