aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralexv-smirnov <alex@ydb.tech>2022-10-26 20:47:36 +0300
committeralexv-smirnov <alex@ydb.tech>2022-10-26 20:47:36 +0300
commit7d977b6f917a50979779188b685a9201975048d6 (patch)
tree503a904fe41bb8a010013e60e90fcdb9e48b5b16
parent608252fc836bcc17fd845053126f1e60bee2f767 (diff)
downloadydb-7d977b6f917a50979779188b685a9201975048d6.tar.gz
move ycloud client lib + server mocks to ydb/library
-rw-r--r--CMakeLists.darwin.txt1
-rw-r--r--CMakeLists.linux-aarch64.txt1
-rw-r--r--CMakeLists.linux.txt1
-rw-r--r--cloud/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/CMakeLists.txt10
-rw-r--r--cloud/bitbucket/common-api/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/common-api/yandex/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/common-api/yandex/cloud/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/common-api/yandex/cloud/api/CMakeLists.txt51
-rw-r--r--cloud/bitbucket/common-api/yandex/cloud/api/tools/CMakeLists.txt48
-rw-r--r--cloud/bitbucket/private-api/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/private-api/yandex/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/priv/CMakeLists.txt53
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/priv/access/CMakeLists.txt50
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/priv/operation/CMakeLists.txt48
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/priv/quota/CMakeLists.txt51
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/priv/ydb/CMakeLists.txt9
-rw-r--r--cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/CMakeLists.txt67
-rw-r--r--ydb/library/CMakeLists.txt2
-rw-r--r--ydb/library/testlib/CMakeLists.txt9
-rw-r--r--ydb/library/testlib/service_mocks/CMakeLists.txt19
-rw-r--r--ydb/library/testlib/service_mocks/access_service_mock.h69
-rw-r--r--ydb/library/testlib/service_mocks/database_service_mock.h75
-rw-r--r--ydb/library/testlib/service_mocks/datastreams_service_mock.h31
-rw-r--r--ydb/library/testlib/service_mocks/folder_service_mock.h23
-rw-r--r--ydb/library/testlib/service_mocks/iam_token_service_mock.h43
-rw-r--r--ydb/library/testlib/service_mocks/service_account_service_mock.h65
-rw-r--r--ydb/library/testlib/service_mocks/user_account_service_mock.h22
-rw-r--r--ydb/library/ycloud/CMakeLists.txt10
-rw-r--r--ydb/library/ycloud/api/CMakeLists.txt21
-rw-r--r--ydb/library/ycloud/api/access_service.h33
-rw-r--r--ydb/library/ycloud/api/events.h24
-rw-r--r--ydb/library/ycloud/api/folder_service.h29
-rw-r--r--ydb/library/ycloud/api/iam_token_service.h30
-rw-r--r--ydb/library/ycloud/api/service_account_service.h34
-rw-r--r--ydb/library/ycloud/api/user_account_service.h29
-rw-r--r--ydb/library/ycloud/impl/CMakeLists.txt32
-rw-r--r--ydb/library/ycloud/impl/access_service.cpp91
-rw-r--r--ydb/library/ycloud/impl/access_service.h21
-rw-r--r--ydb/library/ycloud/impl/access_service_ut.cpp111
-rw-r--r--ydb/library/ycloud/impl/folder_service.cpp53
-rw-r--r--ydb/library/ycloud/impl/folder_service.h21
-rw-r--r--ydb/library/ycloud/impl/folder_service_adapter.cpp114
-rw-r--r--ydb/library/ycloud/impl/folder_service_ut.cpp80
-rw-r--r--ydb/library/ycloud/impl/grpc_service_cache.h167
-rw-r--r--ydb/library/ycloud/impl/grpc_service_client.h126
-rw-r--r--ydb/library/ycloud/impl/grpc_service_settings.h14
-rw-r--r--ydb/library/ycloud/impl/iam_token_service.cpp53
-rw-r--r--ydb/library/ycloud/impl/iam_token_service.h21
-rw-r--r--ydb/library/ycloud/impl/service_account_service.cpp69
-rw-r--r--ydb/library/ycloud/impl/service_account_service.h19
-rw-r--r--ydb/library/ycloud/impl/service_account_service_ut.cpp105
-rw-r--r--ydb/library/ycloud/impl/user_account_service.cpp46
-rw-r--r--ydb/library/ycloud/impl/user_account_service.h19
-rw-r--r--ydb/library/ycloud/impl/user_account_service_ut.cpp62
-rw-r--r--ydb/library/ycloud/impl/ut/CMakeLists.darwin.txt51
-rw-r--r--ydb/library/ycloud/impl/ut/CMakeLists.linux-aarch64.txt53
-rw-r--r--ydb/library/ycloud/impl/ut/CMakeLists.linux.txt55
-rw-r--r--ydb/library/ycloud/impl/ut/CMakeLists.txt15
60 files changed, 2419 insertions, 0 deletions
diff --git a/CMakeLists.darwin.txt b/CMakeLists.darwin.txt
index cd6d87e6526..912b806b6a2 100644
--- a/CMakeLists.darwin.txt
+++ b/CMakeLists.darwin.txt
@@ -12,3 +12,4 @@ add_subdirectory(util)
add_subdirectory(library)
add_subdirectory(ydb)
add_subdirectory(certs)
+add_subdirectory(cloud)
diff --git a/CMakeLists.linux-aarch64.txt b/CMakeLists.linux-aarch64.txt
index cd6d87e6526..912b806b6a2 100644
--- a/CMakeLists.linux-aarch64.txt
+++ b/CMakeLists.linux-aarch64.txt
@@ -12,3 +12,4 @@ add_subdirectory(util)
add_subdirectory(library)
add_subdirectory(ydb)
add_subdirectory(certs)
+add_subdirectory(cloud)
diff --git a/CMakeLists.linux.txt b/CMakeLists.linux.txt
index cd6d87e6526..912b806b6a2 100644
--- a/CMakeLists.linux.txt
+++ b/CMakeLists.linux.txt
@@ -12,3 +12,4 @@ add_subdirectory(util)
add_subdirectory(library)
add_subdirectory(ydb)
add_subdirectory(certs)
+add_subdirectory(cloud)
diff --git a/cloud/CMakeLists.txt b/cloud/CMakeLists.txt
new file mode 100644
index 00000000000..3ad0faf5243
--- /dev/null
+++ b/cloud/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(bitbucket)
diff --git a/cloud/bitbucket/CMakeLists.txt b/cloud/bitbucket/CMakeLists.txt
new file mode 100644
index 00000000000..ab8cce84c4f
--- /dev/null
+++ b/cloud/bitbucket/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(common-api)
+add_subdirectory(private-api)
diff --git a/cloud/bitbucket/common-api/CMakeLists.txt b/cloud/bitbucket/common-api/CMakeLists.txt
new file mode 100644
index 00000000000..9fee5b33311
--- /dev/null
+++ b/cloud/bitbucket/common-api/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(yandex)
diff --git a/cloud/bitbucket/common-api/yandex/CMakeLists.txt b/cloud/bitbucket/common-api/yandex/CMakeLists.txt
new file mode 100644
index 00000000000..d51f65f4b47
--- /dev/null
+++ b/cloud/bitbucket/common-api/yandex/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(cloud)
diff --git a/cloud/bitbucket/common-api/yandex/cloud/CMakeLists.txt b/cloud/bitbucket/common-api/yandex/cloud/CMakeLists.txt
new file mode 100644
index 00000000000..0a386de02f7
--- /dev/null
+++ b/cloud/bitbucket/common-api/yandex/cloud/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(api)
diff --git a/cloud/bitbucket/common-api/yandex/cloud/api/CMakeLists.txt b/cloud/bitbucket/common-api/yandex/cloud/api/CMakeLists.txt
new file mode 100644
index 00000000000..f0abd297c94
--- /dev/null
+++ b/cloud/bitbucket/common-api/yandex/cloud/api/CMakeLists.txt
@@ -0,0 +1,51 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(tools)
+
+add_library(yandex-cloud-api)
+set_property(TARGET yandex-cloud-api PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET yandex-cloud-api PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/common-api
+)
+target_include_directories(yandex-cloud-api PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/common-api
+)
+target_link_libraries(yandex-cloud-api PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ cloud-api-tools
+ contrib-libs-protobuf
+)
+target_proto_messages(yandex-cloud-api PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api/yandex/cloud/api/operation.proto
+)
+target_proto_addincls(yandex-cloud-api
+ ./cloud/bitbucket/common-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(yandex-cloud-api
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/common-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/common-api
+)
+target_proto_plugin(yandex-cloud-api
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/cloud/bitbucket/common-api/yandex/cloud/api/tools/CMakeLists.txt b/cloud/bitbucket/common-api/yandex/cloud/api/tools/CMakeLists.txt
new file mode 100644
index 00000000000..12b18776a01
--- /dev/null
+++ b/cloud/bitbucket/common-api/yandex/cloud/api/tools/CMakeLists.txt
@@ -0,0 +1,48 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cloud-api-tools)
+set_property(TARGET cloud-api-tools PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET cloud-api-tools PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/common-api
+)
+target_include_directories(cloud-api-tools PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/common-api
+)
+target_link_libraries(cloud-api-tools PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ contrib-libs-protobuf
+)
+target_proto_messages(cloud-api-tools PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api/yandex/cloud/api/tools/options.proto
+)
+target_proto_addincls(cloud-api-tools
+ ./cloud/bitbucket/common-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cloud-api-tools
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/common-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/common-api
+)
+target_proto_plugin(cloud-api-tools
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/cloud/bitbucket/private-api/CMakeLists.txt b/cloud/bitbucket/private-api/CMakeLists.txt
new file mode 100644
index 00000000000..9fee5b33311
--- /dev/null
+++ b/cloud/bitbucket/private-api/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(yandex)
diff --git a/cloud/bitbucket/private-api/yandex/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/CMakeLists.txt
new file mode 100644
index 00000000000..d51f65f4b47
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(cloud)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/CMakeLists.txt
new file mode 100644
index 00000000000..20e0028be29
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(priv)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/priv/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/priv/CMakeLists.txt
new file mode 100644
index 00000000000..7b85366d1f5
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/priv/CMakeLists.txt
@@ -0,0 +1,53 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(access)
+add_subdirectory(operation)
+add_subdirectory(quota)
+add_subdirectory(ydb)
+
+add_library(yandex-cloud-priv)
+set_property(TARGET yandex-cloud-priv PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET yandex-cloud-priv PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/private-api
+)
+target_include_directories(yandex-cloud-priv PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_link_libraries(yandex-cloud-priv PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ contrib-libs-protobuf
+)
+target_proto_messages(yandex-cloud-priv PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/sensitive.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/validation.proto
+)
+target_proto_addincls(yandex-cloud-priv
+ ./cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(yandex-cloud-priv
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_proto_plugin(yandex-cloud-priv
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/priv/access/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/priv/access/CMakeLists.txt
new file mode 100644
index 00000000000..d4e907cf4fd
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/priv/access/CMakeLists.txt
@@ -0,0 +1,50 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cloud-priv-access)
+set_property(TARGET cloud-priv-access PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET cloud-priv-access PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/private-api
+)
+target_include_directories(cloud-priv-access PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_link_libraries(cloud-priv-access PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ yandex-cloud-priv
+ contrib-libs-protobuf
+)
+target_proto_messages(cloud-priv-access PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/access/access.proto
+)
+target_proto_addincls(cloud-priv-access
+ ./cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cloud-priv-access
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_proto_plugin(cloud-priv-access
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/priv/operation/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/priv/operation/CMakeLists.txt
new file mode 100644
index 00000000000..759162a183c
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/priv/operation/CMakeLists.txt
@@ -0,0 +1,48 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cloud-priv-operation)
+set_property(TARGET cloud-priv-operation PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET cloud-priv-operation PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/private-api
+)
+target_include_directories(cloud-priv-operation PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_link_libraries(cloud-priv-operation PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ contrib-libs-protobuf
+)
+target_proto_messages(cloud-priv-operation PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/operation/operation.proto
+)
+target_proto_addincls(cloud-priv-operation
+ ./cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cloud-priv-operation
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_proto_plugin(cloud-priv-operation
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/priv/quota/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/priv/quota/CMakeLists.txt
new file mode 100644
index 00000000000..d0897999b23
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/priv/quota/CMakeLists.txt
@@ -0,0 +1,51 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cloud-priv-quota)
+set_property(TARGET cloud-priv-quota PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET cloud-priv-quota PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/private-api
+)
+target_include_directories(cloud-priv-quota PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_link_libraries(cloud-priv-quota PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ yandex-cloud-priv
+ contrib-libs-protobuf
+)
+target_proto_messages(cloud-priv-quota PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/quota/quota.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/quota/quota_limit.proto
+)
+target_proto_addincls(cloud-priv-quota
+ ./cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cloud-priv-quota
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_proto_plugin(cloud-priv-quota
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/CMakeLists.txt
new file mode 100644
index 00000000000..0293e4453a3
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(v1)
diff --git a/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/CMakeLists.txt b/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/CMakeLists.txt
new file mode 100644
index 00000000000..ff2361c3f36
--- /dev/null
+++ b/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/CMakeLists.txt
@@ -0,0 +1,67 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(priv-ydb-v1)
+set_property(TARGET priv-ydb-v1 PROPERTY
+ PROTOC_EXTRA_OUTS .grpc.pb.cc .grpc.pb.h
+)
+set_property(TARGET priv-ydb-v1 PROPERTY
+ PROTO_NAMESPACE cloud/bitbucket/private-api
+)
+target_include_directories(priv-ydb-v1 PUBLIC
+ ${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_link_libraries(priv-ydb-v1 PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-grpc
+ contrib-libs-googleapis-common-protos
+ yandex-cloud-api
+ cloud-api-tools
+ yandex-cloud-priv
+ cloud-priv-access
+ cloud-priv-operation
+ cloud-priv-quota
+ contrib-libs-protobuf
+)
+target_proto_messages(priv-ydb-v1 PRIVATE
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/backup.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/backup_service.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/database.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/database_service.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/location.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/location_service.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/operation_service.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/quota_service.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/resource_preset.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/resource_preset_service.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/storage_type.proto
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/storage_type_service.proto
+)
+target_proto_addincls(priv-ydb-v1
+ ./cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_SOURCE_DIR}/contrib/libs/googleapis-common-protos
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/common-api
+ ${CMAKE_SOURCE_DIR}/cloud/bitbucket/private-api
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(priv-ydb-v1
+ --cpp_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/cloud/bitbucket/private-api
+)
+target_proto_plugin(priv-ydb-v1
+ grpc_cpp
+ grpc_cpp
+)
diff --git a/ydb/library/CMakeLists.txt b/ydb/library/CMakeLists.txt
index 5186aee93ab..d0c39c49203 100644
--- a/ydb/library/CMakeLists.txt
+++ b/ydb/library/CMakeLists.txt
@@ -25,6 +25,8 @@ add_subdirectory(pretty_types_print)
add_subdirectory(protobuf_printer)
add_subdirectory(schlab)
add_subdirectory(security)
+add_subdirectory(testlib)
add_subdirectory(workload)
add_subdirectory(yaml_config)
+add_subdirectory(ycloud)
add_subdirectory(yql)
diff --git a/ydb/library/testlib/CMakeLists.txt b/ydb/library/testlib/CMakeLists.txt
new file mode 100644
index 00000000000..52ceab64c9f
--- /dev/null
+++ b/ydb/library/testlib/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(service_mocks)
diff --git a/ydb/library/testlib/service_mocks/CMakeLists.txt b/ydb/library/testlib/service_mocks/CMakeLists.txt
new file mode 100644
index 00000000000..339154ea3b3
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/CMakeLists.txt
@@ -0,0 +1,19 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(library-testlib-service_mocks INTERFACE)
+target_link_libraries(library-testlib-service_mocks INTERFACE
+ contrib-libs-cxxsupp
+ yutil
+ client-yc_private-servicecontrol
+ priv-ydb-v1
+ api-grpc-draft
+ client-yc_private-resourcemanager
+ client-yc_private-iam
+)
diff --git a/ydb/library/testlib/service_mocks/access_service_mock.h b/ydb/library/testlib/service_mocks/access_service_mock.h
new file mode 100644
index 00000000000..5fb88cda0fd
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/access_service_mock.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <ydb/public/api/client/yc_private/servicecontrol/access_service.grpc.pb.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <iterator>
+
+class TAccessServiceMock : public yandex::cloud::priv::servicecontrol::v1::AccessService::Service {
+public:
+ template <class TResonseProto>
+ struct TResponse {
+ TResonseProto Response;
+ grpc::Status Status = grpc::Status::OK;
+ bool RequireRequestId = false;
+ };
+
+ THashMap<TString, TResponse<yandex::cloud::priv::servicecontrol::v1::AuthenticateResponse>> AuthenticateData;
+ THashMap<TString, TResponse<yandex::cloud::priv::servicecontrol::v1::AuthorizeResponse>> AuthorizeData;
+
+ template <class TResonseProto>
+ void CheckRequestId(grpc::ServerContext* ctx, const TResponse<TResonseProto>& resp, const TString& token) {
+ if (resp.RequireRequestId) {
+ auto [reqIdBegin, reqIdEnd] = ctx->client_metadata().equal_range("x-request-id");
+ UNIT_ASSERT_C(reqIdBegin != reqIdEnd, "RequestId is expected. Token: " << token);
+ UNIT_ASSERT_VALUES_EQUAL_C(std::distance(reqIdBegin, reqIdEnd), 1, "Only one RequestId is expected. Token: " << token);
+ UNIT_ASSERT_C(!reqIdBegin->second.empty(), "RequestId is expected to be not empty. Token: " << token);
+ }
+ }
+
+ virtual grpc::Status Authenticate(
+ grpc::ServerContext* ctx,
+ const yandex::cloud::priv::servicecontrol::v1::AuthenticateRequest* request,
+ yandex::cloud::priv::servicecontrol::v1::AuthenticateResponse* response) override
+ {
+ TString key;
+ if (request->has_signature()) {
+ key = request->signature().v4_parameters().service();
+ } else {
+ key = request->iam_token();
+ }
+
+ auto it = AuthenticateData.find(key);
+ if (it != AuthenticateData.end()) {
+ response->CopyFrom(it->second.Response);
+ CheckRequestId(ctx, it->second, key);
+ return it->second.Status;
+ } else {
+ return grpc::Status(grpc::StatusCode::PERMISSION_DENIED, "Permission Denied");
+ }
+ }
+
+ virtual grpc::Status Authorize(
+ grpc::ServerContext* ctx,
+ const yandex::cloud::priv::servicecontrol::v1::AuthorizeRequest* request,
+ yandex::cloud::priv::servicecontrol::v1::AuthorizeResponse* response) override
+ {
+ const TString& lastResourceId = request->resource_path(request->resource_path_size() - 1).id();
+ const TString& token = request->signature().access_key_id() + request->iam_token() + "-" + request->permission() + "-" + lastResourceId;
+ auto it = AuthorizeData.find(token);
+ if (it != AuthorizeData.end()) {
+ response->CopyFrom(it->second.Response);
+ CheckRequestId(ctx, it->second, token);
+ return it->second.Status;
+ } else {
+ return grpc::Status(grpc::StatusCode::PERMISSION_DENIED, "Permission Denied");
+ }
+ }
+};
diff --git a/ydb/library/testlib/service_mocks/database_service_mock.h b/ydb/library/testlib/service_mocks/database_service_mock.h
new file mode 100644
index 00000000000..59fff6dde99
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/database_service_mock.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <cloud/bitbucket/private-api/yandex/cloud/priv/ydb/v1/database_service.grpc.pb.h>
+#include <util/string/join.h>
+#include <util/string/vector.h>
+
+class TDatabaseServiceMock : public yandex::cloud::priv::ydb::v1::DatabaseService::Service {
+public:
+ THashMap<TString, yandex::cloud::priv::ydb::v1::Database> PersistentDatabases;
+ THashMap<TString, yandex::cloud::priv::ydb::v1::Database> TemporaryDatabases;
+ TString Identity;
+
+ TMaybe<grpc::Status> CheckAuthorization(grpc::ServerContext* context) {
+ if (!Identity.empty()) {
+ auto[reqIdBegin, reqIdEnd] = context->client_metadata().equal_range("authorization");
+ UNIT_ASSERT_C(reqIdBegin != reqIdEnd, "Authorization is expected.");
+ if (Identity != TStringBuf(reqIdBegin->second.cbegin(), reqIdBegin->second.cend())) {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED,
+ TStringBuilder() << "Access for user " << Identity << " is forbidden");
+ }
+ }
+
+ return Nothing();
+ }
+
+ virtual grpc::Status GetByPath(grpc::ServerContext* context,
+ const yandex::cloud::priv::ydb::v1::GetDatabaseByPathRequest* request,
+ yandex::cloud::priv::ydb::v1::Database* response) override
+ {
+ auto status = CheckAuthorization(context);
+ auto parts = SplitString(request->path(), "/");
+ Y_ENSURE(parts.size() >= 3);
+ TString canonizedPath = "/" + JoinRange("/", parts.begin(), parts.begin() + 3);
+
+ if (auto itPersistent = PersistentDatabases.find(canonizedPath); itPersistent != PersistentDatabases.end()) {
+ *response = itPersistent->second;
+ return grpc::Status::OK;
+ } else {
+ auto it = TemporaryDatabases.find(canonizedPath);
+ if (it == TemporaryDatabases.end()) {
+ return grpc::Status(grpc::StatusCode::NOT_FOUND, TStringBuilder() << " database with name " << request->path() << " not found");
+ } else {
+ *response = it->second;
+ return grpc::Status::OK;
+ }
+ }
+ }
+
+ virtual grpc::Status ListAll(grpc::ServerContext* context,
+ const yandex::cloud::priv::ydb::v1::ListAllDatabasesRequest* request,
+ yandex::cloud::priv::ydb::v1::ListAllDatabasesResponse* response) override
+ {
+ auto status = CheckAuthorization(context);
+ if (status.Defined()) {
+ return *status;
+ }
+
+ if (PersistentDatabases.empty()) {
+ return grpc::Status::OK;
+ }
+ auto it = PersistentDatabases.begin();
+ if (!request->page_token().empty()) {
+ it = PersistentDatabases.find(request->page_token());
+ }
+ Y_ENSURE(it != PersistentDatabases.end());
+ *response->add_databases() = it->second;
+ it++;
+ if (it != PersistentDatabases.end()) {
+ response->set_next_page_token(it->first);
+ }
+ return grpc::Status::OK;
+ }
+
+};
+
diff --git a/ydb/library/testlib/service_mocks/datastreams_service_mock.h b/ydb/library/testlib/service_mocks/datastreams_service_mock.h
new file mode 100644
index 00000000000..3f65899cfc4
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/datastreams_service_mock.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <ydb/public/api/grpc/draft/ydb_datastreams_v1.grpc.pb.h>
+#include "access_service_mock.h"
+#include "datastreams_service_mock.h"
+
+class TDataStreamsServiceMock : public Ydb::DataStreams::V1::DataStreamsService::Service {
+public:
+ virtual grpc::Status PutRecords(grpc::ServerContext*,
+ const Ydb::DataStreams::V1::PutRecordsRequest* request,
+ Ydb::DataStreams::V1::PutRecordsResponse* response) override
+ {
+ Y_UNUSED(response);
+ for (const auto& record : request->records()) {
+ if (record.partition_key() == "Sleep") {
+ Sleep(TDuration::Seconds(3));
+ } else if (record.partition_key() == "InvalidArgument") {
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Invalid argument");
+ } else if (record.partition_key() == "Unauthenticated") {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "Access denied");
+ }
+ }
+ response->mutable_operation()->set_status(Ydb::StatusIds::SUCCESS);
+ response->mutable_operation()->set_ready(true);
+ response->mutable_operation()->set_id("12345");
+
+ return grpc::Status::OK;
+ }
+
+};
+
diff --git a/ydb/library/testlib/service_mocks/folder_service_mock.h b/ydb/library/testlib/service_mocks/folder_service_mock.h
new file mode 100644
index 00000000000..90ba4c6fde1
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/folder_service_mock.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <ydb/public/api/client/yc_private/resourcemanager/folder_service.grpc.pb.h>
+
+class TFolderServiceMock : public yandex::cloud::priv::resourcemanager::v1::transitional::FolderService::Service {
+public:
+ THashMap<TString, yandex::cloud::priv::resourcemanager::v1::Folder> Folders;
+
+ virtual grpc::Status List(
+ grpc::ServerContext*,
+ const yandex::cloud::priv::resourcemanager::v1::transitional::ListFoldersRequest* request,
+ yandex::cloud::priv::resourcemanager::v1::transitional::ListFoldersResponse* response) override {
+ TString key = request->id();
+ auto it = Folders.find(key);
+ if (it != Folders.end()) {
+ response->add_result()->CopyFrom(it->second);
+ return grpc::Status::OK;
+ } else {
+ return grpc::Status(grpc::StatusCode::NOT_FOUND, "Not Found");
+ }
+ }
+};
+
diff --git a/ydb/library/testlib/service_mocks/iam_token_service_mock.h b/ydb/library/testlib/service_mocks/iam_token_service_mock.h
new file mode 100644
index 00000000000..c2cc6e3b80b
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/iam_token_service_mock.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <ydb/public/api/client/yc_private/iam/iam_token_service.grpc.pb.h>
+
+class TIamTokenServiceMock : public yandex::cloud::priv::iam::v1::IamTokenService::Service {
+public:
+ THashMap<TString, yandex::cloud::priv::iam::v1::CreateIamTokenResponse> IamTokens;
+ TString Identity;
+
+ TMaybe<grpc::Status> CheckAuthorization(grpc::ServerContext* context) {
+ if (!Identity.empty()) {
+ auto[reqIdBegin, reqIdEnd] = context->client_metadata().equal_range("authorization");
+ UNIT_ASSERT_C(reqIdBegin != reqIdEnd, "Authorization is expected.");
+ if (Identity != TStringBuf(reqIdBegin->second.cbegin(), reqIdBegin->second.cend())) {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED,
+ TStringBuilder() << "Access for user " << Identity << " is forbidden");
+ }
+ }
+
+ return Nothing();
+ }
+
+ virtual grpc::Status CreateForServiceAccount(grpc::ServerContext* context,
+ const yandex::cloud::priv::iam::v1::CreateIamTokenForServiceAccountRequest* request,
+ yandex::cloud::priv::iam::v1::CreateIamTokenResponse* response) override
+ {
+ auto status = CheckAuthorization(context);
+ if (status.Defined()) {
+ return *status;
+ }
+
+ TString id = request->service_account_id();
+ auto it = IamTokens.find(id);
+ if (it != IamTokens.end()) {
+ response->CopyFrom(it->second);
+ return grpc::Status::OK;
+ } else {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "Iam token not found");
+ }
+ }
+
+};
+
diff --git a/ydb/library/testlib/service_mocks/service_account_service_mock.h b/ydb/library/testlib/service_mocks/service_account_service_mock.h
new file mode 100644
index 00000000000..9ba55a1176c
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/service_account_service_mock.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <ydb/public/api/client/yc_private/iam/service_account_service.grpc.pb.h>
+
+class TServiceAccountServiceMock : public yandex::cloud::priv::iam::v1::ServiceAccountService::Service {
+public:
+ THashMap<TString, yandex::cloud::priv::iam::v1::ServiceAccount> ServiceAccountData;
+ THashMap<TString, yandex::cloud::priv::iam::v1::IamToken> IamTokens;
+ TString Identity;
+
+ TMaybe<grpc::Status> CheckAuthorization(grpc::ServerContext* context) {
+ if (!Identity.empty()) {
+ auto[reqIdBegin, reqIdEnd] = context->client_metadata().equal_range("authorization");
+ if (reqIdBegin == reqIdEnd) {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED,
+ TStringBuilder() << "Authorization data is not provided");
+ } else if (Identity != TStringBuf(reqIdBegin->second.cbegin(), reqIdBegin->second.cend())) {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED,
+ TStringBuilder() << "Access for user " << Identity << " is forbidden");
+ }
+ }
+
+ return Nothing();
+ }
+
+ virtual grpc::Status Get(grpc::ServerContext* context,
+ const yandex::cloud::priv::iam::v1::GetServiceAccountRequest* request,
+ yandex::cloud::priv::iam::v1::ServiceAccount* response) override
+ {
+ auto status = CheckAuthorization(context);
+ if (status.Defined()) {
+ return *status;
+ }
+
+ TString id = request->service_account_id();
+ auto it = ServiceAccountData.find(id);
+ if (it != ServiceAccountData.end()) {
+ response->CopyFrom(it->second);
+ return grpc::Status::OK;
+ } else {
+ return grpc::Status(grpc::StatusCode::NOT_FOUND, "Not Found");
+ }
+ }
+
+ virtual grpc::Status IssueToken(grpc::ServerContext* context,
+ const yandex::cloud::priv::iam::v1::IssueTokenRequest* request,
+ yandex::cloud::priv::iam::v1::IamToken* response) override
+ {
+ auto status = CheckAuthorization(context);
+ if (status.Defined()) {
+ return *status;
+ }
+
+ TString id = request->service_account_id();
+ auto it = IamTokens.find(id);
+ if (it != IamTokens.end()) {
+ response->CopyFrom(it->second);
+ return grpc::Status::OK;
+ } else {
+ return grpc::Status(grpc::StatusCode::UNAUTHENTICATED, "Iam token not found");
+ }
+ }
+
+};
+
diff --git a/ydb/library/testlib/service_mocks/user_account_service_mock.h b/ydb/library/testlib/service_mocks/user_account_service_mock.h
new file mode 100644
index 00000000000..e4710aada9e
--- /dev/null
+++ b/ydb/library/testlib/service_mocks/user_account_service_mock.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <ydb/public/api/client/yc_private/iam/user_account_service.grpc.pb.h>
+
+class TUserAccountServiceMock : public yandex::cloud::priv::iam::v1::UserAccountService::Service {
+public:
+ THashMap<TString, yandex::cloud::priv::iam::v1::UserAccount> UserAccountData;
+
+ virtual grpc::Status Get(grpc::ServerContext*,
+ const yandex::cloud::priv::iam::v1::GetUserAccountRequest* request,
+ yandex::cloud::priv::iam::v1::UserAccount* response) override {
+ TString id = request->user_account_id();
+ auto it = UserAccountData.find(id);
+ if (it != UserAccountData.end()) {
+ response->CopyFrom(it->second);
+ return grpc::Status::OK;
+ } else {
+ return grpc::Status(grpc::StatusCode::NOT_FOUND, "Not Found");
+ }
+ }
+};
+
diff --git a/ydb/library/ycloud/CMakeLists.txt b/ydb/library/ycloud/CMakeLists.txt
new file mode 100644
index 00000000000..eabee160c70
--- /dev/null
+++ b/ydb/library/ycloud/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(api)
+add_subdirectory(impl)
diff --git a/ydb/library/ycloud/api/CMakeLists.txt b/ydb/library/ycloud/api/CMakeLists.txt
new file mode 100644
index 00000000000..e97a72b103a
--- /dev/null
+++ b/ydb/library/ycloud/api/CMakeLists.txt
@@ -0,0 +1,21 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(library-ycloud-api INTERFACE)
+target_link_libraries(library-ycloud-api INTERFACE
+ contrib-libs-cxxsupp
+ yutil
+ client-yc_private-iam
+ client-yc_private-servicecontrol
+ client-yc_private-resourcemanager
+ cpp-actors-core
+ cpp-grpc-client
+ ydb-core-base
+ ydb-core-grpc_caching
+)
diff --git a/ydb/library/ycloud/api/access_service.h b/ydb/library/ycloud/api/access_service.h
new file mode 100644
index 00000000000..67149a8e107
--- /dev/null
+++ b/ydb/library/ycloud/api/access_service.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <ydb/core/base/defs.h>
+#include <ydb/core/base/events.h>
+#include <ydb/public/api/client/yc_private/servicecontrol/access_service.grpc.pb.h>
+#include "events.h"
+
+namespace NCloud {
+ using namespace NKikimr;
+
+ struct TEvAccessService {
+ enum EEv {
+ // requests
+ EvAuthenticateRequest = EventSpaceBegin(TKikimrEvents::ES_ACCESS_SERVICE),
+ EvAuthorizeRequest,
+
+ // replies
+ EvAuthenticateResponse = EventSpaceBegin(TKikimrEvents::ES_ACCESS_SERVICE) + 512,
+ EvAuthorizeResponse,
+
+ EvEnd
+ };
+
+ static_assert(EvEnd < EventSpaceEnd(TKikimrEvents::ES_ACCESS_SERVICE), "expect EvEnd < EventSpaceEnd(TKikimrEvents::ES_ACCESS_SERVICE)");
+
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/servicecontrol/proto/servicecontrol/v1/access_service.proto
+
+ struct TEvAuthenticateRequest : TEvGrpcProtoRequest<TEvAuthenticateRequest, EvAuthenticateRequest, yandex::cloud::priv::servicecontrol::v1::AuthenticateRequest> {};
+ struct TEvAuthenticateResponse : TEvGrpcProtoResponse<TEvAuthenticateResponse, EvAuthenticateResponse, yandex::cloud::priv::servicecontrol::v1::AuthenticateResponse> {};
+
+ struct TEvAuthorizeRequest : TEvGrpcProtoRequest<TEvAuthorizeRequest, EvAuthorizeRequest, yandex::cloud::priv::servicecontrol::v1::AuthorizeRequest> {};
+ struct TEvAuthorizeResponse : TEvGrpcProtoResponse<TEvAuthorizeResponse, EvAuthorizeResponse, yandex::cloud::priv::servicecontrol::v1::AuthorizeResponse> {};
+ };
+}
diff --git a/ydb/library/ycloud/api/events.h b/ydb/library/ycloud/api/events.h
new file mode 100644
index 00000000000..c7812b70546
--- /dev/null
+++ b/ydb/library/ycloud/api/events.h
@@ -0,0 +1,24 @@
+#pragma once
+#include <util/generic/ptr.h>
+#include <util/generic/string.h>
+#include <library/cpp/actors/core/actor.h>
+#include <library/cpp/actors/core/event_local.h>
+#include <library/cpp/grpc/client/grpc_client_low.h>
+
+namespace NCloud {
+
+template <typename TEv, ui32 TEventType, typename TProtoMessage>
+struct TEvGrpcProtoRequest : NActors::TEventLocal<TEv, TEventType> {
+ TProtoMessage Request;
+ TString Token;
+ TString RequestId;
+};
+
+template <typename TEv, ui32 TEventType, typename TProtoMessage>
+struct TEvGrpcProtoResponse : NActors::TEventLocal<TEv, TEventType> {
+ THolder<NActors::IEventHandle> Request;
+ TProtoMessage Response;
+ NGrpc::TGrpcStatus Status;
+};
+
+}
diff --git a/ydb/library/ycloud/api/folder_service.h b/ydb/library/ycloud/api/folder_service.h
new file mode 100644
index 00000000000..a1922103c5e
--- /dev/null
+++ b/ydb/library/ycloud/api/folder_service.h
@@ -0,0 +1,29 @@
+#pragma once
+#include <ydb/core/base/defs.h>
+#include <ydb/core/base/events.h>
+#include <ydb/public/api/client/yc_private/resourcemanager/folder_service.grpc.pb.h>
+#include "events.h"
+
+namespace NCloud {
+ using namespace NKikimr;
+
+ struct TEvFolderService {
+ enum EEv {
+ // requests
+ EvListFolderRequest = EventSpaceBegin(TKikimrEvents::ES_FOLDER_SERVICE),
+
+ // replies
+ EvListFolderResponse = EventSpaceBegin(TKikimrEvents::ES_FOLDER_SERVICE) + 512,
+
+ EvEnd
+ };
+
+ static_assert(EvEnd < EventSpaceEnd(TKikimrEvents::ES_FOLDER_SERVICE), "expect EvEnd < EventSpaceEnd(TKikimrEvents::ES_FOLDER_SERVICE)");
+
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/identity/proto/resourcemanager/v1/folder.proto
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/identity/proto/resourcemanager/v1/transitional/folder_service.proto
+
+ struct TEvListFolderRequest : TEvGrpcProtoRequest<TEvListFolderRequest, EvListFolderRequest, yandex::cloud::priv::resourcemanager::v1::transitional::ListFoldersRequest> {};
+ struct TEvListFolderResponse : TEvGrpcProtoResponse<TEvListFolderResponse, EvListFolderResponse, yandex::cloud::priv::resourcemanager::v1::transitional::ListFoldersResponse> {};
+ };
+}
diff --git a/ydb/library/ycloud/api/iam_token_service.h b/ydb/library/ycloud/api/iam_token_service.h
new file mode 100644
index 00000000000..a525ebebbc8
--- /dev/null
+++ b/ydb/library/ycloud/api/iam_token_service.h
@@ -0,0 +1,30 @@
+#pragma once
+#include <ydb/core/base/defs.h>
+#include <ydb/core/base/events.h>
+#include <ydb/public/api/client/yc_private/iam/iam_token_service.grpc.pb.h>
+#include "events.h"
+
+namespace NCloud {
+ using namespace NKikimr;
+
+ class TIamTokenService;
+
+ struct TEvIamTokenService {
+ enum EEv {
+ // requests
+ EvCreateForServiceAccountRequest = EventSpaceBegin(TKikimrEvents::ES_IAM_TOKEN_SERVICE),
+
+ // replies
+ EvCreateResponse = EventSpaceBegin(TKikimrEvents::ES_IAM_TOKEN_SERVICE) + 1024,
+
+ EvEnd
+ };
+
+ static_assert(EvEnd < EventSpaceEnd(TKikimrEvents::ES_IAM_TOKEN_SERVICE), "expect EvEnd < EventSpaceEnd(TKikimrEvents::ES_SERVICE_ACCOUNT_SERVICE)");
+
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/bitbucket/private-api/yandex/cloud/priv/iam/v1/iam_token_service.proto
+
+ struct TEvCreateForServiceAccountRequest : TEvGrpcProtoRequest<TEvCreateForServiceAccountRequest, EvCreateForServiceAccountRequest, yandex::cloud::priv::iam::v1::CreateIamTokenForServiceAccountRequest> {};
+ struct TEvCreateResponse : TEvGrpcProtoResponse<TEvCreateResponse, EvCreateResponse, yandex::cloud::priv::iam::v1::CreateIamTokenResponse> {};
+ };
+}
diff --git a/ydb/library/ycloud/api/service_account_service.h b/ydb/library/ycloud/api/service_account_service.h
new file mode 100644
index 00000000000..5f0f6bbb97d
--- /dev/null
+++ b/ydb/library/ycloud/api/service_account_service.h
@@ -0,0 +1,34 @@
+#pragma once
+#include <ydb/core/base/defs.h>
+#include <ydb/core/base/events.h>
+#include <ydb/public/api/client/yc_private/iam/service_account_service.grpc.pb.h>
+#include "events.h"
+
+namespace NCloud {
+ using namespace NKikimr;
+
+ struct TEvServiceAccountService {
+ enum EEv {
+ // requests
+ EvGetServiceAccountRequest = EventSpaceBegin(TKikimrEvents::ES_SERVICE_ACCOUNT_SERVICE),
+ EvIssueTokenRequest,
+
+ // replies
+ EvGetServiceAccountResponse = EventSpaceBegin(TKikimrEvents::ES_SERVICE_ACCOUNT_SERVICE) + 1024,
+ EvIssueTokenResponse,
+
+ EvEnd
+ };
+
+ static_assert(EvEnd < EventSpaceEnd(TKikimrEvents::ES_SERVICE_ACCOUNT_SERVICE), "expect EvEnd < EventSpaceEnd(TKikimrEvents::ES_SERVICE_ACCOUNT_SERVICE)");
+
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/bitbucket/private-api/yandex/cloud/priv/iam/v1/service_account.proto
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/bitbucket/private-api/yandex/cloud/priv/iam/v1/service_account_service.proto
+
+ struct TEvGetServiceAccountRequest : TEvGrpcProtoRequest<TEvGetServiceAccountRequest, EvGetServiceAccountRequest, yandex::cloud::priv::iam::v1::GetServiceAccountRequest> {};
+ struct TEvGetServiceAccountResponse : TEvGrpcProtoResponse<TEvGetServiceAccountResponse, EvGetServiceAccountResponse, yandex::cloud::priv::iam::v1::ServiceAccount> {};
+
+ struct TEvIssueTokenRequest : TEvGrpcProtoRequest<TEvIssueTokenRequest, EvIssueTokenRequest, yandex::cloud::priv::iam::v1::IssueTokenRequest> {};
+ struct TEvIssueTokenResponse : TEvGrpcProtoResponse<TEvIssueTokenResponse, EvIssueTokenResponse, yandex::cloud::priv::iam::v1::IamToken> {};
+ };
+}
diff --git a/ydb/library/ycloud/api/user_account_service.h b/ydb/library/ycloud/api/user_account_service.h
new file mode 100644
index 00000000000..59ed30b0482
--- /dev/null
+++ b/ydb/library/ycloud/api/user_account_service.h
@@ -0,0 +1,29 @@
+#pragma once
+#include <ydb/core/base/defs.h>
+#include <ydb/core/base/events.h>
+#include <ydb/public/api/client/yc_private/iam/user_account_service.grpc.pb.h>
+#include "events.h"
+
+namespace NCloud {
+ using namespace NKikimr;
+
+ struct TEvUserAccountService {
+ enum EEv {
+ // requests
+ EvGetUserAccountRequest = EventSpaceBegin(TKikimrEvents::ES_USER_ACCOUNT_SERVICE),
+
+ // replies
+ EvGetUserAccountResponse = EventSpaceBegin(TKikimrEvents::ES_USER_ACCOUNT_SERVICE) + 512,
+
+ EvEnd
+ };
+
+ static_assert(EvEnd < EventSpaceEnd(TKikimrEvents::ES_USER_ACCOUNT_SERVICE), "expect EvEnd < EventSpaceEnd(TKikimrEvents::ES_USER_ACCOUNT_SERVICE)");
+
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/identity/proto/iam/v1/user_account.proto
+ // https://a.yandex-team.ru/arc/trunk/arcadia/cloud/identity/proto/iam/v1/user_account_service.proto
+
+ struct TEvGetUserAccountRequest : TEvGrpcProtoRequest<TEvGetUserAccountRequest, EvGetUserAccountRequest, yandex::cloud::priv::iam::v1::GetUserAccountRequest> {};
+ struct TEvGetUserAccountResponse : TEvGrpcProtoResponse<TEvGetUserAccountResponse, EvGetUserAccountResponse, yandex::cloud::priv::iam::v1::UserAccount> {};
+ };
+}
diff --git a/ydb/library/ycloud/impl/CMakeLists.txt b/ydb/library/ycloud/impl/CMakeLists.txt
new file mode 100644
index 00000000000..09f018185e2
--- /dev/null
+++ b/ydb/library/ycloud/impl/CMakeLists.txt
@@ -0,0 +1,32 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(ut)
+
+add_library(library-ycloud-impl)
+target_link_libraries(library-ycloud-impl PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ library-ycloud-api
+ cpp-actors-core
+ cpp-digest-crc32c
+ cpp-grpc-client
+ library-cpp-json
+ ydb-core-base
+ ydb-core-grpc_services
+ lib-deprecated-client
+ lib-deprecated-kicli
+)
+target_sources(library-ycloud-impl PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/access_service.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/folder_service.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/folder_service_adapter.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/iam_token_service.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/service_account_service.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/user_account_service.cpp
+)
diff --git a/ydb/library/ycloud/impl/access_service.cpp b/ydb/library/ycloud/impl/access_service.cpp
new file mode 100644
index 00000000000..fd169fb4c13
--- /dev/null
+++ b/ydb/library/ycloud/impl/access_service.cpp
@@ -0,0 +1,91 @@
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/actor.h>
+#include <library/cpp/json/json_value.h>
+#include <ydb/public/api/client/yc_private/servicecontrol/access_service.grpc.pb.h>
+#include "access_service.h"
+#include "grpc_service_client.h"
+#include "grpc_service_cache.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+class TAccessService : public NActors::TActor<TAccessService>, TGrpcServiceClient<yandex::cloud::priv::servicecontrol::v1::AccessService> {
+ using TThis = TAccessService;
+ using TBase = NActors::TActor<TAccessService>;
+
+ struct TAuthenticateRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::servicecontrol::v1::AccessService::Stub::AsyncAuthenticate;
+ using TRequestEventType = TEvAccessService::TEvAuthenticateRequest;
+ using TResponseEventType = TEvAccessService::TEvAuthenticateResponse;
+
+ static yandex::cloud::priv::servicecontrol::v1::AuthenticateRequest Obfuscate(const yandex::cloud::priv::servicecontrol::v1::AuthenticateRequest& p) {
+ yandex::cloud::priv::servicecontrol::v1::AuthenticateRequest r(p);
+ if (r.iam_token()) {
+ r.set_iam_token(MaskToken(r.iam_token()));
+ }
+ r.clear_iam_cookie();
+ return r;
+ }
+
+ static const yandex::cloud::priv::servicecontrol::v1::AuthenticateResponse& Obfuscate(const yandex::cloud::priv::servicecontrol::v1::AuthenticateResponse& p) {
+ return p;
+ }
+ };
+
+ void Handle(TEvAccessService::TEvAuthenticateRequest::TPtr& ev) {
+ MakeCall<TAuthenticateRequest>(std::move(ev));
+ }
+
+ struct TAuthorizeRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::servicecontrol::v1::AccessService::Stub::AsyncAuthorize;
+ using TRequestEventType = TEvAccessService::TEvAuthorizeRequest;
+ using TResponseEventType = TEvAccessService::TEvAuthorizeResponse;
+
+ static yandex::cloud::priv::servicecontrol::v1::AuthorizeRequest Obfuscate(const yandex::cloud::priv::servicecontrol::v1::AuthorizeRequest& p) {
+ yandex::cloud::priv::servicecontrol::v1::AuthorizeRequest r(p);
+ if (r.iam_token()) {
+ r.set_iam_token(MaskToken(r.iam_token()));
+ }
+ return r;
+ }
+
+ static const yandex::cloud::priv::servicecontrol::v1::AuthorizeResponse& Obfuscate(const yandex::cloud::priv::servicecontrol::v1::AuthorizeResponse& p) {
+ return p;
+ }
+ };
+
+ void Handle(TEvAccessService::TEvAuthorizeRequest::TPtr& ev) {
+ MakeCall<TAuthorizeRequest>(std::move(ev));
+ }
+
+public:
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::ACCESS_SERVICE_ACTOR; }
+
+ TAccessService(const TAccessServiceSettings& settings)
+ : TBase(&TThis::StateWork)
+ , TGrpcServiceClient(settings)
+ {}
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEvAccessService::TEvAuthenticateRequest, Handle);
+ hFunc(TEvAccessService::TEvAuthorizeRequest, Handle);
+ cFunc(TEvents::TSystem::PoisonPill, PassAway);
+ }
+ }
+};
+
+
+IActor* CreateAccessService(const TAccessServiceSettings& settings) {
+ return new TAccessService(settings);
+}
+
+IActor* CreateAccessServiceWithCache(const TAccessServiceSettings& settings) {
+ IActor* accessService = CreateAccessService(settings);
+ accessService = CreateGrpcServiceCache<TEvAccessService::TEvAuthenticateRequest, TEvAccessService::TEvAuthenticateResponse>(accessService);
+ accessService = CreateGrpcServiceCache<TEvAccessService::TEvAuthorizeRequest, TEvAccessService::TEvAuthorizeResponse>(accessService);
+ return accessService;
+}
+
+}
diff --git a/ydb/library/ycloud/impl/access_service.h b/ydb/library/ycloud/impl/access_service.h
new file mode 100644
index 00000000000..bdcf2b1c003
--- /dev/null
+++ b/ydb/library/ycloud/impl/access_service.h
@@ -0,0 +1,21 @@
+#pragma once
+#include <ydb/library/ycloud/api/access_service.h>
+#include "grpc_service_settings.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+struct TAccessServiceSettings : TGrpcClientSettings {};
+
+IActor* CreateAccessService(const TAccessServiceSettings& settings);
+
+inline IActor* CreateAccessService(const TString& endpoint) {
+ TAccessServiceSettings settings;
+ settings.Endpoint = endpoint;
+ return CreateAccessService(settings);
+}
+
+IActor* CreateAccessServiceWithCache(const TAccessServiceSettings& settings); // for compatibility with older code
+
+}
diff --git a/ydb/library/ycloud/impl/access_service_ut.cpp b/ydb/library/ycloud/impl/access_service_ut.cpp
new file mode 100644
index 00000000000..7ebf90fd29f
--- /dev/null
+++ b/ydb/library/ycloud/impl/access_service_ut.cpp
@@ -0,0 +1,111 @@
+#include <library/cpp/actors/core/event.h>
+#include <library/cpp/actors/core/event_local.h>
+#include <ydb/core/testlib/test_client.h>
+#include <ydb/library/testlib/service_mocks/access_service_mock.h>
+#include <ydb/core/grpc_services/grpc_helper.h>
+#include <library/cpp/grpc/server/grpc_server.h>
+#include <ydb/public/lib/deprecated/kicli/kicli.h>
+#include <library/cpp/testing/unittest/registar.h>
+#include <library/cpp/testing/unittest/tests_data.h>
+#include <util/string/builder.h>
+#include "access_service.h"
+
+using namespace NKikimr;
+using namespace Tests;
+
+struct TTestSetup {
+ TPortManager PortManager;
+ ui16 KikimrPort;
+ ui16 ServicePort;
+
+ // Kikimr
+ THolder<TServer> Server;
+ THolder<TClient> Client;
+ THolder<NClient::TKikimr> Kikimr;
+ TActorId EdgeActor;
+ IActor* AccessServiceActor = nullptr;
+
+ // Access service
+ TAccessServiceMock AccessServiceMock;
+ std::unique_ptr<grpc::Server> AccessServer;
+
+ TTestSetup()
+ : KikimrPort(PortManager.GetPort(2134))
+ , ServicePort(PortManager.GetPort(4286))
+ {
+ StartKikimr();
+ StartAccessService();
+ }
+
+ TTestActorRuntime* GetRuntime() {
+ return Server->GetRuntime();
+ }
+
+ void StartKikimr() {
+ NKikimrProto::TAuthConfig authConfig;
+ auto settings = TServerSettings(KikimrPort, authConfig);
+ settings.SetDomainName("Root");
+ Server = MakeHolder<TServer>(settings);
+ Server->GetRuntime()->SetLogPriority(NKikimrServices::GRPC_CLIENT, NLog::PRI_DEBUG);
+ Client = MakeHolder<TClient>(settings);
+ Kikimr = MakeHolder<NClient::TKikimr>(Client->GetClientConfig());
+ Client->InitRootScheme();
+ EdgeActor = GetRuntime()->AllocateEdgeActor();
+
+ //AccessServiceActor = NCloud::CreateAccessService("localhost:" + ToString(ServicePort));
+ NCloud::TAccessServiceSettings sets;
+ sets.Endpoint = "localhost:" + ToString(ServicePort);
+ AccessServiceActor = NCloud::CreateAccessServiceWithCache(sets);
+ GetRuntime()->Register(AccessServiceActor);
+ }
+
+ void StartAccessService() {
+ grpc::ServerBuilder builder;
+ builder.AddListeningPort("[::]:" + ToString(ServicePort), grpc::InsecureServerCredentials()).RegisterService(&AccessServiceMock);
+ AccessServer = builder.BuildAndStart();
+ }
+};
+
+Y_UNIT_TEST_SUITE(TAccessServiceTest) {
+ Y_UNIT_TEST(Authenticate) {
+ TTestSetup setup;
+
+ TAutoPtr<IEventHandle> handle;
+ setup.AccessServiceMock.AuthenticateData["good1"].Response.mutable_subject()->mutable_user_account()->set_id("1234");
+
+ // check for not found
+ auto request = MakeHolder<NCloud::TEvAccessService::TEvAuthenticateRequest>();
+ request->Request.set_iam_token("bad1");
+ setup.GetRuntime()->Send(new IEventHandle(setup.AccessServiceActor->SelfId(), setup.EdgeActor, request.Release()));
+ auto result = setup.GetRuntime()->GrabEdgeEvent<NCloud::TEvAccessService::TEvAuthenticateResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT_VALUES_EQUAL(result->Status.Msg, "Permission Denied");
+
+ // check for found
+ request = MakeHolder<NCloud::TEvAccessService::TEvAuthenticateRequest>();
+ request->Request.set_iam_token("good1");
+ setup.GetRuntime()->Send(new IEventHandle(setup.AccessServiceActor->SelfId(), setup.EdgeActor, request.Release()));
+ result = setup.GetRuntime()->GrabEdgeEvent<NCloud::TEvAccessService::TEvAuthenticateResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Ok());
+ UNIT_ASSERT_VALUES_EQUAL(result->Response.subject().user_account().id(), "1234");
+ }
+
+ Y_UNIT_TEST(PassRequestId) {
+ TTestSetup setup;
+
+ TAutoPtr<IEventHandle> handle;
+ auto& req = setup.AccessServiceMock.AuthenticateData["token"];
+ req.Response.mutable_subject()->mutable_user_account()->set_id("1234");
+ req.RequireRequestId = true;
+
+ // check for not found
+ auto request = MakeHolder<NCloud::TEvAccessService::TEvAuthenticateRequest>();
+ request->Request.set_iam_token("token");
+ request->RequestId = "trololo";
+ setup.GetRuntime()->Send(new IEventHandle(setup.AccessServiceActor->SelfId(), setup.EdgeActor, request.Release()));
+ auto result = setup.GetRuntime()->GrabEdgeEvent<NCloud::TEvAccessService::TEvAuthenticateResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Ok());
+ }
+}
diff --git a/ydb/library/ycloud/impl/folder_service.cpp b/ydb/library/ycloud/impl/folder_service.cpp
new file mode 100644
index 00000000000..2af7c005f0c
--- /dev/null
+++ b/ydb/library/ycloud/impl/folder_service.cpp
@@ -0,0 +1,53 @@
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/actor.h>
+#include <ydb/public/api/client/yc_private/resourcemanager/folder_service.grpc.pb.h>
+#include "folder_service.h"
+#include "grpc_service_client.h"
+#include "grpc_service_cache.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+class TFolderService : public NActors::TActor<TFolderService>, TGrpcServiceClient<yandex::cloud::priv::resourcemanager::v1::transitional::FolderService> {
+ using TThis = TFolderService;
+ using TBase = NActors::TActor<TFolderService>;
+
+ struct TListFolderRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::resourcemanager::v1::transitional::FolderService::Stub::AsyncList;
+ using TRequestEventType = TEvFolderService::TEvListFolderRequest;
+ using TResponseEventType = TEvFolderService::TEvListFolderResponse;
+ };
+
+ void Handle(TEvFolderService::TEvListFolderRequest::TPtr& ev) {
+ MakeCall<TListFolderRequest>(std::move(ev));
+ }
+
+public:
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::FOLDER_SERVICE_ACTOR; }
+
+ TFolderService(const TFolderServiceSettings& settings)
+ : TBase(&TThis::StateWork)
+ , TGrpcServiceClient(settings)
+ {}
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEvFolderService::TEvListFolderRequest, Handle);
+ cFunc(TEvents::TSystem::PoisonPill, PassAway);
+ }
+ }
+};
+
+
+IActor* CreateFolderService(const TFolderServiceSettings& settings) {
+ return new TFolderService(settings);
+}
+
+IActor* CreateFolderServiceWithCache(const TFolderServiceSettings& settings) {
+ IActor* folderService = CreateFolderService(settings);
+ folderService = CreateGrpcServiceCache<TEvFolderService::TEvListFolderRequest, TEvFolderService::TEvListFolderResponse>(folderService);
+ return folderService;
+}
+
+}
diff --git a/ydb/library/ycloud/impl/folder_service.h b/ydb/library/ycloud/impl/folder_service.h
new file mode 100644
index 00000000000..9836b82d86c
--- /dev/null
+++ b/ydb/library/ycloud/impl/folder_service.h
@@ -0,0 +1,21 @@
+#pragma once
+#include <ydb/library/ycloud/api/folder_service.h>
+#include "grpc_service_settings.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+struct TFolderServiceSettings : TGrpcClientSettings {};
+
+IActor* CreateFolderService(const TFolderServiceSettings& settings);
+
+inline IActor* CreateFolderService(const TString& endpoint) {
+ TFolderServiceSettings settings;
+ settings.Endpoint = endpoint;
+ return CreateFolderService(settings);
+}
+
+IActor* CreateFolderServiceWithCache(const TFolderServiceSettings& settings); // for compatibility with older code
+
+}
diff --git a/ydb/library/ycloud/impl/folder_service_adapter.cpp b/ydb/library/ycloud/impl/folder_service_adapter.cpp
new file mode 100644
index 00000000000..87c5b59e8de
--- /dev/null
+++ b/ydb/library/ycloud/impl/folder_service_adapter.cpp
@@ -0,0 +1,114 @@
+#include <ydb/library/folder_service/folder_service.h>
+#include <ydb/library/folder_service/events.h>
+#include <ydb/library/folder_service/mock/mock_folder_service.h>
+
+#include <ydb/library/ycloud/impl/folder_service.h>
+
+#include <library/cpp/actors/core/actor_bootstrapped.h>
+#include <library/cpp/actors/core/hfunc.h>
+
+#include <util/stream/file.h>
+
+namespace {
+
+class TFolderServiceRequestHandler : public NActors::TActor<TFolderServiceRequestHandler> {
+ using TThis = TFolderServiceRequestHandler;
+ using TBase = NActors::TActor<TFolderServiceRequestHandler>;
+
+ NActors::TActorId Sender;
+ NActors::TActorId Delegatee;
+
+ void Handle(NKikimr::NFolderService::TEvFolderService::TEvGetFolderRequest::TPtr& ev) {
+ auto request = std::make_unique<NCloud::TEvFolderService::TEvListFolderRequest>();
+ request->Request.set_id(ev->Get()->Request.folder_id());
+ request->Token = ev->Get()->Token;
+ request->RequestId = ev->Get()->RequestId;
+ Send(Delegatee, request.release());
+ }
+
+ void Handle(NCloud::TEvFolderService::TEvListFolderResponse::TPtr& ev) {
+ auto responseEvent = std::make_unique<NKikimr::NFolderService::TEvFolderService::TEvGetFolderResponse>();
+
+ const auto& status = ev->Get()->Status;
+ responseEvent->Status = status;
+
+ if (status.Ok()) {
+ const auto& response = ev->Get()->Response;
+ if (response.result_size() > 0) {
+ Y_VERIFY(responseEvent->Response.mutable_folder()->ParseFromString(response.result(0).SerializeAsString()));
+ }
+ }
+
+ Send(Sender, responseEvent.release());
+ PassAway();
+ }
+
+ void Handle(NActors::TEvents::TEvUndelivered::TPtr&) {
+ Y_FAIL("Can't deliver local message");
+ }
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(NKikimr::NFolderService::TEvFolderService::TEvGetFolderRequest, Handle);
+ hFunc(NCloud::TEvFolderService::TEvListFolderResponse, Handle);
+ hFunc(NActors::TEvents::TEvUndelivered, Handle);
+ cFunc(NActors::TEvents::TSystem::PoisonPill, PassAway);
+ }
+ }
+
+public:
+ TFolderServiceRequestHandler(NActors::TActorId sender, NActors::TActorId delegatee)
+ : TBase(&TThis::StateWork)
+ , Sender(sender)
+ , Delegatee(delegatee) {
+ }
+};
+}; // namespace
+
+namespace NKikimr::NFolderService {
+
+class TFolderServiceAdapter : public NActors::TActorBootstrapped<TFolderServiceAdapter> {
+ using TThis = TFolderServiceAdapter;
+ using TBase = NActors::TActor<TFolderServiceAdapter>;
+
+ NKikimrProto::NFolderService::TFolderServiceConfig Config;
+ NActors::TActorId Delegatee;
+
+ void Handle(::NKikimr::NFolderService::TEvFolderService::TEvGetFolderRequest::TPtr& ev) {
+ auto actor = Register(new TFolderServiceRequestHandler(ev->Sender, Delegatee));
+ Send(actor, ev->Release().Release());
+ }
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(::NKikimr::NFolderService::TEvFolderService::TEvGetFolderRequest, Handle)
+ cFunc(NActors::TEvents::TSystem::PoisonPill, PassAway)
+ }
+ }
+
+public:
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() {
+ return NKikimrServices::TActivity::FOLDER_SERVICE_ACTOR;
+ }
+
+ void Bootstrap() {
+ NCloud::TFolderServiceSettings settings;
+ settings.Endpoint = Config.GetEndpoint();
+ settings.CertificateRootCA = TUnbufferedFileInput(Config.GetPathToRootCA()).ReadAll();
+ Delegatee = Register(NCloud::CreateFolderServiceWithCache(settings));
+ Become(&TThis::StateWork);
+ }
+
+ explicit TFolderServiceAdapter(const NKikimrProto::NFolderService::TFolderServiceConfig& config)
+ : Config(config) {
+ }
+};
+
+NActors::IActor* CreateFolderServiceActor(const NKikimrProto::NFolderService::TFolderServiceConfig& config) {
+ if (config.GetEnable()) {
+ return new NKikimr::NFolderService::TFolderServiceAdapter(config);
+ } else {
+ return NKikimr::NFolderService::CreateMockFolderServiceActor(config);
+ }
+}
+} // namespace NKikimr::NFolderService
diff --git a/ydb/library/ycloud/impl/folder_service_ut.cpp b/ydb/library/ycloud/impl/folder_service_ut.cpp
new file mode 100644
index 00000000000..3c1261e3297
--- /dev/null
+++ b/ydb/library/ycloud/impl/folder_service_ut.cpp
@@ -0,0 +1,80 @@
+#include <library/cpp/actors/core/event.h>
+#include <library/cpp/actors/core/event_local.h>
+#include <ydb/core/testlib/test_client.h>
+#include <ydb/library/testlib/service_mocks/folder_service_mock.h>
+#include <ydb/core/grpc_services/grpc_helper.h>
+#include <library/cpp/grpc/server/grpc_server.h>
+#include <ydb/public/lib/deprecated/kicli/kicli.h>
+#include <library/cpp/testing/unittest/registar.h>
+#include <library/cpp/testing/unittest/tests_data.h>
+#include <library/cpp/retry/retry.h>
+#include <util/string/builder.h>
+#include "folder_service.h"
+
+Y_UNIT_TEST_SUITE(TFolderServiceTest) {
+ Y_UNIT_TEST(ListFolder) {
+ using namespace NKikimr;
+ using namespace Tests;
+
+ TPortManager tp;
+ // Kikimr
+ ui16 kikimrPort = tp.GetPort(2134);
+ NKikimrProto::TAuthConfig authConfig;
+ auto settings = TServerSettings(kikimrPort, authConfig);
+ settings.SetDomainName("Root");
+ TServer server(settings);
+ TClient client(settings);
+ NClient::TKikimr kikimr(client.GetClientConfig());
+ client.InitRootScheme();
+
+ TTestActorRuntime* runtime = server.GetRuntime();
+ TActorId sender = runtime->AllocateEdgeActor();
+ TAutoPtr<IEventHandle> handle;
+ TString badFolderId = "yrm4564";
+ TString goodFolderId = "yrm4566";
+ TString cloudId = "cloud9999";
+
+ // Folder Service
+ ui16 servicePort = tp.GetPort(4284);
+ IActor* folderService = NCloud::CreateFolderService("localhost:" + ToString(servicePort));
+ runtime->Register(folderService);
+ auto request = MakeHolder<NCloud::TEvFolderService::TEvListFolderRequest>();
+ request->Request.set_id("xxx");
+ runtime->Send(new IEventHandle(folderService->SelfId(), sender, request.Release()));
+ auto result = runtime->GrabEdgeEvent<NCloud::TEvFolderService::TEvListFolderResponse>(handle);
+ UNIT_ASSERT(result); // error because no service is listening
+ UNIT_ASSERT_EQUAL(result->Status.Msg, "failed to connect to all addresses");
+
+ // Folder Service Mock
+ TFolderServiceMock folderServiceMock;
+ grpc::ServerBuilder builder;
+ folderServiceMock.Folders[goodFolderId].set_cloud_id(cloudId);
+ builder.AddListeningPort("[::]:" + ToString(servicePort), grpc::InsecureServerCredentials()).RegisterService(&folderServiceMock);
+ std::unique_ptr<grpc::Server> folderServer(builder.BuildAndStart());
+
+ // check for not found
+ // retry if there is TRANSIENT_FAILURE
+ auto sendEvent = [&] {
+ auto request = MakeHolder<NCloud::TEvFolderService::TEvListFolderRequest>();
+ request->Request.set_id(badFolderId);
+ runtime->Send(new IEventHandle(folderService->SelfId(), sender, request.Release()));
+ result = runtime->GrabEdgeEvent<NCloud::TEvFolderService::TEvListFolderResponse>(handle);
+ return result->Status.GRpcStatusCode != 14;
+ };
+ DoWithRetryOnRetCode(sendEvent, TRetryOptions{});
+ Cerr << result->Status.Msg << " " << result->Status.GRpcStatusCode << "\n";
+
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Msg == "Not Found");
+ // || "channel is in state TRANSIENT_FAILURE"
+
+ // check for found
+ request = MakeHolder<NCloud::TEvFolderService::TEvListFolderRequest>();
+ request->Request.set_id(goodFolderId);
+ runtime->Send(new IEventHandle(folderService->SelfId(), sender, request.Release()));
+ result = runtime->GrabEdgeEvent<NCloud::TEvFolderService::TEvListFolderResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Ok());
+ UNIT_ASSERT_EQUAL(result->Response.result(0).cloud_id(), cloudId);
+ }
+}
diff --git a/ydb/library/ycloud/impl/grpc_service_cache.h b/ydb/library/ycloud/impl/grpc_service_cache.h
new file mode 100644
index 00000000000..70eb9d5c2b3
--- /dev/null
+++ b/ydb/library/ycloud/impl/grpc_service_cache.h
@@ -0,0 +1,167 @@
+#pragma once
+
+#include <variant>
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/actor_bootstrapped.h>
+#include <library/cpp/actors/core/mailbox.h>
+#include <ydb/core/util/simple_cache.h>
+
+namespace NCloud {
+
+template <typename TEventRequestType, typename TEventResponseType>
+class TGrpcServiceCache : public NActors::TActorBootstrapped<TGrpcServiceCache<TEventRequestType, TEventResponseType>> {
+ using TThis = TGrpcServiceCache<TEventRequestType, TEventResponseType>;
+ using TBase = NActors::TActorBootstrapped<TThis>;
+ TDuration SuccessLifeTime = TDuration::Minutes(1);
+ TDuration ErrorLifeTime = TDuration::Seconds(10);
+
+ struct TCacheRecord {
+ TInstant UpdateTimestamp;
+ THolder<TEventResponseType> Response;
+ std::deque<typename TEventRequestType::TPtr> Waiters;
+
+ bool IsSafeToRelease() {
+ return Waiters.empty();
+ }
+ };
+
+ std::variant<NActors::TActorId, NActors::IActor*> UnderlyingActor;
+ NKikimr::TNotSoSimpleCache<TString, TCacheRecord> Cache;
+
+ NActors::TActorId GetUnderlyingActor() {
+ return std::get<NActors::TActorId>(UnderlyingActor);
+ }
+
+ static TString GetCacheKey(const TEventRequestType* request) {
+ return request->Token + request->Request.ShortDebugString();
+ }
+
+ bool IsTemporaryErrorStatus(const TCacheRecord& record) const {
+ if (!record.Response) {
+ return false;
+ }
+ return record.Response->Status.InternalError
+ || record.Response->Status.GRpcStatusCode == grpc::StatusCode::UNKNOWN
+ || record.Response->Status.GRpcStatusCode == grpc::StatusCode::DEADLINE_EXCEEDED
+ || record.Response->Status.GRpcStatusCode == grpc::StatusCode::INTERNAL
+ || record.Response->Status.GRpcStatusCode == grpc::StatusCode::UNAVAILABLE;
+ }
+
+ bool IsCacheRecordValid(const TCacheRecord& record, TInstant now = NActors::TActivationContext::Now()) const {
+ if (record.Response && record.UpdateTimestamp) {
+ TDuration lifeTime = now - record.UpdateTimestamp;
+ if (!record.Response->Status.Ok()) {
+ return !IsTemporaryErrorStatus(record) && (lifeTime < ErrorLifeTime);
+ } else {
+ return lifeTime < SuccessLifeTime;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ void SendReply(typename TEventRequestType::TPtr& ev, TCacheRecord* cacheRecord) {
+ NActors::TActorId sender = ev->Sender;
+ THolder<TEventResponseType> response = MakeHolder<TEventResponseType>();
+ if (ev->HasEvent()) {
+ response->Request = ev;
+ } else {
+ response->Request = std::move(cacheRecord->Response->Request);
+ }
+ response->Response = cacheRecord->Response->Response;
+ response->Status = cacheRecord->Response->Status;
+ TBase::Send(sender, response.Release());
+ }
+
+ void Handle(typename TEventRequestType::TPtr& ev) {
+ TString cacheKey = GetCacheKey(ev->Get());
+ TCacheRecord* cacheRecord = Cache.FindPtr(cacheKey);
+ if (cacheRecord) {
+ if (IsCacheRecordValid(*cacheRecord)) {
+ SendReply(ev, cacheRecord);
+ return;
+ }
+ } else {
+ cacheRecord = &Cache.Update(cacheKey);
+ }
+ if (cacheRecord->Waiters.empty()) {
+ THolder<TEventRequestType> request = ev->Release();
+ TBase::Send(GetUnderlyingActor(), request.Release());
+ }
+ cacheRecord->Waiters.emplace_back(ev);
+ }
+
+ void Handle(typename TEventResponseType::TPtr& ev) {
+ TEventRequestType* request = ev->Get()->Request->template Get<TEventRequestType>();
+ TString cacheKey = GetCacheKey(request);
+ TCacheRecord* cacheRecord = Cache.FindPtr(cacheKey);
+ if (cacheRecord) {
+ cacheRecord->Response = ev->Release();
+ cacheRecord->UpdateTimestamp = NActors::TActivationContext::Now();
+ for (typename TEventRequestType::TPtr& waiter : cacheRecord->Waiters) {
+ SendReply(waiter, cacheRecord);
+ }
+ cacheRecord->Waiters.clear();
+ } else {
+ LOG_ERROR_S(*NActors::TlsActivationContext, NKikimrServices::GRPC_CLIENT, "Couldn't find cache record for request");
+ }
+ }
+
+ void PassAway() {
+ TBase::Send(GetUnderlyingActor(), new TEvents::TEvPoison());
+ TBase::PassAway();
+ }
+
+public:
+ void Bootstrap() {
+ if (std::holds_alternative<NActors::IActor*>(UnderlyingActor)) {
+ // we register underlying actor with the same pool and mailbox type
+ auto& ctx = NActors::TlsActivationContext;
+ const auto& mailbox = ctx->Mailbox;
+ UnderlyingActor = ctx->Register(std::get<NActors::IActor*>(UnderlyingActor), TBase::SelfId(), static_cast<TMailboxType::EType>(mailbox.Type));
+ }
+ TBase::Become(&TThis::StateWork);
+ }
+
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::ACTOR_SERVICE_CACHE; }
+
+ TGrpcServiceCache(const std::variant<NActors::TActorId, NActors::IActor*>& underlyingActor,
+ size_t grpcCacheSize = 1024,
+ TDuration grpcCacheSuccessLifeTime = TDuration::Minutes(1),
+ TDuration grpcCacheErrorLifeTime = TDuration::Seconds(10))
+ : UnderlyingActor(underlyingActor)
+ {
+ Cache.MaxSize = grpcCacheSize;
+ SuccessLifeTime = grpcCacheSuccessLifeTime;
+ ErrorLifeTime = grpcCacheErrorLifeTime;
+ }
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext& ctx) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEventRequestType, Handle);
+ hFunc(TEventResponseType, Handle);
+ cFunc(TEvents::TSystem::Poison, PassAway);
+ default:
+ ctx.Send(ev->Forward(GetUnderlyingActor()));
+ break;
+ }
+ }
+};
+
+template <typename TEventRequestType, typename TEventResponseType>
+inline IActor* CreateGrpcServiceCache(const NActors::TActorId& underlyingActor,
+ size_t grpcCacheSize = 1024,
+ TDuration grpcCacheSuccessLifeTime = TDuration::Minutes(1),
+ TDuration grpcCacheErrorLifeTime = TDuration::Seconds(10)) {
+ return new TGrpcServiceCache<TEventRequestType, TEventResponseType>(underlyingActor, grpcCacheSize, grpcCacheSuccessLifeTime, grpcCacheErrorLifeTime);
+}
+
+template <typename TEventRequestType, typename TEventResponseType>
+inline IActor* CreateGrpcServiceCache(NActors::IActor* underlyingActor,
+ size_t grpcCacheSize = 1024,
+ TDuration grpcCacheSuccessLifeTime = TDuration::Minutes(1),
+ TDuration grpcCacheErrorLifeTime = TDuration::Seconds(10)) {
+ return new TGrpcServiceCache<TEventRequestType, TEventResponseType>(underlyingActor, grpcCacheSize, grpcCacheSuccessLifeTime, grpcCacheErrorLifeTime);
+}
+
+}
diff --git a/ydb/library/ycloud/impl/grpc_service_client.h b/ydb/library/ycloud/impl/grpc_service_client.h
new file mode 100644
index 00000000000..c89515d8654
--- /dev/null
+++ b/ydb/library/ycloud/impl/grpc_service_client.h
@@ -0,0 +1,126 @@
+#pragma once
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/log.h>
+#include <library/cpp/actors/core/actor_bootstrapped.h>
+#include <library/cpp/grpc/client/grpc_client_low.h>
+#include <library/cpp/digest/crc32c/crc32c.h>
+#include "grpc_service_settings.h"
+
+#define BLOG_GRPC_D(stream) LOG_DEBUG_S(*NActors::TlsActivationContext, NKikimrServices::GRPC_CLIENT, stream)
+#define BLOG_GRPC_DC(context, stream) LOG_DEBUG_S(context, NKikimrServices::GRPC_CLIENT, stream)
+
+inline IOutputStream& operator <<(IOutputStream& out, const NGrpc::TGrpcStatus& status) {
+ return out << status.GRpcStatusCode << " " << status.Msg;
+}
+
+template <typename TGrpcService>
+class TGrpcServiceClient {
+ using TServiceConnection = NGrpc::TServiceConnection<TGrpcService>;
+
+ NGrpc::TGRpcClientConfig Config;
+ NGrpc::TGRpcClientLow Client;
+ std::unique_ptr<TServiceConnection> Connection;
+
+ TString Prefix(const TString& requestId = {}) const {
+ if (requestId) {
+ return Sprintf("[%08lx]{%s} ", (ptrdiff_t)this, requestId.c_str());
+ } else {
+ return Sprintf("[%08lx] ", (ptrdiff_t)this);
+ }
+ }
+
+ static TString Trim(const TString& line) {
+ if (line.size() > 512) {
+ return line.substr(0, 512) + "...(truncated)";
+ }
+ return line;
+ }
+
+ template <typename TProtoMessageType>
+ static TString Trim(const TProtoMessageType& message) {
+ TStringBuilder log;
+ log << message.GetDescriptor()->name() << " { " << Trim(message.ShortDebugString()) << " }";
+ return log;
+ }
+
+public:
+ static TString MaskToken(const TString& token) {
+ TStringBuilder mask;
+ if (token.size() >= 16) {
+ mask << token.substr(0, 4);
+ mask << "****";
+ mask << token.substr(token.size() - 4, 4);
+ } else {
+ mask << "****";
+ }
+ mask << " (";
+ mask << Sprintf("%08X", Crc32c(token.data(), token.size()));
+ mask << ")";
+ return mask;
+ }
+
+ static constexpr TDuration DEFAULT_TIMEOUT = TDuration::Seconds(10);
+
+ struct TGrpcRequest {
+ static const google::protobuf::Message& Obfuscate(const google::protobuf::Message& p) {
+ return p;
+ }
+ };
+
+ template <typename TCallType>
+ void MakeCall(typename TCallType::TRequestEventType::TPtr ev) {
+ using TRequestType = decltype(typename TCallType::TRequestEventType().Request);
+ using TResponseType = decltype(typename TCallType::TResponseEventType().Response);
+ const auto& requestId = ev->Get()->RequestId;
+ if (!Connection) {
+ BLOG_GRPC_D(Prefix(requestId) << "Connect to "
+ << ((Config.EnableSsl || !Config.SslCaCert.empty()) ? "grpcs://" : "grpc://")
+ << Config.Locator);
+ Connection = Client.CreateGRpcServiceConnection<TGrpcService>(Config);
+ }
+
+ const TRequestType& request = ev->Get()->Request;
+ NGrpc::TCallMeta meta;
+ meta.Timeout = Config.Timeout;
+ if (const auto& token = ev->Get()->Token) {
+ meta.Aux.push_back({"authorization", "Bearer " + token});
+ }
+ if (requestId) {
+ meta.Aux.push_back({"x-request-id", requestId});
+ }
+
+ NGrpc::TResponseCallback<TResponseType> callback =
+ [actorSystem = NActors::TActivationContext::ActorSystem(), prefix = Prefix(requestId), request = ev](NGrpc::TGrpcStatus&& status, TResponseType&& response) -> void {
+ if (status.Ok()) {
+ BLOG_GRPC_DC(*actorSystem, prefix << "Response " << Trim(TCallType::Obfuscate(response)));
+ } else {
+ BLOG_GRPC_DC(*actorSystem, prefix << "Status " << status);
+ }
+ auto respEv = MakeHolder<typename TCallType::TResponseEventType>();
+ respEv->Request = request;
+ respEv->Status = status;
+ respEv->Response = response;
+ actorSystem->Send(respEv->Request->Sender, respEv.Release());
+ };
+
+ BLOG_GRPC_D(Prefix(requestId) << "Request " << Trim(TCallType::Obfuscate(request)));
+ Connection->DoRequest(request, std::move(callback), TCallType::Request, meta);
+ }
+
+ static NGrpc::TGRpcClientConfig InitGrpcConfig(const NCloud::TGrpcClientSettings& settings) {
+ NGrpc::TGRpcClientConfig config(settings.Endpoint, DEFAULT_TIMEOUT, DEFAULT_GRPC_MESSAGE_SIZE_LIMIT, 0, settings.CertificateRootCA);
+ config.EnableSsl = settings.EnableSsl;
+ config.IntChannelParams[GRPC_ARG_KEEPALIVE_TIME_MS] = settings.GrpcKeepAliveTimeMs;
+ config.IntChannelParams[GRPC_ARG_KEEPALIVE_TIMEOUT_MS] = settings.GrpcKeepAliveTimeoutMs;
+ config.IntChannelParams[GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS] = 1;
+ config.IntChannelParams[GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA] = 0;
+ config.IntChannelParams[GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS] = settings.GrpcKeepAlivePingInterval;
+ config.IntChannelParams[GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS] = settings.GrpcKeepAlivePingInterval;
+ return config;
+ }
+
+ TGrpcServiceClient(const NCloud::TGrpcClientSettings& settings)
+ : Config(InitGrpcConfig(settings))
+ {}
+};
+
diff --git a/ydb/library/ycloud/impl/grpc_service_settings.h b/ydb/library/ycloud/impl/grpc_service_settings.h
new file mode 100644
index 00000000000..07984aaa503
--- /dev/null
+++ b/ydb/library/ycloud/impl/grpc_service_settings.h
@@ -0,0 +1,14 @@
+#pragma once
+
+namespace NCloud {
+
+struct TGrpcClientSettings {
+ TString Endpoint;
+ TString CertificateRootCA; // root CA certificate PEM/x509
+ ui32 GrpcKeepAliveTimeMs = 10000;
+ ui32 GrpcKeepAliveTimeoutMs = 1000;
+ ui32 GrpcKeepAlivePingInterval = 5000;
+ bool EnableSsl = false;
+};
+
+}
diff --git a/ydb/library/ycloud/impl/iam_token_service.cpp b/ydb/library/ycloud/impl/iam_token_service.cpp
new file mode 100644
index 00000000000..4df7c7b0368
--- /dev/null
+++ b/ydb/library/ycloud/impl/iam_token_service.cpp
@@ -0,0 +1,53 @@
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/actor.h>
+#include <ydb/public/api/client/yc_private/iam/iam_token_service.grpc.pb.h>
+#include "iam_token_service.h"
+#include "grpc_service_client.h"
+#include "grpc_service_cache.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+class TIamTokenService : public NActors::TActor<TIamTokenService>, TGrpcServiceClient<yandex::cloud::priv::iam::v1::IamTokenService> {
+ using TThis = TIamTokenService;
+ using TBase = NActors::TActor<TIamTokenService>;
+
+ struct TCreateIamTokenForServiceAccountRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::iam::v1::IamTokenService::Stub::AsyncCreateForServiceAccount;
+ using TRequestEventType = TEvIamTokenService::TEvCreateForServiceAccountRequest;
+ using TResponseEventType = TEvIamTokenService::TEvCreateResponse;
+ };
+
+ void Handle(TEvIamTokenService::TEvCreateForServiceAccountRequest::TPtr& ev) {
+ MakeCall<TCreateIamTokenForServiceAccountRequest>(std::move(ev));
+ }
+
+public:
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::IAM_TOKEN_SERVICE_ACTOR; }
+
+ TIamTokenService(const TIamTokenServiceSettings& settings)
+ : TBase(&TThis::StateWork)
+ , TGrpcServiceClient(settings)
+ {}
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEvIamTokenService::TEvCreateForServiceAccountRequest, Handle);
+ cFunc(TEvents::TSystem::PoisonPill, PassAway);
+ }
+ }
+};
+
+
+IActor* CreateIamTokenService(const TIamTokenServiceSettings& settings) {
+ return new TIamTokenService(settings);
+}
+
+IActor* CreateIamTokenServiceWithCache(const TIamTokenServiceSettings& settings) {
+ IActor* iamTokenService = CreateIamTokenService(settings);
+ iamTokenService = CreateGrpcServiceCache<TEvIamTokenService::TEvCreateForServiceAccountRequest, TEvIamTokenService::TEvCreateResponse>(iamTokenService);
+ return iamTokenService;
+}
+
+}
diff --git a/ydb/library/ycloud/impl/iam_token_service.h b/ydb/library/ycloud/impl/iam_token_service.h
new file mode 100644
index 00000000000..758c0232167
--- /dev/null
+++ b/ydb/library/ycloud/impl/iam_token_service.h
@@ -0,0 +1,21 @@
+#pragma once
+#include <ydb/library/ycloud/api/iam_token_service.h>
+#include "grpc_service_settings.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+struct TIamTokenServiceSettings : TGrpcClientSettings {};
+
+IActor* CreateIamTokenService(const TIamTokenServiceSettings& settings);
+
+inline IActor* CreateIamTokenService(const TString& endpoint) {
+ TIamTokenServiceSettings settings;
+ settings.Endpoint = endpoint;
+ return CreateIamTokenService(settings);
+}
+
+IActor* CreateIamTokenServiceWithCache(const TIamTokenServiceSettings& settings); // for compatibility with older code
+
+}
diff --git a/ydb/library/ycloud/impl/service_account_service.cpp b/ydb/library/ycloud/impl/service_account_service.cpp
new file mode 100644
index 00000000000..1571c1bcb6e
--- /dev/null
+++ b/ydb/library/ycloud/impl/service_account_service.cpp
@@ -0,0 +1,69 @@
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/actor.h>
+#include <ydb/public/api/client/yc_private/iam/service_account_service.grpc.pb.h>
+#include "service_account_service.h"
+#include "grpc_service_client.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+class TServiceAccountService : public NActors::TActor<TServiceAccountService>, TGrpcServiceClient<yandex::cloud::priv::iam::v1::ServiceAccountService> {
+ using TThis = TServiceAccountService;
+ using TBase = NActors::TActor<TServiceAccountService>;
+
+ struct TGetServiceAccountRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::iam::v1::ServiceAccountService::Stub::AsyncGet;
+ using TRequestEventType = TEvServiceAccountService::TEvGetServiceAccountRequest;
+ using TResponseEventType = TEvServiceAccountService::TEvGetServiceAccountResponse;
+ };
+
+ void Handle(TEvServiceAccountService::TEvGetServiceAccountRequest::TPtr& ev) {
+ MakeCall<TGetServiceAccountRequest>(std::move(ev));
+ }
+
+ struct TIssueTokenRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::iam::v1::ServiceAccountService::Stub::AsyncIssueToken;
+ using TRequestEventType = TEvServiceAccountService::TEvIssueTokenRequest;
+ using TResponseEventType = TEvServiceAccountService::TEvIssueTokenResponse;
+
+ static const yandex::cloud::priv::iam::v1::IssueTokenRequest& Obfuscate(const yandex::cloud::priv::iam::v1::IssueTokenRequest& p) {
+ return p;
+ }
+
+ static yandex::cloud::priv::iam::v1::IamToken Obfuscate(const yandex::cloud::priv::iam::v1::IamToken& p) {
+ yandex::cloud::priv::iam::v1::IamToken r(p);
+ if (r.iam_token()) {
+ r.set_iam_token(MaskToken(r.iam_token()));
+ }
+ return r;
+ }
+ };
+
+ void Handle(TEvServiceAccountService::TEvIssueTokenRequest::TPtr& ev) {
+ MakeCall<TIssueTokenRequest>(std::move(ev));
+ }
+
+public:
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::SERVICE_ACCOUNT_SERVICE_ACTOR; }
+
+ TServiceAccountService(const TServiceAccountServiceSettings& settings)
+ : TBase(&TThis::StateWork)
+ , TGrpcServiceClient(settings)
+ {}
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEvServiceAccountService::TEvGetServiceAccountRequest, Handle);
+ hFunc(TEvServiceAccountService::TEvIssueTokenRequest, Handle);
+ cFunc(TEvents::TSystem::PoisonPill, PassAway);
+ }
+ }
+};
+
+
+IActor* CreateServiceAccountService(const TServiceAccountServiceSettings& settings) {
+ return new TServiceAccountService(settings);
+}
+
+}
diff --git a/ydb/library/ycloud/impl/service_account_service.h b/ydb/library/ycloud/impl/service_account_service.h
new file mode 100644
index 00000000000..14ecb5e34d1
--- /dev/null
+++ b/ydb/library/ycloud/impl/service_account_service.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <ydb/library/ycloud/api/service_account_service.h>
+#include "grpc_service_client.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+struct TServiceAccountServiceSettings : TGrpcClientSettings {};
+
+IActor* CreateServiceAccountService(const TServiceAccountServiceSettings& settings);
+
+inline IActor* CreateServiceAccountService(const TString& endpoint) {
+ TServiceAccountServiceSettings settings;
+ settings.Endpoint = endpoint;
+ return CreateServiceAccountService(settings);
+}
+
+}
diff --git a/ydb/library/ycloud/impl/service_account_service_ut.cpp b/ydb/library/ycloud/impl/service_account_service_ut.cpp
new file mode 100644
index 00000000000..55199b81e2a
--- /dev/null
+++ b/ydb/library/ycloud/impl/service_account_service_ut.cpp
@@ -0,0 +1,105 @@
+#include <library/cpp/actors/core/event.h>
+#include <library/cpp/actors/core/event_local.h>
+#include <ydb/core/testlib/test_client.h>
+#include <ydb/core/testlib/test_client.h>
+#include <ydb/library/testlib/service_mocks/service_account_service_mock.h>
+#include <ydb/core/grpc_services/grpc_helper.h>
+#include <library/cpp/grpc/server/grpc_server.h>
+#include <ydb/public/lib/deprecated/kicli/kicli.h>
+#include <library/cpp/testing/unittest/registar.h>
+#include <library/cpp/testing/unittest/tests_data.h>
+#include <util/string/builder.h>
+#include "service_account_service.h"
+
+using namespace NKikimr;
+using namespace Tests;
+
+namespace {
+ static const TString IAM_TOKEN = "a-b-c";
+}
+
+class TServiceAccountServiceFixture : public NUnitTest::TBaseFixture {
+public:
+ TServiceAccountServiceFixture() {
+ GrpcPort = PortManager.GetPort(2134);
+
+ NKikimrProto::TAuthConfig authConfig;
+ auto settings = TServerSettings(GrpcPort, authConfig);
+ settings.SetDomainName("Root");
+ Server = MakeHolder<TServer>(settings);
+ Client = MakeHolder<TClient>(settings);
+ Client->InitRootScheme();
+
+ Runtime = Server->GetRuntime();
+ SenderActorId = Runtime->AllocateEdgeActor();
+ TAutoPtr<IEventHandle> handle;
+
+ // Service Account Service
+ ui16 servicePort = PortManager.GetPort(8443);
+ ServiceAccountService = NCloud::CreateServiceAccountService("localhost:" + ToString(servicePort));
+ Runtime->Register(ServiceAccountService);
+
+ // Service Account Service Mock
+ grpc::ServerBuilder builder;
+ ServiceAccountServiceMock.ServiceAccountData["Service1"].set_id("Service1");
+ ServiceAccountServiceMock.IamTokens["Service1"].set_iam_token(IAM_TOKEN);
+ builder.AddListeningPort("[::]:" + ToString(servicePort), grpc::InsecureServerCredentials()).RegisterService(&ServiceAccountServiceMock);
+ ServiceAccountServer = builder.BuildAndStart();
+ }
+
+public:
+ ui16 GrpcPort;
+ TPortManager PortManager;
+ THolder<TServer> Server;
+ THolder<TClient> Client;
+ TTestActorRuntime* Runtime;
+ TServiceAccountServiceMock ServiceAccountServiceMock;
+ IActor* ServiceAccountService;
+ std::unique_ptr<grpc::Server> ServiceAccountServer;
+ TActorId SenderActorId;
+};
+
+namespace {
+ template<class TEvRequest>
+ TEvRequest* CreateRequest(const TString& accountId) {
+ auto* result = new TEvRequest();
+ result->Request.set_service_account_id(accountId);
+ return result;
+ }
+}
+
+Y_UNIT_TEST_SUITE_F(TServiceAccountServiceTest, TServiceAccountServiceFixture) {
+ Y_UNIT_TEST(Get) {
+ TAutoPtr<IEventHandle> handle;
+ // check for not found
+ Runtime->Send(new IEventHandle(ServiceAccountService->SelfId(), SenderActorId, CreateRequest<NCloud::TEvServiceAccountService::TEvGetServiceAccountRequest>("bad1")));
+ auto result = Runtime->GrabEdgeEvent<NCloud::TEvServiceAccountService::TEvGetServiceAccountResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT_EQUAL(result->Status.Msg, "Not Found");
+
+ // check for found
+ Runtime->Send(new IEventHandle(ServiceAccountService->SelfId(), SenderActorId, CreateRequest<NCloud::TEvServiceAccountService::TEvGetServiceAccountRequest>("Service1")));
+ result = Runtime->GrabEdgeEvent<NCloud::TEvServiceAccountService::TEvGetServiceAccountResponse>(handle);
+
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Ok());
+ UNIT_ASSERT_EQUAL(result->Response.id(), "Service1");
+ }
+
+ Y_UNIT_TEST(IssueToken) {
+ TAutoPtr<IEventHandle> handle;
+ // check for not found
+ Runtime->Send(new IEventHandle(ServiceAccountService->SelfId(), SenderActorId, CreateRequest<NCloud::TEvServiceAccountService::TEvIssueTokenRequest>("bad1")));
+ auto result = Runtime->GrabEdgeEvent<NCloud::TEvServiceAccountService::TEvIssueTokenResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT_EQUAL(result->Status.GRpcStatusCode, grpc::StatusCode::UNAUTHENTICATED);
+
+ // check for found
+ Runtime->Send(new IEventHandle(ServiceAccountService->SelfId(), SenderActorId, CreateRequest<NCloud::TEvServiceAccountService::TEvIssueTokenRequest>("Service1")));
+ result = Runtime->GrabEdgeEvent<NCloud::TEvServiceAccountService::TEvIssueTokenResponse>(handle);
+
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Ok());
+ UNIT_ASSERT_EQUAL(result->Response.iam_token(), IAM_TOKEN);
+ }
+}
diff --git a/ydb/library/ycloud/impl/user_account_service.cpp b/ydb/library/ycloud/impl/user_account_service.cpp
new file mode 100644
index 00000000000..7bddab4a6b9
--- /dev/null
+++ b/ydb/library/ycloud/impl/user_account_service.cpp
@@ -0,0 +1,46 @@
+#include <library/cpp/actors/core/actorsystem.h>
+#include <library/cpp/actors/core/actor.h>
+#include <ydb/public/api/client/yc_private/iam/user_account_service.grpc.pb.h>
+#include "user_account_service.h"
+#include "grpc_service_client.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+class TUserAccountService : public NActors::TActor<TUserAccountService>, TGrpcServiceClient<yandex::cloud::priv::iam::v1::UserAccountService> {
+ using TThis = TUserAccountService;
+ using TBase = NActors::TActor<TUserAccountService>;
+
+ struct TGetUserAccountRequest : TGrpcRequest {
+ static constexpr auto Request = &yandex::cloud::priv::iam::v1::UserAccountService::Stub::AsyncGet;
+ using TRequestEventType = TEvUserAccountService::TEvGetUserAccountRequest;
+ using TResponseEventType = TEvUserAccountService::TEvGetUserAccountResponse;
+ };
+
+ void Handle(TEvUserAccountService::TEvGetUserAccountRequest::TPtr& ev) {
+ MakeCall<TGetUserAccountRequest>(std::move(ev));
+ }
+
+public:
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::USER_ACCOUNT_SERVICE_ACTOR; }
+
+ TUserAccountService(const TUserAccountServiceSettings& settings)
+ : TBase(&TThis::StateWork)
+ , TGrpcServiceClient(settings)
+ {}
+
+ void StateWork(TAutoPtr<NActors::IEventHandle>& ev, const NActors::TActorContext&) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEvUserAccountService::TEvGetUserAccountRequest, Handle);
+ cFunc(TEvents::TSystem::PoisonPill, PassAway);
+ }
+ }
+};
+
+
+IActor* CreateUserAccountService(const TUserAccountServiceSettings& settings) {
+ return new TUserAccountService(settings);
+}
+
+}
diff --git a/ydb/library/ycloud/impl/user_account_service.h b/ydb/library/ycloud/impl/user_account_service.h
new file mode 100644
index 00000000000..9e271bc451a
--- /dev/null
+++ b/ydb/library/ycloud/impl/user_account_service.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <ydb/library/ycloud/api/user_account_service.h>
+#include "grpc_service_client.h"
+
+namespace NCloud {
+
+using namespace NKikimr;
+
+struct TUserAccountServiceSettings : TGrpcClientSettings {};
+
+IActor* CreateUserAccountService(const TUserAccountServiceSettings& settings);
+
+inline IActor* CreateUserAccountService(const TString& endpoint) {
+ TUserAccountServiceSettings settings;
+ settings.Endpoint = endpoint;
+ return CreateUserAccountService(settings);
+}
+
+}
diff --git a/ydb/library/ycloud/impl/user_account_service_ut.cpp b/ydb/library/ycloud/impl/user_account_service_ut.cpp
new file mode 100644
index 00000000000..0903b1bb000
--- /dev/null
+++ b/ydb/library/ycloud/impl/user_account_service_ut.cpp
@@ -0,0 +1,62 @@
+#include <library/cpp/actors/core/event.h>
+#include <library/cpp/actors/core/event_local.h>
+#include <ydb/core/testlib/test_client.h>
+#include <ydb/library/testlib/service_mocks/user_account_service_mock.h>
+#include <ydb/core/grpc_services/grpc_helper.h>
+#include <library/cpp/grpc/server/grpc_server.h>
+#include <ydb/public/lib/deprecated/kicli/kicli.h>
+#include <library/cpp/testing/unittest/registar.h>
+#include <library/cpp/testing/unittest/tests_data.h>
+#include <util/string/builder.h>
+#include "user_account_service.h"
+
+Y_UNIT_TEST_SUITE(TUserAccountServiceTest) {
+ Y_UNIT_TEST(Get) {
+ using namespace NKikimr;
+ using namespace Tests;
+
+ TPortManager tp;
+ // Kikimr
+ ui16 kikimrPort = tp.GetPort(2134);
+ NKikimrProto::TAuthConfig authConfig;
+ auto settings = TServerSettings(kikimrPort, authConfig);
+ settings.SetDomainName("Root");
+ TServer server(settings);
+ TClient client(settings);
+ NClient::TKikimr kikimr(client.GetClientConfig());
+ client.InitRootScheme();
+
+ TTestActorRuntime* runtime = server.GetRuntime();
+ TActorId sender = runtime->AllocateEdgeActor();
+ TAutoPtr<IEventHandle> handle;
+
+ // User Account Service
+ ui16 servicePort = tp.GetPort(8443);
+ IActor* userAccountService = NCloud::CreateUserAccountService("localhost:" + ToString(servicePort));
+ runtime->Register(userAccountService);
+
+ // User Account Service Mock
+ TUserAccountServiceMock userAccountServiceMock;
+ grpc::ServerBuilder builder;
+ userAccountServiceMock.UserAccountData["user1"].set_id("user1");
+ builder.AddListeningPort("[::]:" + ToString(servicePort), grpc::InsecureServerCredentials()).RegisterService(&userAccountServiceMock);
+ std::unique_ptr<grpc::Server> userAccountServer(builder.BuildAndStart());
+
+ // check for not found
+ auto request = MakeHolder<NCloud::TEvUserAccountService::TEvGetUserAccountRequest>();
+ request->Request.set_user_account_id("bad1");
+ runtime->Send(new IEventHandle(userAccountService->SelfId(), sender, request.Release()));
+ auto result = runtime->GrabEdgeEvent<NCloud::TEvUserAccountService::TEvGetUserAccountResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT_EQUAL(result->Status.Msg, "Not Found");
+
+ // check for found
+ request = MakeHolder<NCloud::TEvUserAccountService::TEvGetUserAccountRequest>();
+ request->Request.set_user_account_id("user1");
+ runtime->Send(new IEventHandle(userAccountService->SelfId(), sender, request.Release()));
+ result = runtime->GrabEdgeEvent<NCloud::TEvUserAccountService::TEvGetUserAccountResponse>(handle);
+ UNIT_ASSERT(result);
+ UNIT_ASSERT(result->Status.Ok());
+ UNIT_ASSERT_EQUAL(result->Response.id(), "user1");
+ }
+}
diff --git a/ydb/library/ycloud/impl/ut/CMakeLists.darwin.txt b/ydb/library/ycloud/impl/ut/CMakeLists.darwin.txt
new file mode 100644
index 00000000000..4bab590bae3
--- /dev/null
+++ b/ydb/library/ycloud/impl/ut/CMakeLists.darwin.txt
@@ -0,0 +1,51 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_executable(ydb-library-ycloud-impl-ut)
+target_compile_options(ydb-library-ycloud-impl-ut PRIVATE
+ -DUSE_CURRENT_UDF_ABI_VERSION
+)
+target_include_directories(ydb-library-ycloud-impl-ut PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl
+)
+target_link_libraries(ydb-library-ycloud-impl-ut PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ library-cpp-cpuid_check
+ cpp-testing-unittest_main
+ library-ycloud-impl
+ library-cpp-retry
+ core-testlib-default
+)
+target_link_options(ydb-library-ycloud-impl-ut PRIVATE
+ -Wl,-no_deduplicate
+ -Wl,-sdk_version,10.15
+ -fPIC
+ -fPIC
+ -framework
+ CoreFoundation
+)
+target_sources(ydb-library-ycloud-impl-ut PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/access_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/folder_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/service_account_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/user_account_service_ut.cpp
+)
+add_test(
+ NAME
+ ydb-library-ycloud-impl-ut
+ COMMAND
+ ydb-library-ycloud-impl-ut
+ --print-before-suite
+ --print-before-test
+ --fork-tests
+ --print-times
+ --show-fails
+)
+vcs_info(ydb-library-ycloud-impl-ut)
diff --git a/ydb/library/ycloud/impl/ut/CMakeLists.linux-aarch64.txt b/ydb/library/ycloud/impl/ut/CMakeLists.linux-aarch64.txt
new file mode 100644
index 00000000000..3aa803b7870
--- /dev/null
+++ b/ydb/library/ycloud/impl/ut/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,53 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_executable(ydb-library-ycloud-impl-ut)
+target_compile_options(ydb-library-ycloud-impl-ut PRIVATE
+ -DUSE_CURRENT_UDF_ABI_VERSION
+)
+target_include_directories(ydb-library-ycloud-impl-ut PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl
+)
+target_link_libraries(ydb-library-ycloud-impl-ut PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ library-cpp-lfalloc
+ cpp-testing-unittest_main
+ library-ycloud-impl
+ library-cpp-retry
+ core-testlib-default
+)
+target_link_options(ydb-library-ycloud-impl-ut PRIVATE
+ -ldl
+ -lrt
+ -Wl,--no-as-needed
+ -fPIC
+ -fPIC
+ -lpthread
+ -lrt
+ -ldl
+)
+target_sources(ydb-library-ycloud-impl-ut PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/access_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/folder_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/service_account_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/user_account_service_ut.cpp
+)
+add_test(
+ NAME
+ ydb-library-ycloud-impl-ut
+ COMMAND
+ ydb-library-ycloud-impl-ut
+ --print-before-suite
+ --print-before-test
+ --fork-tests
+ --print-times
+ --show-fails
+)
+vcs_info(ydb-library-ycloud-impl-ut)
diff --git a/ydb/library/ycloud/impl/ut/CMakeLists.linux.txt b/ydb/library/ycloud/impl/ut/CMakeLists.linux.txt
new file mode 100644
index 00000000000..0db7d5d9368
--- /dev/null
+++ b/ydb/library/ycloud/impl/ut/CMakeLists.linux.txt
@@ -0,0 +1,55 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_executable(ydb-library-ycloud-impl-ut)
+target_compile_options(ydb-library-ycloud-impl-ut PRIVATE
+ -DUSE_CURRENT_UDF_ABI_VERSION
+)
+target_include_directories(ydb-library-ycloud-impl-ut PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl
+)
+target_link_libraries(ydb-library-ycloud-impl-ut PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ cpp-malloc-tcmalloc
+ libs-tcmalloc-no_percpu_cache
+ library-cpp-cpuid_check
+ cpp-testing-unittest_main
+ library-ycloud-impl
+ library-cpp-retry
+ core-testlib-default
+)
+target_link_options(ydb-library-ycloud-impl-ut PRIVATE
+ -ldl
+ -lrt
+ -Wl,--no-as-needed
+ -fPIC
+ -fPIC
+ -lpthread
+ -lrt
+ -ldl
+)
+target_sources(ydb-library-ycloud-impl-ut PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/access_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/folder_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/service_account_service_ut.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/ycloud/impl/user_account_service_ut.cpp
+)
+add_test(
+ NAME
+ ydb-library-ycloud-impl-ut
+ COMMAND
+ ydb-library-ycloud-impl-ut
+ --print-before-suite
+ --print-before-test
+ --fork-tests
+ --print-times
+ --show-fails
+)
+vcs_info(ydb-library-ycloud-impl-ut)
diff --git a/ydb/library/ycloud/impl/ut/CMakeLists.txt b/ydb/library/ycloud/impl/ut/CMakeLists.txt
new file mode 100644
index 00000000000..3e0811fb22e
--- /dev/null
+++ b/ydb/library/ycloud/impl/ut/CMakeLists.txt
@@ -0,0 +1,15 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND UNIX AND NOT APPLE AND NOT ANDROID)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (APPLE)
+ include(CMakeLists.darwin.txt)
+elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND UNIX AND NOT APPLE AND NOT ANDROID)
+ include(CMakeLists.linux.txt)
+endif()