diff --git a/internal/interop/BUILD b/internal/interop/BUILD index 9abb983b..a4f9e5f3 100644 --- a/internal/interop/BUILD +++ b/internal/interop/BUILD @@ -24,7 +24,6 @@ cc_library( deps = [ ":authentication_transport_interface", "//internal/platform:connection_info", - "//internal/platform:types", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:variant", ], diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index a8194618..b36d802e 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -93,6 +93,7 @@ cc_library( "//internal/network:__subpackages__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", + "//presence:__subpackages__", "//presence/implementation:__subpackages__", ], deps = [ diff --git a/presence/BUILD b/presence/BUILD index 8969a12c..8fc54766 100644 --- a/presence/BUILD +++ b/presence/BUILD @@ -19,6 +19,7 @@ cc_library( name = "presence", srcs = [ "presence_client_impl.cc", + "presence_device_provider.cc", "presence_service_impl.cc", ], hdrs = [ @@ -30,13 +31,19 @@ cc_library( ], deps = [ ":types", + "//internal/interop:authentication_transport_interface", "//internal/interop:device", + "//internal/platform:base", "//internal/platform:types", + "//internal/platform/implementation:types", "//internal/proto:metadata_cc_proto", "//presence/implementation:internal", # build_cleaner: keep "//presence/implementation/mediums", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", ], ) @@ -175,12 +182,18 @@ cc_test( deps = [ ":presence", ":types", + "//internal/interop:authentication_transport_interface", + "//internal/interop:device", "//internal/platform:test_util", "//internal/platform:types", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:types", "//internal/proto:metadata_cc_proto", + "//presence/implementation:internal_test", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ] + select({ "@platforms//os:windows": [ diff --git a/presence/implementation/mock_service_controller.h b/presence/implementation/mock_service_controller.h index 84960f12..7fc3b7e9 100644 --- a/presence/implementation/mock_service_controller.h +++ b/presence/implementation/mock_service_controller.h @@ -16,8 +16,11 @@ #define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_ #include +#include #include "gmock/gmock.h" +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/credential_callbacks.h" #include "presence/implementation/service_controller.h" namespace nearby { @@ -33,11 +36,35 @@ class MockServiceController : public ServiceController { MOCK_METHOD(absl::StatusOr, StartScan, (ScanRequest scan_request, ScanCallback callback), (override)); + MOCK_METHOD(void, StopScan, (ScanSessionId session_id), (override)); MOCK_METHOD(absl::StatusOr, StartBroadcast, (BroadcastRequest broadcast_request, BroadcastCallback callback), (override)); - - private: + MOCK_METHOD(void, StopBroadcast, (BroadcastSessionId session_id), (override)); + MOCK_METHOD( + void, UpdateLocalDeviceMetadata, + (const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb), + (override)); + MOCK_METHOD(::nearby::internal::Metadata, GetLocalDeviceMetadata, (), + (override)); + MOCK_METHOD(void, GetLocalPublicCredentials, + (const CredentialSelector& credential_selector, + GetPublicCredentialsResultCallback callback), + (override)); + MOCK_METHOD(void, UpdateRemotePublicCredentials, + (absl::string_view manager_app_id, absl::string_view account_name, + const std::vector& + remote_public_creds, + UpdateRemotePublicCredentialsCallback credentials_updated_cb), + (override)); + MOCK_METHOD(void, GetLocalCredentials, + (const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback), + (override)); }; } // namespace presence diff --git a/presence/presence_device_provider.cc b/presence/presence_device_provider.cc new file mode 100644 index 00000000..3b4ec254 --- /dev/null +++ b/presence/presence_device_provider.cc @@ -0,0 +1,128 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "presence/presence_device_provider.h" + +#include +#include +#include + +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "internal/interop/authentication_transport.h" +#include "internal/interop/device.h" +#include "internal/interop/device_provider.h" +#include "internal/platform/exception.h" +#include "internal/platform/future.h" +#include "internal/platform/implementation/system_clock.h" +#include "internal/platform/logging.h" +#include "presence/implementation/service_controller.h" + +namespace nearby { +namespace presence { + +namespace { + +// TODO(b/317215548): Use Status code rather than custom defined +// authentication status. +std::string AuthenticationErrorToString(AuthenticationStatus status) { + switch (status) { + case AuthenticationStatus::kUnknown: + return "AuthenticationStatus::kUnknown"; + case AuthenticationStatus::kSuccess: + return "AuthenticationStatus::kSuccess"; + case AuthenticationStatus::kFailure: + return "AuthenticationStatus::kFailure"; + } + return "AuthenticationStatus::kUnknown"; +} + +std::optional GetValidCredential( + std::vector local_credentials) { + absl::Time now = SystemClock::ElapsedRealtime(); + for (auto& credential : local_credentials) { + if (absl::FromUnixMillis(credential.start_time_millis()) <= now && + absl::FromUnixMillis(credential.end_time_millis()) > now) { + return credential; + } + } + return std::nullopt; +} + +} // namespace + +PresenceDeviceProvider::PresenceDeviceProvider( + ServiceController* service_controller) + : service_controller_(*service_controller), + device_{service_controller_.GetLocalDeviceMetadata()} {} + +AuthenticationStatus PresenceDeviceProvider::AuthenticateAsInitiator( + const NearbyDevice& remote_device, absl::string_view shared_secret, + const AuthenticationTransport& authentication_transport) const { + Future response; + + // 1. Fetch the local credentials and select the correct one to use + // for authentication by calling `GetValidCredential()`, which + // iterates over the returned list and returns the local credential + // that corresponds with the current time. + // + // TODO(b/304843571): Add support for additional IdentityTypes. Currently, + // only `IDENTITY_TYPE_PRIVATE` is supported in order to unblock Nearby + // Presence MVP. + service_controller_.GetLocalCredentials( + /*credential_selector=*/{.manager_app_id = manager_app_id_, + .account_name = + device_.GetMetadata().account_name(), + .identity_type = ::nearby::internal:: + IdentityType::IDENTITY_TYPE_PRIVATE}, + /*callback=*/{ + .credentials_fetched_cb = [&response](auto status_or_credentials) { + if (!status_or_credentials.ok()) { + NEARBY_LOGS(INFO) + << __func__ << ": failure to fetch local credentials"; + response.Set(AuthenticationStatus::kFailure); + return; + } + + auto credential = GetValidCredential(status_or_credentials.value()); + if (!credential.has_value()) { + NEARBY_LOGS(INFO) + << __func__ << ": failure to find a valid local credential"; + response.Set(AuthenticationStatus::kFailure); + return; + } + + // TODO(b/282027237): Continue with the following steps, which will + // be done in follow up CL's. + // 2. Construct the frame and write to the + // |authentication_transport|. + // 3. Read the message from the remote device via + // |authentication_transport|. + // 4. Return the status of the authentication to the callers. + // For now, return success on the Future. + response.Set(AuthenticationStatus::kSuccess); + }}); + + NEARBY_LOGS(INFO) << __func__ << ": Waiting for future to complete"; + ExceptionOr result = response.Get(); + CHECK(result.ok()); + + NEARBY_LOGS(INFO) << "Future:[" << __func__ << "] completed with status:" + << AuthenticationErrorToString(result.result()); + return result.result(); +} + +} // namespace presence +} // namespace nearby diff --git a/presence/presence_device_provider.h b/presence/presence_device_provider.h index 6a627bdc..e343a548 100644 --- a/presence/presence_device_provider.h +++ b/presence/presence_device_provider.h @@ -15,6 +15,10 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ #define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ +#include + +#include "absl/strings/string_view.h" +#include "internal/interop/device.h" #include "internal/interop/device_provider.h" #include "internal/proto/metadata.pb.h" #include "presence/presence_device.h" @@ -22,18 +26,24 @@ namespace nearby { namespace presence { +class ServiceController; + class PresenceDeviceProvider : public NearbyDeviceProvider { public: - explicit PresenceDeviceProvider(::nearby::internal::Metadata metadata) - : device_{metadata} {} + explicit PresenceDeviceProvider(ServiceController* service_controller); const NearbyDevice* GetLocalDevice() override { return &device_; } + + // To authenticate as an initiator (when the device is in the scanning role), + // the PresenceDeviceProvider will block and: + // 1. Fetch the local credentials and select the correct one to use for + // authentication. + // 2. Construct the frame and write to the |authentication_transport|. + // 3. Read the message from the remote device via |authentication_transport|. + // 4. Return the status of the authentication to the callers. AuthenticationStatus AuthenticateAsInitiator( const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const override { - // TODO(b/282027237): Implement. - return AuthenticationStatus::kUnknown; - } + const AuthenticationTransport& authentication_transport) const override; AuthenticationStatus AuthenticateAsResponder( absl::string_view shared_secret, @@ -55,6 +65,7 @@ class PresenceDeviceProvider : public NearbyDeviceProvider { } private: + ServiceController& service_controller_; PresenceDevice device_; std::string manager_app_id_; }; diff --git a/presence/presence_device_provider_test.cc b/presence/presence_device_provider_test.cc index b7a6eb79..cabab771 100644 --- a/presence/presence_device_provider_test.cc +++ b/presence/presence_device_provider_test.cc @@ -14,14 +14,25 @@ #include "presence/presence_device_provider.h" +#include +#include #include +#include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "internal/interop/authentication_transport.h" +#include "internal/interop/device_provider.h" +#include "internal/platform/implementation/credential_callbacks.h" +#include "internal/platform/implementation/system_clock.h" #include "internal/proto/metadata.pb.h" #include "internal/proto/metadata.proto.h" +#include "presence/implementation/mock_service_controller.h" #include "presence/presence_device.h" namespace nearby { @@ -43,37 +54,128 @@ Metadata CreateTestMetadata() { return metadata; } -TEST(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) { +nearby::internal::LocalCredential CreateValidLocalCredential() { + nearby::internal::LocalCredential credential; + absl::Time now = SystemClock::ElapsedRealtime(); + credential.set_start_time_millis(absl::ToUnixMillis(now)); + credential.set_end_time_millis(absl::ToUnixMillis(now + absl::Minutes(10))); + return credential; +} + +nearby::internal::LocalCredential CreateInvalidLocalCredential() { + nearby::internal::LocalCredential credential; + absl::Time now = SystemClock::ElapsedRealtime(); + credential.set_start_time_millis(absl::ToUnixMillis(now - absl::Minutes(30))); + credential.set_end_time_millis(absl::ToUnixMillis(now - absl::Minutes(10))); + return credential; +} + +class MockAuthenticationTransport : public AuthenticationTransport { + MOCK_METHOD(void, WriteMessage, (absl::string_view), (const override)); + MOCK_METHOD(std::string, ReadMessage, (), (const override)); +}; + +class PresenceDeviceProviderTest : public ::testing::Test { + public: + PresenceDeviceProviderTest() { + ON_CALL(mock_service_controller_, GetLocalDeviceMetadata) + .WillByDefault(testing::Return(CreateTestMetadata())); + provider_ = + std::make_unique(&mock_service_controller_); + } + + protected: + MockServiceController mock_service_controller_; + std::unique_ptr provider_; +}; + +TEST_F(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) { EXPECT_FALSE(std::is_trivially_constructible::value); } -TEST(PresenceDeviceProviderTest, DeviceProviderWorks) { - PresenceDeviceProvider provider(CreateTestMetadata()); - auto device = provider.GetLocalDevice(); +TEST_F(PresenceDeviceProviderTest, DeviceProviderWorks) { + auto device = provider_->GetLocalDevice(); ASSERT_EQ(device->GetType(), NearbyDevice::Type::kPresenceDevice); auto presence_device = static_cast(device); EXPECT_EQ(presence_device->GetMetadata().SerializeAsString(), CreateTestMetadata().SerializeAsString()); } -TEST(PresenceDeviceProviderTest, DeviceProviderCanUpdateDevice) { - PresenceDeviceProvider provider(CreateTestMetadata()); - auto device = provider.GetLocalDevice(); +TEST_F(PresenceDeviceProviderTest, DeviceProviderCanUpdateDevice) { + auto device = provider_->GetLocalDevice(); ASSERT_EQ(device->GetType(), NearbyDevice::Type::kPresenceDevice); auto presence_device = static_cast(device); EXPECT_EQ(presence_device->GetMetadata().SerializeAsString(), CreateTestMetadata().SerializeAsString()); Metadata new_metadata = CreateTestMetadata(); new_metadata.set_device_name("NP interop device"); - provider.UpdateMetadata(new_metadata); + provider_->UpdateMetadata(new_metadata); EXPECT_EQ(presence_device->GetMetadata().SerializeAsString(), new_metadata.SerializeAsString()); } -TEST(PresenceDeviceProviderTest, SetManagerAppId) { - PresenceDeviceProvider provider(CreateTestMetadata()); - provider.SetManagerAppId(kManagerAppId); - EXPECT_EQ(provider.GetManagerAppId(), kManagerAppId); +TEST_F(PresenceDeviceProviderTest, SetGetManagerAppId) { + provider_->SetManagerAppId(kManagerAppId); + EXPECT_EQ(provider_->GetManagerAppId(), kManagerAppId); +} + +TEST_F(PresenceDeviceProviderTest, + AuthenticateAsInitiatorFails_FailToFetchCredentials) { + EXPECT_CALL(mock_service_controller_, GetLocalCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback) { + std::move(callback.credentials_fetched_cb)( + absl::Status(absl::StatusCode::kCancelled, /*msg=*/"")); + }); + + PresenceDevice remote_device{CreateTestMetadata()}; + MockAuthenticationTransport authentication_transport; + auto status = provider_->AuthenticateAsInitiator( + /*remote_device=*/remote_device, /*shared_secret=*/"", + /*authentication_transport=*/authentication_transport); + EXPECT_EQ(AuthenticationStatus::kFailure, status); +} + +TEST_F(PresenceDeviceProviderTest, + AuthenticateAsInitiatorFails_NoValidCredentials) { + EXPECT_CALL(mock_service_controller_, GetLocalCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback) { + std::vector credentials; + credentials.push_back(CreateInvalidLocalCredential()); + credentials.push_back(CreateInvalidLocalCredential()); + credentials.push_back(CreateInvalidLocalCredential()); + std::move(callback.credentials_fetched_cb)(credentials); + }); + + PresenceDevice remote_device{CreateTestMetadata()}; + MockAuthenticationTransport authentication_transport; + auto status = provider_->AuthenticateAsInitiator( + /*remote_device=*/remote_device, /*shared_secret=*/"", + /*authentication_transport=*/authentication_transport); + EXPECT_EQ(AuthenticationStatus::kFailure, status); +} + +TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiatorSuccess) { + EXPECT_CALL(mock_service_controller_, GetLocalCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback) { + std::vector credentials = { + CreateInvalidLocalCredential(), CreateValidLocalCredential(), + CreateInvalidLocalCredential()}; + std::move(callback.credentials_fetched_cb)(credentials); + }); + + PresenceDevice remote_device{CreateTestMetadata()}; + MockAuthenticationTransport authentication_transport; + auto status = provider_->AuthenticateAsInitiator( + /*remote_device=*/remote_device, /*shared_secret=*/"", + /*authentication_transport=*/authentication_transport); + + // TODO(b/282027237): Once additional logic is added in follow up CL's + // to continue the authentication, add coverage in this unit test for a + // success case. + EXPECT_EQ(AuthenticationStatus::kSuccess, status); } } // namespace diff --git a/presence/presence_service_impl.cc b/presence/presence_service_impl.cc index 8b14d936..660bb609 100644 --- a/presence/presence_service_impl.cc +++ b/presence/presence_service_impl.cc @@ -22,6 +22,7 @@ #include "presence/data_types.h" #include "presence/implementation/service_controller_impl.h" #include "presence/presence_client_impl.h" +#include "presence/presence_device_provider.h" namespace nearby { namespace presence { @@ -29,8 +30,8 @@ namespace presence { PresenceServiceImpl::PresenceServiceImpl() { service_controller_ = std::make_unique( &executor_, &credential_manager_, &scan_manager_, &broadcast_manager_); - provider_ = std::make_unique( - service_controller_->GetLocalDeviceMetadata()); + provider_ = + std::make_unique(service_controller_.get()); } std::unique_ptr PresenceServiceImpl::CreatePresenceClient() {