// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/policy/consumer_enrollment_handler.h" #include "base/bind.h" #include "base/callback.h" #include "base/location.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h" #include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/notifications/notification.h" #include "chrome/browser/notifications/notification_delegate.h" #include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/common/url_constants.h" #include "components/policy/core/common/cloud/device_management_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager_base.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/google_service_auth_error.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "policy/proto/device_management_backend.pb.h" #include "third_party/WebKit/public/web/WebTextDirection.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/page_transition_types.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/window_open_disposition.h" #include "ui/message_center/notification.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/notifier_settings.h" #include "url/gurl.h" namespace em = enterprise_management; namespace { // Desktop notification constants. const char kEnrollmentNotificationId[] = "consumer_management.enroll"; const char kEnrollmentNotificationUrl[] = "chrome://consumer-management/enroll"; // The path to the consumer management enrollment/unenrollment confirmation // overlay, relative to the settings page URL. const char kConsumerManagementOverlay[] = "consumer-management-overlay"; // Returns the account ID signed in to |profile|. const std::string& GetAccountIdFromProfile(Profile* profile) { return SigninManagerFactory::GetForProfile(profile)-> GetAuthenticatedAccountId(); } class DesktopNotificationDelegate : public NotificationDelegate { public: // |button_click_callback| is called when the button in the notification is // clicked. DesktopNotificationDelegate(const std::string& id, const base::Closure& button_click_callback); // NotificationDelegate: virtual std::string id() const override; virtual void Display() override; virtual void ButtonClick(int button_index) override; virtual void Error() override; virtual void Close(bool by_user) override; virtual void Click() override; private: virtual ~DesktopNotificationDelegate(); std::string id_; base::Closure button_click_callback_; DISALLOW_COPY_AND_ASSIGN(DesktopNotificationDelegate); }; DesktopNotificationDelegate::DesktopNotificationDelegate( const std::string& id, const base::Closure& button_click_callback) : id_(id), button_click_callback_(button_click_callback) { } DesktopNotificationDelegate::~DesktopNotificationDelegate() { } std::string DesktopNotificationDelegate::id() const { return id_; } void DesktopNotificationDelegate::Display() { } void DesktopNotificationDelegate::ButtonClick(int button_index) { button_click_callback_.Run(); } void DesktopNotificationDelegate::Error() { } void DesktopNotificationDelegate::Close(bool by_user) { } void DesktopNotificationDelegate::Click() { } } // namespace namespace policy { ConsumerEnrollmentHandler::ConsumerEnrollmentHandler( ConsumerManagementService* consumer_management_service, DeviceManagementService* device_management_service) : Consumer("consumer_enrollment_handler"), consumer_management_service_(consumer_management_service), device_management_service_(device_management_service), enrolling_profile_(NULL), weak_ptr_factory_(this) { registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, content::NotificationService::AllSources()); } ConsumerEnrollmentHandler::~ConsumerEnrollmentHandler() { if (enrolling_profile_) { ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)-> RemoveObserver(this); } registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, content::NotificationService::AllSources()); } void ConsumerEnrollmentHandler::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) { NOTREACHED() << "Unexpected notification " << type; return; } Profile* profile = content::Details(details).ptr(); if (chromeos::ProfileHelper::IsOwnerProfile(profile)) OnOwnerSignin(profile); } void ConsumerEnrollmentHandler::OnRefreshTokenAvailable( const std::string& account_id) { CHECK(enrolling_profile_); if (account_id == GetAccountIdFromProfile(enrolling_profile_)) { ProfileOAuth2TokenServiceFactory::GetForProfile(enrolling_profile_)-> RemoveObserver(this); OnOwnerRefreshTokenAvailable(); } } void ConsumerEnrollmentHandler::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { DCHECK_EQ(token_request_, request); base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release()); OnOwnerAccessTokenAvailable(access_token); } void ConsumerEnrollmentHandler::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { DCHECK_EQ(token_request_, request); base::MessageLoop::current()->DeleteSoon(FROM_HERE, token_request_.release()); LOG(ERROR) << "Failed to get the access token: " << error.ToString(); EndEnrollment(ConsumerManagementService::ENROLLMENT_STAGE_GET_TOKEN_FAILED); } void ConsumerEnrollmentHandler::OnOwnerSignin(Profile* profile) { const ConsumerManagementService::EnrollmentStage stage = consumer_management_service_->GetEnrollmentStage(); switch (stage) { case ConsumerManagementService::ENROLLMENT_STAGE_NONE: // Do nothing. return; case ConsumerManagementService::ENROLLMENT_STAGE_OWNER_STORED: // Continue the enrollment process after the owner signs in. ContinueEnrollmentProcess(profile); return; case ConsumerManagementService::ENROLLMENT_STAGE_SUCCESS: case ConsumerManagementService::ENROLLMENT_STAGE_CANCELED: case ConsumerManagementService::ENROLLMENT_STAGE_BOOT_LOCKBOX_FAILED: case ConsumerManagementService::ENROLLMENT_STAGE_DM_SERVER_FAILED: case ConsumerManagementService::ENROLLMENT_STAGE_GET_TOKEN_FAILED: ShowDesktopNotificationAndResetStage(stage, profile); return; case ConsumerManagementService::ENROLLMENT_STAGE_REQUESTED: case ConsumerManagementService::ENROLLMENT_STAGE_LAST: NOTREACHED() << "Unexpected enrollment stage " << stage; return; } } void ConsumerEnrollmentHandler::ContinueEnrollmentProcess(Profile* profile) { enrolling_profile_ = profile; // First, we need to ensure that the refresh token is available. const std::string& account_id = GetAccountIdFromProfile(profile); ProfileOAuth2TokenService* token_service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile); if (token_service->RefreshTokenIsAvailable(account_id)) { OnOwnerRefreshTokenAvailable(); } else { token_service->AddObserver(this); } } void ConsumerEnrollmentHandler::OnOwnerRefreshTokenAvailable() { CHECK(enrolling_profile_); // Now we can request the OAuth access token for device management to send the // device registration request to the device management server. OAuth2TokenService::ScopeSet oauth_scopes; oauth_scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth); const std::string& account_id = GetAccountIdFromProfile(enrolling_profile_); token_request_ = ProfileOAuth2TokenServiceFactory::GetForProfile( enrolling_profile_)->StartRequest(account_id, oauth_scopes, this); } void ConsumerEnrollmentHandler::OnOwnerAccessTokenAvailable( const std::string& access_token) { // Now that we have the access token, we got everything we need to send the // device registration request to the device management server. BrowserPolicyConnectorChromeOS* connector = g_browser_process->platform_part()->browser_policy_connector_chromeos(); DeviceCloudPolicyInitializer* initializer = connector->GetDeviceCloudPolicyInitializer(); CHECK(initializer); policy::DeviceCloudPolicyInitializer::AllowedDeviceModes device_modes; device_modes[policy::DEVICE_MODE_ENTERPRISE] = true; initializer->StartEnrollment( em::PolicyData::CONSUMER_MANAGED, device_management_service_, access_token, false, // is_auto_enrollment device_modes, base::Bind(&ConsumerEnrollmentHandler::OnEnrollmentCompleted, weak_ptr_factory_.GetWeakPtr())); } void ConsumerEnrollmentHandler::OnEnrollmentCompleted(EnrollmentStatus status) { if (status.status() != EnrollmentStatus::STATUS_SUCCESS) { LOG(ERROR) << "Failed to enroll the device." << " status=" << status.status() << " client_status=" << status.client_status() << " http_status=" << status.http_status() << " store_status=" << status.store_status() << " validation_status=" << status.validation_status(); EndEnrollment(ConsumerManagementService::ENROLLMENT_STAGE_DM_SERVER_FAILED); return; } EndEnrollment(ConsumerManagementService::ENROLLMENT_STAGE_SUCCESS); } void ConsumerEnrollmentHandler::EndEnrollment( ConsumerManagementService::EnrollmentStage stage) { Profile* profile = enrolling_profile_; enrolling_profile_ = NULL; consumer_management_service_->SetEnrollmentStage(stage); if (user_manager::UserManager::Get()->IsCurrentUserOwner()) ShowDesktopNotificationAndResetStage(stage, profile); } void ConsumerEnrollmentHandler::ShowDesktopNotificationAndResetStage( ConsumerManagementService::EnrollmentStage stage, Profile* profile) { base::string16 title; base::string16 body; base::string16 button_label; base::Closure button_click_callback; if (stage == ConsumerManagementService::ENROLLMENT_STAGE_SUCCESS) { title = l10n_util::GetStringUTF16( IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_TITLE); body = l10n_util::GetStringUTF16( IDS_CONSUMER_MANAGEMENT_ENROLLMENT_NOTIFICATION_BODY); button_label = l10n_util::GetStringUTF16( IDS_CONSUMER_MANAGEMENT_NOTIFICATION_MODIFY_SETTINGS_BUTTON); button_click_callback = base::Bind( &ConsumerEnrollmentHandler::OpenSettingsPage, weak_ptr_factory_.GetWeakPtr(), profile); } else { title = l10n_util::GetStringUTF16( IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_TITLE); body = l10n_util::GetStringUTF16( IDS_CONSUMER_MANAGEMENT_ENROLLMENT_FAILURE_NOTIFICATION_BODY); button_label = l10n_util::GetStringUTF16( IDS_CONSUMER_MANAGEMENT_NOTIFICATION_TRY_AGAIN_BUTTON); button_click_callback = base::Bind( &ConsumerEnrollmentHandler::TryEnrollmentAgain, weak_ptr_factory_.GetWeakPtr(), profile); } message_center::RichNotificationData optional_field; optional_field.buttons.push_back(message_center::ButtonInfo(button_label)); Notification notification( message_center::NOTIFICATION_TYPE_SIMPLE, GURL(kEnrollmentNotificationUrl), title, body, ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_CONSUMER_MANAGEMENT_NOTIFICATION_ICON), blink::WebTextDirectionDefault, message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, kEnrollmentNotificationId), base::string16(), // display_source base::UTF8ToUTF16(kEnrollmentNotificationId), optional_field, new DesktopNotificationDelegate(kEnrollmentNotificationId, button_click_callback)); notification.SetSystemPriority(); g_browser_process->notification_ui_manager()->Add(notification, profile); consumer_management_service_->SetEnrollmentStage( ConsumerManagementService::ENROLLMENT_STAGE_NONE); } void ConsumerEnrollmentHandler::OpenSettingsPage(Profile* profile) const { const GURL url(chrome::kChromeUISettingsURL); chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK); params.disposition = NEW_FOREGROUND_TAB; chrome::Navigate(¶ms); } void ConsumerEnrollmentHandler::TryEnrollmentAgain(Profile* profile) const { const GURL base_url(chrome::kChromeUISettingsURL); const GURL url = base_url.Resolve(kConsumerManagementOverlay); chrome::NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK); params.disposition = NEW_FOREGROUND_TAB; chrome::Navigate(¶ms); } } // namespace policy