1 // Copyright 2014 The Chromium Authors. All rights reserved.
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 "chrome/browser/chromeos/policy/consumer_management_service.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/browser_process_platform_part.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
20 #include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h"
21 #include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
22 #include "chrome/browser/chromeos/profiles/profile_helper.h"
23 #include "chrome/browser/notifications/notification.h"
24 #include "chrome/browser/notifications/notification_delegate.h"
25 #include "chrome/browser/notifications/notification_ui_manager.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28 #include "chrome/browser/signin/signin_manager_factory.h"
29 #include "chrome/browser/ui/browser_navigator.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/url_constants.h"
32 #include "chromeos/dbus/cryptohome/rpc.pb.h"
33 #include "chromeos/dbus/cryptohome_client.h"
34 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
35 #include "components/signin/core/browser/profile_oauth2_token_service.h"
36 #include "components/signin/core/browser/signin_manager_base.h"
37 #include "components/user_manager/user_manager.h"
38 #include "content/public/browser/notification_details.h"
39 #include "content/public/browser/notification_service.h"
40 #include "content/public/browser/notification_source.h"
41 #include "google_apis/gaia/gaia_constants.h"
42 #include "google_apis/gaia/google_service_auth_error.h"
43 #include "grit/generated_resources.h"
44 #include "grit/theme_resources.h"
45 #include "policy/proto/device_management_backend.pb.h"
46 #include "third_party/WebKit/public/web/WebTextDirection.h"
47 #include "ui/base/l10n/l10n_util.h"
48 #include "ui/base/page_transition_types.h"
49 #include "ui/base/resource/resource_bundle.h"
50 #include "ui/base/window_open_disposition.h"
51 #include "ui/message_center/notification.h"
52 #include "ui/message_center/notification_types.h"
53 #include "ui/message_center/notifier_settings.h"
54 #include "url/gurl.h"
55
56 namespace {
57
58 // Boot atttributes ID.
59 const char kAttributeOwnerId[] = "consumer_management.owner_id";
60
61 // Desktop notification constants.
62 const char kEnrollmentNotificationId[] = "consumer_management.enroll";
63 const char kEnrollmentNotificationUrl[] = "chrome://consumer-management/enroll";
64
65 // The path to the consumer management enrollment/unenrollment confirmation
66 // overlay, relative to the settings page URL.
67 const char kConsumerManagementOverlay[] = "consumer-management-overlay";
68
69 // Returns the account ID signed in to |profile|.
GetAccountIdFromProfile(Profile * profile)70 const std::string& GetAccountIdFromProfile(Profile* profile) {
71 return SigninManagerFactory::GetForProfile(profile)->
72 GetAuthenticatedAccountId();
73 }
74
75 class DesktopNotificationDelegate : public NotificationDelegate {
76 public:
77 // |button_click_callback| is called when the button in the notification is
78 // clicked.
79 DesktopNotificationDelegate(const std::string& id,
80 const base::Closure& button_click_callback);
81
82 // NotificationDelegate:
83 virtual std::string id() const OVERRIDE;
84 virtual content::WebContents* GetWebContents() const OVERRIDE;
85 virtual void Display() OVERRIDE;
86 virtual void ButtonClick(int button_index) OVERRIDE;
87 virtual void Error() OVERRIDE;
88 virtual void Close(bool by_user) OVERRIDE;
89 virtual void Click() OVERRIDE;
90
91 private:
92 virtual ~DesktopNotificationDelegate();
93
94 std::string id_;
95 base::Closure button_click_callback_;
96
97 DISALLOW_COPY_AND_ASSIGN(DesktopNotificationDelegate);
98 };
99
DesktopNotificationDelegate(const std::string & id,const base::Closure & button_click_callback)100 DesktopNotificationDelegate::DesktopNotificationDelegate(
101 const std::string& id,
102 const base::Closure& button_click_callback)
103 : id_(id), button_click_callback_(button_click_callback) {
104 }
105
~DesktopNotificationDelegate()106 DesktopNotificationDelegate::~DesktopNotificationDelegate() {
107 }
108
id() const109 std::string DesktopNotificationDelegate::id() const {
110 return id_;
111 }
112
GetWebContents() const113 content::WebContents* DesktopNotificationDelegate::GetWebContents() const {
114 return NULL;
115 }
116
Display()117 void DesktopNotificationDelegate::Display() {
118 }
119
ButtonClick(int button_index)120 void DesktopNotificationDelegate::ButtonClick(int button_index) {
121 button_click_callback_.Run();
122 }
123
Error()124 void DesktopNotificationDelegate::Error() {
125 }
126
Close(bool by_user)127 void DesktopNotificationDelegate::Close(bool by_user) {
128 }
129
Click()130 void DesktopNotificationDelegate::Click() {
131 }
132
133 // The string of Status enum.
134 const char* kStatusString[] = {
135 "StatusUnknown",
136 "StatusEnrolled",
137 "StatusEnrolling",
138 "StatusUnenrolled",
139 "StatusUnenrolling",
140 };
141
142 COMPILE_ASSERT(
143 arraysize(kStatusString) == policy::ConsumerManagementService::STATUS_LAST,
144 "invalid kStatusString array size.");
145
146 } // namespace
147
148 namespace em = enterprise_management;
149
150 namespace policy {
151
ConsumerManagementService(chromeos::CryptohomeClient * client,chromeos::DeviceSettingsService * device_settings_service)152 ConsumerManagementService::ConsumerManagementService(
153 chromeos::CryptohomeClient* client,
154 chromeos::DeviceSettingsService* device_settings_service)
155 : Consumer("consumer_management_service"),
156 client_(client),
157 device_settings_service_(device_settings_service),
158 enrolling_profile_(NULL),
159 weak_ptr_factory_(this) {
160 registrar_.Add(this,
161 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
162 content::NotificationService::AllSources());
163 // A NULL value may be passed in tests.
164 if (device_settings_service_)
165 device_settings_service_->AddObserver(this);
166 }
167
~ConsumerManagementService()168 ConsumerManagementService::~ConsumerManagementService() {
169 if (enrolling_profile_) {
170 ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)->
171 RemoveObserver(this);
172 }
173 if (device_settings_service_)
174 device_settings_service_->RemoveObserver(this);
175 registrar_.Remove(this,
176 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
177 content::NotificationService::AllSources());
178 }
179
180 // static
RegisterPrefs(PrefRegistrySimple * registry)181 void ConsumerManagementService::RegisterPrefs(PrefRegistrySimple* registry) {
182 registry->RegisterIntegerPref(
183 prefs::kConsumerManagementEnrollmentStage, ENROLLMENT_STAGE_NONE);
184 }
185
AddObserver(Observer * observer)186 void ConsumerManagementService::AddObserver(Observer* observer) {
187 observers_.AddObserver(observer);
188 }
189
RemoveObserver(Observer * observer)190 void ConsumerManagementService::RemoveObserver(Observer* observer) {
191 observers_.RemoveObserver(observer);
192 }
193
194 ConsumerManagementService::Status
GetStatus() const195 ConsumerManagementService::GetStatus() const {
196 if (!device_settings_service_)
197 return STATUS_UNKNOWN;
198
199 const enterprise_management::PolicyData* policy_data =
200 device_settings_service_->policy_data();
201 if (!policy_data)
202 return STATUS_UNKNOWN;
203
204 if (policy_data->management_mode() == em::PolicyData::CONSUMER_MANAGED) {
205 // TODO(davidyu): Check if unenrollment is in progress.
206 // http://crbug.com/353050.
207 return STATUS_ENROLLED;
208 }
209
210 EnrollmentStage stage = GetEnrollmentStage();
211 if (stage > ENROLLMENT_STAGE_NONE && stage < ENROLLMENT_STAGE_SUCCESS)
212 return STATUS_ENROLLING;
213
214 return STATUS_UNENROLLED;
215 }
216
GetStatusString() const217 std::string ConsumerManagementService::GetStatusString() const {
218 return kStatusString[GetStatus()];
219 }
220
221 ConsumerManagementService::EnrollmentStage
GetEnrollmentStage() const222 ConsumerManagementService::GetEnrollmentStage() const {
223 const PrefService* prefs = g_browser_process->local_state();
224 int stage = prefs->GetInteger(prefs::kConsumerManagementEnrollmentStage);
225 if (stage < 0 || stage >= ENROLLMENT_STAGE_LAST) {
226 LOG(ERROR) << "Unknown enrollment stage: " << stage;
227 stage = 0;
228 }
229 return static_cast<EnrollmentStage>(stage);
230 }
231
SetEnrollmentStage(EnrollmentStage stage)232 void ConsumerManagementService::SetEnrollmentStage(EnrollmentStage stage) {
233 PrefService* prefs = g_browser_process->local_state();
234 prefs->SetInteger(prefs::kConsumerManagementEnrollmentStage, stage);
235
236 NotifyStatusChanged();
237 }
238
GetOwner(const GetOwnerCallback & callback)239 void ConsumerManagementService::GetOwner(const GetOwnerCallback& callback) {
240 cryptohome::GetBootAttributeRequest request;
241 request.set_name(kAttributeOwnerId);
242 client_->GetBootAttribute(
243 request,
244 base::Bind(&ConsumerManagementService::OnGetBootAttributeDone,
245 weak_ptr_factory_.GetWeakPtr(),
246 callback));
247 }
248
SetOwner(const std::string & user_id,const SetOwnerCallback & callback)249 void ConsumerManagementService::SetOwner(const std::string& user_id,
250 const SetOwnerCallback& callback) {
251 cryptohome::SetBootAttributeRequest request;
252 request.set_name(kAttributeOwnerId);
253 request.set_value(user_id.data(), user_id.size());
254 client_->SetBootAttribute(
255 request,
256 base::Bind(&ConsumerManagementService::OnSetBootAttributeDone,
257 weak_ptr_factory_.GetWeakPtr(),
258 callback));
259 }
260
OwnershipStatusChanged()261 void ConsumerManagementService::OwnershipStatusChanged() {
262 }
263
DeviceSettingsUpdated()264 void ConsumerManagementService::DeviceSettingsUpdated() {
265 NotifyStatusChanged();
266 }
267
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)268 void ConsumerManagementService::Observe(
269 int type,
270 const content::NotificationSource& source,
271 const content::NotificationDetails& details) {
272 if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
273 NOTREACHED() << "Unexpected notification " << type;
274 return;
275 }
276
277 Profile* profile = content::Details<Profile>(details).ptr();
278 if (chromeos::ProfileHelper::IsOwnerProfile(profile))
279 OnOwnerSignin(profile);
280 }
281
OnRefreshTokenAvailable(const std::string & account_id)282 void ConsumerManagementService::OnRefreshTokenAvailable(
283 const std::string& account_id) {
284 CHECK(enrolling_profile_);
285
286 if (account_id == GetAccountIdFromProfile(enrolling_profile_)) {
287 ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)->
288 RemoveObserver(this);
289 OnOwnerRefreshTokenAvailable();
290 }
291 }
292
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)293 void ConsumerManagementService::OnGetTokenSuccess(
294 const OAuth2TokenService::Request* request,
295 const std::string& access_token,
296 const base::Time& expiration_time) {
297 DCHECK_EQ(token_request_, request);
298 base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release());
299
300 OnOwnerAccessTokenAvailable(access_token);
301 }
302
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)303 void ConsumerManagementService::OnGetTokenFailure(
304 const OAuth2TokenService::Request* request,
305 const GoogleServiceAuthError& error) {
306 DCHECK_EQ(token_request_, request);
307 base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release());
308
309 LOG(ERROR) << "Failed to get the access token: " << error.ToString();
310 EndEnrollment(ENROLLMENT_STAGE_GET_TOKEN_FAILED);
311 }
312
OnGetBootAttributeDone(const GetOwnerCallback & callback,chromeos::DBusMethodCallStatus call_status,bool dbus_success,const cryptohome::BaseReply & reply)313 void ConsumerManagementService::OnGetBootAttributeDone(
314 const GetOwnerCallback& callback,
315 chromeos::DBusMethodCallStatus call_status,
316 bool dbus_success,
317 const cryptohome::BaseReply& reply) {
318 if (!dbus_success || reply.error() != 0) {
319 LOG(ERROR) << "Failed to get the owner info from boot lockbox.";
320 callback.Run("");
321 return;
322 }
323
324 callback.Run(
325 reply.GetExtension(cryptohome::GetBootAttributeReply::reply).value());
326 }
327
OnSetBootAttributeDone(const SetOwnerCallback & callback,chromeos::DBusMethodCallStatus call_status,bool dbus_success,const cryptohome::BaseReply & reply)328 void ConsumerManagementService::OnSetBootAttributeDone(
329 const SetOwnerCallback& callback,
330 chromeos::DBusMethodCallStatus call_status,
331 bool dbus_success,
332 const cryptohome::BaseReply& reply) {
333 if (!dbus_success || reply.error() != 0) {
334 LOG(ERROR) << "Failed to set owner info in boot lockbox.";
335 callback.Run(false);
336 return;
337 }
338
339 cryptohome::FlushAndSignBootAttributesRequest request;
340 client_->FlushAndSignBootAttributes(
341 request,
342 base::Bind(&ConsumerManagementService::OnFlushAndSignBootAttributesDone,
343 weak_ptr_factory_.GetWeakPtr(),
344 callback));
345 }
346
OnFlushAndSignBootAttributesDone(const SetOwnerCallback & callback,chromeos::DBusMethodCallStatus call_status,bool dbus_success,const cryptohome::BaseReply & reply)347 void ConsumerManagementService::OnFlushAndSignBootAttributesDone(
348 const SetOwnerCallback& callback,
349 chromeos::DBusMethodCallStatus call_status,
350 bool dbus_success,
351 const cryptohome::BaseReply& reply) {
352 if (!dbus_success || reply.error() != 0) {
353 LOG(ERROR) << "Failed to flush and sign boot lockbox.";
354 callback.Run(false);
355 return;
356 }
357
358 callback.Run(true);
359 }
360
OnOwnerSignin(Profile * profile)361 void ConsumerManagementService::OnOwnerSignin(Profile* profile) {
362 const EnrollmentStage stage = GetEnrollmentStage();
363 switch (stage) {
364 case ENROLLMENT_STAGE_NONE:
365 // Do nothing.
366 return;
367
368 case ENROLLMENT_STAGE_OWNER_STORED:
369 // Continue the enrollment process after the owner signs in.
370 ContinueEnrollmentProcess(profile);
371 return;
372
373 case ENROLLMENT_STAGE_SUCCESS:
374 case ENROLLMENT_STAGE_CANCELED:
375 case ENROLLMENT_STAGE_BOOT_LOCKBOX_FAILED:
376 case ENROLLMENT_STAGE_DM_SERVER_FAILED:
377 case ENROLLMENT_STAGE_GET_TOKEN_FAILED:
378 ShowDesktopNotificationAndResetStage(stage, profile);
379 return;
380
381 case ENROLLMENT_STAGE_REQUESTED:
382 case ENROLLMENT_STAGE_LAST:
383 NOTREACHED() << "Unexpected enrollment stage " << stage;
384 return;
385 }
386 }
387
ContinueEnrollmentProcess(Profile * profile)388 void ConsumerManagementService::ContinueEnrollmentProcess(Profile* profile) {
389 enrolling_profile_ = profile;
390
391 // First, we need to ensure that the refresh token is available.
392 const std::string& account_id = GetAccountIdFromProfile(profile);
393 ProfileOAuth2TokenService* token_service =
394 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
395 if (token_service->RefreshTokenIsAvailable(account_id)) {
396 OnOwnerRefreshTokenAvailable();
397 } else {
398 token_service->AddObserver(this);
399 }
400 }
401
OnOwnerRefreshTokenAvailable()402 void ConsumerManagementService::OnOwnerRefreshTokenAvailable() {
403 CHECK(enrolling_profile_);
404
405 // Now we can request the OAuth access token for device management to send the
406 // device registration request to the device management server.
407 OAuth2TokenService::ScopeSet oauth_scopes;
408 oauth_scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
409 const std::string& account_id = GetAccountIdFromProfile(enrolling_profile_);
410 token_request_ = ProfileOAuth2TokenServiceFactory::GetForProfile(
411 enrolling_profile_)->StartRequest(account_id, oauth_scopes, this);
412 }
413
OnOwnerAccessTokenAvailable(const std::string & access_token)414 void ConsumerManagementService::OnOwnerAccessTokenAvailable(
415 const std::string& access_token) {
416 // Now that we have the access token, we got everything we need to send the
417 // device registration request to the device management server.
418 BrowserPolicyConnectorChromeOS* connector =
419 g_browser_process->platform_part()->browser_policy_connector_chromeos();
420 DeviceCloudPolicyInitializer* initializer =
421 connector->GetDeviceCloudPolicyInitializer();
422 CHECK(initializer);
423
424 policy::DeviceCloudPolicyInitializer::AllowedDeviceModes device_modes;
425 device_modes[policy::DEVICE_MODE_ENTERPRISE] = true;
426
427 initializer->StartEnrollment(
428 enterprise_management::PolicyData::ENTERPRISE_MANAGED,
429 connector->GetDeviceManagementServiceForConsumer(),
430 access_token,
431 false, // is_auto_enrollment
432 device_modes,
433 base::Bind(&ConsumerManagementService::OnEnrollmentCompleted,
434 weak_ptr_factory_.GetWeakPtr()));
435 }
436
OnEnrollmentCompleted(EnrollmentStatus status)437 void ConsumerManagementService::OnEnrollmentCompleted(EnrollmentStatus status) {
438 if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
439 LOG(ERROR) << "Failed to enroll the device."
440 << " status=" << status.status()
441 << " client_status=" << status.client_status()
442 << " http_status=" << status.http_status()
443 << " store_status=" << status.store_status()
444 << " validation_status=" << status.validation_status();
445 EndEnrollment(ENROLLMENT_STAGE_DM_SERVER_FAILED);
446 return;
447 }
448
449 EndEnrollment(ENROLLMENT_STAGE_SUCCESS);
450 }
451
EndEnrollment(EnrollmentStage stage)452 void ConsumerManagementService::EndEnrollment(EnrollmentStage stage) {
453 Profile* profile = enrolling_profile_;
454 enrolling_profile_ = NULL;
455
456 SetEnrollmentStage(stage);
457 if (user_manager::UserManager::Get()->IsCurrentUserOwner())
458 ShowDesktopNotificationAndResetStage(stage, profile);
459 }
460
ShowDesktopNotificationAndResetStage(EnrollmentStage stage,Profile * profile)461 void ConsumerManagementService::ShowDesktopNotificationAndResetStage(
462 EnrollmentStage stage, Profile* profile) {
463 base::string16 title;
464 base::string16 body;
465 base::string16 button_label;
466 base::Closure button_click_callback;
467
468 if (stage == ENROLLMENT_STAGE_SUCCESS) {
469 title = l10n_util::GetStringUTF16(
470 IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_TITLE);
471 body = l10n_util::GetStringUTF16(
472 IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_BODY);
473 button_label = l10n_util::GetStringUTF16(
474 IDS_CONSUMER_MANAGEMENT_NOTIFICATION_MODIFY_SETTINGS_BUTTON);
475 button_click_callback = base::Bind(
476 &ConsumerManagementService::OpenSettingsPage,
477 weak_ptr_factory_.GetWeakPtr(),
478 profile);
479 } else {
480 title = l10n_util::GetStringUTF16(
481 IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_TITLE);
482 body = l10n_util::GetStringUTF16(
483 IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_BODY);
484 button_label = l10n_util::GetStringUTF16(
485 IDS_CONSUMER_MANAGEMENT_NOTIFICATION_TRY_AGAIN_BUTTON);
486 button_click_callback = base::Bind(
487 &ConsumerManagementService::TryEnrollmentAgain,
488 weak_ptr_factory_.GetWeakPtr(),
489 profile);
490 }
491
492 message_center::RichNotificationData optional_field;
493 optional_field.buttons.push_back(message_center::ButtonInfo(button_label));
494 Notification notification(
495 message_center::NOTIFICATION_TYPE_SIMPLE,
496 GURL(kEnrollmentNotificationUrl),
497 title,
498 body,
499 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
500 IDR_CONSUMER_MANAGEMENT_NOTIFICATION_ICON),
501 blink::WebTextDirectionDefault,
502 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
503 kEnrollmentNotificationId),
504 base::string16(), // display_source
505 base::UTF8ToUTF16(kEnrollmentNotificationId),
506 optional_field,
507 new DesktopNotificationDelegate(kEnrollmentNotificationId,
508 button_click_callback));
509 notification.SetSystemPriority();
510 g_browser_process->notification_ui_manager()->Add(notification, profile);
511
512 SetEnrollmentStage(ENROLLMENT_STAGE_NONE);
513 }
514
OpenSettingsPage(Profile * profile) const515 void ConsumerManagementService::OpenSettingsPage(Profile* profile) const {
516 const GURL url(chrome::kChromeUISettingsURL);
517 chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
518 params.disposition = NEW_FOREGROUND_TAB;
519 chrome::Navigate(¶ms);
520 }
521
TryEnrollmentAgain(Profile * profile) const522 void ConsumerManagementService::TryEnrollmentAgain(Profile* profile) const {
523 const GURL base_url(chrome::kChromeUISettingsURL);
524 const GURL url = base_url.Resolve(kConsumerManagementOverlay);
525
526 chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
527 params.disposition = NEW_FOREGROUND_TAB;
528 chrome::Navigate(¶ms);
529 }
530
NotifyStatusChanged()531 void ConsumerManagementService::NotifyStatusChanged() {
532 FOR_EACH_OBSERVER(Observer, observers_, OnConsumerManagementStatusChanged());
533 }
534
535 } // namespace policy
536