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|. 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 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 106 DesktopNotificationDelegate::~DesktopNotificationDelegate() { 107 } 108 109 std::string DesktopNotificationDelegate::id() const { 110 return id_; 111 } 112 113 content::WebContents* DesktopNotificationDelegate::GetWebContents() const { 114 return NULL; 115 } 116 117 void DesktopNotificationDelegate::Display() { 118 } 119 120 void DesktopNotificationDelegate::ButtonClick(int button_index) { 121 button_click_callback_.Run(); 122 } 123 124 void DesktopNotificationDelegate::Error() { 125 } 126 127 void DesktopNotificationDelegate::Close(bool by_user) { 128 } 129 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 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 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 181 void ConsumerManagementService::RegisterPrefs(PrefRegistrySimple* registry) { 182 registry->RegisterIntegerPref( 183 prefs::kConsumerManagementEnrollmentStage, ENROLLMENT_STAGE_NONE); 184 } 185 186 void ConsumerManagementService::AddObserver(Observer* observer) { 187 observers_.AddObserver(observer); 188 } 189 190 void ConsumerManagementService::RemoveObserver(Observer* observer) { 191 observers_.RemoveObserver(observer); 192 } 193 194 ConsumerManagementService::Status 195 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 217 std::string ConsumerManagementService::GetStatusString() const { 218 return kStatusString[GetStatus()]; 219 } 220 221 ConsumerManagementService::EnrollmentStage 222 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 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 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 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 261 void ConsumerManagementService::OwnershipStatusChanged() { 262 } 263 264 void ConsumerManagementService::DeviceSettingsUpdated() { 265 NotifyStatusChanged(); 266 } 267 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 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 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 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 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 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 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 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 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 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 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 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 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 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 515 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 522 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 531 void ConsumerManagementService::NotifyStatusChanged() { 532 FOR_EACH_OBSERVER(Observer, observers_, OnConsumerManagementStatusChanged()); 533 } 534 535 } // namespace policy 536