Skip to content

Commit

Permalink
Allow explicitly whitelisted apps/extensions in public sessions
Browse files Browse the repository at this point in the history
This CL adds an extension management policy provider that allows
explicitly whitelisted apps/extensions to be installed in public sessions.
Right now, QuickOffice and all hosted apps are whitelisted.

BUG=296868
TEST=New browser and unit tests

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=226494

Review URL: https://codereview.chromium.org/24261010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227641 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
bartfab@chromium.org committed Oct 9, 2013
1 parent 5906629 commit 1a64361
Show file tree
Hide file tree
Showing 20 changed files with 721 additions and 63 deletions.
3 changes: 3 additions & 0 deletions chrome/app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -4403,6 +4403,9 @@ Make sure you do not expose any sensitive information.
<message name="IDS_EXTENSION_CANT_INSTALL_POLICY_BLOCKED" desc="Error message when a user tries to install an extension that is blocked by administrator policy.">
<ph name="EXTENSION_NAME">$1<ex>Google Talk</ex></ph> (extension ID "<ph name="EXTENSION_ID">$2<ex>nckgahadagoaajjgafhacjanaoiihapd</ex></ph>") is blocked by the administrator.
</message>
<message name="IDS_EXTENSION_CANT_INSTALL_IN_DEVICE_LOCAL_ACCOUNT" desc="Error message when a user tries to install or the administrator tries to force-install through policy an extension that is not allowed in a device-local account.">
<ph name="EXTENSION_NAME">$1<ex>Google Talk</ex></ph> (extension ID "<ph name="EXTENSION_ID">$2<ex>nckgahadagoaajjgafhacjanaoiihapd</ex></ph>") is not allowed in this type of session.
</message>
<message name="IDS_EXTENSION_CANT_MODIFY_POLICY_REQUIRED" desc="Error message when a user tries to remove or change an extension that is required by administrator policy.">
The administrator of this machine requires <ph name="EXTENSION_NAME">$1<ex>Google Talk</ex></ph> to be installed. It cannot be removed or modified.
</message>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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/extensions/device_local_account_management_policy_provider.h"

#include <string>

#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/extensions/extension.h"
#include "extensions/common/manifest.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

namespace chromeos {

namespace {

// Apps/extensions explicitly whitelisted for use in device-local accounts.
const char* kDeviceLocalAccountWhitelist[] = {
"bpmcpldpdmajfigpchkicefoigmkfalc", // QuickOffice
};

} // namespace

DeviceLocalAccountManagementPolicyProvider::
DeviceLocalAccountManagementPolicyProvider(
policy::DeviceLocalAccount::Type account_type)
: account_type_(account_type) {
}

DeviceLocalAccountManagementPolicyProvider::
~DeviceLocalAccountManagementPolicyProvider() {
}

std::string DeviceLocalAccountManagementPolicyProvider::
GetDebugPolicyProviderName() const {
#if defined(NDEBUG)
NOTREACHED();
return std::string();
#else
return "whitelist for device-local accounts";
#endif
}

bool DeviceLocalAccountManagementPolicyProvider::UserMayLoad(
const extensions::Extension* extension,
string16* error) const {
if (account_type_ == policy::DeviceLocalAccount::TYPE_KIOSK_APP) {
// For single-app kiosk sessions, allow only platform apps.
if (extension->GetType() == extensions::Manifest::TYPE_PLATFORM_APP)
return true;

} else {
// Allow extension if its type is whitelisted for use in device-local
// accounts.
if (extension->GetType() == extensions::Manifest::TYPE_HOSTED_APP)
return true;

// Allow extension if its specific ID is whitelisted for use in device-local
// accounts.
for (size_t i = 0; i < arraysize(kDeviceLocalAccountWhitelist); ++i) {
if (extension->id() == kDeviceLocalAccountWhitelist[i])
return true;
}
}

// Disallow all other extensions.
if (error) {
*error = l10n_util::GetStringFUTF16(
IDS_EXTENSION_CANT_INSTALL_IN_DEVICE_LOCAL_ACCOUNT,
UTF8ToUTF16(extension->name()),
UTF8ToUTF16(extension->id()));
}
return false;
}

} // namespace chromeos
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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.

#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_MANAGEMENT_POLICY_PROVIDER_H_
#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_MANAGEMENT_POLICY_PROVIDER_H_

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/extensions/management_policy.h"

namespace chromeos {

// A managed policy for device-local accounts that ensures only extensions whose
// type or ID has been whitelisted for use in device-local accounts can be
// installed.
class DeviceLocalAccountManagementPolicyProvider
: public extensions::ManagementPolicy::Provider {
public:
explicit DeviceLocalAccountManagementPolicyProvider(
policy::DeviceLocalAccount::Type account_type);
virtual ~DeviceLocalAccountManagementPolicyProvider();

// extensions::ManagementPolicy::Provider:
virtual std::string GetDebugPolicyProviderName() const OVERRIDE;
virtual bool UserMayLoad(const extensions::Extension* extension,
string16* error) const OVERRIDE;

private:
const policy::DeviceLocalAccount::Type account_type_;

DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountManagementPolicyProvider);
};

} // namespace chromeos

#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_DEVICE_LOCAL_ACCOUNT_MANAGEMENT_POLICY_PROVIDER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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/extensions/device_local_account_management_policy_provider.h"

#include <string>

#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/values.h"
#include "chrome/common/extensions/extension.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace chromeos {

namespace {

const char kWhitelistedId[] = "bpmcpldpdmajfigpchkicefoigmkfalc";

scoped_refptr<const extensions::Extension> CreateExtensionFromValues(
const std::string& id,
base::DictionaryValue* values) {
values->SetString(extensions::manifest_keys::kName, "test");
values->SetString(extensions::manifest_keys::kVersion, "0.1");
std::string error;
return extensions::Extension::Create(base::FilePath(),
extensions::Manifest::INTERNAL,
*values,
extensions::Extension::NO_FLAGS,
id,
&error);
}

scoped_refptr<const extensions::Extension> CreateExtension(
const std::string& id) {
base::DictionaryValue values;
return CreateExtensionFromValues(id, &values);
}

scoped_refptr<const extensions::Extension> CreateHostedApp() {
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kApp, new base::DictionaryValue);
values.Set(extensions::manifest_keys::kWebURLs, new base::ListValue);
return CreateExtensionFromValues(std::string(), &values);
}

scoped_refptr<const extensions::Extension> CreatePlatformApp() {
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kApp, new base::DictionaryValue);
values.Set(extensions::manifest_keys::kPlatformAppBackground,
new base::DictionaryValue);
values.Set(extensions::manifest_keys::kPlatformAppBackgroundPage,
new base::StringValue("background.html"));
return CreateExtensionFromValues(std::string(), &values);
}

} // namespace

TEST(DeviceLocalAccountManagementPolicyProviderTest, PublicSession) {
DeviceLocalAccountManagementPolicyProvider
provider(policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION);

// Verify that if an extension's type has been whitelisted for use in
// device-local accounts, the extension can be installed.
scoped_refptr<const extensions::Extension> extension = CreateHostedApp();
ASSERT_TRUE(extension);
string16 error;
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(string16(), error);
error.clear();

// Verify that if an extension's ID has been explicitly whitelisted for use in
// device-local accounts, the extension can be installed.
extension = CreateExtension(kWhitelistedId);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(string16(), error);
error.clear();

// Verify that if neither the type nor the ID of an extension have been
// whitelisted for use in device-local accounts, the extension cannot be
// installed.
extension = CreateExtension(std::string());
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(string16(), error);
error.clear();
}

TEST(DeviceLocalAccountManagementPolicyProviderTest, KioskAppSession) {
DeviceLocalAccountManagementPolicyProvider
provider(policy::DeviceLocalAccount::TYPE_KIOSK_APP);

// Verify that a platform app can be installed.
scoped_refptr<const extensions::Extension> extension = CreatePlatformApp();
ASSERT_TRUE(extension);
string16 error;
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(string16(), error);
error.clear();

// Verify that an extension whose type has been whitelisted for use in other
// types of device-local accounts cannot be installed in a single-app kiosk
// session.
extension = CreateHostedApp();
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(string16(), error);
error.clear();

// Verify that an extension whose ID has been whitelisted for use in other
// types of device-local accounts cannot be installed in a single-app kiosk
// session.
extension = CreateExtension(kWhitelistedId);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(string16(), error);
error.clear();
}

} // namespace chromeos
12 changes: 10 additions & 2 deletions chrome/browser/chromeos/login/user_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,15 @@ void UserManagerImpl::UserLoggedIn(const std::string& email,
return;
}

policy::DeviceLocalAccount::Type device_local_account_type;
if (email == UserManager::kGuestUserName) {
GuestUserLoggedIn();
} else if (email == UserManager::kRetailModeUserName) {
RetailModeUserLoggedIn();
} else if (policy::IsKioskAppUser(email)) {
} else if (policy::IsDeviceLocalAccountUser(email,
&device_local_account_type) &&
device_local_account_type ==
policy::DeviceLocalAccount::TYPE_KIOSK_APP) {
KioskAppLoggedIn(email);
} else {
EnsureUsersLoaded();
Expand Down Expand Up @@ -1402,7 +1406,11 @@ void UserManagerImpl::PublicAccountUserLoggedIn(User* user) {

void UserManagerImpl::KioskAppLoggedIn(const std::string& username) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(policy::IsKioskAppUser(username));
policy::DeviceLocalAccount::Type device_local_account_type;
DCHECK(policy::IsDeviceLocalAccountUser(username,
&device_local_account_type));
DCHECK_EQ(policy::DeviceLocalAccount::TYPE_KIOSK_APP,
device_local_account_type);

active_user_ = User::CreateKioskAppUser(username);
active_user_->SetStubImage(User::kInvalidImageIndex, false);
Expand Down
39 changes: 30 additions & 9 deletions chrome/browser/chromeos/policy/device_local_account.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

#include <set>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/cros_settings_names.h"
#include "google_apis/gaia/gaia_auth_util.h"
Expand Down Expand Up @@ -56,16 +58,35 @@ std::string GenerateDeviceLocalAccountUserId(const std::string& account_id,
domain_prefix + kDeviceLocalAccountDomainSuffix);
}

bool IsDeviceLocalAccountUser(const std::string& user_id) {
return EndsWith(gaia::ExtractDomainName(user_id),
kDeviceLocalAccountDomainSuffix,
true);
}
bool IsDeviceLocalAccountUser(const std::string& user_id,
DeviceLocalAccount::Type* type) {
// For historical reasons, the guest user ID does not contain an @ symbol and
// therefore, cannot be parsed by gaia::ExtractDomainName().
if (user_id == chromeos::UserManager::kGuestUserName)
return false;
const std::string domain = gaia::ExtractDomainName(user_id);
if (!EndsWith(domain, kDeviceLocalAccountDomainSuffix, true))
return false;

const std::string domain_prefix = domain.substr(
0, domain.size() - arraysize(kDeviceLocalAccountDomainSuffix) + 1);

if (domain_prefix == kPublicAccountDomainPrefix) {
if (type)
*type = DeviceLocalAccount::TYPE_PUBLIC_SESSION;
return true;
}
if (domain_prefix == kKioskAppAccountDomainPrefix) {
if (type)
*type = DeviceLocalAccount::TYPE_KIOSK_APP;
return true;
}

bool IsKioskAppUser(const std::string& user_id) {
return gaia::ExtractDomainName(user_id) ==
std::string(kKioskAppAccountDomainPrefix) +
kDeviceLocalAccountDomainSuffix;
// |user_id| is a device-local account but its type is not recognized.
NOTREACHED();
if (type)
*type = DeviceLocalAccount::TYPE_COUNT;
return true;
}

void SetDeviceLocalAccounts(
Expand Down
7 changes: 4 additions & 3 deletions chrome/browser/chromeos/policy/device_local_account.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ struct DeviceLocalAccount {
std::string GenerateDeviceLocalAccountUserId(const std::string& account_id,
DeviceLocalAccount::Type type);

bool IsDeviceLocalAccountUser(const std::string& user_id);

bool IsKioskAppUser(const std::string& user_id);
// Determines whether |user_id| belongs to a device-local account and if so,
// returns the type of device-local account in |type| unless |type| is NULL.
bool IsDeviceLocalAccountUser(const std::string& user_id,
DeviceLocalAccount::Type* type);

// Stores a list of device-local accounts in |cros_settings|. The accounts are
// stored as a list of dictionaries with each dictionary containing the
Expand Down
Loading

0 comments on commit 1a64361

Please sign in to comment.