Skip to content

Commit

Permalink
Curtain mode interface, hooks and unit tests.
Browse files Browse the repository at this point in the history
BUG=
TEST=

Review URL: http://codereview.chromium.org/6735010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79816 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
jamiewalch@chromium.org committed Mar 30, 2011
1 parent 5b47922 commit 37961b1
Show file tree
Hide file tree
Showing 16 changed files with 326 additions and 19 deletions.
28 changes: 26 additions & 2 deletions remoting/host/chromoting_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "remoting/base/encoder_vp8.h"
#include "remoting/host/capturer.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/curtain.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/event_executor.h"
#include "remoting/host/host_config.h"
Expand All @@ -37,8 +38,9 @@ ChromotingHost* ChromotingHost::Create(ChromotingHostContext* context,
Capturer* capturer = Capturer::Create();
InputStub* input_stub = CreateEventExecutor(context->ui_message_loop(),
capturer);
Curtain* curtain = Curtain::Create();
return Create(context, config,
new DesktopEnvironment(capturer, input_stub));
new DesktopEnvironment(capturer, input_stub, curtain));
}

// static
Expand All @@ -55,7 +57,8 @@ ChromotingHost::ChromotingHost(ChromotingHostContext* context,
config_(config),
desktop_environment_(environment),
state_(kInitial),
protocol_config_(protocol::CandidateSessionConfig::CreateDefault()) {
protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
is_curtained_(false) {
DCHECK(desktop_environment_.get());
}

Expand Down Expand Up @@ -200,6 +203,8 @@ void ChromotingHost::OnClientDisconnected(ConnectionToClient* connection) {
break;
}
}
if (!HasAuthenticatedClients())
EnableCurtainMode(false);
}

////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -369,6 +374,24 @@ std::string ChromotingHost::GenerateHostAuthToken(
return encoded_client_token;
}

bool ChromotingHost::HasAuthenticatedClients() const {
std::vector<scoped_refptr<ClientSession> >::const_iterator it;
for (it = clients_.begin(); it != clients_.end(); ++it) {
if (it->get()->connection()->client_authenticated())
return true;
}
return false;
}

void ChromotingHost::EnableCurtainMode(bool enable) {
// TODO(jamiewalch): This will need to be more sophisticated when we think
// about proper crash recovery and daemon mode.
if (enable == is_curtained_)
return;
desktop_environment_->curtain()->EnableCurtainMode(enable);
is_curtained_ = enable;
}

void ChromotingHost::LocalLoginSucceeded(
scoped_refptr<ConnectionToClient> connection) {
if (MessageLoop::current() != context_->main_message_loop()) {
Expand Down Expand Up @@ -417,6 +440,7 @@ void ChromotingHost::LocalLoginSucceeded(
// Immediately add the connection and start the session.
recorder_->AddConnection(connection);
recorder_->Start();
EnableCurtainMode(true);
}

void ChromotingHost::LocalLoginFailed(
Expand Down
7 changes: 7 additions & 0 deletions remoting/host/chromoting_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,

std::string GenerateHostAuthToken(const std::string& encoded_client_token);

bool HasAuthenticatedClients() const;

void EnableCurtainMode(bool enable);

// The context that the chromoting host runs on.
ChromotingHostContext* context_;

Expand Down Expand Up @@ -186,6 +190,9 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
// Configuration of the protocol.
scoped_ptr<protocol::CandidateSessionConfig> protocol_config_;

// Whether or not the host is currently curtained.
bool is_curtained_;

DISALLOW_COPY_AND_ASSIGN(ChromotingHost);
};

Expand Down
110 changes: 97 additions & 13 deletions remoting/host/chromoting_host_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/scoped_ptr.h"
#include "base/task.h"
#include "remoting/host/capturer_fake.h"
#include "remoting/host/chromoting_host.h"
Expand All @@ -27,12 +28,14 @@ using ::remoting::protocol::SessionConfig;

using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::CreateFunctor;
using testing::DeleteArg;
using testing::DoAll;
using testing::InSequence;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::Sequence;

namespace remoting {

Expand Down Expand Up @@ -80,8 +83,9 @@ class ChromotingHostTest : public testing::Test {
host_stub2_ = new MockHostStub();
input_stub_ = new MockInputStub();
input_stub2_ = new MockInputStub();
curtain_ = new MockCurtain();
DesktopEnvironment* desktop =
new DesktopEnvironment(capturer, input_stub_);
new DesktopEnvironment(capturer, input_stub_, curtain_);
host_ = ChromotingHost::Create(&context_, config_, desktop);
connection_ = new MockConnectionToClient(
&message_loop_, &handler_, host_stub_, input_stub_);
Expand Down Expand Up @@ -130,14 +134,13 @@ class ChromotingHostTest : public testing::Test {
.Times(AnyNumber());
EXPECT_CALL(*session2_.get(), config())
.Times(AnyNumber());

}

virtual void TearDown() {
}

// Helper method to pretend a client is connected to ChromotingHost.
void SimulateClientConnection(int connection_index) {
void SimulateClientConnection(int connection_index, bool authenticate) {
scoped_refptr<MockConnectionToClient> connection =
(connection_index == 0) ? connection_ : connection2_;
scoped_refptr<ClientSession> client = new ClientSession(host_.get(),
Expand All @@ -154,11 +157,19 @@ class ChromotingHostTest : public testing::Test {
NewRunnableMethod(host_.get(),
&ChromotingHost::OnClientConnected,
connection));
context_.network_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(host_.get(),
&ChromotingHost::LocalLoginSucceeded,
connection));
if (authenticate) {
context_.network_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(host_.get(),
&ChromotingHost::LocalLoginSucceeded,
connection));
} else {
context_.network_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(host_.get(),
&ChromotingHost::LocalLoginFailed,
connection));
}
}

// Helper method to remove a client connection from ChromotingHost.
Expand All @@ -183,6 +194,7 @@ class ChromotingHostTest : public testing::Test {
MockClientStub client_stub_;
MockHostStub* host_stub_;
MockInputStub* input_stub_;
MockCurtain* curtain_;
scoped_refptr<MockConnectionToClient> connection2_;
scoped_refptr<MockSession> session2_;
scoped_ptr<SessionConfig> session_config2_;
Expand Down Expand Up @@ -210,6 +222,8 @@ TEST_F(ChromotingHostTest, Connect) {
// When the video packet is received we first shutdown ChromotingHost
// then execute the done task.
InSequence s;
EXPECT_CALL(*curtain_, EnableCurtainMode(true))
.Times(1);
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(host_.get(), &ChromotingHost::Shutdown),
Expand All @@ -220,7 +234,7 @@ TEST_F(ChromotingHostTest, Connect) {
EXPECT_CALL(*connection_.get(), Disconnect())
.RetiresOnSaturation();

SimulateClientConnection(0);
SimulateClientConnection(0, true);
message_loop_.Run();
}

Expand All @@ -235,6 +249,8 @@ TEST_F(ChromotingHostTest, Reconnect) {
// connection.
{
InSequence s;
EXPECT_CALL(*curtain_, EnableCurtainMode(true))
.Times(1);
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(this,
Expand All @@ -243,19 +259,23 @@ TEST_F(ChromotingHostTest, Reconnect) {
.RetiresOnSaturation();
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.Times(AnyNumber());
EXPECT_CALL(*curtain_, EnableCurtainMode(false))
.Times(1);
}

// If Disconnect() is called we can break the main message loop.
EXPECT_CALL(*connection_.get(), Disconnect())
.WillOnce(QuitMainMessageLoop(&message_loop_))
.RetiresOnSaturation();

SimulateClientConnection(0);
SimulateClientConnection(0, true);
message_loop_.Run();

// Connect the client again.
{
InSequence s;
EXPECT_CALL(*curtain_, EnableCurtainMode(true))
.Times(1);
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(host_.get(), &ChromotingHost::Shutdown),
Expand All @@ -267,7 +287,7 @@ TEST_F(ChromotingHostTest, Reconnect) {
EXPECT_CALL(*connection_.get(), Disconnect())
.RetiresOnSaturation();

SimulateClientConnection(0);
SimulateClientConnection(0, true);
message_loop_.Run();
}

Expand All @@ -286,13 +306,20 @@ TEST_F(ChromotingHostTest, ConnectTwice) {
// connection.
{
InSequence s;
EXPECT_CALL(*curtain_, EnableCurtainMode(true))
.Times(1)
.WillOnce(QuitMainMessageLoop(&message_loop_));
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(
CreateFunctor(
this, &ChromotingHostTest::SimulateClientConnection, 1)),
this,
&ChromotingHostTest::SimulateClientConnection, 1, true)),
RunDoneTask()))
.RetiresOnSaturation();
// Check that the second connection does not affect curtain mode.
EXPECT_CALL(*curtain_, EnableCurtainMode(_))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.Times(AnyNumber());
EXPECT_CALL(video_stub2_, ProcessVideoPacket(_, _))
Expand All @@ -309,7 +336,64 @@ TEST_F(ChromotingHostTest, ConnectTwice) {
EXPECT_CALL(*connection2_.get(), Disconnect())
.RetiresOnSaturation();

SimulateClientConnection(0);
SimulateClientConnection(0, true);
message_loop_.Run();
}

TEST_F(ChromotingHostTest, CurtainModeFail) {
host_->Start(NewRunnableFunction(&PostQuitTask, &message_loop_));

EXPECT_CALL(client_stub_, BeginSessionResponse(_, _))
.Times(1)
.WillRepeatedly(RunDoneTask());

// Ensure that curtain mode is not activated if a connection does not
// authenticate.
EXPECT_CALL(*curtain_, EnableCurtainMode(_))
.Times(0);
EXPECT_CALL(*connection_.get(), Disconnect())
.WillOnce(QuitMainMessageLoop(&message_loop_));
SimulateClientConnection(0, false);
RemoveClientConnection();
message_loop_.Run();
}

TEST_F(ChromotingHostTest, CurtainModeFailSecond) {
host_->Start(NewRunnableFunction(&PostQuitTask, &message_loop_));

EXPECT_CALL(client_stub_, BeginSessionResponse(_, _))
.Times(1)
.WillRepeatedly(RunDoneTask());

EXPECT_CALL(client_stub2_, BeginSessionResponse(_, _))
.Times(1)
.WillRepeatedly(RunDoneTask());

// When a video packet is received we connect the second mock
// connection.
{
InSequence s;
EXPECT_CALL(*curtain_, EnableCurtainMode(true))
.Times(1)
.WillOnce(QuitMainMessageLoop(&message_loop_));
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.WillOnce(DoAll(
InvokeWithoutArgs(
CreateFunctor(
this,
&ChromotingHostTest::SimulateClientConnection, 1, false)),
RunDoneTask()))
.RetiresOnSaturation();
// Check that the second connection does not affect curtain mode.
EXPECT_CALL(*curtain_, EnableCurtainMode(_))
.Times(0);
EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
.Times(AnyNumber());
EXPECT_CALL(video_stub2_, ProcessVideoPacket(_, _))
.Times(0);
}

SimulateClientConnection(0, true);
message_loop_.Run();
}

Expand Down
41 changes: 41 additions & 0 deletions remoting/host/curtain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2011 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 REMOTING_HOST_CURTAIN_H_
#define REMOTING_HOST_CURTAIN_H_

namespace remoting {

// An interface for enabling or disabling "curtain mode" on a Chromoting host.
// Curtain mode is designed to ensure privacy for remote users. It guarantees
// the following:
// 1. The local display of the host does not display the remote user's
// actions during the connection.
// 2. The local keyboard and mouse (and other input devices) do not interfere
// with the remote user's session.
// 3. When the remote session terminates, the host computer is left in a
// secure state (for example, locked).
class Curtain {
public:
virtual ~Curtain() { }

// Enable or disable curtain mode. This method is called with |enable| = true
// when a connection authenticates and with |enable| = false when a connection
// terminates (even if due to abnormal termination of the host process).
virtual void EnableCurtainMode(bool enable) = 0;

// Create the platform-specific curtain mode implementation.
// TODO(jamiewalch): Until the daemon architecture is implemented, curtain
// mode implementations that cannot easily be reset by the user should check
// to see if curtain mode is already enabled here and disable it if so. This
// is to provide an easy way of recovering if the host process crashes while
// a connection is active. Once the daemon architecture is in place, it will
// be responsible for calling EnableCurtainMode(false) as part of its crash
// recovery logic.
static Curtain* Create();
};

} // namespace remoting

#endif // REMOTING_HOST_CURTAIN_H_
18 changes: 18 additions & 0 deletions remoting/host/curtain_linux.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2011 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 "remoting/host/curtain_linux.h"
#include "base/logging.h"

namespace remoting {

void CurtainLinux::EnableCurtainMode(bool enable) {
NOTIMPLEMENTED();
}

Curtain* Curtain::Create() {
return new CurtainLinux();
}

} // namespace remoting
Loading

0 comments on commit 37961b1

Please sign in to comment.