root/chrome/browser/chromeos/login/managed/supervised_user_login_flow.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_factory_
  2. CanLockScreen
  3. ShouldLaunchBrowser
  4. ShouldSkipPostLoginScreens
  5. HandleLoginFailure
  6. HandlePasswordChangeDetected
  7. HandleOAuthTokenStatusChange
  8. OnSyncSetupDataLoaded
  9. ConfigureSync
  10. HandleLoginSuccess
  11. OnPasswordChangeDataLoaded
  12. OnNewKeyAdded
  13. OnOldKeyRemoved
  14. OnPasswordChangeDataLoadFailed
  15. OnAuthenticationFailure
  16. OnPasswordUpdated
  17. Finish
  18. LaunchExtraSteps

// Copyright 2013 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/login/managed/supervised_user_login_flow.h"

#include "base/base64.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/login_display_host_impl.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/managed/locally_managed_user_constants.h"
#include "chrome/browser/chromeos/login/managed/locally_managed_user_creation_screen.h"
#include "chrome/browser/chromeos/login/managed/supervised_user_authentication.h"
#include "chrome/browser/chromeos/login/supervised_user_manager.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace chromeos {

SupervisedUserLoginFlow::SupervisedUserLoginFlow(
    const std::string& user_id)
    : ExtendedUserFlow(user_id),
      data_loaded_(false),
      weak_factory_(this) {
}

SupervisedUserLoginFlow::~SupervisedUserLoginFlow() {}

bool SupervisedUserLoginFlow::CanLockScreen() {
  return true;
}

bool SupervisedUserLoginFlow::ShouldLaunchBrowser() {
  return data_loaded_;
}

bool SupervisedUserLoginFlow::ShouldSkipPostLoginScreens() {
  return true;
}

bool SupervisedUserLoginFlow::HandleLoginFailure(
    const LoginFailure& failure) {
  return false;
}

bool SupervisedUserLoginFlow::HandlePasswordChangeDetected() {
  return false;
}

void SupervisedUserLoginFlow::HandleOAuthTokenStatusChange(
    User::OAuthTokenStatus status) {
}

void SupervisedUserLoginFlow::OnSyncSetupDataLoaded(
    const std::string& token) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  ConfigureSync(token);
}

void SupervisedUserLoginFlow::ConfigureSync(const std::string& token) {
  data_loaded_ = true;

  // TODO(antrim): add error handling (no token loaded).
  // See also: http://crbug.com/312751
  UserManager::Get()->GetSupervisedUserManager()->ConfigureSyncWithToken(
      profile_, token);
  SupervisedUserAuthentication* auth =
      UserManager::Get()->GetSupervisedUserManager()->GetAuthentication();

  if (auth->HasScheduledPasswordUpdate(user_id())) {
    auth->LoadPasswordUpdateData(
        user_id(),
        base::Bind(&SupervisedUserLoginFlow::OnPasswordChangeDataLoaded,
                   weak_factory_.GetWeakPtr()),
        base::Bind(&SupervisedUserLoginFlow::OnPasswordChangeDataLoadFailed,
                   weak_factory_.GetWeakPtr()));
    return;
  }
  Finish();
}

void SupervisedUserLoginFlow::HandleLoginSuccess(
    const UserContext& login_context) {
  context_.CopyFrom(login_context);
}

void SupervisedUserLoginFlow::OnPasswordChangeDataLoaded(
    const base::DictionaryValue* password_data) {
  // Edge case, when manager has signed in and already updated the password.
  SupervisedUserAuthentication* auth =
      UserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
  if (!auth->NeedPasswordChange(user_id(), password_data)) {
    VLOG(1) << "Password already changed for " << user_id();
    auth->ClearScheduledPasswordUpdate(user_id());
    Finish();
    return;
  }

  // Two cases now - we can currently have either old-style password, or new
  // password.
  std::string base64_signature;
  std::string signature;
  std::string password;
  int revision = 0;
  int schema = 0;
  bool success = password_data->GetStringWithoutPathExpansion(
      kPasswordSignature, &base64_signature);
  success &= password_data->GetIntegerWithoutPathExpansion(kPasswordRevision,
                                                           &revision);
  success &=
      password_data->GetIntegerWithoutPathExpansion(kSchemaVersion, &schema);
  success &= password_data->GetStringWithoutPathExpansion(kEncryptedPassword,
                                                          &password);
  if (!success) {
    LOG(ERROR) << "Incomplete data for password change";

    UMA_HISTOGRAM_ENUMERATION(
        "ManagedUsers.ChromeOS.PasswordChange",
        SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_INCOMPLETE_DATA,
        SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
    Finish();
    return;
  }
  base::Base64Decode(base64_signature, &signature);
  scoped_ptr<base::DictionaryValue> data_copy(password_data->DeepCopy());
  cryptohome::KeyDefinition key(password,
                                kCryptohomeManagedUserKeyLabel,
                                kCryptohomeManagedUserKeyPrivileges);

  authenticator_ = new ExtendedAuthenticator(this);
  SupervisedUserAuthentication::Schema current_schema =
      auth->GetPasswordSchema(user_id());

  key.revision = revision;

  if (SupervisedUserAuthentication::SCHEMA_PLAIN == current_schema) {
    // We need to add new key, and block old one. As we don't actually have
    // signature key, use Migrate privilege instead of AuthorizedUpdate.
    key.privileges = kCryptohomeManagedUserIncompleteKeyPrivileges;

    VLOG(1) << "Adding new schema key";
    DCHECK_EQ(context_.key_label, std::string());
    authenticator_->AddKey(context_,
                           key,
                           false /* no key exists */,
                           base::Bind(&SupervisedUserLoginFlow::OnNewKeyAdded,
                                      weak_factory_.GetWeakPtr(),
                                      Passed(&data_copy)));
  } else if (SupervisedUserAuthentication::SCHEMA_PLAIN == current_schema) {
    VLOG(1) << "Updating the key";

    if (auth->HasIncompleteKey(user_id())) {
      // We need to use Migrate instead of Authorized Update privilege.
      key.privileges = kCryptohomeManagedUserIncompleteKeyPrivileges;
    }

    // Just update the key.
    DCHECK_EQ(context_.key_label, kCryptohomeManagedUserKeyLabel);
    authenticator_->UpdateKeyAuthorized(
        context_,
        key,
        signature,
        base::Bind(&SupervisedUserLoginFlow::OnPasswordUpdated,
                   weak_factory_.GetWeakPtr(),
                   Passed(&data_copy)));
  } else {
    NOTREACHED() << "Unsupported password schema";
  }
}

void SupervisedUserLoginFlow::OnNewKeyAdded(
    scoped_ptr<base::DictionaryValue> password_data) {
  VLOG(1) << "New key added";
  SupervisedUserAuthentication* auth =
      UserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
  auth->StorePasswordData(user_id(), *password_data.get());
  auth->MarkKeyIncomplete(user_id(), true /* incomplete */);
  authenticator_->RemoveKey(
      context_,
      kLegacyCryptohomeManagedUserKeyLabel,
      base::Bind(&SupervisedUserLoginFlow::OnOldKeyRemoved,
                 weak_factory_.GetWeakPtr()));
}

void SupervisedUserLoginFlow::OnOldKeyRemoved() {
  UMA_HISTOGRAM_ENUMERATION(
      "ManagedUsers.ChromeOS.PasswordChange",
      SupervisedUserAuthentication::PASSWORD_CHANGED_IN_USER_SESSION,
      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
  Finish();
}

void SupervisedUserLoginFlow::OnPasswordChangeDataLoadFailed() {
  LOG(ERROR) << "Could not load data for password change";

  UMA_HISTOGRAM_ENUMERATION(
      "ManagedUsers.ChromeOS.PasswordChange",
      SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_LOADING_DATA,
      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
  Finish();
}

void SupervisedUserLoginFlow::OnAuthenticationFailure(
    ExtendedAuthenticator::AuthState state) {
  LOG(ERROR) << "Authentication error during password change";

  UMA_HISTOGRAM_ENUMERATION(
      "ManagedUsers.ChromeOS.PasswordChange",
      SupervisedUserAuthentication::
          PASSWORD_CHANGE_FAILED_AUTHENTICATION_FAILURE,
      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
  Finish();
}

void SupervisedUserLoginFlow::OnPasswordUpdated(
    scoped_ptr<base::DictionaryValue> password_data) {
  VLOG(1) << "Updated password for supervised user";

  SupervisedUserAuthentication* auth =
      UserManager::Get()->GetSupervisedUserManager()->GetAuthentication();

  // Incomplete state is not there in password_data, carry it from old state.
  bool was_incomplete = auth->HasIncompleteKey(user_id());
  auth->StorePasswordData(user_id(), *password_data.get());
  if (was_incomplete)
    auth->MarkKeyIncomplete(user_id(), true /* incomplete */);

  UMA_HISTOGRAM_ENUMERATION(
      "ManagedUsers.ChromeOS.PasswordChange",
      SupervisedUserAuthentication::PASSWORD_CHANGED_IN_USER_SESSION,
      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
  Finish();
}

void SupervisedUserLoginFlow::Finish() {
  LoginUtils::Get()->DoBrowserLaunch(profile_, host());
  profile_ = NULL;
  UnregisterFlowSoon();
}

void SupervisedUserLoginFlow::LaunchExtraSteps(
    Profile* profile) {
  profile_ = profile;
  UserManager::Get()->GetSupervisedUserManager()->LoadSupervisedUserToken(
      profile,
      base::Bind(
           &SupervisedUserLoginFlow::OnSyncSetupDataLoaded,
           weak_factory_.GetWeakPtr()));
}

}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */