[PrivacyPrivilegeManager] Add new API for checking & requesting multiple privileges...
[platform/core/csapi/tizenfx.git] / src / Tizen.Security.PrivacyPrivilegeManager / Tizen.Security / PrivacyPrivilegeManager.cs
1 /*
2  * Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 using System.Threading.Tasks;
21 using Tizen.Internals.Errors;
22
23 namespace Tizen.Security
24 {
25     /// <summary>
26     /// The PrivacyPrivilegeManager provides the properties or methods to check and request a permission for privacy privilege.
27     /// </summary>
28     /// <since_tizen> 4 </since_tizen>
29     public static class PrivacyPrivilegeManager
30     {
31         private const string LogTag = "Tizen.Privilege";
32         private static IDictionary<string, WeakReference<ResponseContext>> s_responseWeakMap = new Dictionary<string, WeakReference<ResponseContext>>();
33         private static Interop.PrivacyPrivilegeManager.RequestResponseCallback s_requestResponseCb =
34                        (Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult result,
35                            string privilege, IntPtr userData) =>
36                         {
37                             try
38                             {
39                                 if (s_responseWeakMap.TryGetValue(privilege, out WeakReference<ResponseContext> weakRef))
40                                 {
41                                     if (weakRef.TryGetTarget(out ResponseContext context))
42                                     {
43                                         context.FireEvent((CallCause)cause, (RequestResult)result);
44                                     }
45                                     else
46                                     {
47                                         s_responseWeakMap.Remove(privilege);
48                                         Log.Error(LogTag, "No response context for: " + privilege);
49                                     }
50                                 }
51                                 else
52                                 {
53                                     Log.Error(LogTag, "No listener for: " + privilege);
54                                 }
55                             }
56                             catch (Exception e)
57                             {
58                                 Log.Error(LogTag, "Exception in callback : " + e.Message);
59                             }
60                             s_PrivilegesInProgress.Remove(privilege);
61                         };
62
63         private static IDictionary<string, ResponseContext> s_responseMap = new Dictionary<string, ResponseContext>();
64         private static HashSet<string> s_PrivilegesInProgress = new HashSet<string>();
65
66         private static string[] CheckPrivilegesArgument(IEnumerable<string> privileges, string methodName)
67         {
68             if (privileges == null || !privileges.Any())
69             {
70                 Log.Error(LogTag, "privileges for " + methodName + " are null or empty.");
71                 throw new ArgumentException("privileges for " + methodName + " are null or empty.");
72             }
73
74             foreach (var privilege in privileges)
75             {
76                 if (string.IsNullOrEmpty(privilege))
77                 {
78                     Log.Error(LogTag, " At least one privilege for " + methodName + " is null or empty.");
79                     throw new ArgumentException(" At least one privilege for " + methodName + " is null or empty.");
80                 }
81             }
82
83             return privileges as string[] ?? privileges.ToArray();
84         }
85
86         /// <summary>
87         /// Gets the status of a privacy privilege permission.
88         /// </summary>
89         /// <param name="privilege">The privacy privilege to be checked.</param>
90         /// <returns>The permission setting for a respective privilege.</returns>
91         /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
92         /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
93         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
94         /// <example>
95         /// <code>
96         ///     CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
97         ///     switch (result)
98         ///     {
99         ///         case Allow:
100         ///             // Privilege can be used
101         ///             break;
102         ///         case Deny:
103         ///             // Privilege can't be used
104         ///             break;
105         ///         case Ask:
106         ///             // User permission request required
107         ///             PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
108         ///             break;
109         ///     }
110         /// </code>
111         /// </example>
112         /// <since_tizen> 4 </since_tizen>
113         public static CheckResult CheckPermission(string privilege)
114         {
115             Interop.PrivacyPrivilegeManager.CheckResult result;
116             int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermission(privilege, out result);
117             if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
118             {
119                 Log.Error(LogTag, "Failed to check permission");
120                 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
121             }
122             return (CheckResult)result;
123         }
124
125         /// <summary>
126         /// Gets the status of a privacy privileges permission.
127         /// </summary>
128         /// <param name="privileges">The privacy privileges to be checked.</param>
129         /// <returns>The permission setting for a respective privileges.</returns>
130         /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
131         /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
132         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
133         /// <example>
134         /// <code>
135         ///     string[] privileges = new [] {"http://tizen.org/privilege/account.read",
136         ///                                   "http://tizen.org/privilege/alarm"};
137         ///     CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
138         ///     List&lt;string&gt; privilegesWithAskStatus = new List&lt;string&gt;();
139         ///     for (int iterator = 0; iterator &lt; results.Length; ++iterator)
140         ///     {
141         ///         switch (results[iterator])
142         ///         {
143         ///             case CheckResult.Allow:
144         ///                 // Privilege can be used
145         ///                 break;
146         ///             case CheckResult.Deny:
147         ///                 // Privilege can't be used
148         ///                 break;
149         ///             case CheckResult.Ask:
150         ///                 // User permission request required
151         ///                 privilegesWithAskStatus.Add(privileges[iterator]);
152         ///                 break;
153         ///         }
154         ///     }
155         ///     PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus);
156         /// </code>
157         /// </example>
158         /// <since_tizen> 6 </since_tizen>
159         public static IEnumerable<CheckResult> CheckPermissions(IEnumerable<string> privileges)
160         {
161             string[] privilegesArray = CheckPrivilegesArgument(privileges, "CheckPermissions");
162
163             Interop.PrivacyPrivilegeManager.CheckResult[] results = new Interop.PrivacyPrivilegeManager.CheckResult[privilegesArray.Length];
164             int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermissions(privilegesArray, (uint)privilegesArray.Length, results);
165             if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
166             {
167                 Log.Error(LogTag, "Failed to check permission");
168                 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
169             }
170
171             CheckResult[] checkResults = new CheckResult[results.Length];
172             for (int iterator = 0; iterator < results.Length; ++iterator)
173             {
174                 checkResults[iterator] = (CheckResult)results[iterator];
175             }
176             return checkResults;
177         }
178
179
180         /// <summary>
181         /// Triggers the permission request for a user.
182         /// </summary>
183         /// <param name="privilege">The privacy privilege to be requested.</param>
184         /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
185         /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
186         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
187         /// <example>
188         /// <code>
189         ///     CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
190         ///     switch (result)
191         ///     {
192         ///         case Allow:
193         ///             // Privilege can be used
194         ///             break;
195         ///         case Deny:
196         ///             // Privilege can't be used
197         ///             break;
198         ///         case Ask:
199         ///             // User permission request required
200         ///             PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
201         ///             break;
202         ///     }
203         /// </code>
204         /// </example>
205         /// <since_tizen> 4 </since_tizen>
206         public static void RequestPermission(string privilege)
207         {
208             if (!s_PrivilegesInProgress.Add(privilege))
209             {
210                 Log.Error(LogTag, "Request for this privilege: " + privilege + " is already in progress.");
211                 throw new ArgumentException("Request for this privilege: " + privilege + " is already in progress.");
212             }
213
214             int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermission(privilege, s_requestResponseCb, IntPtr.Zero);
215             if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
216             {
217                 Log.Error(LogTag, "Failed to request permission");
218                 s_PrivilegesInProgress.Remove(privilege);
219                 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
220             }
221         }
222
223         /// <summary>
224         /// Triggers the permissions request for a user.
225         /// </summary>
226         /// <param name="privileges">The privacy privileges to be requested.</param>
227         /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
228         /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
229         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
230         /// <returns>Permission request Task</returns>
231         /// <example>
232         /// <code>
233         ///     string[] privileges = new [] {"http://tizen.org/privilege/account.read",
234         ///                                   "http://tizen.org/privilege/alarm"};
235         ///     CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
236         ///     List&lt;string&gt; privilegesWithAskStatus = new List&lt;string&gt;();
237         ///     for (int iterator = 0; iterator &lt; results.Length; ++iterator)
238         ///     {
239         ///         switch (results[iterator])
240         ///         {
241         ///             case CheckResult.Allow:
242         ///                 // Privilege can be used
243         ///                 break;
244         ///             case CheckResult.Deny:
245         ///                 // Privilege can't be used
246         ///                 break;
247         ///             case CheckResult.Ask:
248         ///                 // User permission request required
249         ///                 privilegesWithAskStatus.Add(privileges[iterator]);
250         ///                 break;
251         ///         }
252         ///     }
253         ///     IEnumerable&lt;PermissionRequestResponse&gt; responses = PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus).Result;
254         ///     //handle responses
255         /// </code>
256         /// </example>
257         /// <since_tizen> 6 </since_tizen>
258         public static Task<RequestMultipleResponseEventArgs> RequestPermissions(IEnumerable<string> privileges)
259         {
260             string[] privilegesArray = CheckPrivilegesArgument(privileges, "RequestPermissions");
261
262             for (int iterator = 0; iterator < privilegesArray.Length; ++iterator)
263             {
264                 if (!s_PrivilegesInProgress.Add(privilegesArray[iterator]))
265                 {
266                     Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
267
268                     for (int removeIterator = iterator - 1; removeIterator >= 0; --removeIterator)
269                     {
270                         s_PrivilegesInProgress.Remove(privilegesArray[removeIterator]);
271                     }
272                     Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
273                     throw new ArgumentException("Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
274                 }
275             }
276
277             Log.Info(LogTag, "Sending request for permissions: " + string.Join(" ", privilegesArray));
278
279             TaskCompletionSource<RequestMultipleResponseEventArgs> permissionResponsesTask = new TaskCompletionSource<RequestMultipleResponseEventArgs>();
280             int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermissions(privilegesArray, (uint)privilegesArray.Length,
281                         (Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult[] results,
282                         string[] requestedPrivileges, uint privilegesCount, IntPtr userData) =>
283                         {
284                             Log.Info(LogTag, "Sending request for permissions: ");
285                             RequestMultipleResponseEventArgs requestResponse = new RequestMultipleResponseEventArgs();
286                             PermissionRequestResponse[] permissionResponses = new PermissionRequestResponse[privilegesCount];
287
288                             for (int iterator = 0; iterator < privilegesCount; ++iterator)
289                             {
290                                 permissionResponses[iterator] = new PermissionRequestResponse
291                                 {
292                                     Privilege = requestedPrivileges[iterator],
293                                     Result = (RequestResult)results[iterator]
294                                 };
295                             }
296                             requestResponse.Cause = (CallCause)cause;
297                             requestResponse.Responses = permissionResponses;
298
299                             foreach (string privilege in requestedPrivileges)
300                             {
301                                 s_PrivilegesInProgress.Remove(privilege);
302                             }
303                             permissionResponsesTask.SetResult(requestResponse);
304                         }, IntPtr.Zero);
305
306             if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
307             {
308                 Log.Error(LogTag, "Failed to request permissions.");
309                 foreach (string privilege in privileges)
310                 {
311                     s_PrivilegesInProgress.Remove(privilege);
312                 }
313                 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
314             }
315             else
316             {
317                 Log.Info(LogTag, "Requesting permissions successfull.");
318                 return permissionResponsesTask.Task;
319             }
320         }
321
322         /// <summary>
323         /// Gets the response context for a given privilege.
324         /// </summary>
325         /// <seealso cref="ResponseContext"/>
326         /// <param name="privilege">The privilege.</param>
327         /// <returns>The response context of a respective privilege.</returns>
328         /// <exception cref="ArgumentException">Thrown if the key is an invalid parameter.</exception>
329         /// <example>
330         /// <code>
331         /// private static void PPM_RequestResponse(object sender, RequestResponseEventArgs e)
332         /// {
333         ///     if (e.cause == CallCause.Answer)
334         ///     {
335         ///        switch(e.result)
336         ///
337         ///        {
338         ///
339         ///         case RequestResult.AllowForever:
340         ///             Console.WriteLine("User allowed usage of privilege {0} definitely", e.privilege);
341         ///             break;
342         ///         case RequestResult.DenyForever:
343         ///             Console.WriteLine("User denied usage of privilege {0} definitely", e.privilege);
344         ///             break;
345         ///         case RequestResult.DenyOnce:
346         ///             Console.WriteLine("User denied usage of privilege {0} this time", e.privilege);
347         ///             break;
348         ///         };
349         ///     }
350         ///     else
351         ///     {
352         ///         Console.WriteLine("Error occured during requesting permission for {0}", e.privilege);
353         ///     }
354         ///}
355         ///
356         ///     PrivacyPrivilegeManager.ResponseContext context = null;
357         ///     PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
358         ///     if(context != null)
359         ///     {
360         ///         context.ResponseFetched += PPM_RequestResponse;
361         ///     }
362         ///
363         ///     PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
364         ///
365         ///     PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
366         ///     if(context != null)
367         ///     {
368         ///         context.ResponseFetched -= PPM_RequestResponse;
369         ///     }
370         /// </code>
371         /// </example>
372         /// <since_tizen> 4 </since_tizen>
373         public static WeakReference<ResponseContext> GetResponseContext(string privilege)
374         {
375             if (!(s_responseWeakMap.TryGetValue(privilege, out WeakReference<ResponseContext> weakRef) && weakRef.TryGetTarget(out ResponseContext context)))
376             {
377                 context = new ResponseContext(privilege);
378                 s_responseWeakMap[privilege] = new WeakReference<ResponseContext>(context);
379             }
380             return s_responseWeakMap[privilege];
381         }
382
383         /// <summary>
384         /// This class manages event handlers of the privilege permission requests.
385         /// This class enables having event handlers for an individual privilege.
386         /// </summary>
387         /// <since_tizen> 4 </since_tizen>
388         public class ResponseContext
389         {
390             private string _privilege;
391
392             internal ResponseContext(string privilege)
393             {
394                 _privilege = privilege;
395             }
396             /// <summary>
397             /// Occurs when the response for a permission request is fetched.
398             /// </summary>
399             /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
400             /// <since_tizen> 4 </since_tizen>
401             public event EventHandler<RequestResponseEventArgs> ResponseFetched
402             {
403                 add
404                 {
405                     if (_ResponseFetched == null)
406                     {
407                         if (!s_responseMap.ContainsKey(_privilege))
408                         {
409                             s_responseMap[_privilege] = this;
410                         }
411                     }
412                     _ResponseFetched += value;
413                 }
414
415                 remove
416                 {
417                     _ResponseFetched -= value;
418                     if (_ResponseFetched == null)
419                     {
420                         if (s_responseMap.ContainsKey(_privilege))
421                         {
422                             s_responseMap.Remove(_privilege);
423                         }
424                     }
425                 }
426             }
427
428             private event EventHandler<RequestResponseEventArgs> _ResponseFetched;
429
430             internal void FireEvent(CallCause _cause, RequestResult _result)
431             {
432                 _ResponseFetched?.Invoke(this, new RequestResponseEventArgs { cause = _cause, result = _result, privilege = _privilege });
433             }
434         }
435     }
436
437     internal static class PrivacyPrivilegeManagerErrorFactory
438     {
439         static internal Exception GetException(int error)
440         {
441             Interop.PrivacyPrivilegeManager.ErrorCode errCode = (Interop.PrivacyPrivilegeManager.ErrorCode)error;
442             switch (errCode)
443             {
444                 case Interop.PrivacyPrivilegeManager.ErrorCode.InvalidParameter:
445                     return new ArgumentException("Invalid parameter");
446                 case Interop.PrivacyPrivilegeManager.ErrorCode.IoError:
447                     return new System.IO.IOException("I/O Error");
448                 case Interop.PrivacyPrivilegeManager.ErrorCode.OutOfMemory:
449                     return new OutOfMemoryException("Out of memory");
450                 case Interop.PrivacyPrivilegeManager.ErrorCode.Unknown:
451                 default:
452                     return new ArgumentException("Unknown error");
453             }
454         }
455     }
456 }