Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: chrome/browser/media/extension_media_access_handler.cc

Issue 2532323003: Public Sessions - prompt the user for audioCapture/videoCapture requests (Closed)
Patch Set: Fixed unit test Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/media/extension_media_access_handler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/media/extension_media_access_handler.h" 5 #include "chrome/browser/media/extension_media_access_handler.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" 9 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
10 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h" 10 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
11 #include "chrome/browser/media/webrtc/media_stream_device_permissions.h" 11 #include "chrome/browser/media/webrtc/media_stream_device_permissions.h"
12 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/pref_names.h" 13 #include "chrome/common/pref_names.h"
14 #include "chromeos/login/login_state.h"
14 #include "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents.h"
15 #include "extensions/common/extension.h" 16 #include "extensions/common/extension.h"
17 #include "extensions/common/permissions/manifest_permission_set.h"
16 #include "extensions/common/permissions/permissions_data.h" 18 #include "extensions/common/permissions/permissions_data.h"
19 #include "extensions/common/url_pattern_set.h"
17 20
18 namespace { 21 namespace {
19 22
20 // This is a short-term solution to grant camera and/or microphone access to 23 // This is a short-term solution to grant camera and/or microphone access to
21 // extensions: 24 // extensions:
22 // 1. Virtual keyboard extension. 25 // 1. Virtual keyboard extension.
23 // 2. Flutter gesture recognition extension. 26 // 2. Flutter gesture recognition extension.
24 // 3. TODO(smus): Airbender experiment 1. 27 // 3. TODO(smus): Airbender experiment 1.
25 // 4. TODO(smus): Airbender experiment 2. 28 // 4. TODO(smus): Airbender experiment 2.
26 // 5. Hotwording component extension. 29 // 5. Hotwording component extension.
27 // 6. XKB input method component extension. 30 // 6. XKB input method component extension.
28 // 7. M17n/T13n/CJK input method component extension. 31 // 7. M17n/T13n/CJK input method component extension.
29 // Once http://crbug.com/292856 is fixed, remove this whitelist. 32 // Once http://crbug.com/292856 is fixed, remove this whitelist.
30 bool IsMediaRequestWhitelistedForExtension( 33 bool IsMediaRequestWhitelistedForExtension(
31 const extensions::Extension* extension) { 34 const extensions::Extension* extension) {
32 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" || 35 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
33 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" || 36 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
34 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" || 37 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
35 extension->id() == "nnckehldicaciogcbchegobnafnjkcne" || 38 extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
36 extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" || 39 extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" ||
37 extension->id() == "jkghodnilhceideoidjikpgommlajknk" || 40 extension->id() == "jkghodnilhceideoidjikpgommlajknk" ||
38 extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop"; 41 extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop";
39 } 42 }
40 43
44 // Returns true if we're in a Public Session.
45 bool IsPublicSession() {
46 #if defined(OS_CHROMEOS)
47 if (chromeos::LoginState::IsInitialized()) {
48 return chromeos::LoginState::Get()->IsPublicSessionUser();
49 }
50 #endif
51 return false;
52 }
53
41 } // namespace 54 } // namespace
42 55
43 ExtensionMediaAccessHandler::ExtensionMediaAccessHandler() { 56 ExtensionMediaAccessHandler::ExtensionMediaAccessHandler() {
44 } 57 }
45 58
46 ExtensionMediaAccessHandler::~ExtensionMediaAccessHandler() { 59 ExtensionMediaAccessHandler::~ExtensionMediaAccessHandler() {
47 } 60 }
48 61
49 bool ExtensionMediaAccessHandler::SupportsStreamType( 62 bool ExtensionMediaAccessHandler::SupportsStreamType(
50 const content::MediaStreamType type, 63 const content::MediaStreamType type,
51 const extensions::Extension* extension) { 64 const extensions::Extension* extension) {
52 return extension && (extension->is_platform_app() || 65 return extension && (extension->is_platform_app() ||
53 IsMediaRequestWhitelistedForExtension(extension)) && 66 IsMediaRequestWhitelistedForExtension(extension)) &&
Sergey Ulanov 2016/11/29 19:12:05 This class is applicable for platform apps and whi
Ivan Šandrk 2016/11/29 23:33:46 You have a good point, the goal of this CL is to s
54 (type == content::MEDIA_DEVICE_AUDIO_CAPTURE || 67 (type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
55 type == content::MEDIA_DEVICE_VIDEO_CAPTURE); 68 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
56 } 69 }
57 70
58 bool ExtensionMediaAccessHandler::CheckMediaAccessPermission( 71 bool ExtensionMediaAccessHandler::CheckMediaAccessPermission(
59 content::WebContents* web_contents, 72 content::WebContents* web_contents,
60 const GURL& security_origin, 73 const GURL& security_origin,
61 content::MediaStreamType type, 74 content::MediaStreamType type,
62 const extensions::Extension* extension) { 75 const extensions::Extension* extension) {
63 return extension->permissions_data()->HasAPIPermission( 76 return extension->permissions_data()->HasAPIPermission(
64 type == content::MEDIA_DEVICE_AUDIO_CAPTURE 77 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
65 ? extensions::APIPermission::kAudioCapture 78 ? extensions::APIPermission::kAudioCapture
66 : extensions::APIPermission::kVideoCapture); 79 : extensions::APIPermission::kVideoCapture);
67 } 80 }
68 81
82 bool ExtensionMediaAccessHandler::UserChoice::Disallowed(
Sergey Ulanov 2016/11/29 19:12:06 IsAllowed()? That would make code that uses this f
Ivan Šandrk 2016/11/29 23:33:46 I believe there's a subtle semantic difference bet
Ivan Šandrk 2016/11/30 17:00:50 Decided to go with the IsAllowed in the end.
83 content::MediaStreamType type) const {
84 if (IsPublicSession()) {
85 CHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
Sergey Ulanov 2016/11/29 19:12:06 Why is this CHECK instead of DCHECK? https://chrom
Ivan Šandrk 2016/11/29 23:33:46 Good point, done. And changed the other CHECK as w
86 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
87 if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
88 return audio_prompted_ && !audio_allowed_;
89 } else {
90 return video_prompted_ && !video_allowed_;
91 }
92 }
93 return false;
94 }
95
96 bool ExtensionMediaAccessHandler::UserChoice::NeedsPrompting(
97 content::MediaStreamType type) const {
98 if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
99 return !audio_prompted_;
100 }
101 if (type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
102 return !video_prompted_;
103 }
104 return false;
105 }
106
107 void ExtensionMediaAccessHandler::UserChoice::Set(
108 content::MediaStreamType type, bool allowed) {
109 CHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
110 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
111 if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
112 DCHECK(!audio_prompted_);
113 audio_prompted_ = true;
114 audio_allowed_ = allowed;
115 } else {
116 DCHECK(!video_prompted_);
117 video_prompted_ = true;
118 video_allowed_ = allowed;
119 }
120 }
121
122 void ExtensionMediaAccessHandler::ResolvePermissionPrompt(
123 content::WebContents* web_contents,
124 const content::MediaStreamRequest& request,
125 const content::MediaResponseCallback& callback,
126 const extensions::Extension* extension,
127 ExtensionInstallPrompt::Result prompt_result) {
128 bool allowed = (prompt_result == ExtensionInstallPrompt::Result::ACCEPTED);
129 UserChoice& user_choice = user_choice_cache_[extension->id()];
130
131 if (user_choice.NeedsPrompting(request.audio_type))
132 user_choice.Set(content::MEDIA_DEVICE_AUDIO_CAPTURE, allowed);
133 if (user_choice.NeedsPrompting(request.video_type))
134 user_choice.Set(content::MEDIA_DEVICE_VIDEO_CAPTURE, allowed);
135
136 HandleRequestContinuation(web_contents, request, callback, extension);
137 }
138
69 void ExtensionMediaAccessHandler::HandleRequest( 139 void ExtensionMediaAccessHandler::HandleRequest(
70 content::WebContents* web_contents, 140 content::WebContents* web_contents,
71 const content::MediaStreamRequest& request, 141 const content::MediaStreamRequest& request,
72 const content::MediaResponseCallback& callback, 142 const content::MediaResponseCallback& callback,
73 const extensions::Extension* extension) { 143 const extensions::Extension* extension) {
74 // TODO(vrk): This code is largely duplicated in 144 // TODO(vrk): This code is largely duplicated in
Sergey Ulanov 2016/11/29 19:12:06 Move this TODO to HandleRequestContinuation()
Ivan Šandrk 2016/11/29 23:33:46 Done.
75 // MediaStreamDevicesController::Accept(). Move this code into a shared method 145 // MediaStreamDevicesController::Accept(). Move this code into a shared method
76 // between the two classes. 146 // between the two classes.
77 147
148 // In Public Sessions extensions are installed by admin policy hence they can
149 // freely use the camera and microphone without the user knowing. This is not
150 // acceptable from a security/privacy standpoint so we show the user a dialog
151 // where he can choose whether to allow the extension access to camera and/or
152 // microphone.
153 if (IsPublicSession()) {
Sergey Ulanov 2016/11/29 19:12:05 Did you consider adding a separate MediaAccessHand
Ivan Šandrk 2016/11/29 23:33:46 This is a good idea :) I will investigate it more
Ivan Šandrk 2016/11/30 17:00:50 Implemented a separate MediaAccessHandler :)
154 bool needs_prompt = false;
155 extensions::APIPermissionSet new_apis;
156 const UserChoice& user_choice = user_choice_cache_[extension->id()];
157
158 if (user_choice.NeedsPrompting(request.audio_type)) {
159 new_apis.insert(extensions::APIPermission::kAudioCapture);
160 needs_prompt = true;
161 }
162 if (user_choice.NeedsPrompting(request.video_type)) {
163 new_apis.insert(extensions::APIPermission::kVideoCapture);
164 needs_prompt = true;
165 }
166
167 if (needs_prompt) {
168 std::unique_ptr<const extensions::PermissionSet> permission_set(
169 new extensions::PermissionSet(
170 new_apis, extensions::ManifestPermissionSet(),
171 extensions::URLPatternSet(), extensions::URLPatternSet()));
172
173 prompt_.reset(new ExtensionInstallPrompt(web_contents));
Sergey Ulanov 2016/11/29 19:12:06 There may be multiple requests that will need to b
Sergey Ulanov 2016/11/29 19:12:06 Does ExtensionInstallPrompt() work when the web_co
Ivan Šandrk 2016/11/29 23:33:46 I believe it should always work when there's web_c
Ivan Šandrk 2016/11/29 23:33:46 Now I've prompted to save the ExtensionInstallProm
174 prompt_->ShowDialog(
175 base::Bind(&ExtensionMediaAccessHandler::ResolvePermissionPrompt,
176 base::Unretained(this), web_contents, request, callback,
177 extension),
178 extension,
179 nullptr,
180 base::MakeUnique<ExtensionInstallPrompt::Prompt>(
181 ExtensionInstallPrompt::PERMISSIONS_PROMPT),
182 std::move(permission_set),
183 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
184 return;
185 }
186 }
187
188 HandleRequestContinuation(web_contents, request, callback, extension);
189 }
190
191 void ExtensionMediaAccessHandler::HandleRequestContinuation(
192 content::WebContents* web_contents,
193 const content::MediaStreamRequest& request,
194 const content::MediaResponseCallback& callback,
195 const extensions::Extension* extension) {
78 Profile* profile = 196 Profile* profile =
79 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 197 Profile::FromBrowserContext(web_contents->GetBrowserContext());
198 const UserChoice& user_choice = user_choice_cache_[extension->id()];
80 199
81 bool audio_allowed = 200 bool audio_allowed =
82 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && 201 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
83 extension->permissions_data()->HasAPIPermission( 202 extension->permissions_data()->HasAPIPermission(
84 extensions::APIPermission::kAudioCapture) && 203 extensions::APIPermission::kAudioCapture) &&
85 GetDevicePolicy(profile, extension->url(), prefs::kAudioCaptureAllowed, 204 GetDevicePolicy(profile, extension->url(), prefs::kAudioCaptureAllowed,
86 prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY; 205 prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY &&
206 !user_choice.Disallowed(content::MEDIA_DEVICE_AUDIO_CAPTURE);
87 bool video_allowed = 207 bool video_allowed =
88 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && 208 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
89 extension->permissions_data()->HasAPIPermission( 209 extension->permissions_data()->HasAPIPermission(
90 extensions::APIPermission::kVideoCapture) && 210 extensions::APIPermission::kVideoCapture) &&
91 GetDevicePolicy(profile, extension->url(), prefs::kVideoCaptureAllowed, 211 GetDevicePolicy(profile, extension->url(), prefs::kVideoCaptureAllowed,
92 prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY; 212 prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY &&
213 !user_choice.Disallowed(content::MEDIA_DEVICE_VIDEO_CAPTURE);
93 214
94 bool get_default_audio_device = audio_allowed; 215 bool get_default_audio_device = audio_allowed;
95 bool get_default_video_device = video_allowed; 216 bool get_default_video_device = video_allowed;
96 217
97 content::MediaStreamDevices devices; 218 content::MediaStreamDevices devices;
98 219
99 // Set an initial error result. If neither audio or video is allowed, we'll 220 // Set an initial error result. If neither audio or video is allowed, we'll
100 // never try to get any device below but will just create |ui| and return an 221 // never try to get any device below but will just create |ui| and return an
101 // empty list with "invalid state" result. If at least one is allowed, we'll 222 // empty list with "invalid state" result. If at least one is allowed, we'll
102 // try to get device(s), and if failure, we want to return "no hardware" 223 // try to get device(s), and if failure, we want to return "no hardware"
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 std::unique_ptr<content::MediaStreamUI> ui; 261 std::unique_ptr<content::MediaStreamUI> ui;
141 if (!devices.empty()) { 262 if (!devices.empty()) {
142 result = content::MEDIA_DEVICE_OK; 263 result = content::MEDIA_DEVICE_OK;
143 ui = MediaCaptureDevicesDispatcher::GetInstance() 264 ui = MediaCaptureDevicesDispatcher::GetInstance()
144 ->GetMediaStreamCaptureIndicator() 265 ->GetMediaStreamCaptureIndicator()
145 ->RegisterMediaStream(web_contents, devices); 266 ->RegisterMediaStream(web_contents, devices);
146 } 267 }
147 268
148 callback.Run(devices, result, std::move(ui)); 269 callback.Run(devices, result, std::move(ui));
149 } 270 }
OLDNEW
« no previous file with comments | « chrome/browser/media/extension_media_access_handler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698