feat(api): new settings service (#5775)

* feat: add v2alpha policies service

* feat: add v2alpha policies service

* fix: rename of attributes and messages in v2alpha api

* fix: rename of attributes and messages in v2alpha api

* fix: linter corrections

* fix: review corrections

* fix: review corrections

* fix: review corrections

* fix: review corrections

* fix grpc

* refactor: rename to settings and more

* Apply suggestions from code review

Co-authored-by: Fabi <fabienne.gerschwiler@gmail.com>

* add service to docs and rename legal settings

* unit tests for converters

* go mod tidy

* ensure idp name and return list details

* fix: use correct resource owner for active idps

* change query to join

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
Co-authored-by: Fabi <fabienne.gerschwiler@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
Stefan Benz
2023-05-11 11:23:40 +02:00
committed by GitHub
parent c07411e314
commit 8d13f170e8
22 changed files with 1720 additions and 39 deletions

View File

@@ -15,6 +15,13 @@ message Organisation {
}
}
message RequestContext {
oneof resource_owner {
string org_id = 1;
bool instance = 2 [(validate.rules).bool = {const: true}];
}
}
message ListQuery {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {

View File

@@ -0,0 +1,81 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
import "zitadel/settings/v2alpha/settings.proto";
message BrandingSettings {
Theme light_theme = 1;
Theme dark_theme = 2;
string font_url = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "url to the font used";
example: "\"https://acme.com/assets/v1/165617850692654601/policy/label/font-180950243237405441\"";
}
];
// hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set
bool hide_login_name_suffix = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set";
}
];
bool disable_watermark = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "boolean to disable the watermark";
}
];
// resource_owner_type returns if the setting is managed on the organization or on the instance
ResourceOwnerType resource_owner_type = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "resource_owner_type returns if the setting is managed on the organization or on the instance";
}
];
}
message Theme {
// hex value for primary color
string primary_color = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "hex value for primary color";
example: "\"#5469d4\"";
}
];
// hex value for background color
string background_color = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "hex value for background color";
example: "\"#FAFAFA\"";
}
];
// hex value for warning color
string warn_color = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "hex value for warn color";
example: "\"#CD3D56\"";
}
];
// hex value for font color
string font_color = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "hex value for font color";
example: "\"#000000\"";
}
];
// url where the logo is served
string logo_url = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "url to the logo";
example: "\"https://acme.com/assets/v1/165617850692654601/policy/label/logo-180950416321494657\"";
}
];
// url where the icon is served
string icon_url = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "url to the icon";
example: "\"https://acme.com/assets/v1/165617850692654601/policy/label/icon-180950498874178817\"";
}
];
}

View File

@@ -0,0 +1,33 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
import "zitadel/settings/v2alpha/settings.proto";
message DomainSettings {
bool login_name_includes_domain = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "the username has to end with the domain of its organization"
}
];
bool require_org_domain_verification = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if organization domains should be verified upon creation, otherwise will be created already verified"
}
];
bool smtp_sender_address_matches_instance_domain = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the SMTP sender address domain should match an existing domain on the instance"
}
];
// resource_owner_type returns if the setting is managed on the organization or on the instance
ResourceOwnerType resource_owner_type = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "resource_owner_type returns if the setting is managed on the organization or on the instance";
}
];
}

View File

@@ -0,0 +1,40 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
import "zitadel/settings/v2alpha/settings.proto";
import "validate/validate.proto";
message LegalAndSupportSettings {
string tos_link = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://zitadel.com/docs/legal/terms-of-service\"";
}
];
string privacy_policy_link = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://zitadel.com/docs/legal/privacy-policy\"";
}
];
string help_link = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://zitadel.com/docs/manuals/introduction\"";
}
];
string support_email = 4 [
(validate.rules).string = {ignore_empty: true, max_len: 320, email: true},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"support-email@test.com\"";
description: "help / support email address."
}
];
// resource_owner_type returns if the setting is managed on the organization or on the instance
ResourceOwnerType resource_owner_type = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "resource_owner_type returns if the setting is managed on the organization or on the instance";
}
];
}

View File

@@ -0,0 +1,23 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
import "zitadel/settings/v2alpha/settings.proto";
message LockoutSettings {
uint64 max_password_attempts = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correctly or the password is reset. If set to 0 the account will never be locked."
example: "\"10\""
}
];
// resource_owner_type returns if the settings is managed on the organization or on the instance
ResourceOwnerType resource_owner_type = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "resource_owner_type returns if the settings is managed on the organization or on the instance";
}
];
}

View File

@@ -0,0 +1,143 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
import "zitadel/settings/v2alpha/settings.proto";
import "google/protobuf/duration.proto";
message LoginSettings {
bool allow_username_password = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if a user is allowed to log in with his username and password";
}
];
bool allow_register = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if a person is allowed to register a user on this organization";
}
];
bool allow_external_idp = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if a user is allowed to add a defined identity provider. E.g. Google auth";
}
];
bool force_mfa = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if a user MUST use a multi-factor to log in";
}
];
PasskeysType passkeys_type = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if passkeys are allowed for users"
}
];
bool hide_password_reset = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if password reset link should be shown in the login screen"
}
];
bool ignore_unknown_usernames = 7 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if unknown username on login screen directly returns an error or always displays the password screen"
}
];
string default_redirect_uri = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)";
example: "\"https://acme.com/ui/console\"";
}
];
google.protobuf.Duration password_check_lifetime = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Defines after how much time the user has to re-authenticate with the password.";
example: "\"864000s\"";
}
];
google.protobuf.Duration external_login_check_lifetime = 10 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Defines after how much time the user has to re-authenticate with an external provider.";
example: "\"864000s\"";
}
];
google.protobuf.Duration mfa_init_skip_lifetime = 11 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Defines after how much time the mfa prompt will be shown again.";
example: "\"2592000s\"";
}
];
google.protobuf.Duration second_factor_check_lifetime = 12 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Defines after how long the second-factor check is valid.";
example: "\"64800s\"";
}
];
google.protobuf.Duration multi_factor_check_lifetime = 13 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Defines how long the multi-factor check is valid.";
example: "\"43200s\"";
}
];
repeated SecondFactorType second_factors = 14;
repeated MultiFactorType multi_factors = 15;
// If set to true, the suffix (@domain.com) of an unknown username input on the login screen will be matched against the org domains and will redirect to the registration of that organization on success.
bool allow_domain_discovery = 16 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "If set to true, the suffix (@domain.com) of an unknown username input on the login screen will be matched against the org domains and will redirect to the registration of that organization on success."
}
];
bool disable_login_with_email = 17 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the user can additionally (to the login name) be identified by their verified email address"
}
];
bool disable_login_with_phone = 18 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the user can additionally (to the login name) be identified by their verified phone number"
}
];
// resource_owner_type returns if the settings is managed on the organization or on the instance
ResourceOwnerType resource_owner_type = 19 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "resource_owner_type returns if the settings is managed on the organization or on the instance";
}
];
}
enum SecondFactorType {
SECOND_FACTOR_TYPE_UNSPECIFIED = 0;
SECOND_FACTOR_TYPE_OTP = 1;
SECOND_FACTOR_TYPE_U2F = 2;
}
enum MultiFactorType {
MULTI_FACTOR_TYPE_UNSPECIFIED = 0;
MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION = 1;
}
enum PasskeysType {
PASSKEYS_TYPE_NOT_ALLOWED = 0;
PASSKEYS_TYPE_ALLOWED = 1;
}
message IdentityProvider {
string id = 1;
string name = 2;
IdentityProviderType type = 3;
}
enum IdentityProviderType {
IDENTITY_PROVIDER_TYPE_UNSPECIFIED = 0;
IDENTITY_PROVIDER_TYPE_OIDC = 1;
IDENTITY_PROVIDER_TYPE_JWT = 2;
IDENTITY_PROVIDER_TYPE_LDAP = 3;
IDENTITY_PROVIDER_TYPE_OAUTH = 4;
IDENTITY_PROVIDER_TYPE_AZURE_AD = 5;
IDENTITY_PROVIDER_TYPE_GITHUB = 6;
IDENTITY_PROVIDER_TYPE_GITHUB_ES = 7;
IDENTITY_PROVIDER_TYPE_GITLAB = 8;
IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED = 9;
IDENTITY_PROVIDER_TYPE_GOOGLE = 10;
}

View File

@@ -0,0 +1,43 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
import "zitadel/settings/v2alpha/settings.proto";
message PasswordComplexitySettings {
uint64 min_length = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Defines the minimum length of a password.";
example: "\"8\""
}
];
bool requires_uppercase = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the password MUST contain an upper case letter"
}
];
bool requires_lowercase = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the password MUST contain a lowercase letter"
}
];
bool requires_number = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the password MUST contain a number"
}
];
bool requires_symbol = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the password MUST contain a symbol. E.g. \"$\""
}
];
// resource_owner_type returns if the settings is managed on the organization or on the instance
ResourceOwnerType resource_owner_type = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "resource_owner_type returns if the settings is managed on the organization or on the instance";
}
];
}

View File

@@ -0,0 +1,13 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
import "protoc-gen-openapiv2/options/annotations.proto";
enum ResourceOwnerType {
RESOURCE_OWNER_TYPE_UNSPECIFIED = 0;
RESOURCE_OWNER_TYPE_INSTANCE = 1;
RESOURCE_OWNER_TYPE_ORG = 2;
}

View File

@@ -0,0 +1,356 @@
syntax = "proto3";
package zitadel.settings.v2alpha;
import "zitadel/protoc_gen_zitadel/v2/options.proto";
import "zitadel/object/v2alpha/object.proto";
import "zitadel/settings/v2alpha/branding_settings.proto";
import "zitadel/settings/v2alpha/domain_settings.proto";
import "zitadel/settings/v2alpha/legal_settings.proto";
import "zitadel/settings/v2alpha/lockout_settings.proto";
import "zitadel/settings/v2alpha/login_settings.proto";
import "zitadel/settings/v2alpha/password_settings.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha;settings";
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Settings Service";
version: "2.0-alpha";
description: "This API is intended to manage settings in a ZITADEL instance. This project is in alpha state. It can AND will continue breaking until the services provide the same functionality as the current login.";
contact:{
name: "ZITADEL"
url: "https://zitadel.com"
email: "hi@zitadel.com"
}
license: {
name: "Apache 2.0",
url: "https://github.com/zitadel/zitadel/blob/main/LICENSE";
};
};
schemes: HTTPS;
schemes: HTTP;
consumes: "application/json";
consumes: "application/grpc";
produces: "application/json";
produces: "application/grpc";
consumes: "application/grpc-web+proto";
produces: "application/grpc-web+proto";
host: "$ZITADEL_DOMAIN";
base_path: "/";
external_docs: {
description: "Detailed information about ZITADEL",
url: "https://zitadel.com/docs"
}
responses: {
key: "403";
value: {
description: "Returned when the user does not have permission to access the resource.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
responses: {
key: "404";
value: {
description: "Returned when the resource does not exist.";
schema: {
json_schema: {
ref: "#/definitions/rpcStatus";
}
}
}
}
};
service SettingsService {
// Get basic information over the instance
rpc GetGeneralSettings (GetGeneralSettingsRequest) returns (GetGeneralSettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get basic information over the instance";
description: "Return the basic information of the instance for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the login settings
rpc GetLoginSettings (GetLoginSettingsRequest) returns (GetLoginSettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/login"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the login settings";
description: "Return the settings for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the current active identity providers
rpc GetActiveIdentityProviders (GetActiveIdentityProvidersRequest) returns (GetActiveIdentityProvidersResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/login/idps"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the current active identity providers";
description: "Return the current active identity providers for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the password complexity settings
rpc GetPasswordComplexitySettings (GetPasswordComplexitySettingsRequest) returns (GetPasswordComplexitySettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/password/complexity"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the password complexity settings";
description: "Return the password complexity settings for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the current active branding settings
rpc GetBrandingSettings (GetBrandingSettingsRequest) returns (GetBrandingSettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/branding"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the current active branding settings";
description: "Return the current active branding settings for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the domain settings
rpc GetDomainSettings (GetDomainSettingsRequest) returns (GetDomainSettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/domain"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the domain settings";
description: "Return the domain settings for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the legal and support settings
rpc GetLegalAndSupportSettings (GetLegalAndSupportSettingsRequest) returns (GetLegalAndSupportSettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/legal_support"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the legal and support settings";
description: "Return the legal settings for the requested context"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
// Get the lockout settings
rpc GetLockoutSettings (GetLockoutSettingsRequest) returns (GetLockoutSettingsResponse) {
option (google.api.http) = {
get: "/v2alpha/settings/lockout"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "policy.read"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Get the lockout settings";
description: "Return the lockout settings for the requested context, which define when a user will be locked"
responses: {
key: "200"
value: {
description: "OK";
}
};
};
}
}
message GetLoginSettingsRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetLoginSettingsResponse {
zitadel.object.v2alpha.Details details = 1;
zitadel.settings.v2alpha.LoginSettings settings = 2;
}
message GetPasswordComplexitySettingsRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetPasswordComplexitySettingsResponse {
zitadel.object.v2alpha.Details details = 1;
zitadel.settings.v2alpha.PasswordComplexitySettings settings = 2;
}
message GetBrandingSettingsRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetBrandingSettingsResponse {
zitadel.object.v2alpha.Details details = 1;
zitadel.settings.v2alpha.BrandingSettings settings = 2;
}
message GetDomainSettingsRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetDomainSettingsResponse {
zitadel.object.v2alpha.Details details = 1;
zitadel.settings.v2alpha.DomainSettings settings = 2;
}
message GetLegalAndSupportSettingsRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetLegalAndSupportSettingsResponse {
zitadel.object.v2alpha.Details details = 1;
zitadel.settings.v2alpha.LegalAndSupportSettings settings = 2;
}
message GetLockoutSettingsRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetLockoutSettingsResponse {
zitadel.object.v2alpha.Details details = 1;
zitadel.settings.v2alpha.LockoutSettings settings = 2;
}
message GetActiveIdentityProvidersRequest {
zitadel.object.v2alpha.RequestContext ctx = 1;
}
message GetActiveIdentityProvidersResponse {
zitadel.object.v2alpha.ListDetails details = 1;
repeated zitadel.settings.v2alpha.IdentityProvider identity_providers = 2;
}
message GetGeneralSettingsRequest {}
message GetGeneralSettingsResponse {
string default_org_id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "default organization for the current context"
}
];
string default_language = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "default language for the current context"
example: "\"en\""
}
];
repeated string supported_languages = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[\"en\", \"de\", \"it\"]"
}
];
}