From f776dd38989d3675fc8214ba8299f3f1c93c83cc Mon Sep 17 00:00:00 2001 From: Orne Brocaar Date: Tue, 14 Feb 2023 13:36:21 +0000 Subject: [PATCH] Split private gateways under tenant in uplink / downlink. This makes it possible to share uplink data with other tenants, but do not allow other tenants to use these gateways for downlinks. --- api/csharp/Chirpstack/api/Tenant.cs | 301 +++++++---- api/go/api/tenant.pb.go | 469 ++++++++++-------- api/grpc-web/api/tenant_pb.d.ts | 20 +- api/grpc-web/api/tenant_pb.js | 84 +++- api/js/api/tenant_pb.d.ts | 20 +- api/js/api/tenant_pb.js | 84 +++- api/md/api/api.md | 6 +- api/proto/api/tenant.proto | 20 +- api/proto/internal/internal.proto | 9 + .../proto/chirpstack-api/api/tenant.proto | 20 +- .../chirpstack-api/internal/internal.proto | 9 + .../src/chirpstack_api/api/tenant_pb2.py | 80 +-- .../src/chirpstack_api/api/tenant_pb2.pyi | 20 +- api/rust/proto/chirpstack/api/tenant.proto | 20 +- .../proto/chirpstack/internal/internal.proto | 9 + .../down.sql | 5 + .../up.sql | 8 + chirpstack/src/api/backend/mod.rs | 9 +- chirpstack/src/api/tenant.rs | 12 +- chirpstack/src/downlink/data.rs | 1 + chirpstack/src/downlink/helpers.rs | 154 +++++- chirpstack/src/downlink/join.rs | 65 ++- chirpstack/src/downlink/roaming.rs | 39 +- chirpstack/src/maccommand/dev_status.rs | 3 +- chirpstack/src/maccommand/device_time.rs | 3 +- chirpstack/src/maccommand/link_adr.rs | 3 +- chirpstack/src/maccommand/link_check.rs | 3 +- chirpstack/src/maccommand/mod.rs | 3 +- chirpstack/src/storage/device.rs | 3 +- chirpstack/src/storage/gateway.rs | 6 +- chirpstack/src/storage/schema.rs | 3 +- chirpstack/src/storage/tenant.rs | 12 +- chirpstack/src/test/class_a_test.rs | 4 +- chirpstack/src/test/otaa_test.rs | 4 +- chirpstack/src/uplink/data.rs | 39 +- chirpstack/src/uplink/join.rs | 1 + chirpstack/src/uplink/mod.rs | 22 +- ui/src/views/tenants/ListTenants.tsx | 21 +- ui/src/views/tenants/TenantForm.tsx | 23 +- 39 files changed, 1090 insertions(+), 527 deletions(-) create mode 100644 chirpstack/migrations/2023-02-13-103316_update_private_gateways/down.sql create mode 100644 chirpstack/migrations/2023-02-13-103316_update_private_gateways/up.sql diff --git a/api/csharp/Chirpstack/api/Tenant.cs b/api/csharp/Chirpstack/api/Tenant.cs index 4fb7df71..05741e33 100644 --- a/api/csharp/Chirpstack/api/Tenant.cs +++ b/api/csharp/Chirpstack/api/Tenant.cs @@ -26,80 +26,81 @@ namespace Chirpstack.Api { string.Concat( "ChBhcGkvdGVuYW50LnByb3RvEgNhcGkaHGdvb2dsZS9hcGkvYW5ub3RhdGlv", "bnMucHJvdG8aH2dvb2dsZS9wcm90b2J1Zi90aW1lc3RhbXAucHJvdG8aG2dv", - "b2dsZS9wcm90b2J1Zi9lbXB0eS5wcm90byKhAQoGVGVuYW50EgoKAmlkGAEg", + "b2dsZS9wcm90b2J1Zi9lbXB0eS5wcm90byLDAQoGVGVuYW50EgoKAmlkGAEg", "ASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSGQoRY2Fu", "X2hhdmVfZ2F0ZXdheXMYBCABKAgSGQoRbWF4X2dhdGV3YXlfY291bnQYBSAB", - "KA0SGAoQbWF4X2RldmljZV9jb3VudBgGIAEoDRIYChBwcml2YXRlX2dhdGV3", - "YXlzGAcgASgIIvQBCg5UZW5hbnRMaXN0SXRlbRIKCgJpZBgBIAEoCRIuCgpj", - "cmVhdGVkX2F0GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIu", - "Cgp1cGRhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFt", - "cBIMCgRuYW1lGAQgASgJEhkKEWNhbl9oYXZlX2dhdGV3YXlzGAUgASgIEhgK", - "EHByaXZhdGVfZ2F0ZXdheXMYBiABKAgSGQoRbWF4X2dhdGV3YXlfY291bnQY", - "ByABKA0SGAoQbWF4X2RldmljZV9jb3VudBgIIAEoDSIyChNDcmVhdGVUZW5h", - "bnRSZXF1ZXN0EhsKBnRlbmFudBgBIAEoCzILLmFwaS5UZW5hbnQiIgoUQ3Jl", - "YXRlVGVuYW50UmVzcG9uc2USCgoCaWQYASABKAkiHgoQR2V0VGVuYW50UmVx", - "dWVzdBIKCgJpZBgBIAEoCSKQAQoRR2V0VGVuYW50UmVzcG9uc2USGwoGdGVu", - "YW50GAEgASgLMgsuYXBpLlRlbmFudBIuCgpjcmVhdGVkX2F0GAIgASgLMhou", - "Z29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAMgASgL", - "MhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCIyChNVcGRhdGVUZW5hbnRS", - "ZXF1ZXN0EhsKBnRlbmFudBgBIAEoCzILLmFwaS5UZW5hbnQiIQoTRGVsZXRl", - "VGVuYW50UmVxdWVzdBIKCgJpZBgBIAEoCSJUChJMaXN0VGVuYW50c1JlcXVl", - "c3QSDQoFbGltaXQYASABKA0SDgoGb2Zmc2V0GAIgASgNEg4KBnNlYXJjaBgD", - "IAEoCRIPCgd1c2VyX2lkGAQgASgJIk8KE0xpc3RUZW5hbnRzUmVzcG9uc2US", - "EwoLdG90YWxfY291bnQYASABKA0SIwoGcmVzdWx0GAIgAygLMhMuYXBpLlRl", - "bmFudExpc3RJdGVtIoQBCgpUZW5hbnRVc2VyEhEKCXRlbmFudF9pZBgBIAEo", - "CRIPCgd1c2VyX2lkGAIgASgJEhAKCGlzX2FkbWluGAMgASgIEhcKD2lzX2Rl", - "dmljZV9hZG1pbhgEIAEoCBIYChBpc19nYXRld2F5X2FkbWluGAUgASgIEg0K", - "BWVtYWlsGAYgASgJIuwBChJUZW5hbnRVc2VyTGlzdEl0ZW0SEQoJdGVuYW50", - "X2lkGAEgASgJEg8KB3VzZXJfaWQYAiABKAkSLgoKY3JlYXRlZF9hdBgDIAEo", - "CzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgE", - "IAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDQoFZW1haWwYBSAB", - "KAkSEAoIaXNfYWRtaW4YBiABKAgSFwoPaXNfZGV2aWNlX2FkbWluGAcgASgI", - "EhgKEGlzX2dhdGV3YXlfYWRtaW4YCCABKAgiPAoUQWRkVGVuYW50VXNlclJl", - "cXVlc3QSJAoLdGVuYW50X3VzZXIYASABKAsyDy5hcGkuVGVuYW50VXNlciI6", - "ChRHZXRUZW5hbnRVc2VyUmVxdWVzdBIRCgl0ZW5hbnRfaWQYASABKAkSDwoH", - "dXNlcl9pZBgCIAEoCSKdAQoVR2V0VGVuYW50VXNlclJlc3BvbnNlEiQKC3Rl", - "bmFudF91c2VyGAEgASgLMg8uYXBpLlRlbmFudFVzZXISLgoKY3JlYXRlZF9h", - "dBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRl", - "ZF9hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiPwoXVXBk", - "YXRlVGVuYW50VXNlclJlcXVlc3QSJAoLdGVuYW50X3VzZXIYASABKAsyDy5h", - "cGkuVGVuYW50VXNlciI9ChdEZWxldGVUZW5hbnRVc2VyUmVxdWVzdBIRCgl0", - "ZW5hbnRfaWQYASABKAkSDwoHdXNlcl9pZBgCIAEoCSJKChZMaXN0VGVuYW50", - "VXNlcnNSZXF1ZXN0EhEKCXRlbmFudF9pZBgBIAEoCRINCgVsaW1pdBgCIAEo", - "DRIOCgZvZmZzZXQYAyABKA0iVwoXTGlzdFRlbmFudFVzZXJzUmVzcG9uc2US", - "EwoLdG90YWxfY291bnQYASABKA0SJwoGcmVzdWx0GAIgAygLMhcuYXBpLlRl", - "bmFudFVzZXJMaXN0SXRlbTKiCAoNVGVuYW50U2VydmljZRJWCgZDcmVhdGUS", - "GC5hcGkuQ3JlYXRlVGVuYW50UmVxdWVzdBoZLmFwaS5DcmVhdGVUZW5hbnRS", - "ZXNwb25zZSIXgtPkkwIRIgwvYXBpL3RlbmFudHM6ASoSTwoDR2V0EhUuYXBp", - "LkdldFRlbmFudFJlcXVlc3QaFi5hcGkuR2V0VGVuYW50UmVzcG9uc2UiGYLT", - "5JMCExIRL2FwaS90ZW5hbnRzL3tpZH0SXwoGVXBkYXRlEhguYXBpLlVwZGF0", - "ZVRlbmFudFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiI4LT5JMC", - "HRoYL2FwaS90ZW5hbnRzL3t0ZW5hbnQuaWR9OgEqElUKBkRlbGV0ZRIYLmFw", - "aS5EZWxldGVUZW5hbnRSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5", - "IhmC0+STAhMqES9hcGkvdGVuYW50cy97aWR9Ek8KBExpc3QSFy5hcGkuTGlz", - "dFRlbmFudHNSZXF1ZXN0GhguYXBpLkxpc3RUZW5hbnRzUmVzcG9uc2UiFILT", - "5JMCDhIML2FwaS90ZW5hbnRzEnMKB0FkZFVzZXISGS5hcGkuQWRkVGVuYW50", - "VXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiNYLT5JMCLyIq", - "L2FwaS90ZW5hbnRzL3t0ZW5hbnRfdXNlci50ZW5hbnRfaWR9L3VzZXJzOgEq", - "EnIKB0dldFVzZXISGS5hcGkuR2V0VGVuYW50VXNlclJlcXVlc3QaGi5hcGku", - "R2V0VGVuYW50VXNlclJlc3BvbnNlIjCC0+STAioSKC9hcGkvdGVuYW50cy97", - "dGVuYW50X2lkfS91c2Vycy97dXNlcl9pZH0SjwEKClVwZGF0ZVVzZXISHC5h", - "cGkuVXBkYXRlVGVuYW50VXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYu", - "RW1wdHkiS4LT5JMCRRpAL2FwaS90ZW5hbnRzL3t0ZW5hbnRfdXNlci50ZW5h", - "bnRfaWR9L3VzZXJzL3t0ZW5hbnRfdXNlci51c2VyX2lkfToBKhJ0CgpEZWxl", - "dGVVc2VyEhwuYXBpLkRlbGV0ZVRlbmFudFVzZXJSZXF1ZXN0GhYuZ29vZ2xl", - "LnByb3RvYnVmLkVtcHR5IjCC0+STAioqKC9hcGkvdGVuYW50cy97dGVuYW50", - "X2lkfS91c2Vycy97dXNlcl9pZH0SbgoJTGlzdFVzZXJzEhsuYXBpLkxpc3RU", - "ZW5hbnRVc2Vyc1JlcXVlc3QaHC5hcGkuTGlzdFRlbmFudFVzZXJzUmVzcG9u", - "c2UiJoLT5JMCIBIeL2FwaS90ZW5hbnRzL3t0ZW5hbnRfaWR9L3VzZXJzQmMK", - "EWlvLmNoaXJwc3RhY2suYXBpQgtUZW5hbnRQcm90b1ABWi5naXRodWIuY29t", - "L2NoaXJwc3RhY2svY2hpcnBzdGFjay9hcGkvZ28vdjQvYXBpqgIOQ2hpcnBz", - "dGFjay5BcGliBnByb3RvMw==")); + "KA0SGAoQbWF4X2RldmljZV9jb3VudBgGIAEoDRIbChNwcml2YXRlX2dhdGV3", + "YXlzX3VwGAcgASgIEh0KFXByaXZhdGVfZ2F0ZXdheXNfZG93bhgIIAEoCCKW", + "AgoOVGVuYW50TGlzdEl0ZW0SCgoCaWQYASABKAkSLgoKY3JlYXRlZF9hdBgC", + "IAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9h", + "dBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDAoEbmFtZRgE", + "IAEoCRIZChFjYW5faGF2ZV9nYXRld2F5cxgFIAEoCBIbChNwcml2YXRlX2dh", + "dGV3YXlzX3VwGAYgASgIEh0KFXByaXZhdGVfZ2F0ZXdheXNfZG93bhgJIAEo", + "CBIZChFtYXhfZ2F0ZXdheV9jb3VudBgHIAEoDRIYChBtYXhfZGV2aWNlX2Nv", + "dW50GAggASgNIjIKE0NyZWF0ZVRlbmFudFJlcXVlc3QSGwoGdGVuYW50GAEg", + "ASgLMgsuYXBpLlRlbmFudCIiChRDcmVhdGVUZW5hbnRSZXNwb25zZRIKCgJp", + "ZBgBIAEoCSIeChBHZXRUZW5hbnRSZXF1ZXN0EgoKAmlkGAEgASgJIpABChFH", + "ZXRUZW5hbnRSZXNwb25zZRIbCgZ0ZW5hbnQYASABKAsyCy5hcGkuVGVuYW50", + "Ei4KCmNyZWF0ZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0", + "YW1wEi4KCnVwZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGlt", + "ZXN0YW1wIjIKE1VwZGF0ZVRlbmFudFJlcXVlc3QSGwoGdGVuYW50GAEgASgL", + "MgsuYXBpLlRlbmFudCIhChNEZWxldGVUZW5hbnRSZXF1ZXN0EgoKAmlkGAEg", + "ASgJIlQKEkxpc3RUZW5hbnRzUmVxdWVzdBINCgVsaW1pdBgBIAEoDRIOCgZv", + "ZmZzZXQYAiABKA0SDgoGc2VhcmNoGAMgASgJEg8KB3VzZXJfaWQYBCABKAki", + "TwoTTGlzdFRlbmFudHNSZXNwb25zZRITCgt0b3RhbF9jb3VudBgBIAEoDRIj", + "CgZyZXN1bHQYAiADKAsyEy5hcGkuVGVuYW50TGlzdEl0ZW0ihAEKClRlbmFu", + "dFVzZXISEQoJdGVuYW50X2lkGAEgASgJEg8KB3VzZXJfaWQYAiABKAkSEAoI", + "aXNfYWRtaW4YAyABKAgSFwoPaXNfZGV2aWNlX2FkbWluGAQgASgIEhgKEGlz", + "X2dhdGV3YXlfYWRtaW4YBSABKAgSDQoFZW1haWwYBiABKAki7AEKElRlbmFu", + "dFVzZXJMaXN0SXRlbRIRCgl0ZW5hbnRfaWQYASABKAkSDwoHdXNlcl9pZBgC", + "IAEoCRIuCgpjcmVhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRp", + "bWVzdGFtcBIuCgp1cGRhdGVkX2F0GAQgASgLMhouZ29vZ2xlLnByb3RvYnVm", + "LlRpbWVzdGFtcBINCgVlbWFpbBgFIAEoCRIQCghpc19hZG1pbhgGIAEoCBIX", + "Cg9pc19kZXZpY2VfYWRtaW4YByABKAgSGAoQaXNfZ2F0ZXdheV9hZG1pbhgI", + "IAEoCCI8ChRBZGRUZW5hbnRVc2VyUmVxdWVzdBIkCgt0ZW5hbnRfdXNlchgB", + "IAEoCzIPLmFwaS5UZW5hbnRVc2VyIjoKFEdldFRlbmFudFVzZXJSZXF1ZXN0", + "EhEKCXRlbmFudF9pZBgBIAEoCRIPCgd1c2VyX2lkGAIgASgJIp0BChVHZXRU", + "ZW5hbnRVc2VyUmVzcG9uc2USJAoLdGVuYW50X3VzZXIYASABKAsyDy5hcGku", + "VGVuYW50VXNlchIuCgpjcmVhdGVkX2F0GAIgASgLMhouZ29vZ2xlLnByb3Rv", + "YnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnBy", + "b3RvYnVmLlRpbWVzdGFtcCI/ChdVcGRhdGVUZW5hbnRVc2VyUmVxdWVzdBIk", + "Cgt0ZW5hbnRfdXNlchgBIAEoCzIPLmFwaS5UZW5hbnRVc2VyIj0KF0RlbGV0", + "ZVRlbmFudFVzZXJSZXF1ZXN0EhEKCXRlbmFudF9pZBgBIAEoCRIPCgd1c2Vy", + "X2lkGAIgASgJIkoKFkxpc3RUZW5hbnRVc2Vyc1JlcXVlc3QSEQoJdGVuYW50", + "X2lkGAEgASgJEg0KBWxpbWl0GAIgASgNEg4KBm9mZnNldBgDIAEoDSJXChdM", + "aXN0VGVuYW50VXNlcnNSZXNwb25zZRITCgt0b3RhbF9jb3VudBgBIAEoDRIn", + "CgZyZXN1bHQYAiADKAsyFy5hcGkuVGVuYW50VXNlckxpc3RJdGVtMqIICg1U", + "ZW5hbnRTZXJ2aWNlElYKBkNyZWF0ZRIYLmFwaS5DcmVhdGVUZW5hbnRSZXF1", + "ZXN0GhkuYXBpLkNyZWF0ZVRlbmFudFJlc3BvbnNlIheC0+STAhEiDC9hcGkv", + "dGVuYW50czoBKhJPCgNHZXQSFS5hcGkuR2V0VGVuYW50UmVxdWVzdBoWLmFw", + "aS5HZXRUZW5hbnRSZXNwb25zZSIZgtPkkwITEhEvYXBpL3RlbmFudHMve2lk", + "fRJfCgZVcGRhdGUSGC5hcGkuVXBkYXRlVGVuYW50UmVxdWVzdBoWLmdvb2ds", + "ZS5wcm90b2J1Zi5FbXB0eSIjgtPkkwIdGhgvYXBpL3RlbmFudHMve3RlbmFu", + "dC5pZH06ASoSVQoGRGVsZXRlEhguYXBpLkRlbGV0ZVRlbmFudFJlcXVlc3Qa", + "Fi5nb29nbGUucHJvdG9idWYuRW1wdHkiGYLT5JMCEyoRL2FwaS90ZW5hbnRz", + "L3tpZH0STwoETGlzdBIXLmFwaS5MaXN0VGVuYW50c1JlcXVlc3QaGC5hcGku", + "TGlzdFRlbmFudHNSZXNwb25zZSIUgtPkkwIOEgwvYXBpL3RlbmFudHMScwoH", + "QWRkVXNlchIZLmFwaS5BZGRUZW5hbnRVc2VyUmVxdWVzdBoWLmdvb2dsZS5w", + "cm90b2J1Zi5FbXB0eSI1gtPkkwIvIiovYXBpL3RlbmFudHMve3RlbmFudF91", + "c2VyLnRlbmFudF9pZH0vdXNlcnM6ASoScgoHR2V0VXNlchIZLmFwaS5HZXRU", + "ZW5hbnRVc2VyUmVxdWVzdBoaLmFwaS5HZXRUZW5hbnRVc2VyUmVzcG9uc2Ui", + "MILT5JMCKhIoL2FwaS90ZW5hbnRzL3t0ZW5hbnRfaWR9L3VzZXJzL3t1c2Vy", + "X2lkfRKPAQoKVXBkYXRlVXNlchIcLmFwaS5VcGRhdGVUZW5hbnRVc2VyUmVx", + "dWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSJLgtPkkwJFGkAvYXBpL3Rl", + "bmFudHMve3RlbmFudF91c2VyLnRlbmFudF9pZH0vdXNlcnMve3RlbmFudF91", + "c2VyLnVzZXJfaWR9OgEqEnQKCkRlbGV0ZVVzZXISHC5hcGkuRGVsZXRlVGVu", + "YW50VXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiMILT5JMC", + "KiooL2FwaS90ZW5hbnRzL3t0ZW5hbnRfaWR9L3VzZXJzL3t1c2VyX2lkfRJu", + "CglMaXN0VXNlcnMSGy5hcGkuTGlzdFRlbmFudFVzZXJzUmVxdWVzdBocLmFw", + "aS5MaXN0VGVuYW50VXNlcnNSZXNwb25zZSImgtPkkwIgEh4vYXBpL3RlbmFu", + "dHMve3RlbmFudF9pZH0vdXNlcnNCYwoRaW8uY2hpcnBzdGFjay5hcGlCC1Rl", + "bmFudFByb3RvUAFaLmdpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNr", + "L2FwaS9nby92NC9hcGmqAg5DaGlycHN0YWNrLkFwaWIGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Tenant), global::Chirpstack.Api.Tenant.Parser, new[]{ "Id", "Name", "Description", "CanHaveGateways", "MaxGatewayCount", "MaxDeviceCount", "PrivateGateways" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.TenantListItem), global::Chirpstack.Api.TenantListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "CanHaveGateways", "PrivateGateways", "MaxGatewayCount", "MaxDeviceCount" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Tenant), global::Chirpstack.Api.Tenant.Parser, new[]{ "Id", "Name", "Description", "CanHaveGateways", "MaxGatewayCount", "MaxDeviceCount", "PrivateGatewaysUp", "PrivateGatewaysDown" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.TenantListItem), global::Chirpstack.Api.TenantListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "CanHaveGateways", "PrivateGatewaysUp", "PrivateGatewaysDown", "MaxGatewayCount", "MaxDeviceCount" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateTenantRequest), global::Chirpstack.Api.CreateTenantRequest.Parser, new[]{ "Tenant" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateTenantResponse), global::Chirpstack.Api.CreateTenantResponse.Parser, new[]{ "Id" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetTenantRequest), global::Chirpstack.Api.GetTenantRequest.Parser, new[]{ "Id" }, null, null, null, null), @@ -163,7 +164,8 @@ namespace Chirpstack.Api { canHaveGateways_ = other.canHaveGateways_; maxGatewayCount_ = other.maxGatewayCount_; maxDeviceCount_ = other.maxDeviceCount_; - privateGateways_ = other.privateGateways_; + privateGatewaysUp_ = other.privateGatewaysUp_; + privateGatewaysDown_ = other.privateGatewaysDown_; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } @@ -266,19 +268,38 @@ namespace Chirpstack.Api { } } - /// Field number for the "private_gateways" field. - public const int PrivateGatewaysFieldNumber = 7; - private bool privateGateways_; + /// Field number for the "private_gateways_up" field. + public const int PrivateGatewaysUpFieldNumber = 7; + private bool privateGatewaysUp_; /// - /// Private gateways. - /// Gateways under this tenant are private. + /// Private gateways (uplink). + /// If enabled, then uplink messages will not be shared with other tenants. /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool PrivateGateways { - get { return privateGateways_; } + public bool PrivateGatewaysUp { + get { return privateGatewaysUp_; } set { - privateGateways_ = value; + privateGatewaysUp_ = value; + } + } + + /// Field number for the "private_gateways_down" field. + public const int PrivateGatewaysDownFieldNumber = 8; + private bool privateGatewaysDown_; + /// + /// Private gateways (downlink). + /// If enabled, then other tenants will not be able to schedule downlink + /// messages through the gateways of this tenant. For example, in case you + /// do want to share uplinks with other tenants (private_gateways_up=false), + /// but you want to prevent other tenants from using gateway airtime. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool PrivateGatewaysDown { + get { return privateGatewaysDown_; } + set { + privateGatewaysDown_ = value; } } @@ -303,7 +324,8 @@ namespace Chirpstack.Api { if (CanHaveGateways != other.CanHaveGateways) return false; if (MaxGatewayCount != other.MaxGatewayCount) return false; if (MaxDeviceCount != other.MaxDeviceCount) return false; - if (PrivateGateways != other.PrivateGateways) return false; + if (PrivateGatewaysUp != other.PrivateGatewaysUp) return false; + if (PrivateGatewaysDown != other.PrivateGatewaysDown) return false; return Equals(_unknownFields, other._unknownFields); } @@ -317,7 +339,8 @@ namespace Chirpstack.Api { if (CanHaveGateways != false) hash ^= CanHaveGateways.GetHashCode(); if (MaxGatewayCount != 0) hash ^= MaxGatewayCount.GetHashCode(); if (MaxDeviceCount != 0) hash ^= MaxDeviceCount.GetHashCode(); - if (PrivateGateways != false) hash ^= PrivateGateways.GetHashCode(); + if (PrivateGatewaysUp != false) hash ^= PrivateGatewaysUp.GetHashCode(); + if (PrivateGatewaysDown != false) hash ^= PrivateGatewaysDown.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } @@ -360,9 +383,13 @@ namespace Chirpstack.Api { output.WriteRawTag(48); output.WriteUInt32(MaxDeviceCount); } - if (PrivateGateways != false) { + if (PrivateGatewaysUp != false) { output.WriteRawTag(56); - output.WriteBool(PrivateGateways); + output.WriteBool(PrivateGatewaysUp); + } + if (PrivateGatewaysDown != false) { + output.WriteRawTag(64); + output.WriteBool(PrivateGatewaysDown); } if (_unknownFields != null) { _unknownFields.WriteTo(output); @@ -398,9 +425,13 @@ namespace Chirpstack.Api { output.WriteRawTag(48); output.WriteUInt32(MaxDeviceCount); } - if (PrivateGateways != false) { + if (PrivateGatewaysUp != false) { output.WriteRawTag(56); - output.WriteBool(PrivateGateways); + output.WriteBool(PrivateGatewaysUp); + } + if (PrivateGatewaysDown != false) { + output.WriteRawTag(64); + output.WriteBool(PrivateGatewaysDown); } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); @@ -430,7 +461,10 @@ namespace Chirpstack.Api { if (MaxDeviceCount != 0) { size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MaxDeviceCount); } - if (PrivateGateways != false) { + if (PrivateGatewaysUp != false) { + size += 1 + 1; + } + if (PrivateGatewaysDown != false) { size += 1 + 1; } if (_unknownFields != null) { @@ -463,8 +497,11 @@ namespace Chirpstack.Api { if (other.MaxDeviceCount != 0) { MaxDeviceCount = other.MaxDeviceCount; } - if (other.PrivateGateways != false) { - PrivateGateways = other.PrivateGateways; + if (other.PrivateGatewaysUp != false) { + PrivateGatewaysUp = other.PrivateGatewaysUp; + } + if (other.PrivateGatewaysDown != false) { + PrivateGatewaysDown = other.PrivateGatewaysDown; } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } @@ -506,7 +543,11 @@ namespace Chirpstack.Api { break; } case 56: { - PrivateGateways = input.ReadBool(); + PrivateGatewaysUp = input.ReadBool(); + break; + } + case 64: { + PrivateGatewaysDown = input.ReadBool(); break; } } @@ -549,7 +590,11 @@ namespace Chirpstack.Api { break; } case 56: { - PrivateGateways = input.ReadBool(); + PrivateGatewaysUp = input.ReadBool(); + break; + } + case 64: { + PrivateGatewaysDown = input.ReadBool(); break; } } @@ -598,7 +643,8 @@ namespace Chirpstack.Api { updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null; name_ = other.name_; canHaveGateways_ = other.canHaveGateways_; - privateGateways_ = other.privateGateways_; + privateGatewaysUp_ = other.privateGatewaysUp_; + privateGatewaysDown_ = other.privateGatewaysDown_; maxGatewayCount_ = other.maxGatewayCount_; maxDeviceCount_ = other.maxDeviceCount_; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); @@ -685,18 +731,33 @@ namespace Chirpstack.Api { } } - /// Field number for the "private_gateways" field. - public const int PrivateGatewaysFieldNumber = 6; - private bool privateGateways_; + /// Field number for the "private_gateways_up" field. + public const int PrivateGatewaysUpFieldNumber = 6; + private bool privateGatewaysUp_; /// - /// Gateways are private to tenant. + /// Private gateways (uplink). /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool PrivateGateways { - get { return privateGateways_; } + public bool PrivateGatewaysUp { + get { return privateGatewaysUp_; } set { - privateGateways_ = value; + privateGatewaysUp_ = value; + } + } + + /// Field number for the "private_gateways_down" field. + public const int PrivateGatewaysDownFieldNumber = 9; + private bool privateGatewaysDown_; + /// + /// Private gateways (downlink). + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool PrivateGatewaysDown { + get { return privateGatewaysDown_; } + set { + privateGatewaysDown_ = value; } } @@ -752,7 +813,8 @@ namespace Chirpstack.Api { if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false; if (Name != other.Name) return false; if (CanHaveGateways != other.CanHaveGateways) return false; - if (PrivateGateways != other.PrivateGateways) return false; + if (PrivateGatewaysUp != other.PrivateGatewaysUp) return false; + if (PrivateGatewaysDown != other.PrivateGatewaysDown) return false; if (MaxGatewayCount != other.MaxGatewayCount) return false; if (MaxDeviceCount != other.MaxDeviceCount) return false; return Equals(_unknownFields, other._unknownFields); @@ -767,7 +829,8 @@ namespace Chirpstack.Api { if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode(); if (Name.Length != 0) hash ^= Name.GetHashCode(); if (CanHaveGateways != false) hash ^= CanHaveGateways.GetHashCode(); - if (PrivateGateways != false) hash ^= PrivateGateways.GetHashCode(); + if (PrivateGatewaysUp != false) hash ^= PrivateGatewaysUp.GetHashCode(); + if (PrivateGatewaysDown != false) hash ^= PrivateGatewaysDown.GetHashCode(); if (MaxGatewayCount != 0) hash ^= MaxGatewayCount.GetHashCode(); if (MaxDeviceCount != 0) hash ^= MaxDeviceCount.GetHashCode(); if (_unknownFields != null) { @@ -808,9 +871,9 @@ namespace Chirpstack.Api { output.WriteRawTag(40); output.WriteBool(CanHaveGateways); } - if (PrivateGateways != false) { + if (PrivateGatewaysUp != false) { output.WriteRawTag(48); - output.WriteBool(PrivateGateways); + output.WriteBool(PrivateGatewaysUp); } if (MaxGatewayCount != 0) { output.WriteRawTag(56); @@ -820,6 +883,10 @@ namespace Chirpstack.Api { output.WriteRawTag(64); output.WriteUInt32(MaxDeviceCount); } + if (PrivateGatewaysDown != false) { + output.WriteRawTag(72); + output.WriteBool(PrivateGatewaysDown); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -850,9 +917,9 @@ namespace Chirpstack.Api { output.WriteRawTag(40); output.WriteBool(CanHaveGateways); } - if (PrivateGateways != false) { + if (PrivateGatewaysUp != false) { output.WriteRawTag(48); - output.WriteBool(PrivateGateways); + output.WriteBool(PrivateGatewaysUp); } if (MaxGatewayCount != 0) { output.WriteRawTag(56); @@ -862,6 +929,10 @@ namespace Chirpstack.Api { output.WriteRawTag(64); output.WriteUInt32(MaxDeviceCount); } + if (PrivateGatewaysDown != false) { + output.WriteRawTag(72); + output.WriteBool(PrivateGatewaysDown); + } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } @@ -887,7 +958,10 @@ namespace Chirpstack.Api { if (CanHaveGateways != false) { size += 1 + 1; } - if (PrivateGateways != false) { + if (PrivateGatewaysUp != false) { + size += 1 + 1; + } + if (PrivateGatewaysDown != false) { size += 1 + 1; } if (MaxGatewayCount != 0) { @@ -929,8 +1003,11 @@ namespace Chirpstack.Api { if (other.CanHaveGateways != false) { CanHaveGateways = other.CanHaveGateways; } - if (other.PrivateGateways != false) { - PrivateGateways = other.PrivateGateways; + if (other.PrivateGatewaysUp != false) { + PrivateGatewaysUp = other.PrivateGatewaysUp; + } + if (other.PrivateGatewaysDown != false) { + PrivateGatewaysDown = other.PrivateGatewaysDown; } if (other.MaxGatewayCount != 0) { MaxGatewayCount = other.MaxGatewayCount; @@ -980,7 +1057,7 @@ namespace Chirpstack.Api { break; } case 48: { - PrivateGateways = input.ReadBool(); + PrivateGatewaysUp = input.ReadBool(); break; } case 56: { @@ -991,6 +1068,10 @@ namespace Chirpstack.Api { MaxDeviceCount = input.ReadUInt32(); break; } + case 72: { + PrivateGatewaysDown = input.ReadBool(); + break; + } } } #endif @@ -1033,7 +1114,7 @@ namespace Chirpstack.Api { break; } case 48: { - PrivateGateways = input.ReadBool(); + PrivateGatewaysUp = input.ReadBool(); break; } case 56: { @@ -1044,6 +1125,10 @@ namespace Chirpstack.Api { MaxDeviceCount = input.ReadUInt32(); break; } + case 72: { + PrivateGatewaysDown = input.ReadBool(); + break; + } } } } diff --git a/api/go/api/tenant.pb.go b/api/go/api/tenant.pb.go index ba379148..790509fc 100644 --- a/api/go/api/tenant.pb.go +++ b/api/go/api/tenant.pb.go @@ -43,9 +43,15 @@ type Tenant struct { // Max. device count for tenant. // When set to 0, the tenant can have unlimited devices. MaxDeviceCount uint32 `protobuf:"varint,6,opt,name=max_device_count,json=maxDeviceCount,proto3" json:"max_device_count,omitempty"` - // Private gateways. - // Gateways under this tenant are private. - PrivateGateways bool `protobuf:"varint,7,opt,name=private_gateways,json=privateGateways,proto3" json:"private_gateways,omitempty"` + // Private gateways (uplink). + // If enabled, then uplink messages will not be shared with other tenants. + PrivateGatewaysUp bool `protobuf:"varint,7,opt,name=private_gateways_up,json=privateGatewaysUp,proto3" json:"private_gateways_up,omitempty"` + // Private gateways (downlink). + // If enabled, then other tenants will not be able to schedule downlink + // messages through the gateways of this tenant. For example, in case you + // do want to share uplinks with other tenants (private_gateways_up=false), + // but you want to prevent other tenants from using gateway airtime. + PrivateGatewaysDown bool `protobuf:"varint,8,opt,name=private_gateways_down,json=privateGatewaysDown,proto3" json:"private_gateways_down,omitempty"` } func (x *Tenant) Reset() { @@ -122,9 +128,16 @@ func (x *Tenant) GetMaxDeviceCount() uint32 { return 0 } -func (x *Tenant) GetPrivateGateways() bool { +func (x *Tenant) GetPrivateGatewaysUp() bool { if x != nil { - return x.PrivateGateways + return x.PrivateGatewaysUp + } + return false +} + +func (x *Tenant) GetPrivateGatewaysDown() bool { + if x != nil { + return x.PrivateGatewaysDown } return false } @@ -144,8 +157,10 @@ type TenantListItem struct { Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` // Can the tenant create and "own" Gateways? CanHaveGateways bool `protobuf:"varint,5,opt,name=can_have_gateways,json=canHaveGateways,proto3" json:"can_have_gateways,omitempty"` - // Gateways are private to tenant. - PrivateGateways bool `protobuf:"varint,6,opt,name=private_gateways,json=privateGateways,proto3" json:"private_gateways,omitempty"` + // Private gateways (uplink). + PrivateGatewaysUp bool `protobuf:"varint,6,opt,name=private_gateways_up,json=privateGatewaysUp,proto3" json:"private_gateways_up,omitempty"` + // Private gateways (downlink). + PrivateGatewaysDown bool `protobuf:"varint,9,opt,name=private_gateways_down,json=privateGatewaysDown,proto3" json:"private_gateways_down,omitempty"` // Max gateway count. // 0 = unlimited. MaxGatewayCount uint32 `protobuf:"varint,7,opt,name=max_gateway_count,json=maxGatewayCount,proto3" json:"max_gateway_count,omitempty"` @@ -221,9 +236,16 @@ func (x *TenantListItem) GetCanHaveGateways() bool { return false } -func (x *TenantListItem) GetPrivateGateways() bool { +func (x *TenantListItem) GetPrivateGatewaysUp() bool { if x != nil { - return x.PrivateGateways + return x.PrivateGatewaysUp + } + return false +} + +func (x *TenantListItem) GetPrivateGatewaysDown() bool { + if x != nil { + return x.PrivateGatewaysDown } return false } @@ -1298,7 +1320,7 @@ var file_api_tenant_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xfb, 0x01, 0x0a, 0x06, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x0e, + 0x6f, 0x74, 0x6f, 0x22, 0xb4, 0x02, 0x0a, 0x06, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, @@ -1311,221 +1333,228 @@ var file_api_tenant_proto_rawDesc = []byte{ 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x73, 0x22, 0xd7, 0x02, 0x0a, 0x0e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, - 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, - 0x0a, 0x11, 0x63, 0x61, 0x6e, 0x5f, 0x68, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x63, 0x61, 0x6e, 0x48, 0x61, - 0x76, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6d, 0x61, 0x78, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x13, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, - 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x22, 0x26, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x22, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x06, 0x74, 0x65, 0x6e, - 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x39, - 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x64, 0x41, 0x74, 0x22, 0x3a, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, - 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x74, + 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x75, 0x70, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x11, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x73, 0x55, 0x70, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0x90, 0x03, 0x0a, 0x0e, 0x54, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x61, 0x6e, 0x5f, 0x68, + 0x61, 0x76, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x63, 0x61, 0x6e, 0x48, 0x61, 0x76, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x75, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x11, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x73, 0x55, 0x70, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6d, + 0x61, 0x78, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x0a, + 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, + 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x22, 0x26, 0x0a, 0x14, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x22, 0x22, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, - 0x22, 0x25, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x73, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x63, 0x0a, 0x13, - 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x0a, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, - 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, - 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x64, - 0x6d, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0xc3, 0x02, 0x0a, 0x12, 0x54, 0x65, - 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, - 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, - 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x26, 0x0a, - 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x69, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x22, - 0x48, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x0b, 0x74, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0a, 0x74, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x22, 0x4c, 0x0a, 0x14, 0x47, 0x65, 0x74, - 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, - 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, - 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0a, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x3a, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, + 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x22, 0x25, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x73, 0x0a, 0x12, 0x4c, 0x69, 0x73, + 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x63, + 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x0a, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, + 0x6d, 0x69, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x69, + 0x73, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0xc3, 0x02, 0x0a, 0x12, + 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, + 0x65, 0x6d, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, - 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x4b, 0x0a, 0x17, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x0b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, - 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0a, 0x74, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x22, 0x4f, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, - 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x63, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x6b, 0x0a, 0x17, - 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, - 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0xa2, 0x08, 0x0a, 0x0d, 0x54, 0x65, - 0x6e, 0x61, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x56, 0x0a, 0x06, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x4f, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x15, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x13, 0x12, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5f, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x1a, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x2e, 0x69, - 0x64, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x55, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, - 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x2a, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x4f, 0x0a, 0x04, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, - 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x73, 0x0a, - 0x07, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, - 0x64, 0x64, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x35, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2f, 0x22, 0x2a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, - 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x74, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x19, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, - 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x12, 0x28, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8f, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, + 0x26, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x69, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x64, 0x6d, 0x69, + 0x6e, 0x22, 0x48, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x0b, 0x74, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x0a, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x22, 0x4c, 0x0a, 0x14, 0x47, + 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0a, 0x74, 0x65, 0x6e, 0x61, 0x6e, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x4b, 0x0a, 0x17, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x0b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0a, 0x74, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x22, 0x4f, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x4b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x45, 0x1a, 0x40, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, - 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x74, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, - 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x30, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2a, 0x2a, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, - 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6e, - 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x63, 0x0a, 0x16, 0x4c, 0x69, 0x73, + 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x6b, + 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x49, + 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0xa2, 0x08, 0x0a, 0x0d, + 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x56, 0x0a, + 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, + 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x4f, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x15, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5f, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x1a, 0x18, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x2e, 0x69, 0x64, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x55, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x2a, 0x11, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x4f, + 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x0e, 0x12, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x12, + 0x73, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x41, 0x64, 0x64, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x35, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x2a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, + 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x12, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, - 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x42, 0x63, - 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, - 0x61, 0x70, 0x69, 0x42, 0x0b, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, - 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, - 0x70, 0x69, 0xaa, 0x02, 0x0e, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, - 0x41, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8f, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x4b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x45, 0x1a, 0x40, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, + 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x0a, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x30, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x2a, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, + 0x12, 0x6e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, + 0x12, 0x1e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b, + 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, + 0x42, 0x63, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, + 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0b, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, + 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, + 0x2f, 0x61, 0x70, 0x69, 0xaa, 0x02, 0x0e, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, + 0x6b, 0x2e, 0x41, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/grpc-web/api/tenant_pb.d.ts b/api/grpc-web/api/tenant_pb.d.ts index 608b761a..df9067f9 100644 --- a/api/grpc-web/api/tenant_pb.d.ts +++ b/api/grpc-web/api/tenant_pb.d.ts @@ -24,8 +24,11 @@ export class Tenant extends jspb.Message { getMaxDeviceCount(): number; setMaxDeviceCount(value: number): Tenant; - getPrivateGateways(): boolean; - setPrivateGateways(value: boolean): Tenant; + getPrivateGatewaysUp(): boolean; + setPrivateGatewaysUp(value: boolean): Tenant; + + getPrivateGatewaysDown(): boolean; + setPrivateGatewaysDown(value: boolean): Tenant; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): Tenant.AsObject; @@ -43,7 +46,8 @@ export namespace Tenant { canHaveGateways: boolean, maxGatewayCount: number, maxDeviceCount: number, - privateGateways: boolean, + privateGatewaysUp: boolean, + privateGatewaysDown: boolean, } } @@ -67,8 +71,11 @@ export class TenantListItem extends jspb.Message { getCanHaveGateways(): boolean; setCanHaveGateways(value: boolean): TenantListItem; - getPrivateGateways(): boolean; - setPrivateGateways(value: boolean): TenantListItem; + getPrivateGatewaysUp(): boolean; + setPrivateGatewaysUp(value: boolean): TenantListItem; + + getPrivateGatewaysDown(): boolean; + setPrivateGatewaysDown(value: boolean): TenantListItem; getMaxGatewayCount(): number; setMaxGatewayCount(value: number): TenantListItem; @@ -91,7 +98,8 @@ export namespace TenantListItem { updatedAt?: google_protobuf_timestamp_pb.Timestamp.AsObject, name: string, canHaveGateways: boolean, - privateGateways: boolean, + privateGatewaysUp: boolean, + privateGatewaysDown: boolean, maxGatewayCount: number, maxDeviceCount: number, } diff --git a/api/grpc-web/api/tenant_pb.js b/api/grpc-web/api/tenant_pb.js index c2228d72..c73228f7 100644 --- a/api/grpc-web/api/tenant_pb.js +++ b/api/grpc-web/api/tenant_pb.js @@ -474,7 +474,8 @@ proto.api.Tenant.toObject = function(includeInstance, msg) { canHaveGateways: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), maxGatewayCount: jspb.Message.getFieldWithDefault(msg, 5, 0), maxDeviceCount: jspb.Message.getFieldWithDefault(msg, 6, 0), - privateGateways: jspb.Message.getBooleanFieldWithDefault(msg, 7, false) + privateGatewaysUp: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), + privateGatewaysDown: jspb.Message.getBooleanFieldWithDefault(msg, 8, false) }; if (includeInstance) { @@ -537,7 +538,11 @@ proto.api.Tenant.deserializeBinaryFromReader = function(msg, reader) { break; case 7: var value = /** @type {boolean} */ (reader.readBool()); - msg.setPrivateGateways(value); + msg.setPrivateGatewaysUp(value); + break; + case 8: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setPrivateGatewaysDown(value); break; default: reader.skipField(); @@ -610,13 +615,20 @@ proto.api.Tenant.serializeBinaryToWriter = function(message, writer) { f ); } - f = message.getPrivateGateways(); + f = message.getPrivateGatewaysUp(); if (f) { writer.writeBool( 7, f ); } + f = message.getPrivateGatewaysDown(); + if (f) { + writer.writeBool( + 8, + f + ); + } }; @@ -729,10 +741,10 @@ proto.api.Tenant.prototype.setMaxDeviceCount = function(value) { /** - * optional bool private_gateways = 7; + * optional bool private_gateways_up = 7; * @return {boolean} */ -proto.api.Tenant.prototype.getPrivateGateways = function() { +proto.api.Tenant.prototype.getPrivateGatewaysUp = function() { return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); }; @@ -741,11 +753,29 @@ proto.api.Tenant.prototype.getPrivateGateways = function() { * @param {boolean} value * @return {!proto.api.Tenant} returns this */ -proto.api.Tenant.prototype.setPrivateGateways = function(value) { +proto.api.Tenant.prototype.setPrivateGatewaysUp = function(value) { return jspb.Message.setProto3BooleanField(this, 7, value); }; +/** + * optional bool private_gateways_down = 8; + * @return {boolean} + */ +proto.api.Tenant.prototype.getPrivateGatewaysDown = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api.Tenant} returns this + */ +proto.api.Tenant.prototype.setPrivateGatewaysDown = function(value) { + return jspb.Message.setProto3BooleanField(this, 8, value); +}; + + @@ -783,7 +813,8 @@ proto.api.TenantListItem.toObject = function(includeInstance, msg) { updatedAt: (f = msg.getUpdatedAt()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), name: jspb.Message.getFieldWithDefault(msg, 4, ""), canHaveGateways: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), - privateGateways: jspb.Message.getBooleanFieldWithDefault(msg, 6, false), + privateGatewaysUp: jspb.Message.getBooleanFieldWithDefault(msg, 6, false), + privateGatewaysDown: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), maxGatewayCount: jspb.Message.getFieldWithDefault(msg, 7, 0), maxDeviceCount: jspb.Message.getFieldWithDefault(msg, 8, 0) }; @@ -846,7 +877,11 @@ proto.api.TenantListItem.deserializeBinaryFromReader = function(msg, reader) { break; case 6: var value = /** @type {boolean} */ (reader.readBool()); - msg.setPrivateGateways(value); + msg.setPrivateGatewaysUp(value); + break; + case 9: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setPrivateGatewaysDown(value); break; case 7: var value = /** @type {number} */ (reader.readUint32()); @@ -922,13 +957,20 @@ proto.api.TenantListItem.serializeBinaryToWriter = function(message, writer) { f ); } - f = message.getPrivateGateways(); + f = message.getPrivateGatewaysUp(); if (f) { writer.writeBool( 6, f ); } + f = message.getPrivateGatewaysDown(); + if (f) { + writer.writeBool( + 9, + f + ); + } f = message.getMaxGatewayCount(); if (f !== 0) { writer.writeUint32( @@ -1075,10 +1117,10 @@ proto.api.TenantListItem.prototype.setCanHaveGateways = function(value) { /** - * optional bool private_gateways = 6; + * optional bool private_gateways_up = 6; * @return {boolean} */ -proto.api.TenantListItem.prototype.getPrivateGateways = function() { +proto.api.TenantListItem.prototype.getPrivateGatewaysUp = function() { return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 6, false)); }; @@ -1087,11 +1129,29 @@ proto.api.TenantListItem.prototype.getPrivateGateways = function() { * @param {boolean} value * @return {!proto.api.TenantListItem} returns this */ -proto.api.TenantListItem.prototype.setPrivateGateways = function(value) { +proto.api.TenantListItem.prototype.setPrivateGatewaysUp = function(value) { return jspb.Message.setProto3BooleanField(this, 6, value); }; +/** + * optional bool private_gateways_down = 9; + * @return {boolean} + */ +proto.api.TenantListItem.prototype.getPrivateGatewaysDown = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api.TenantListItem} returns this + */ +proto.api.TenantListItem.prototype.setPrivateGatewaysDown = function(value) { + return jspb.Message.setProto3BooleanField(this, 9, value); +}; + + /** * optional uint32 max_gateway_count = 7; * @return {number} diff --git a/api/js/api/tenant_pb.d.ts b/api/js/api/tenant_pb.d.ts index 43e649b9..d66d0999 100644 --- a/api/js/api/tenant_pb.d.ts +++ b/api/js/api/tenant_pb.d.ts @@ -25,8 +25,11 @@ export class Tenant extends jspb.Message { getMaxDeviceCount(): number; setMaxDeviceCount(value: number): void; - getPrivateGateways(): boolean; - setPrivateGateways(value: boolean): void; + getPrivateGatewaysUp(): boolean; + setPrivateGatewaysUp(value: boolean): void; + + getPrivateGatewaysDown(): boolean; + setPrivateGatewaysDown(value: boolean): void; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): Tenant.AsObject; @@ -46,7 +49,8 @@ export namespace Tenant { canHaveGateways: boolean, maxGatewayCount: number, maxDeviceCount: number, - privateGateways: boolean, + privateGatewaysUp: boolean, + privateGatewaysDown: boolean, } } @@ -70,8 +74,11 @@ export class TenantListItem extends jspb.Message { getCanHaveGateways(): boolean; setCanHaveGateways(value: boolean): void; - getPrivateGateways(): boolean; - setPrivateGateways(value: boolean): void; + getPrivateGatewaysUp(): boolean; + setPrivateGatewaysUp(value: boolean): void; + + getPrivateGatewaysDown(): boolean; + setPrivateGatewaysDown(value: boolean): void; getMaxGatewayCount(): number; setMaxGatewayCount(value: number): void; @@ -96,7 +103,8 @@ export namespace TenantListItem { updatedAt?: google_protobuf_timestamp_pb.Timestamp.AsObject, name: string, canHaveGateways: boolean, - privateGateways: boolean, + privateGatewaysUp: boolean, + privateGatewaysDown: boolean, maxGatewayCount: number, maxDeviceCount: number, } diff --git a/api/js/api/tenant_pb.js b/api/js/api/tenant_pb.js index c2228d72..c73228f7 100644 --- a/api/js/api/tenant_pb.js +++ b/api/js/api/tenant_pb.js @@ -474,7 +474,8 @@ proto.api.Tenant.toObject = function(includeInstance, msg) { canHaveGateways: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), maxGatewayCount: jspb.Message.getFieldWithDefault(msg, 5, 0), maxDeviceCount: jspb.Message.getFieldWithDefault(msg, 6, 0), - privateGateways: jspb.Message.getBooleanFieldWithDefault(msg, 7, false) + privateGatewaysUp: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), + privateGatewaysDown: jspb.Message.getBooleanFieldWithDefault(msg, 8, false) }; if (includeInstance) { @@ -537,7 +538,11 @@ proto.api.Tenant.deserializeBinaryFromReader = function(msg, reader) { break; case 7: var value = /** @type {boolean} */ (reader.readBool()); - msg.setPrivateGateways(value); + msg.setPrivateGatewaysUp(value); + break; + case 8: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setPrivateGatewaysDown(value); break; default: reader.skipField(); @@ -610,13 +615,20 @@ proto.api.Tenant.serializeBinaryToWriter = function(message, writer) { f ); } - f = message.getPrivateGateways(); + f = message.getPrivateGatewaysUp(); if (f) { writer.writeBool( 7, f ); } + f = message.getPrivateGatewaysDown(); + if (f) { + writer.writeBool( + 8, + f + ); + } }; @@ -729,10 +741,10 @@ proto.api.Tenant.prototype.setMaxDeviceCount = function(value) { /** - * optional bool private_gateways = 7; + * optional bool private_gateways_up = 7; * @return {boolean} */ -proto.api.Tenant.prototype.getPrivateGateways = function() { +proto.api.Tenant.prototype.getPrivateGatewaysUp = function() { return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); }; @@ -741,11 +753,29 @@ proto.api.Tenant.prototype.getPrivateGateways = function() { * @param {boolean} value * @return {!proto.api.Tenant} returns this */ -proto.api.Tenant.prototype.setPrivateGateways = function(value) { +proto.api.Tenant.prototype.setPrivateGatewaysUp = function(value) { return jspb.Message.setProto3BooleanField(this, 7, value); }; +/** + * optional bool private_gateways_down = 8; + * @return {boolean} + */ +proto.api.Tenant.prototype.getPrivateGatewaysDown = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api.Tenant} returns this + */ +proto.api.Tenant.prototype.setPrivateGatewaysDown = function(value) { + return jspb.Message.setProto3BooleanField(this, 8, value); +}; + + @@ -783,7 +813,8 @@ proto.api.TenantListItem.toObject = function(includeInstance, msg) { updatedAt: (f = msg.getUpdatedAt()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), name: jspb.Message.getFieldWithDefault(msg, 4, ""), canHaveGateways: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), - privateGateways: jspb.Message.getBooleanFieldWithDefault(msg, 6, false), + privateGatewaysUp: jspb.Message.getBooleanFieldWithDefault(msg, 6, false), + privateGatewaysDown: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), maxGatewayCount: jspb.Message.getFieldWithDefault(msg, 7, 0), maxDeviceCount: jspb.Message.getFieldWithDefault(msg, 8, 0) }; @@ -846,7 +877,11 @@ proto.api.TenantListItem.deserializeBinaryFromReader = function(msg, reader) { break; case 6: var value = /** @type {boolean} */ (reader.readBool()); - msg.setPrivateGateways(value); + msg.setPrivateGatewaysUp(value); + break; + case 9: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setPrivateGatewaysDown(value); break; case 7: var value = /** @type {number} */ (reader.readUint32()); @@ -922,13 +957,20 @@ proto.api.TenantListItem.serializeBinaryToWriter = function(message, writer) { f ); } - f = message.getPrivateGateways(); + f = message.getPrivateGatewaysUp(); if (f) { writer.writeBool( 6, f ); } + f = message.getPrivateGatewaysDown(); + if (f) { + writer.writeBool( + 9, + f + ); + } f = message.getMaxGatewayCount(); if (f !== 0) { writer.writeUint32( @@ -1075,10 +1117,10 @@ proto.api.TenantListItem.prototype.setCanHaveGateways = function(value) { /** - * optional bool private_gateways = 6; + * optional bool private_gateways_up = 6; * @return {boolean} */ -proto.api.TenantListItem.prototype.getPrivateGateways = function() { +proto.api.TenantListItem.prototype.getPrivateGatewaysUp = function() { return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 6, false)); }; @@ -1087,11 +1129,29 @@ proto.api.TenantListItem.prototype.getPrivateGateways = function() { * @param {boolean} value * @return {!proto.api.TenantListItem} returns this */ -proto.api.TenantListItem.prototype.setPrivateGateways = function(value) { +proto.api.TenantListItem.prototype.setPrivateGatewaysUp = function(value) { return jspb.Message.setProto3BooleanField(this, 6, value); }; +/** + * optional bool private_gateways_down = 9; + * @return {boolean} + */ +proto.api.TenantListItem.prototype.getPrivateGatewaysDown = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api.TenantListItem} returns this + */ +proto.api.TenantListItem.prototype.setPrivateGatewaysDown = function(value) { + return jspb.Message.setProto3BooleanField(this, 9, value); +}; + + /** * optional uint32 max_gateway_count = 7; * @return {number} diff --git a/api/md/api/api.md b/api/md/api/api.md index 58215124..9255af82 100644 --- a/api/md/api/api.md +++ b/api/md/api/api.md @@ -3638,7 +3638,8 @@ MulticastGroupService is the service managing multicast-groups. | can_have_gateways | [bool](#bool) | | Can the tenant create and "own" Gateways? | | max_gateway_count | [uint32](#uint32) | | Max. gateway count for tenant. When set to 0, the tenant can have unlimited gateways. | | max_device_count | [uint32](#uint32) | | Max. device count for tenant. When set to 0, the tenant can have unlimited devices. | -| private_gateways | [bool](#bool) | | Private gateways. Gateways under this tenant are private. | +| private_gateways_up | [bool](#bool) | | Private gateways (uplink). If enabled, then uplink messages will not be shared with other tenants. | +| private_gateways_down | [bool](#bool) | | Private gateways (downlink). If enabled, then other tenants will not be able to schedule downlink messages through the gateways of this tenant. For example, in case you do want to share uplinks with other tenants (private_gateways_up=false), but you want to prevent other tenants from using gateway airtime. | @@ -3658,7 +3659,8 @@ MulticastGroupService is the service managing multicast-groups. | updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | Last update timestamp. | | name | [string](#string) | | Tenant name. | | can_have_gateways | [bool](#bool) | | Can the tenant create and "own" Gateways? | -| private_gateways | [bool](#bool) | | Gateways are private to tenant. | +| private_gateways_up | [bool](#bool) | | Private gateways (uplink). | +| private_gateways_down | [bool](#bool) | | Private gateways (downlink). | | max_gateway_count | [uint32](#uint32) | | Max gateway count. 0 = unlimited. | | max_device_count | [uint32](#uint32) | | Max device count. 0 = unlimited. | diff --git a/api/proto/api/tenant.proto b/api/proto/api/tenant.proto index bd942790..809fb88d 100644 --- a/api/proto/api/tenant.proto +++ b/api/proto/api/tenant.proto @@ -112,9 +112,16 @@ message Tenant { // When set to 0, the tenant can have unlimited devices. uint32 max_device_count = 6; - // Private gateways. - // Gateways under this tenant are private. - bool private_gateways = 7; + // Private gateways (uplink). + // If enabled, then uplink messages will not be shared with other tenants. + bool private_gateways_up = 7; + + // Private gateways (downlink). + // If enabled, then other tenants will not be able to schedule downlink + // messages through the gateways of this tenant. For example, in case you + // do want to share uplinks with other tenants (private_gateways_up=false), + // but you want to prevent other tenants from using gateway airtime. + bool private_gateways_down = 8; } message TenantListItem { @@ -133,8 +140,11 @@ message TenantListItem { // Can the tenant create and "own" Gateways? bool can_have_gateways = 5; - // Gateways are private to tenant. - bool private_gateways = 6; + // Private gateways (uplink). + bool private_gateways_up = 6; + + // Private gateways (downlink). + bool private_gateways_down = 9; // Max gateway count. // 0 = unlimited. diff --git a/api/proto/internal/internal.proto b/api/proto/internal/internal.proto index 0abe48d5..93823dfe 100644 --- a/api/proto/internal/internal.proto +++ b/api/proto/internal/internal.proto @@ -196,6 +196,15 @@ message DeviceGatewayRxInfoItem { // Context blob. bytes context = 6; + + // Gateway is private (uplink). + bool is_private_up = 7; + + // Gateway is private (downlink). + bool is_private_down = 8; + + // Tenant ID (UUID). + bytes tenant_id = 9; } message DownlinkFrame { diff --git a/api/python/proto/chirpstack-api/api/tenant.proto b/api/python/proto/chirpstack-api/api/tenant.proto index bd942790..809fb88d 100644 --- a/api/python/proto/chirpstack-api/api/tenant.proto +++ b/api/python/proto/chirpstack-api/api/tenant.proto @@ -112,9 +112,16 @@ message Tenant { // When set to 0, the tenant can have unlimited devices. uint32 max_device_count = 6; - // Private gateways. - // Gateways under this tenant are private. - bool private_gateways = 7; + // Private gateways (uplink). + // If enabled, then uplink messages will not be shared with other tenants. + bool private_gateways_up = 7; + + // Private gateways (downlink). + // If enabled, then other tenants will not be able to schedule downlink + // messages through the gateways of this tenant. For example, in case you + // do want to share uplinks with other tenants (private_gateways_up=false), + // but you want to prevent other tenants from using gateway airtime. + bool private_gateways_down = 8; } message TenantListItem { @@ -133,8 +140,11 @@ message TenantListItem { // Can the tenant create and "own" Gateways? bool can_have_gateways = 5; - // Gateways are private to tenant. - bool private_gateways = 6; + // Private gateways (uplink). + bool private_gateways_up = 6; + + // Private gateways (downlink). + bool private_gateways_down = 9; // Max gateway count. // 0 = unlimited. diff --git a/api/python/proto/chirpstack-api/internal/internal.proto b/api/python/proto/chirpstack-api/internal/internal.proto index d7db550f..2d42f4ec 100644 --- a/api/python/proto/chirpstack-api/internal/internal.proto +++ b/api/python/proto/chirpstack-api/internal/internal.proto @@ -196,6 +196,15 @@ message DeviceGatewayRxInfoItem { // Context blob. bytes context = 6; + + // Gateway is private (uplink). + bool is_private_up = 7; + + // Gateway is private (downlink). + bool is_private_down = 8; + + // Tenant ID (UUID). + bytes tenant_id = 9; } message DownlinkFrame { diff --git a/api/python/src/chirpstack_api/api/tenant_pb2.py b/api/python/src/chirpstack_api/api/tenant_pb2.py index d13b7d8a..bbb7717b 100644 --- a/api/python/src/chirpstack_api/api/tenant_pb2.py +++ b/api/python/src/chirpstack_api/api/tenant_pb2.py @@ -16,7 +16,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__ from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63hirpstack-api/api/tenant.proto\x12\x03\x61pi\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xa1\x01\n\x06Tenant\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x19\n\x11\x63\x61n_have_gateways\x18\x04 \x01(\x08\x12\x19\n\x11max_gateway_count\x18\x05 \x01(\r\x12\x18\n\x10max_device_count\x18\x06 \x01(\r\x12\x18\n\x10private_gateways\x18\x07 \x01(\x08\"\xf4\x01\n\x0eTenantListItem\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x19\n\x11\x63\x61n_have_gateways\x18\x05 \x01(\x08\x12\x18\n\x10private_gateways\x18\x06 \x01(\x08\x12\x19\n\x11max_gateway_count\x18\x07 \x01(\r\x12\x18\n\x10max_device_count\x18\x08 \x01(\r\"2\n\x13\x43reateTenantRequest\x12\x1b\n\x06tenant\x18\x01 \x01(\x0b\x32\x0b.api.Tenant\"\"\n\x14\x43reateTenantResponse\x12\n\n\x02id\x18\x01 \x01(\t\"\x1e\n\x10GetTenantRequest\x12\n\n\x02id\x18\x01 \x01(\t\"\x90\x01\n\x11GetTenantResponse\x12\x1b\n\x06tenant\x18\x01 \x01(\x0b\x32\x0b.api.Tenant\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"2\n\x13UpdateTenantRequest\x12\x1b\n\x06tenant\x18\x01 \x01(\x0b\x32\x0b.api.Tenant\"!\n\x13\x44\x65leteTenantRequest\x12\n\n\x02id\x18\x01 \x01(\t\"T\n\x12ListTenantsRequest\x12\r\n\x05limit\x18\x01 \x01(\r\x12\x0e\n\x06offset\x18\x02 \x01(\r\x12\x0e\n\x06search\x18\x03 \x01(\t\x12\x0f\n\x07user_id\x18\x04 \x01(\t\"O\n\x13ListTenantsResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12#\n\x06result\x18\x02 \x03(\x0b\x32\x13.api.TenantListItem\"\x84\x01\n\nTenantUser\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\x12\x10\n\x08is_admin\x18\x03 \x01(\x08\x12\x17\n\x0fis_device_admin\x18\x04 \x01(\x08\x12\x18\n\x10is_gateway_admin\x18\x05 \x01(\x08\x12\r\n\x05\x65mail\x18\x06 \x01(\t\"\xec\x01\n\x12TenantUserListItem\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05\x65mail\x18\x05 \x01(\t\x12\x10\n\x08is_admin\x18\x06 \x01(\x08\x12\x17\n\x0fis_device_admin\x18\x07 \x01(\x08\x12\x18\n\x10is_gateway_admin\x18\x08 \x01(\x08\"<\n\x14\x41\x64\x64TenantUserRequest\x12$\n\x0btenant_user\x18\x01 \x01(\x0b\x32\x0f.api.TenantUser\":\n\x14GetTenantUserRequest\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\"\x9d\x01\n\x15GetTenantUserResponse\x12$\n\x0btenant_user\x18\x01 \x01(\x0b\x32\x0f.api.TenantUser\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"?\n\x17UpdateTenantUserRequest\x12$\n\x0btenant_user\x18\x01 \x01(\x0b\x32\x0f.api.TenantUser\"=\n\x17\x44\x65leteTenantUserRequest\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\"J\n\x16ListTenantUsersRequest\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\r\n\x05limit\x18\x02 \x01(\r\x12\x0e\n\x06offset\x18\x03 \x01(\r\"W\n\x17ListTenantUsersResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12\'\n\x06result\x18\x02 \x03(\x0b\x32\x17.api.TenantUserListItem2\xa2\x08\n\rTenantService\x12V\n\x06\x43reate\x12\x18.api.CreateTenantRequest\x1a\x19.api.CreateTenantResponse\"\x17\x82\xd3\xe4\x93\x02\x11\"\x0c/api/tenants:\x01*\x12O\n\x03Get\x12\x15.api.GetTenantRequest\x1a\x16.api.GetTenantResponse\"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/api/tenants/{id}\x12_\n\x06Update\x12\x18.api.UpdateTenantRequest\x1a\x16.google.protobuf.Empty\"#\x82\xd3\xe4\x93\x02\x1d\x1a\x18/api/tenants/{tenant.id}:\x01*\x12U\n\x06\x44\x65lete\x12\x18.api.DeleteTenantRequest\x1a\x16.google.protobuf.Empty\"\x19\x82\xd3\xe4\x93\x02\x13*\x11/api/tenants/{id}\x12O\n\x04List\x12\x17.api.ListTenantsRequest\x1a\x18.api.ListTenantsResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\x12\x0c/api/tenants\x12s\n\x07\x41\x64\x64User\x12\x19.api.AddTenantUserRequest\x1a\x16.google.protobuf.Empty\"5\x82\xd3\xe4\x93\x02/\"*/api/tenants/{tenant_user.tenant_id}/users:\x01*\x12r\n\x07GetUser\x12\x19.api.GetTenantUserRequest\x1a\x1a.api.GetTenantUserResponse\"0\x82\xd3\xe4\x93\x02*\x12(/api/tenants/{tenant_id}/users/{user_id}\x12\x8f\x01\n\nUpdateUser\x12\x1c.api.UpdateTenantUserRequest\x1a\x16.google.protobuf.Empty\"K\x82\xd3\xe4\x93\x02\x45\x1a@/api/tenants/{tenant_user.tenant_id}/users/{tenant_user.user_id}:\x01*\x12t\n\nDeleteUser\x12\x1c.api.DeleteTenantUserRequest\x1a\x16.google.protobuf.Empty\"0\x82\xd3\xe4\x93\x02**(/api/tenants/{tenant_id}/users/{user_id}\x12n\n\tListUsers\x12\x1b.api.ListTenantUsersRequest\x1a\x1c.api.ListTenantUsersResponse\"&\x82\xd3\xe4\x93\x02 \x12\x1e/api/tenants/{tenant_id}/usersBc\n\x11io.chirpstack.apiB\x0bTenantProtoP\x01Z.github.com/chirpstack/chirpstack/api/go/v4/api\xaa\x02\x0e\x43hirpstack.Apib\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63hirpstack-api/api/tenant.proto\x12\x03\x61pi\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xc3\x01\n\x06Tenant\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x19\n\x11\x63\x61n_have_gateways\x18\x04 \x01(\x08\x12\x19\n\x11max_gateway_count\x18\x05 \x01(\r\x12\x18\n\x10max_device_count\x18\x06 \x01(\r\x12\x1b\n\x13private_gateways_up\x18\x07 \x01(\x08\x12\x1d\n\x15private_gateways_down\x18\x08 \x01(\x08\"\x96\x02\n\x0eTenantListItem\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x19\n\x11\x63\x61n_have_gateways\x18\x05 \x01(\x08\x12\x1b\n\x13private_gateways_up\x18\x06 \x01(\x08\x12\x1d\n\x15private_gateways_down\x18\t \x01(\x08\x12\x19\n\x11max_gateway_count\x18\x07 \x01(\r\x12\x18\n\x10max_device_count\x18\x08 \x01(\r\"2\n\x13\x43reateTenantRequest\x12\x1b\n\x06tenant\x18\x01 \x01(\x0b\x32\x0b.api.Tenant\"\"\n\x14\x43reateTenantResponse\x12\n\n\x02id\x18\x01 \x01(\t\"\x1e\n\x10GetTenantRequest\x12\n\n\x02id\x18\x01 \x01(\t\"\x90\x01\n\x11GetTenantResponse\x12\x1b\n\x06tenant\x18\x01 \x01(\x0b\x32\x0b.api.Tenant\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"2\n\x13UpdateTenantRequest\x12\x1b\n\x06tenant\x18\x01 \x01(\x0b\x32\x0b.api.Tenant\"!\n\x13\x44\x65leteTenantRequest\x12\n\n\x02id\x18\x01 \x01(\t\"T\n\x12ListTenantsRequest\x12\r\n\x05limit\x18\x01 \x01(\r\x12\x0e\n\x06offset\x18\x02 \x01(\r\x12\x0e\n\x06search\x18\x03 \x01(\t\x12\x0f\n\x07user_id\x18\x04 \x01(\t\"O\n\x13ListTenantsResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12#\n\x06result\x18\x02 \x03(\x0b\x32\x13.api.TenantListItem\"\x84\x01\n\nTenantUser\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\x12\x10\n\x08is_admin\x18\x03 \x01(\x08\x12\x17\n\x0fis_device_admin\x18\x04 \x01(\x08\x12\x18\n\x10is_gateway_admin\x18\x05 \x01(\x08\x12\r\n\x05\x65mail\x18\x06 \x01(\t\"\xec\x01\n\x12TenantUserListItem\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05\x65mail\x18\x05 \x01(\t\x12\x10\n\x08is_admin\x18\x06 \x01(\x08\x12\x17\n\x0fis_device_admin\x18\x07 \x01(\x08\x12\x18\n\x10is_gateway_admin\x18\x08 \x01(\x08\"<\n\x14\x41\x64\x64TenantUserRequest\x12$\n\x0btenant_user\x18\x01 \x01(\x0b\x32\x0f.api.TenantUser\":\n\x14GetTenantUserRequest\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\"\x9d\x01\n\x15GetTenantUserResponse\x12$\n\x0btenant_user\x18\x01 \x01(\x0b\x32\x0f.api.TenantUser\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"?\n\x17UpdateTenantUserRequest\x12$\n\x0btenant_user\x18\x01 \x01(\x0b\x32\x0f.api.TenantUser\"=\n\x17\x44\x65leteTenantUserRequest\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\"J\n\x16ListTenantUsersRequest\x12\x11\n\ttenant_id\x18\x01 \x01(\t\x12\r\n\x05limit\x18\x02 \x01(\r\x12\x0e\n\x06offset\x18\x03 \x01(\r\"W\n\x17ListTenantUsersResponse\x12\x13\n\x0btotal_count\x18\x01 \x01(\r\x12\'\n\x06result\x18\x02 \x03(\x0b\x32\x17.api.TenantUserListItem2\xa2\x08\n\rTenantService\x12V\n\x06\x43reate\x12\x18.api.CreateTenantRequest\x1a\x19.api.CreateTenantResponse\"\x17\x82\xd3\xe4\x93\x02\x11\"\x0c/api/tenants:\x01*\x12O\n\x03Get\x12\x15.api.GetTenantRequest\x1a\x16.api.GetTenantResponse\"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/api/tenants/{id}\x12_\n\x06Update\x12\x18.api.UpdateTenantRequest\x1a\x16.google.protobuf.Empty\"#\x82\xd3\xe4\x93\x02\x1d\x1a\x18/api/tenants/{tenant.id}:\x01*\x12U\n\x06\x44\x65lete\x12\x18.api.DeleteTenantRequest\x1a\x16.google.protobuf.Empty\"\x19\x82\xd3\xe4\x93\x02\x13*\x11/api/tenants/{id}\x12O\n\x04List\x12\x17.api.ListTenantsRequest\x1a\x18.api.ListTenantsResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\x12\x0c/api/tenants\x12s\n\x07\x41\x64\x64User\x12\x19.api.AddTenantUserRequest\x1a\x16.google.protobuf.Empty\"5\x82\xd3\xe4\x93\x02/\"*/api/tenants/{tenant_user.tenant_id}/users:\x01*\x12r\n\x07GetUser\x12\x19.api.GetTenantUserRequest\x1a\x1a.api.GetTenantUserResponse\"0\x82\xd3\xe4\x93\x02*\x12(/api/tenants/{tenant_id}/users/{user_id}\x12\x8f\x01\n\nUpdateUser\x12\x1c.api.UpdateTenantUserRequest\x1a\x16.google.protobuf.Empty\"K\x82\xd3\xe4\x93\x02\x45\x1a@/api/tenants/{tenant_user.tenant_id}/users/{tenant_user.user_id}:\x01*\x12t\n\nDeleteUser\x12\x1c.api.DeleteTenantUserRequest\x1a\x16.google.protobuf.Empty\"0\x82\xd3\xe4\x93\x02**(/api/tenants/{tenant_id}/users/{user_id}\x12n\n\tListUsers\x12\x1b.api.ListTenantUsersRequest\x1a\x1c.api.ListTenantUsersResponse\"&\x82\xd3\xe4\x93\x02 \x12\x1e/api/tenants/{tenant_id}/usersBc\n\x11io.chirpstack.apiB\x0bTenantProtoP\x01Z.github.com/chirpstack/chirpstack/api/go/v4/api\xaa\x02\x0e\x43hirpstack.Apib\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chirpstack_api.api.tenant_pb2', globals()) @@ -45,43 +45,43 @@ if _descriptor._USE_C_DESCRIPTORS == False: _TENANTSERVICE.methods_by_name['ListUsers']._options = None _TENANTSERVICE.methods_by_name['ListUsers']._serialized_options = b'\202\323\344\223\002 \022\036/api/tenants/{tenant_id}/users' _TENANT._serialized_start=133 - _TENANT._serialized_end=294 - _TENANTLISTITEM._serialized_start=297 - _TENANTLISTITEM._serialized_end=541 - _CREATETENANTREQUEST._serialized_start=543 - _CREATETENANTREQUEST._serialized_end=593 - _CREATETENANTRESPONSE._serialized_start=595 - _CREATETENANTRESPONSE._serialized_end=629 - _GETTENANTREQUEST._serialized_start=631 - _GETTENANTREQUEST._serialized_end=661 - _GETTENANTRESPONSE._serialized_start=664 - _GETTENANTRESPONSE._serialized_end=808 - _UPDATETENANTREQUEST._serialized_start=810 - _UPDATETENANTREQUEST._serialized_end=860 - _DELETETENANTREQUEST._serialized_start=862 - _DELETETENANTREQUEST._serialized_end=895 - _LISTTENANTSREQUEST._serialized_start=897 - _LISTTENANTSREQUEST._serialized_end=981 - _LISTTENANTSRESPONSE._serialized_start=983 - _LISTTENANTSRESPONSE._serialized_end=1062 - _TENANTUSER._serialized_start=1065 - _TENANTUSER._serialized_end=1197 - _TENANTUSERLISTITEM._serialized_start=1200 - _TENANTUSERLISTITEM._serialized_end=1436 - _ADDTENANTUSERREQUEST._serialized_start=1438 - _ADDTENANTUSERREQUEST._serialized_end=1498 - _GETTENANTUSERREQUEST._serialized_start=1500 - _GETTENANTUSERREQUEST._serialized_end=1558 - _GETTENANTUSERRESPONSE._serialized_start=1561 - _GETTENANTUSERRESPONSE._serialized_end=1718 - _UPDATETENANTUSERREQUEST._serialized_start=1720 - _UPDATETENANTUSERREQUEST._serialized_end=1783 - _DELETETENANTUSERREQUEST._serialized_start=1785 - _DELETETENANTUSERREQUEST._serialized_end=1846 - _LISTTENANTUSERSREQUEST._serialized_start=1848 - _LISTTENANTUSERSREQUEST._serialized_end=1922 - _LISTTENANTUSERSRESPONSE._serialized_start=1924 - _LISTTENANTUSERSRESPONSE._serialized_end=2011 - _TENANTSERVICE._serialized_start=2014 - _TENANTSERVICE._serialized_end=3072 + _TENANT._serialized_end=328 + _TENANTLISTITEM._serialized_start=331 + _TENANTLISTITEM._serialized_end=609 + _CREATETENANTREQUEST._serialized_start=611 + _CREATETENANTREQUEST._serialized_end=661 + _CREATETENANTRESPONSE._serialized_start=663 + _CREATETENANTRESPONSE._serialized_end=697 + _GETTENANTREQUEST._serialized_start=699 + _GETTENANTREQUEST._serialized_end=729 + _GETTENANTRESPONSE._serialized_start=732 + _GETTENANTRESPONSE._serialized_end=876 + _UPDATETENANTREQUEST._serialized_start=878 + _UPDATETENANTREQUEST._serialized_end=928 + _DELETETENANTREQUEST._serialized_start=930 + _DELETETENANTREQUEST._serialized_end=963 + _LISTTENANTSREQUEST._serialized_start=965 + _LISTTENANTSREQUEST._serialized_end=1049 + _LISTTENANTSRESPONSE._serialized_start=1051 + _LISTTENANTSRESPONSE._serialized_end=1130 + _TENANTUSER._serialized_start=1133 + _TENANTUSER._serialized_end=1265 + _TENANTUSERLISTITEM._serialized_start=1268 + _TENANTUSERLISTITEM._serialized_end=1504 + _ADDTENANTUSERREQUEST._serialized_start=1506 + _ADDTENANTUSERREQUEST._serialized_end=1566 + _GETTENANTUSERREQUEST._serialized_start=1568 + _GETTENANTUSERREQUEST._serialized_end=1626 + _GETTENANTUSERRESPONSE._serialized_start=1629 + _GETTENANTUSERRESPONSE._serialized_end=1786 + _UPDATETENANTUSERREQUEST._serialized_start=1788 + _UPDATETENANTUSERREQUEST._serialized_end=1851 + _DELETETENANTUSERREQUEST._serialized_start=1853 + _DELETETENANTUSERREQUEST._serialized_end=1914 + _LISTTENANTUSERSREQUEST._serialized_start=1916 + _LISTTENANTUSERSREQUEST._serialized_end=1990 + _LISTTENANTUSERSRESPONSE._serialized_start=1992 + _LISTTENANTUSERSRESPONSE._serialized_end=2079 + _TENANTSERVICE._serialized_start=2082 + _TENANTSERVICE._serialized_end=3140 # @@protoc_insertion_point(module_scope) diff --git a/api/python/src/chirpstack_api/api/tenant_pb2.pyi b/api/python/src/chirpstack_api/api/tenant_pb2.pyi index bd298dbc..a52ecee2 100644 --- a/api/python/src/chirpstack_api/api/tenant_pb2.pyi +++ b/api/python/src/chirpstack_api/api/tenant_pb2.pyi @@ -113,32 +113,35 @@ class ListTenantsResponse(_message.Message): def __init__(self, total_count: _Optional[int] = ..., result: _Optional[_Iterable[_Union[TenantListItem, _Mapping]]] = ...) -> None: ... class Tenant(_message.Message): - __slots__ = ["can_have_gateways", "description", "id", "max_device_count", "max_gateway_count", "name", "private_gateways"] + __slots__ = ["can_have_gateways", "description", "id", "max_device_count", "max_gateway_count", "name", "private_gateways_down", "private_gateways_up"] CAN_HAVE_GATEWAYS_FIELD_NUMBER: _ClassVar[int] DESCRIPTION_FIELD_NUMBER: _ClassVar[int] ID_FIELD_NUMBER: _ClassVar[int] MAX_DEVICE_COUNT_FIELD_NUMBER: _ClassVar[int] MAX_GATEWAY_COUNT_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] - PRIVATE_GATEWAYS_FIELD_NUMBER: _ClassVar[int] + PRIVATE_GATEWAYS_DOWN_FIELD_NUMBER: _ClassVar[int] + PRIVATE_GATEWAYS_UP_FIELD_NUMBER: _ClassVar[int] can_have_gateways: bool description: str id: str max_device_count: int max_gateway_count: int name: str - private_gateways: bool - def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., description: _Optional[str] = ..., can_have_gateways: bool = ..., max_gateway_count: _Optional[int] = ..., max_device_count: _Optional[int] = ..., private_gateways: bool = ...) -> None: ... + private_gateways_down: bool + private_gateways_up: bool + def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., description: _Optional[str] = ..., can_have_gateways: bool = ..., max_gateway_count: _Optional[int] = ..., max_device_count: _Optional[int] = ..., private_gateways_up: bool = ..., private_gateways_down: bool = ...) -> None: ... class TenantListItem(_message.Message): - __slots__ = ["can_have_gateways", "created_at", "id", "max_device_count", "max_gateway_count", "name", "private_gateways", "updated_at"] + __slots__ = ["can_have_gateways", "created_at", "id", "max_device_count", "max_gateway_count", "name", "private_gateways_down", "private_gateways_up", "updated_at"] CAN_HAVE_GATEWAYS_FIELD_NUMBER: _ClassVar[int] CREATED_AT_FIELD_NUMBER: _ClassVar[int] ID_FIELD_NUMBER: _ClassVar[int] MAX_DEVICE_COUNT_FIELD_NUMBER: _ClassVar[int] MAX_GATEWAY_COUNT_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] - PRIVATE_GATEWAYS_FIELD_NUMBER: _ClassVar[int] + PRIVATE_GATEWAYS_DOWN_FIELD_NUMBER: _ClassVar[int] + PRIVATE_GATEWAYS_UP_FIELD_NUMBER: _ClassVar[int] UPDATED_AT_FIELD_NUMBER: _ClassVar[int] can_have_gateways: bool created_at: _timestamp_pb2.Timestamp @@ -146,9 +149,10 @@ class TenantListItem(_message.Message): max_device_count: int max_gateway_count: int name: str - private_gateways: bool + private_gateways_down: bool + private_gateways_up: bool updated_at: _timestamp_pb2.Timestamp - def __init__(self, id: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., name: _Optional[str] = ..., can_have_gateways: bool = ..., private_gateways: bool = ..., max_gateway_count: _Optional[int] = ..., max_device_count: _Optional[int] = ...) -> None: ... + def __init__(self, id: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., name: _Optional[str] = ..., can_have_gateways: bool = ..., private_gateways_up: bool = ..., private_gateways_down: bool = ..., max_gateway_count: _Optional[int] = ..., max_device_count: _Optional[int] = ...) -> None: ... class TenantUser(_message.Message): __slots__ = ["email", "is_admin", "is_device_admin", "is_gateway_admin", "tenant_id", "user_id"] diff --git a/api/rust/proto/chirpstack/api/tenant.proto b/api/rust/proto/chirpstack/api/tenant.proto index bd942790..809fb88d 100644 --- a/api/rust/proto/chirpstack/api/tenant.proto +++ b/api/rust/proto/chirpstack/api/tenant.proto @@ -112,9 +112,16 @@ message Tenant { // When set to 0, the tenant can have unlimited devices. uint32 max_device_count = 6; - // Private gateways. - // Gateways under this tenant are private. - bool private_gateways = 7; + // Private gateways (uplink). + // If enabled, then uplink messages will not be shared with other tenants. + bool private_gateways_up = 7; + + // Private gateways (downlink). + // If enabled, then other tenants will not be able to schedule downlink + // messages through the gateways of this tenant. For example, in case you + // do want to share uplinks with other tenants (private_gateways_up=false), + // but you want to prevent other tenants from using gateway airtime. + bool private_gateways_down = 8; } message TenantListItem { @@ -133,8 +140,11 @@ message TenantListItem { // Can the tenant create and "own" Gateways? bool can_have_gateways = 5; - // Gateways are private to tenant. - bool private_gateways = 6; + // Private gateways (uplink). + bool private_gateways_up = 6; + + // Private gateways (downlink). + bool private_gateways_down = 9; // Max gateway count. // 0 = unlimited. diff --git a/api/rust/proto/chirpstack/internal/internal.proto b/api/rust/proto/chirpstack/internal/internal.proto index 0abe48d5..93823dfe 100644 --- a/api/rust/proto/chirpstack/internal/internal.proto +++ b/api/rust/proto/chirpstack/internal/internal.proto @@ -196,6 +196,15 @@ message DeviceGatewayRxInfoItem { // Context blob. bytes context = 6; + + // Gateway is private (uplink). + bool is_private_up = 7; + + // Gateway is private (downlink). + bool is_private_down = 8; + + // Tenant ID (UUID). + bytes tenant_id = 9; } message DownlinkFrame { diff --git a/chirpstack/migrations/2023-02-13-103316_update_private_gateways/down.sql b/chirpstack/migrations/2023-02-13-103316_update_private_gateways/down.sql new file mode 100644 index 00000000..22bd8ca8 --- /dev/null +++ b/chirpstack/migrations/2023-02-13-103316_update_private_gateways/down.sql @@ -0,0 +1,5 @@ +alter table tenant + drop column private_gateways_down; + +alter table tenant + rename column private_gateways_up to private_gateways; diff --git a/chirpstack/migrations/2023-02-13-103316_update_private_gateways/up.sql b/chirpstack/migrations/2023-02-13-103316_update_private_gateways/up.sql new file mode 100644 index 00000000..5d613dcb --- /dev/null +++ b/chirpstack/migrations/2023-02-13-103316_update_private_gateways/up.sql @@ -0,0 +1,8 @@ +alter table tenant + rename column private_gateways to private_gateways_up; + +alter table tenant + add column private_gateways_down boolean not null default false; + +alter table tenant + alter column private_gateways_down drop default; diff --git a/chirpstack/src/api/backend/mod.rs b/chirpstack/src/api/backend/mod.rs index 2a3ab06d..bd90bd73 100644 --- a/chirpstack/src/api/backend/mod.rs +++ b/chirpstack/src/api/backend/mod.rs @@ -238,7 +238,8 @@ async fn _handle_pr_start_req_join( phy_payload: phy, tx_info, rx_info_set: rx_info, - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name, region_config_id, @@ -269,7 +270,8 @@ async fn _handle_pr_start_req_data( phy_payload: phy, tx_info, rx_info_set: rx_info, - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name, region_config_id, @@ -444,7 +446,8 @@ async fn _handle_xmit_data_req( phy_payload: phy, tx_info, rx_info_set: rx_info, - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name, region_config_id, diff --git a/chirpstack/src/api/tenant.rs b/chirpstack/src/api/tenant.rs index 71a59ad3..535da9c1 100644 --- a/chirpstack/src/api/tenant.rs +++ b/chirpstack/src/api/tenant.rs @@ -47,7 +47,8 @@ impl TenantService for Tenant { can_have_gateways: req_tenant.can_have_gateways, max_device_count: req_tenant.max_device_count as i32, max_gateway_count: req_tenant.max_gateway_count as i32, - private_gateways: req_tenant.private_gateways, + private_gateways_up: req_tenant.private_gateways_up, + private_gateways_down: req_tenant.private_gateways_down, ..Default::default() }; @@ -86,7 +87,8 @@ impl TenantService for Tenant { can_have_gateways: t.can_have_gateways, max_gateway_count: t.max_gateway_count as u32, max_device_count: t.max_device_count as u32, - private_gateways: t.private_gateways, + private_gateways_up: t.private_gateways_up, + private_gateways_down: t.private_gateways_down, }), created_at: Some(helpers::datetime_to_prost_timestamp(&t.created_at)), updated_at: Some(helpers::datetime_to_prost_timestamp(&t.updated_at)), @@ -124,7 +126,8 @@ impl TenantService for Tenant { can_have_gateways: req_tenant.can_have_gateways, max_device_count: req_tenant.max_device_count as i32, max_gateway_count: req_tenant.max_gateway_count as i32, - private_gateways: req_tenant.private_gateways, + private_gateways_up: req_tenant.private_gateways_up, + private_gateways_down: req_tenant.private_gateways_down, ..Default::default() }) .await @@ -219,7 +222,8 @@ impl TenantService for Tenant { updated_at: Some(helpers::datetime_to_prost_timestamp(&t.updated_at)), name: t.name.clone(), can_have_gateways: t.can_have_gateways, - private_gateways: t.private_gateways, + private_gateways_up: t.private_gateways_up, + private_gateways_down: t.private_gateways_down, max_gateway_count: t.max_gateway_count as u32, max_device_count: t.max_device_count as u32, }) diff --git a/chirpstack/src/downlink/data.rs b/chirpstack/src/downlink/data.rs index 40584e7c..287d1c4f 100644 --- a/chirpstack/src/downlink/data.rs +++ b/chirpstack/src/downlink/data.rs @@ -217,6 +217,7 @@ impl Data { trace!("Selecting downlink gateway"); let gw_down = helpers::select_downlink_gateway( + Some(self.tenant.id), &self.device_session.region_config_id, self.network_conf.gateway_prefer_min_margin, self.device_gateway_rx_info.as_mut().unwrap(), diff --git a/chirpstack/src/downlink/helpers.rs b/chirpstack/src/downlink/helpers.rs index 1f7594ad..affb5d59 100644 --- a/chirpstack/src/downlink/helpers.rs +++ b/chirpstack/src/downlink/helpers.rs @@ -2,23 +2,49 @@ use std::str::FromStr; use anyhow::Result; use rand::seq::SliceRandom; +use uuid::Uuid; -use chirpstack_api::gw; +use chirpstack_api::{gw, internal}; use lrwn::region::DataRateModulation; use crate::config; use crate::region; // Returns the gateway to use for downlink. -// In the current implementation it will sort the given slice based on SNR / RSSI, -// and return: +// It will filter out private gateways (gateways from a different tenant ID, +// that do not allow downlinks). The result will be sorted based on SNR / RSSI. +// The returned value is: // * A random item from the elements with an SNR > minSNR // * The first item of the sorted slice (failing the above) +// * An error in case no gateways are available pub fn select_downlink_gateway( + tenant_id: Option, region_config_id: &str, min_snr_margin: f32, - rx_info: &mut chirpstack_api::internal::DeviceGatewayRxInfo, -) -> Result { + rx_info: &mut internal::DeviceGatewayRxInfo, +) -> Result { + rx_info.items = rx_info + .items + .iter() + .filter(|rx_info| { + if let Some(tenant_id) = &tenant_id { + if tenant_id.as_bytes().to_vec() == rx_info.tenant_id { + // The tenant is the same as the gateway tenant. + true + } else { + // If tenant_id is different, filter out rx_info elements that have + // is_private_down=true. + !rx_info.is_private_down + } + } else { + // If tenant_id is None, filter out rx_info elements that have + // is_private_down=true. + !rx_info.is_private_down + } + }) + .cloned() + .collect(); + if rx_info.items.is_empty() { return Err(anyhow!("rx_info.items can not be empty")); } @@ -96,11 +122,13 @@ mod tests { use std::collections::HashMap; use super::*; + use crate::storage::tenant; use crate::test; struct Test { min_snr_margin: f32, - rx_info: chirpstack_api::internal::DeviceGatewayRxInfo, + tenant_id: Option, + rx_info: internal::DeviceGatewayRxInfo, expected_gws: Vec>, } @@ -108,13 +136,21 @@ mod tests { async fn test_select_downlink_gateway() { let _guard = test::prepare().await; + let t = tenant::create(tenant::Tenant { + name: "test-tenant".into(), + ..Default::default() + }) + .await + .unwrap(); + let tests = vec![ // single item Test { + tenant_id: None, min_snr_margin: 0.0, - rx_info: chirpstack_api::internal::DeviceGatewayRxInfo { + rx_info: internal::DeviceGatewayRxInfo { dr: 0, - items: vec![chirpstack_api::internal::DeviceGatewayRxInfoItem { + items: vec![internal::DeviceGatewayRxInfoItem { lora_snr: -5.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], ..Default::default() @@ -125,16 +161,17 @@ mod tests { }, // two items, below min snr Test { + tenant_id: None, min_snr_margin: 5.0, - rx_info: chirpstack_api::internal::DeviceGatewayRxInfo { + rx_info: internal::DeviceGatewayRxInfo { dr: 2, // -15 is required items: vec![ - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -12.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], ..Default::default() }, - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -11.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], ..Default::default() @@ -146,16 +183,17 @@ mod tests { }, // two items, one below min snr Test { + tenant_id: None, min_snr_margin: 5.0, - rx_info: chirpstack_api::internal::DeviceGatewayRxInfo { + rx_info: internal::DeviceGatewayRxInfo { dr: 2, // -15 is required items: vec![ - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -12.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], ..Default::default() }, - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -10.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], ..Default::default() @@ -167,26 +205,27 @@ mod tests { }, // four items, two below min snr Test { + tenant_id: None, min_snr_margin: 5.0, - rx_info: chirpstack_api::internal::DeviceGatewayRxInfo { + rx_info: internal::DeviceGatewayRxInfo { dr: 2, // -15 is required items: vec![ - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -12.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], ..Default::default() }, - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -11.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], ..Default::default() }, - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -10.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03], ..Default::default() }, - chirpstack_api::internal::DeviceGatewayRxInfoItem { + internal::DeviceGatewayRxInfoItem { lora_snr: -9.0, gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04], ..Default::default() @@ -199,6 +238,74 @@ mod tests { vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04], ], }, + // is_private_down is set, first gateway matches tenant. + Test { + tenant_id: Some(t.id), + min_snr_margin: 0.0, + rx_info: internal::DeviceGatewayRxInfo { + items: vec![ + internal::DeviceGatewayRxInfoItem { + gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], + is_private_down: true, + tenant_id: t.id.as_bytes().to_vec(), + ..Default::default() + }, + internal::DeviceGatewayRxInfoItem { + gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], + is_private_down: true, + tenant_id: Uuid::new_v4().as_bytes().to_vec(), + ..Default::default() + }, + ], + ..Default::default() + }, + expected_gws: vec![vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]], + }, + // is_private_down is set, second gateway matches tenant. + Test { + tenant_id: Some(t.id), + min_snr_margin: 0.0, + rx_info: internal::DeviceGatewayRxInfo { + items: vec![ + internal::DeviceGatewayRxInfoItem { + gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], + is_private_down: true, + tenant_id: Uuid::new_v4().as_bytes().to_vec(), + ..Default::default() + }, + internal::DeviceGatewayRxInfoItem { + gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], + is_private_down: true, + tenant_id: t.id.as_bytes().to_vec(), + ..Default::default() + }, + ], + ..Default::default() + }, + expected_gws: vec![vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]], + }, + // is_private_down is set for one gateway, no tenant id given. + Test { + tenant_id: None, + min_snr_margin: 0.0, + rx_info: internal::DeviceGatewayRxInfo { + items: vec![ + internal::DeviceGatewayRxInfoItem { + gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], + is_private_down: true, + tenant_id: t.id.as_bytes().to_vec(), + ..Default::default() + }, + internal::DeviceGatewayRxInfoItem { + gateway_id: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], + is_private_down: false, + ..Default::default() + }, + ], + ..Default::default() + }, + expected_gws: vec![vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]], + }, ]; for test in &tests { @@ -211,8 +318,13 @@ mod tests { } for _ in 0..100 { - let out = - select_downlink_gateway(&"eu868", test.min_snr_margin, &mut rx_info).unwrap(); + let out = select_downlink_gateway( + test.tenant_id, + &"eu868", + test.min_snr_margin, + &mut rx_info, + ) + .unwrap(); gw_map.insert(out.gateway_id, ()); } diff --git a/chirpstack/src/downlink/join.rs b/chirpstack/src/downlink/join.rs index 967692f3..d8892275 100644 --- a/chirpstack/src/downlink/join.rs +++ b/chirpstack/src/downlink/join.rs @@ -1,20 +1,22 @@ +use std::str::FromStr; use std::sync::Arc; use anyhow::{Context, Result}; use rand::Rng; use tracing::{span, trace, Instrument, Level}; -use lrwn::PhyPayload; +use lrwn::{PhyPayload, EUI64}; use super::helpers; use crate::gateway::backend::send_downlink; -use crate::storage::{device, downlink_frame}; +use crate::storage::{device, downlink_frame, tenant}; use crate::uplink::UplinkFrameSet; use crate::{config, region}; use chirpstack_api::{gw, internal}; pub struct JoinAccept<'a> { uplink_frame_set: &'a UplinkFrameSet, + tenant: &'a tenant::Tenant, device: &'a device::Device, device_session: &'a internal::DeviceSession, join_accept: &'a PhyPayload, @@ -29,24 +31,27 @@ pub struct JoinAccept<'a> { impl JoinAccept<'_> { pub async fn handle( ufs: &UplinkFrameSet, + tenant: &tenant::Tenant, device: &device::Device, device_session: &internal::DeviceSession, join_accept: &PhyPayload, ) -> Result<()> { let span = span!(Level::TRACE, "join_accept", downlink_id = %ufs.uplink_set_id); - let fut = JoinAccept::_handle(ufs, device, device_session, join_accept); + let fut = JoinAccept::_handle(ufs, tenant, device, device_session, join_accept); fut.instrument(span).await } async fn _handle( ufs: &UplinkFrameSet, + tenant: &tenant::Tenant, device: &device::Device, device_session: &internal::DeviceSession, join_accept: &PhyPayload, ) -> Result<()> { let mut ctx = JoinAccept { uplink_frame_set: ufs, + tenant, device, device_session, join_accept, @@ -74,26 +79,45 @@ impl JoinAccept<'_> { fn set_device_gateway_rx_info(&mut self) -> Result<()> { trace!("Set device-gateway rx-info"); - let mut d_gw_rx_info = chirpstack_api::internal::DeviceGatewayRxInfo { + self.device_gateway_rx_info = Some(internal::DeviceGatewayRxInfo { dev_eui: self.device.dev_eui.to_be_bytes().to_vec(), dr: self.uplink_frame_set.dr as u32, - items: vec![], - }; + items: self + .uplink_frame_set + .rx_info_set + .iter() + .map(|rx_info| { + let gw_id = EUI64::from_str(&rx_info.gateway_id).unwrap_or_default(); - for rx_info in &self.uplink_frame_set.rx_info_set { - d_gw_rx_info - .items - .push(chirpstack_api::internal::DeviceGatewayRxInfoItem { - gateway_id: hex::decode(&rx_info.gateway_id)?, - rssi: rx_info.rssi, - lora_snr: rx_info.snr, - antenna: rx_info.antenna, - board: rx_info.board, - context: rx_info.context.clone(), - }); - } - - self.device_gateway_rx_info = Some(d_gw_rx_info); + internal::DeviceGatewayRxInfoItem { + gateway_id: gw_id.to_vec(), + rssi: rx_info.rssi, + lora_snr: rx_info.snr, + antenna: rx_info.antenna, + board: rx_info.board, + context: rx_info.context.clone(), + is_private_up: self + .uplink_frame_set + .gateway_private_up_map + .get(&gw_id) + .cloned() + .unwrap_or_default(), + is_private_down: self + .uplink_frame_set + .gateway_private_down_map + .get(&gw_id) + .cloned() + .unwrap_or_default(), + tenant_id: self + .uplink_frame_set + .gateway_tenant_id_map + .get(&gw_id) + .map(|v| v.into_bytes().to_vec()) + .unwrap_or_else(|| Vec::new()), + } + }) + .collect(), + }); Ok(()) } @@ -102,6 +126,7 @@ impl JoinAccept<'_> { trace!("Select downlink gateway"); let gw_down = helpers::select_downlink_gateway( + Some(self.tenant.id), &self.uplink_frame_set.region_config_id, self.network_conf.gateway_prefer_min_margin, self.device_gateway_rx_info.as_mut().unwrap(), diff --git a/chirpstack/src/downlink/roaming.rs b/chirpstack/src/downlink/roaming.rs index 7b55c2cc..92c8124b 100644 --- a/chirpstack/src/downlink/roaming.rs +++ b/chirpstack/src/downlink/roaming.rs @@ -1,3 +1,4 @@ +use std::str::FromStr; use std::sync::Arc; use anyhow::{Context, Result}; @@ -10,6 +11,7 @@ use crate::uplink::UplinkFrameSet; use crate::{config, gateway, region}; use backend::DLMetaData; use chirpstack_api::{gw, internal}; +use lrwn::EUI64; pub struct PassiveRoamingDownlink { uplink_frame_set: UplinkFrameSet, @@ -63,18 +65,41 @@ impl PassiveRoamingDownlink { .uplink_frame_set .rx_info_set .iter() - .map(|rx_info| internal::DeviceGatewayRxInfoItem { - gateway_id: hex::decode(&rx_info.gateway_id).unwrap(), - rssi: rx_info.rssi, - lora_snr: rx_info.snr, - antenna: rx_info.antenna, - board: rx_info.board, - context: rx_info.context.clone(), + .map(|rx_info| { + let gw_id = EUI64::from_str(&rx_info.gateway_id).unwrap_or_default(); + + internal::DeviceGatewayRxInfoItem { + gateway_id: gw_id.to_vec(), + rssi: rx_info.rssi, + lora_snr: rx_info.snr, + antenna: rx_info.antenna, + board: rx_info.board, + context: rx_info.context.clone(), + is_private_up: self + .uplink_frame_set + .gateway_private_up_map + .get(&gw_id) + .cloned() + .unwrap_or_default(), + is_private_down: self + .uplink_frame_set + .gateway_private_down_map + .get(&gw_id) + .cloned() + .unwrap_or_default(), + tenant_id: self + .uplink_frame_set + .gateway_tenant_id_map + .get(&gw_id) + .map(|v| v.into_bytes().to_vec()) + .unwrap_or_else(|| Vec::new()), + } }) .collect(), }; let gw_down = helpers::select_downlink_gateway( + None, &self.uplink_frame_set.region_config_id, self.network_conf.gateway_prefer_min_margin, &mut dev_gw_rx_info, diff --git a/chirpstack/src/maccommand/dev_status.rs b/chirpstack/src/maccommand/dev_status.rs index 4f869ee0..793214ac 100644 --- a/chirpstack/src/maccommand/dev_status.rs +++ b/chirpstack/src/maccommand/dev_status.rs @@ -139,7 +139,8 @@ pub mod test { time: Some(rx_time.into()), ..Default::default() }], - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name: lrwn::region::CommonName::EU868, region_config_id: "eu868".into(), diff --git a/chirpstack/src/maccommand/device_time.rs b/chirpstack/src/maccommand/device_time.rs index d589723b..1f2ed7a2 100644 --- a/chirpstack/src/maccommand/device_time.rs +++ b/chirpstack/src/maccommand/device_time.rs @@ -68,7 +68,8 @@ pub mod test { time: Some(rx_time.into()), ..Default::default() }], - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name: lrwn::region::CommonName::EU868, region_config_id: "eu868".into(), diff --git a/chirpstack/src/maccommand/link_adr.rs b/chirpstack/src/maccommand/link_adr.rs index a9e7dd1a..3e1ee03c 100644 --- a/chirpstack/src/maccommand/link_adr.rs +++ b/chirpstack/src/maccommand/link_adr.rs @@ -336,7 +336,8 @@ pub mod test { }, tx_info: Default::default(), rx_info_set: vec![], - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name: lrwn::region::CommonName::EU868, region_config_id: "eu868".into(), diff --git a/chirpstack/src/maccommand/link_check.rs b/chirpstack/src/maccommand/link_check.rs index e096ac72..f92771ee 100644 --- a/chirpstack/src/maccommand/link_check.rs +++ b/chirpstack/src/maccommand/link_check.rs @@ -97,7 +97,8 @@ pub mod test { ..Default::default() }, ], - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), region_common_name: lrwn::region::CommonName::EU868, region_config_id: "eu868".into(), diff --git a/chirpstack/src/maccommand/mod.rs b/chirpstack/src/maccommand/mod.rs index 812f5865..93569f79 100644 --- a/chirpstack/src/maccommand/mod.rs +++ b/chirpstack/src/maccommand/mod.rs @@ -181,7 +181,8 @@ pub mod test { }, tx_info: Default::default(), rx_info_set: Default::default(), - gateway_private_map: Default::default(), + gateway_private_up_map: Default::default(), + gateway_private_down_map: Default::default(), gateway_tenant_id_map: Default::default(), region_common_name: lrwn::region::CommonName::EU868, region_config_id: "eu868".into(), diff --git a/chirpstack/src/storage/device.rs b/chirpstack/src/storage/device.rs index 3174b67a..44a47ad9 100644 --- a/chirpstack/src/storage/device.rs +++ b/chirpstack/src/storage/device.rs @@ -137,7 +137,8 @@ pub async fn create(d: Device) -> Result { tenant::dsl::can_have_gateways, tenant::dsl::max_device_count, tenant::dsl::max_gateway_count, - tenant::dsl::private_gateways, + tenant::dsl::private_gateways_up, + tenant::dsl::private_gateways_down, )) .inner_join(application::table) .filter(application::dsl::id.eq(&d.application_id)) diff --git a/chirpstack/src/storage/gateway.rs b/chirpstack/src/storage/gateway.rs index 2182b125..0a25c115 100644 --- a/chirpstack/src/storage/gateway.rs +++ b/chirpstack/src/storage/gateway.rs @@ -64,7 +64,8 @@ pub struct GatewayMeta { pub latitude: f64, pub longitude: f64, pub altitude: f32, - pub is_private: bool, + pub is_private_up: bool, + pub is_private_down: bool, } #[derive(Default, Clone)] @@ -396,7 +397,8 @@ pub async fn get_meta(gateway_id: &EUI64) -> Result { gateway::latitude, gateway::longitude, gateway::altitude, - tenant::private_gateways, + tenant::private_gateways_up, + tenant::private_gateways_down, )) .filter(gateway::dsl::gateway_id.eq(&gateway_id)) .first(&mut c) diff --git a/chirpstack/src/storage/schema.rs b/chirpstack/src/storage/schema.rs index fc09092e..81b73175 100644 --- a/chirpstack/src/storage/schema.rs +++ b/chirpstack/src/storage/schema.rs @@ -236,7 +236,8 @@ diesel::table! { can_have_gateways -> Bool, max_device_count -> Int4, max_gateway_count -> Int4, - private_gateways -> Bool, + private_gateways_up -> Bool, + private_gateways_down -> Bool, } } diff --git a/chirpstack/src/storage/tenant.rs b/chirpstack/src/storage/tenant.rs index 7847a359..3daab72e 100644 --- a/chirpstack/src/storage/tenant.rs +++ b/chirpstack/src/storage/tenant.rs @@ -21,7 +21,8 @@ pub struct Tenant { pub can_have_gateways: bool, pub max_device_count: i32, pub max_gateway_count: i32, - pub private_gateways: bool, + pub private_gateways_up: bool, + pub private_gateways_down: bool, } impl Tenant { @@ -46,7 +47,8 @@ impl Default for Tenant { can_have_gateways: false, max_device_count: 0, max_gateway_count: 0, - private_gateways: false, + private_gateways_up: false, + private_gateways_down: false, } } } @@ -141,7 +143,8 @@ pub async fn update(t: Tenant) -> Result { tenant::can_have_gateways.eq(&t.can_have_gateways), tenant::max_device_count.eq(&t.max_device_count), tenant::max_gateway_count.eq(&t.max_gateway_count), - tenant::private_gateways.eq(&t.private_gateways), + tenant::private_gateways_up.eq(&t.private_gateways_up), + tenant::private_gateways_down.eq(&t.private_gateways_down), )) .get_result(&mut c) .map_err(|e| Error::from_diesel(e, t.id.to_string())) @@ -403,7 +406,8 @@ pub mod test { can_have_gateways: true, max_device_count: 20, max_gateway_count: 10, - private_gateways: true, + private_gateways_up: true, + private_gateways_down: true, }; create(t).await.unwrap() } diff --git a/chirpstack/src/test/class_a_test.rs b/chirpstack/src/test/class_a_test.rs index 28e79605..9b33bf43 100644 --- a/chirpstack/src/test/class_a_test.rs +++ b/chirpstack/src/test/class_a_test.rs @@ -33,7 +33,7 @@ async fn test_gateway_filtering() { let t_a = tenant::create(tenant::Tenant { name: "tenant-a".into(), - private_gateways: true, + private_gateways_up: true, can_have_gateways: true, ..Default::default() }) @@ -41,7 +41,7 @@ async fn test_gateway_filtering() { .unwrap(); let t_b = tenant::create(tenant::Tenant { name: "tenant-b".into(), - private_gateways: true, + private_gateways_up: true, can_have_gateways: true, ..Default::default() }) diff --git a/chirpstack/src/test/otaa_test.rs b/chirpstack/src/test/otaa_test.rs index b2d3f71c..9b2292fe 100644 --- a/chirpstack/src/test/otaa_test.rs +++ b/chirpstack/src/test/otaa_test.rs @@ -31,7 +31,7 @@ async fn test_gateway_filtering() { let _guard = test::prepare().await; let t_a = tenant::create(tenant::Tenant { name: "tenant-a".into(), - private_gateways: true, + private_gateways_up: true, can_have_gateways: true, ..Default::default() }) @@ -39,7 +39,7 @@ async fn test_gateway_filtering() { .unwrap(); let t_b = tenant::create(tenant::Tenant { name: "tenant-b".into(), - private_gateways: true, + private_gateways_up: true, can_have_gateways: true, ..Default::default() }) diff --git a/chirpstack/src/uplink/data.rs b/chirpstack/src/uplink/data.rs index 35df0fd4..9a4c622a 100644 --- a/chirpstack/src/uplink/data.rs +++ b/chirpstack/src/uplink/data.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::str::FromStr; use anyhow::{Context, Result}; use chrono::{DateTime, Duration, Local, Utc}; @@ -18,7 +19,7 @@ use crate::storage::{ }; use crate::{codec, config, downlink, framelog, integration, maccommand, metalog, region}; use chirpstack_api::{api, common, integration as integration_pb, internal, meta}; -use lrwn::AES128Key; +use lrwn::{AES128Key, EUI64}; pub struct Data { uplink_frame_set: UplinkFrameSet, @@ -260,13 +261,35 @@ impl Data { .uplink_frame_set .rx_info_set .iter() - .map(|rx_info| internal::DeviceGatewayRxInfoItem { - gateway_id: hex::decode(&rx_info.gateway_id).unwrap(), - rssi: rx_info.rssi, - lora_snr: rx_info.snr, - antenna: rx_info.antenna, - board: rx_info.board, - context: rx_info.context.clone(), + .map(|rx_info| { + let gw_id = EUI64::from_str(&rx_info.gateway_id).unwrap_or_default(); + + internal::DeviceGatewayRxInfoItem { + gateway_id: gw_id.to_vec(), + rssi: rx_info.rssi, + lora_snr: rx_info.snr, + antenna: rx_info.antenna, + board: rx_info.board, + context: rx_info.context.clone(), + is_private_up: self + .uplink_frame_set + .gateway_private_up_map + .get(&gw_id) + .cloned() + .unwrap_or_default(), + is_private_down: self + .uplink_frame_set + .gateway_private_down_map + .get(&gw_id) + .cloned() + .unwrap_or_default(), + tenant_id: self + .uplink_frame_set + .gateway_tenant_id_map + .get(&gw_id) + .map(|v| v.into_bytes().to_vec()) + .unwrap_or_else(|| Vec::new()), + } }) .collect(), }); diff --git a/chirpstack/src/uplink/join.rs b/chirpstack/src/uplink/join.rs index 6d88e8f6..be2ac819 100644 --- a/chirpstack/src/uplink/join.rs +++ b/chirpstack/src/uplink/join.rs @@ -730,6 +730,7 @@ impl JoinRequest { trace!("Starting downlink join-accept flow"); downlink::join::JoinAccept::handle( &self.uplink_frame_set, + self.tenant.as_ref().unwrap(), self.device.as_ref().unwrap(), self.device_session.as_ref().unwrap(), self.join_accept.as_ref().unwrap(), diff --git a/chirpstack/src/uplink/mod.rs b/chirpstack/src/uplink/mod.rs index a1471f4f..1d300cd7 100644 --- a/chirpstack/src/uplink/mod.rs +++ b/chirpstack/src/uplink/mod.rs @@ -36,7 +36,8 @@ pub struct UplinkFrameSet { pub phy_payload: PhyPayload, pub tx_info: gw::UplinkTxInfo, pub rx_info_set: Vec, - pub gateway_private_map: HashMap, + pub gateway_private_up_map: HashMap, + pub gateway_private_down_map: HashMap, pub gateway_tenant_id_map: HashMap, pub region_common_name: CommonName, pub region_config_id: String, @@ -271,7 +272,8 @@ pub async fn handle_uplink(deduplication_id: Uuid, uplink: gw::UplinkFrameSet) - phy_payload: PhyPayload::from_slice(&uplink.phy_payload)?, tx_info: uplink.tx_info.context("tx_info must not be None")?, rx_info_set: uplink.rx_info, - gateway_private_map: HashMap::new(), + gateway_private_up_map: HashMap::new(), + gateway_private_down_map: HashMap::new(), gateway_tenant_id_map: HashMap::new(), roaming_meta_data: None, }; @@ -320,7 +322,8 @@ async fn update_gateway_metadata(ufs: &mut UplinkFrameSet) -> Result<()> { Err(e) => { if conf.gateway.allow_unknown_gateways { if let StorageError::NotFound(_) = e { - ufs.gateway_private_map.insert(gw_id, false); + ufs.gateway_private_up_map.insert(gw_id, false); + ufs.gateway_private_down_map.insert(gw_id, false); continue; } } @@ -341,7 +344,10 @@ async fn update_gateway_metadata(ufs: &mut UplinkFrameSet) -> Result<()> { ..Default::default() }); - ufs.gateway_private_map.insert(gw_id, gw_meta.is_private); + ufs.gateway_private_up_map + .insert(gw_id, gw_meta.is_private_up); + ufs.gateway_private_down_map + .insert(gw_id, gw_meta.is_private_down); ufs.gateway_tenant_id_map.insert(gw_id, gw_meta.tenant_id); } @@ -361,9 +367,9 @@ fn filter_rx_info_by_tenant_id(tenant_id: &Uuid, uplink: &mut UplinkFrameSet) -> let force_gws_private = config::get_force_gws_private(®ion_config_id)?; if !(*uplink - .gateway_private_map + .gateway_private_up_map .get(&gateway_id) - .ok_or_else(|| anyhow!("gateway_id missing in gateway_private_map"))? + .ok_or_else(|| anyhow!("gateway_id missing in gateway_private_up_map"))? || force_gws_private) || uplink .gateway_tenant_id_map @@ -389,9 +395,9 @@ fn filter_rx_info_by_public_only(uplink: &mut UplinkFrameSet) -> Result<()> { for rx_info in &uplink.rx_info_set { let gateway_id = EUI64::from_str(&rx_info.gateway_id)?; if !(*uplink - .gateway_private_map + .gateway_private_up_map .get(&gateway_id) - .ok_or_else(|| anyhow!("gateway_id missing in gateway_private_map"))?) + .ok_or_else(|| anyhow!("gateway_id missing in gateway_private_up_map"))?) { rx_info_set.push(rx_info.clone()); } diff --git a/ui/src/views/tenants/ListTenants.tsx b/ui/src/views/tenants/ListTenants.tsx index ac876201..15127900 100644 --- a/ui/src/views/tenants/ListTenants.tsx +++ b/ui/src/views/tenants/ListTenants.tsx @@ -37,12 +37,25 @@ class ListTenants extends Component { }, }, { - title: "Private gateways", - dataIndex: "privateGateways", - key: "privateGateways", + title: "Private gateways (uplink)", + dataIndex: "privateGatewaysUp", + key: "privateGatewaysUp", width: 250, render: (text, record) => { - if (record.privateGateways) { + if (record.privateGatewaysUp) { + return "yes"; + } else { + return "no"; + } + }, + }, + { + title: "Private gateways (down)", + dataIndex: "privateGatewaysDown", + key: "privateGatewaysDown", + width: 250, + render: (text, record) => { + if (record.privateGatewaysDown) { return "yes"; } else { return "no"; diff --git a/ui/src/views/tenants/TenantForm.tsx b/ui/src/views/tenants/TenantForm.tsx index 597b666f..8b797bb0 100644 --- a/ui/src/views/tenants/TenantForm.tsx +++ b/ui/src/views/tenants/TenantForm.tsx @@ -23,7 +23,8 @@ class TenantForm extends Component { tenant.setCanHaveGateways(values.canHaveGateways); tenant.setMaxGatewayCount(values.maxGatewayCount); tenant.setMaxDeviceCount(values.maxDeviceCount); - tenant.setPrivateGateways(values.privateGateways); + tenant.setPrivateGatewaysUp(values.privateGatewaysUp); + tenant.setPrivateGatewaysDown(values.privateGatewaysDown); this.props.onFinish(tenant); }; @@ -38,7 +39,7 @@ class TenantForm extends Component { - + { - + + + + + +