Fix emulator build error
[platform/framework/web/chromium-efl.git] / components / permissions / permission_decision_auto_blocker.cc
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/permissions/permission_decision_auto_blocker.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11
12 #include "base/feature_list.h"
13 #include "base/metrics/field_trial_params.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/values.h"
16 #include "components/content_settings/core/browser/host_content_settings_map.h"
17 #include "components/keyed_service/content/browser_context_dependency_manager.h"
18 #include "components/permissions/features.h"
19 #include "components/permissions/permission_util.h"
20 #include "url/gurl.h"
21
22 namespace permissions {
23 namespace {
24
25 using PermissionStatus = blink::mojom::PermissionStatus;
26
27 constexpr int kDefaultDismissalsBeforeBlock = 3;
28 constexpr int kDefaultIgnoresBeforeBlock = 4;
29 constexpr int kDefaultDismissalsBeforeBlockWithQuietUi = 1;
30 constexpr int kDefaultIgnoresBeforeBlockWithQuietUi = 2;
31 constexpr int kDefaultEmbargoDays = 7;
32
33 // The number of times that users may explicitly dismiss a
34 // FEDERATED_IDENTITY_API permission prompt from an origin before it is
35 // automatically blocked.
36 constexpr int kFederatedIdentityApiDismissalsBeforeBlock = 1;
37
38 // The durations that an origin will stay under embargo for the
39 // FEDERATED_IDENTITY_API permission due to the user explicitly dismissing the
40 // permission prompt.
41 constexpr base::TimeDelta kFederatedIdentityApiEmbargoDurationDismiss[] = {
42     base::Hours(2) /* 1st dismissal */, base::Days(1) /* 2nd dismissal */,
43     base::Days(7), base::Days(28)};
44
45 // The duration that an origin will stay under embargo for the
46 // FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION permission due to an auto re-authn
47 // prompt being displayed recently.
48 constexpr base::TimeDelta kFederatedIdentityAutoReauthnEmbargoDuration =
49     base::Minutes(10);
50
51 // The number of times that users may explicitly dismiss a permission prompt
52 // from an origin before it is automatically blocked.
53 int g_dismissals_before_block = kDefaultDismissalsBeforeBlock;
54
55 // The number of times that users may ignore a permission prompt from an origin
56 // before it is automatically blocked.
57 int g_ignores_before_block = kDefaultIgnoresBeforeBlock;
58
59 // The number of times that users may dismiss a permission prompt that uses the
60 // quiet UI from an origin before it is automatically blocked.
61 int g_dismissals_before_block_with_quiet_ui =
62     kDefaultDismissalsBeforeBlockWithQuietUi;
63
64 // The number of times that users may ignore a permission prompt that uses the
65 // quiet UI from an origin before it is automatically blocked.
66 int g_ignores_before_block_with_quiet_ui =
67     kDefaultIgnoresBeforeBlockWithQuietUi;
68
69 // The number of days that an origin will stay under embargo for a requested
70 // permission due to repeated dismissals.
71 int g_dismissal_embargo_days = kDefaultEmbargoDays;
72
73 // The number of days that an origin will stay under embargo for a requested
74 // permission due to repeated ignores.
75 int g_ignore_embargo_days = kDefaultEmbargoDays;
76
77 std::string GetStringForContentType(ContentSettingsType content_type) {
78   if (content_type == ContentSettingsType::FEDERATED_IDENTITY_API)
79     return "FederatedIdentityApi";
80
81   if (content_type ==
82       ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION) {
83     return "FederatedIdentityAutoReauthn";
84   }
85
86   if (content_type == ContentSettingsType::FILE_SYSTEM_WRITE_GUARD) {
87     return "FileSystemWriteGuard";
88   }
89
90   if (content_type == ContentSettingsType::AUTO_PICTURE_IN_PICTURE) {
91     return "AutoPictureInPicture";
92   }
93
94   return PermissionUtil::GetPermissionString(content_type);
95 }
96
97 base::Value::Dict GetOriginAutoBlockerData(HostContentSettingsMap* settings,
98                                            const GURL& origin_url) {
99   base::Value website_setting = settings->GetWebsiteSetting(
100       origin_url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA);
101   if (!website_setting.is_dict()) {
102     return base::Value::Dict();
103   }
104
105   return std::move(website_setting.GetDict());
106 }
107
108 base::Value::Dict* GetOrCreatePermissionDict(base::Value::Dict& origin_dict,
109                                              const std::string& permission) {
110   return origin_dict.EnsureDict(permission);
111 }
112
113 int RecordActionInWebsiteSettings(const GURL& url,
114                                   ContentSettingsType permission,
115                                   const char* key,
116                                   HostContentSettingsMap* settings_map) {
117   base::Value::Dict dict = GetOriginAutoBlockerData(settings_map, url);
118
119   base::Value::Dict* permission_dict =
120       GetOrCreatePermissionDict(dict, GetStringForContentType(permission));
121
122   absl::optional<int> value = permission_dict->FindInt(key);
123   int current_count = value.value_or(0);
124   permission_dict->Set(key, base::Value(++current_count));
125
126   settings_map->SetWebsiteSettingDefaultScope(
127       url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
128       base::Value(std::move(dict)));
129
130   return current_count;
131 }
132
133 int GetActionCount(const GURL& url,
134                    ContentSettingsType permission,
135                    const char* key,
136                    HostContentSettingsMap* settings_map) {
137   base::Value::Dict dict = GetOriginAutoBlockerData(settings_map, url);
138   base::Value::Dict* permission_dict =
139       GetOrCreatePermissionDict(dict, GetStringForContentType(permission));
140
141   absl::optional<int> value = permission_dict->FindInt(key);
142   return value.value_or(0);
143 }
144
145 // Returns the number of times that users may explicitly dismiss a permission
146 // prompt for an origin for the passed-in |permission| before it is
147 // automatically blocked.
148 int GetDismissalsBeforeBlockForContentSettingsType(
149     ContentSettingsType permission) {
150   return (permission == ContentSettingsType::FEDERATED_IDENTITY_API)
151              ? kFederatedIdentityApiDismissalsBeforeBlock
152              : g_dismissals_before_block;
153 }
154
155 // The duration that an origin will stay under embargo for the passed-in
156 // |permission| due to the user explicitly dismissing the permission prompt.
157 base::TimeDelta GetEmbargoDurationForContentSettingsType(
158     ContentSettingsType permission,
159     int dismiss_count) {
160   if (permission == ContentSettingsType::FEDERATED_IDENTITY_API) {
161     int duration_index = std::clamp(
162         dismiss_count - 1, 0,
163         static_cast<int>(
164             std::size(kFederatedIdentityApiEmbargoDurationDismiss) - 1));
165     return kFederatedIdentityApiEmbargoDurationDismiss[duration_index];
166   }
167
168   if (permission ==
169       ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION) {
170     return kFederatedIdentityAutoReauthnEmbargoDuration;
171   }
172
173   return base::Days(g_dismissal_embargo_days);
174 }
175
176 base::Time GetEmbargoStartTime(base::Value::Dict* permission_dict,
177                                const base::Feature& feature,
178                                const char* key) {
179   absl::optional<double> found = permission_dict->FindDouble(key);
180   if (found && base::FeatureList::IsEnabled(feature)) {
181     return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(*found));
182   }
183   return base::Time();
184 }
185
186 bool IsUnderEmbargo(base::Value::Dict* permission_dict,
187                     const base::Feature& feature,
188                     const char* key,
189                     base::Time current_time,
190                     base::TimeDelta offset) {
191   absl::optional<double> found = permission_dict->FindDouble(key);
192   if (found && base::FeatureList::IsEnabled(feature) &&
193       current_time < base::Time::FromInternalValue(*found) + offset) {
194     return true;
195   }
196
197   return false;
198 }
199
200 void UpdateValueFromVariation(const std::string& variation_value,
201                               int* value_store,
202                               const int default_value) {
203   int tmp_value = -1;
204   if (base::StringToInt(variation_value, &tmp_value) && tmp_value > 0)
205     *value_store = tmp_value;
206   else
207     *value_store = default_value;
208 }
209
210 }  // namespace
211
212 // static
213 const char PermissionDecisionAutoBlocker::kPromptDismissCountKey[] =
214     "dismiss_count";
215
216 // static
217 const char PermissionDecisionAutoBlocker::kPromptIgnoreCountKey[] =
218     "ignore_count";
219
220 // static
221 const char PermissionDecisionAutoBlocker::kPromptDismissCountWithQuietUiKey[] =
222     "dismiss_count_quiet_ui";
223
224 // static
225 const char PermissionDecisionAutoBlocker::kPromptIgnoreCountWithQuietUiKey[] =
226     "ignore_count_quiet_ui";
227
228 // static
229 const char PermissionDecisionAutoBlocker::kPermissionDismissalEmbargoKey[] =
230     "dismissal_embargo_days";
231
232 // static
233 const char PermissionDecisionAutoBlocker::kPermissionIgnoreEmbargoKey[] =
234     "ignore_embargo_days";
235
236 // static
237 const char PermissionDecisionAutoBlocker::kPermissionDisplayEmbargoKey[] =
238     "display_embargo_minutes";
239
240 // static
241 bool PermissionDecisionAutoBlocker::IsEnabledForContentSetting(
242     ContentSettingsType content_setting) {
243   return PermissionUtil::IsPermission(content_setting) ||
244          content_setting == ContentSettingsType::FEDERATED_IDENTITY_API ||
245          content_setting ==
246              ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION ||
247          content_setting == ContentSettingsType::FILE_SYSTEM_WRITE_GUARD ||
248          content_setting == ContentSettingsType::AUTO_PICTURE_IN_PICTURE;
249 }
250
251 // static
252 absl::optional<content::PermissionResult>
253 PermissionDecisionAutoBlocker::GetEmbargoResult(
254     HostContentSettingsMap* settings_map,
255     const GURL& request_origin,
256     ContentSettingsType permission,
257     base::Time current_time) {
258   DCHECK(settings_map);
259   DCHECK(IsEnabledForContentSetting(permission));
260
261   base::Value::Dict dict =
262       GetOriginAutoBlockerData(settings_map, request_origin);
263   base::Value::Dict* permission_dict =
264       GetOrCreatePermissionDict(dict, GetStringForContentType(permission));
265
266   int dismiss_count = GetActionCount(request_origin, permission,
267                                      kPromptDismissCountKey, settings_map);
268   if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfDismissedOften,
269                      kPermissionDismissalEmbargoKey, current_time,
270                      GetEmbargoDurationForContentSettingsType(permission,
271                                                               dismiss_count))) {
272     return content::PermissionResult(
273         PermissionStatus::DENIED,
274         content::PermissionStatusSource::MULTIPLE_DISMISSALS);
275   }
276
277   if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfIgnoredOften,
278                      kPermissionIgnoreEmbargoKey, current_time,
279                      base::Days(g_ignore_embargo_days))) {
280     return content::PermissionResult(
281         PermissionStatus::DENIED,
282         content::PermissionStatusSource::MULTIPLE_IGNORES);
283   }
284
285   if (IsUnderEmbargo(permission_dict,
286                      features::kBlockRepeatedAutoReauthnPrompts,
287                      kPermissionDisplayEmbargoKey, current_time,
288                      GetEmbargoDurationForContentSettingsType(
289                          permission, /*dismiss_count=*/0))) {
290     return content::PermissionResult(
291         PermissionStatus::DENIED,
292         content::PermissionStatusSource::RECENT_DISPLAY);
293   }
294
295   return absl::nullopt;
296 }
297
298 // static
299 void PermissionDecisionAutoBlocker::UpdateFromVariations() {
300   std::string dismissals_before_block_value =
301       base::GetFieldTrialParamValueByFeature(
302           features::kBlockPromptsIfDismissedOften, kPromptDismissCountKey);
303   std::string ignores_before_block_value =
304       base::GetFieldTrialParamValueByFeature(
305           features::kBlockPromptsIfIgnoredOften, kPromptIgnoreCountKey);
306   std::string dismissals_before_block_value_with_quiet_ui =
307       base::GetFieldTrialParamValueByFeature(
308           features::kBlockPromptsIfDismissedOften,
309           kPromptDismissCountWithQuietUiKey);
310   std::string ignores_before_block_value_with_quiet_ui =
311       base::GetFieldTrialParamValueByFeature(
312           features::kBlockPromptsIfIgnoredOften,
313           kPromptIgnoreCountWithQuietUiKey);
314   std::string dismissal_embargo_days_value =
315       base::GetFieldTrialParamValueByFeature(
316           features::kBlockPromptsIfDismissedOften,
317           kPermissionDismissalEmbargoKey);
318   std::string ignore_embargo_days_value =
319       base::GetFieldTrialParamValueByFeature(
320           features::kBlockPromptsIfIgnoredOften, kPermissionIgnoreEmbargoKey);
321
322   // If converting the value fails, revert to the original value.
323   UpdateValueFromVariation(dismissals_before_block_value,
324                            &g_dismissals_before_block,
325                            kDefaultDismissalsBeforeBlock);
326   UpdateValueFromVariation(ignores_before_block_value, &g_ignores_before_block,
327                            kDefaultIgnoresBeforeBlock);
328   UpdateValueFromVariation(dismissals_before_block_value_with_quiet_ui,
329                            &g_dismissals_before_block_with_quiet_ui,
330                            kDefaultDismissalsBeforeBlockWithQuietUi);
331   UpdateValueFromVariation(ignores_before_block_value_with_quiet_ui,
332                            &g_ignores_before_block_with_quiet_ui,
333                            kDefaultIgnoresBeforeBlockWithQuietUi);
334   UpdateValueFromVariation(dismissal_embargo_days_value,
335                            &g_dismissal_embargo_days, kDefaultEmbargoDays);
336   UpdateValueFromVariation(ignore_embargo_days_value, &g_ignore_embargo_days,
337                            kDefaultEmbargoDays);
338 }
339
340 bool PermissionDecisionAutoBlocker::IsEmbargoed(
341     const GURL& request_origin,
342     ContentSettingsType permission) {
343   return GetEmbargoResult(request_origin, permission).has_value();
344 }
345
346 absl::optional<content::PermissionResult>
347 PermissionDecisionAutoBlocker::GetEmbargoResult(
348     const GURL& request_origin,
349     ContentSettingsType permission) {
350   return GetEmbargoResult(settings_map_, request_origin, permission,
351                           clock_->Now());
352 }
353
354 base::Time PermissionDecisionAutoBlocker::GetEmbargoStartTime(
355     const GURL& request_origin,
356     ContentSettingsType permission) {
357   DCHECK(settings_map_);
358   base::Value::Dict dict =
359       GetOriginAutoBlockerData(settings_map_, request_origin);
360   base::Value::Dict* permission_dict =
361       GetOrCreatePermissionDict(dict, GetStringForContentType(permission));
362
363   // A permission may have a record for both dismisal and ignore, return the
364   // most recent. A permission will only actually be under one embargo, but
365   // the record of embargo start will persist until explicitly deleted
366   base::Time dismissal_start_time = permissions::GetEmbargoStartTime(
367       permission_dict, features::kBlockPromptsIfDismissedOften,
368       kPermissionDismissalEmbargoKey);
369   base::Time ignore_start_time = permissions::GetEmbargoStartTime(
370       permission_dict, features::kBlockPromptsIfIgnoredOften,
371       kPermissionIgnoreEmbargoKey);
372
373   return dismissal_start_time > ignore_start_time ? dismissal_start_time
374                                                   : ignore_start_time;
375 }
376
377 std::set<GURL> PermissionDecisionAutoBlocker::GetEmbargoedOrigins(
378     ContentSettingsType content_type) {
379   return GetEmbargoedOrigins(std::vector<ContentSettingsType>{content_type});
380 }
381
382 std::set<GURL> PermissionDecisionAutoBlocker::GetEmbargoedOrigins(
383     std::vector<ContentSettingsType> content_types) {
384   DCHECK(settings_map_);
385
386   std::vector<ContentSettingsType> filtered_content_types;
387   for (ContentSettingsType content_type : content_types) {
388     if (IsEnabledForContentSetting(content_type))
389       filtered_content_types.emplace_back(content_type);
390   }
391   if (filtered_content_types.empty())
392     return std::set<GURL>();
393
394   std::set<GURL> origins;
395   for (const auto& e : settings_map_->GetSettingsForOneType(
396            ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA)) {
397     for (auto content_type : filtered_content_types) {
398       const GURL url(e.primary_pattern.ToString());
399       if (IsEmbargoed(url, content_type)) {
400         origins.insert(url);
401         break;
402       }
403     }
404   }
405   return origins;
406 }
407
408 int PermissionDecisionAutoBlocker::GetDismissCount(
409     const GURL& url,
410     ContentSettingsType permission) {
411   return GetActionCount(url, permission, kPromptDismissCountKey, settings_map_);
412 }
413
414 int PermissionDecisionAutoBlocker::GetIgnoreCount(
415     const GURL& url,
416     ContentSettingsType permission) {
417   return GetActionCount(url, permission, kPromptIgnoreCountKey, settings_map_);
418 }
419
420 bool PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
421     const GURL& url,
422     ContentSettingsType permission,
423     bool dismissed_prompt_was_quiet) {
424   int current_dismissal_count = RecordActionInWebsiteSettings(
425       url, permission, kPromptDismissCountKey, settings_map_);
426
427   int current_dismissal_count_with_quiet_ui =
428       dismissed_prompt_was_quiet
429           ? RecordActionInWebsiteSettings(url, permission,
430                                           kPromptDismissCountWithQuietUiKey,
431                                           settings_map_)
432           : -1;
433
434   // TODO(dominickn): ideally we would have a method
435   // PermissionContextBase::ShouldEmbargoAfterRepeatedDismissals() to specify
436   // if a permission is opted in. This is difficult right now because:
437   // 1. PermissionQueueController needs to call this method at a point where it
438   //    does not have a PermissionContextBase available
439   // 2. Not calling RecordDismissAndEmbargo means no repeated dismissal metrics
440   //    are recorded
441   if (base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften)) {
442     if (current_dismissal_count >=
443         GetDismissalsBeforeBlockForContentSettingsType(permission)) {
444       PlaceUnderEmbargo(url, permission, kPermissionDismissalEmbargoKey);
445       return true;
446     }
447
448     if (current_dismissal_count_with_quiet_ui >=
449         g_dismissals_before_block_with_quiet_ui) {
450       DCHECK(permission == ContentSettingsType::NOTIFICATIONS ||
451              permission == ContentSettingsType::GEOLOCATION);
452       PlaceUnderEmbargo(url, permission, kPermissionDismissalEmbargoKey);
453       return true;
454     }
455   }
456   return false;
457 }
458
459 bool PermissionDecisionAutoBlocker::RecordIgnoreAndEmbargo(
460     const GURL& url,
461     ContentSettingsType permission,
462     bool ignored_prompt_was_quiet) {
463   int current_ignore_count = RecordActionInWebsiteSettings(
464       url, permission, kPromptIgnoreCountKey, settings_map_);
465
466   int current_ignore_count_with_quiet_ui =
467       ignored_prompt_was_quiet
468           ? RecordActionInWebsiteSettings(url, permission,
469                                           kPromptIgnoreCountWithQuietUiKey,
470                                           settings_map_)
471           : -1;
472
473   if (base::FeatureList::IsEnabled(features::kBlockPromptsIfIgnoredOften)) {
474     if (current_ignore_count >= g_ignores_before_block) {
475       PlaceUnderEmbargo(url, permission, kPermissionIgnoreEmbargoKey);
476       return true;
477     }
478
479     if (current_ignore_count_with_quiet_ui >=
480         g_ignores_before_block_with_quiet_ui) {
481       DCHECK(permission == ContentSettingsType::NOTIFICATIONS ||
482              permission == ContentSettingsType::GEOLOCATION);
483       PlaceUnderEmbargo(url, permission, kPermissionIgnoreEmbargoKey);
484       return true;
485     }
486   }
487
488   return false;
489 }
490
491 bool PermissionDecisionAutoBlocker::RecordDisplayAndEmbargo(
492     const GURL& url,
493     ContentSettingsType permission) {
494   DCHECK_EQ(permission,
495             ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION);
496   if (base::FeatureList::IsEnabled(
497           features::kBlockRepeatedAutoReauthnPrompts)) {
498     PlaceUnderEmbargo(url, permission, kPermissionDisplayEmbargoKey);
499     return true;
500   }
501   return false;
502 }
503
504 void PermissionDecisionAutoBlocker::RemoveEmbargoAndResetCounts(
505     const GURL& url,
506     ContentSettingsType permission) {
507   if (!IsEnabledForContentSetting(permission))
508     return;
509
510   base::Value::Dict dict = GetOriginAutoBlockerData(settings_map_, url);
511
512   dict.Remove(GetStringForContentType(permission));
513
514   settings_map_->SetWebsiteSettingDefaultScope(
515       url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
516       base::Value(std::move(dict)));
517 }
518
519 void PermissionDecisionAutoBlocker::RemoveEmbargoAndResetCounts(
520     base::RepeatingCallback<bool(const GURL& url)> filter) {
521   for (const auto& site : settings_map_->GetSettingsForOneType(
522            ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA)) {
523     GURL origin(site.primary_pattern.ToString());
524
525     if (origin.is_valid() && filter.Run(origin)) {
526       settings_map_->SetWebsiteSettingDefaultScope(
527           origin, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
528           base::Value());
529     }
530   }
531 }
532
533 void PermissionDecisionAutoBlocker::AddObserver(Observer* obs) {
534   observers_.AddObserver(obs);
535 }
536
537 void PermissionDecisionAutoBlocker::RemoveObserver(Observer* obs) {
538   observers_.RemoveObserver(obs);
539 }
540
541 // static
542 const char*
543 PermissionDecisionAutoBlocker::GetPromptDismissCountKeyForTesting() {
544   return kPromptDismissCountKey;
545 }
546
547 PermissionDecisionAutoBlocker::PermissionDecisionAutoBlocker(
548     HostContentSettingsMap* settings_map)
549     : settings_map_(settings_map), clock_(base::DefaultClock::GetInstance()) {}
550
551 PermissionDecisionAutoBlocker::~PermissionDecisionAutoBlocker() {}
552
553 void PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
554     const GURL& request_origin,
555     ContentSettingsType permission,
556     const char* key) {
557   base::Value::Dict dict =
558       GetOriginAutoBlockerData(settings_map_, request_origin);
559   base::Value::Dict* permission_dict =
560       GetOrCreatePermissionDict(dict, GetStringForContentType(permission));
561   permission_dict->Set(
562       key, base::Value(static_cast<double>(clock_->Now().ToInternalValue())));
563   settings_map_->SetWebsiteSettingDefaultScope(
564       request_origin, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
565       base::Value(std::move(dict)));
566   NotifyEmbargoStarted(request_origin, permission);
567 }
568
569 void PermissionDecisionAutoBlocker::NotifyEmbargoStarted(
570     const GURL& origin,
571     ContentSettingsType content_setting) {
572   for (Observer& obs : observers_)
573     obs.OnEmbargoStarted(origin, content_setting);
574 }
575
576 void PermissionDecisionAutoBlocker::SetClockForTesting(base::Clock* clock) {
577   clock_ = clock;
578 }
579
580 }  // namespace permissions