Home | History | Annotate | Download | only in policy
      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(&params);
    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(&params);
    529 }
    530 
    531 void ConsumerManagementService::NotifyStatusChanged() {
    532   FOR_EACH_OBSERVER(Observer, observers_, OnConsumerManagementStatusChanged());
    533 }
    534 
    535 }  // namespace policy
    536